• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nats-io / nats-server / 24228026749

09 Apr 2026 02:28PM UTC coverage: 83.103% (+0.06%) from 83.041%
24228026749

push

github

web-flow
[FIXED] Stream leader can catchup from snapshot if required (#8021)

After scaling up a stream, a follower could have received a snapshot
(through `SendSnapshot`) but not have caught up from it. Based on its
log it could already become the new stream leader. When it got to
`processSnapshot` it would error with `errAlreadyLeader` due to
`n.PauseApply()` and then skip catchup.

This PR fixes that by always processing the incoming snapshot, even if
we're leader, since we check that we're up-to-date first and otherwise
step down and perform catch up. This will usually not happen on a
leader, but can happen under certain edge cases during scale up.

Resolves https://github.com/nats-io/nats-server/issues/8020

Signed-off-by: Maurice van Veen <github@mauricevanveen.com>

75953 of 91396 relevant lines covered (83.1%)

346121.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.31
/server/leafnode.go
1
// Copyright 2019-2025 The NATS Authors
2
// Licensed under the Apache License, Version 2.0 (the "License");
3
// you may not use this file except in compliance with the License.
4
// You may obtain a copy of the License at
5
//
6
// http://www.apache.org/licenses/LICENSE-2.0
7
//
8
// Unless required by applicable law or agreed to in writing, software
9
// distributed under the License is distributed on an "AS IS" BASIS,
10
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
// See the License for the specific language governing permissions and
12
// limitations under the License.
13

14
package server
15

16
import (
17
        "bufio"
18
        "bytes"
19
        "crypto/tls"
20
        "encoding/base64"
21
        "encoding/json"
22
        "fmt"
23
        "io"
24
        "math/rand"
25
        "net"
26
        "net/http"
27
        "net/url"
28
        "os"
29
        "path"
30
        "reflect"
31
        "regexp"
32
        "runtime"
33
        "strconv"
34
        "strings"
35
        "sync"
36
        "sync/atomic"
37
        "time"
38

39
        "github.com/klauspost/compress/s2"
40
        "github.com/nats-io/jwt/v2"
41
        "github.com/nats-io/nkeys"
42
        "github.com/nats-io/nuid"
43
)
44

45
const (
46
        // Warning when user configures leafnode TLS insecure
47
        leafnodeTLSInsecureWarning = "TLS certificate chain and hostname of solicited leafnodes will not be verified. DO NOT USE IN PRODUCTION!"
48

49
        // When a loop is detected, delay the reconnect of solicited connection.
50
        leafNodeReconnectDelayAfterLoopDetected = 30 * time.Second
51

52
        // When a server receives a message causing a permission violation, the
53
        // connection is closed and it won't attempt to reconnect for that long.
54
        leafNodeReconnectAfterPermViolation = 30 * time.Second
55

56
        // When we have the same cluster name as the hub.
57
        leafNodeReconnectDelayAfterClusterNameSame = 30 * time.Second
58

59
        // Prefix for loop detection subject
60
        leafNodeLoopDetectionSubjectPrefix = "$LDS."
61

62
        // Path added to URL to indicate to WS server that the connection is a
63
        // LEAF connection as opposed to a CLIENT.
64
        leafNodeWSPath = "/leafnode"
65

66
        // When a soliciting leafnode is rejected because it does not meet the
67
        // configured minimum version, delay the next reconnect attempt by this long.
68
        leafNodeMinVersionReconnectDelay = 5 * time.Second
69
)
70

71
type leaf struct {
72
        // We have any auth stuff here for solicited connections.
73
        remote *leafNodeCfg
74
        // isSpoke tells us what role we are playing.
75
        // Used when we receive a connection but otherside tells us they are a hub.
76
        isSpoke bool
77
        // remoteCluster is when we are a hub but the spoke leafnode is part of a cluster.
78
        remoteCluster string
79
        // remoteServer holds onto the remote server's name or ID.
80
        remoteServer string
81
        // domain name of remote server
82
        remoteDomain string
83
        // account name of remote server
84
        remoteAccName string
85
        // Whether or not we want to propagate east-west interest from other LNs.
86
        isolated bool
87
        // Used to suppress sub and unsub interest. Same as routes but our audience
88
        // here is tied to this leaf node. This will hold all subscriptions except this
89
        // leaf nodes. This represents all the interest we want to send to the other side.
90
        smap map[string]int32
91
        // This map will contain all the subscriptions that have been added to the smap
92
        // during initLeafNodeSmapAndSendSubs. It is short lived and is there to avoid
93
        // race between processing of a sub where sub is added to account sublist but
94
        // updateSmap has not be called on that "thread", while in the LN readloop,
95
        // when processing CONNECT, initLeafNodeSmapAndSendSubs is invoked and add
96
        // this subscription to smap. When processing of the sub then calls updateSmap,
97
        // we would add it a second time in the smap causing later unsub to suppress the LS-.
98
        tsub  map[*subscription]struct{}
99
        tsubt *time.Timer
100
        // Selected compression mode, which may be different from the server configured mode.
101
        compression string
102
        // This is for GW map replies.
103
        gwSub *subscription
104
}
105

106
// Used for remote (solicited) leafnodes.
107
type leafNodeCfg struct {
108
        sync.RWMutex
109
        *RemoteLeafOpts
110
        urls           []*url.URL
111
        curURL         *url.URL
112
        tlsName        string
113
        username       string
114
        password       string
115
        perms          *Permissions
116
        connDelay      time.Duration // Delay before a connect, could be used while detecting loop condition, etc..
117
        jsMigrateTimer *time.Timer
118
}
119

120
// Check to see if this is a solicited leafnode. We do special processing for solicited.
121
func (c *client) isSolicitedLeafNode() bool {
2,104✔
122
        return c.kind == LEAF && c.leaf.remote != nil
2,104✔
123
}
2,104✔
124

125
// Returns true if this is a solicited leafnode and is not configured to be treated as a hub or a receiving
126
// connection leafnode where the otherside has declared itself to be the hub.
127
func (c *client) isSpokeLeafNode() bool {
5,805,642✔
128
        return c.kind == LEAF && c.leaf.isSpoke
5,805,642✔
129
}
5,805,642✔
130

131
func (c *client) isHubLeafNode() bool {
18,458✔
132
        return c.kind == LEAF && !c.leaf.isSpoke
18,458✔
133
}
18,458✔
134

135
func (c *client) isIsolatedLeafNode() bool {
10,332✔
136
        // TODO(nat): In future we may want to pass in and consider an isolation
10,332✔
137
        // group name here, which the hub and/or leaf could provide, so that we
10,332✔
138
        // can isolate away certain LNs but not others on an opt-in basis. For
10,332✔
139
        // now we will just isolate all LN interest until then.
10,332✔
140
        return c.kind == LEAF && c.leaf.isolated
10,332✔
141
}
10,332✔
142

143
// This will spin up go routines to solicit the remote leaf node connections.
144
func (s *Server) solicitLeafNodeRemotes(remotes []*RemoteLeafOpts) {
1,228✔
145
        sysAccName := _EMPTY_
1,228✔
146
        sAcc := s.SystemAccount()
1,228✔
147
        if sAcc != nil {
2,433✔
148
                sysAccName = sAcc.Name
1,205✔
149
        }
1,205✔
150
        addRemote := func(r *RemoteLeafOpts, isSysAccRemote bool) *leafNodeCfg {
2,603✔
151
                s.mu.Lock()
1,375✔
152
                remote := newLeafNodeCfg(r)
1,375✔
153
                creds := remote.Credentials
1,375✔
154
                accName := remote.LocalAccount
1,375✔
155
                s.leafRemoteCfgs = append(s.leafRemoteCfgs, remote)
1,375✔
156
                // Print notice if
1,375✔
157
                if isSysAccRemote {
1,469✔
158
                        if len(remote.DenyExports) > 0 {
95✔
159
                                s.Noticef("Remote for System Account uses restricted export permissions")
1✔
160
                        }
1✔
161
                        if len(remote.DenyImports) > 0 {
95✔
162
                                s.Noticef("Remote for System Account uses restricted import permissions")
1✔
163
                        }
1✔
164
                }
165
                s.mu.Unlock()
1,375✔
166
                if creds != _EMPTY_ {
1,427✔
167
                        contents, err := os.ReadFile(creds)
52✔
168
                        defer wipeSlice(contents)
52✔
169
                        if err != nil {
52✔
170
                                s.Errorf("Error reading LeafNode Remote Credentials file %q: %v", creds, err)
×
171
                        } else if items := credsRe.FindAllSubmatch(contents, -1); len(items) < 2 {
52✔
172
                                s.Errorf("LeafNode Remote Credentials file %q malformed", creds)
×
173
                        } else if _, err := nkeys.FromSeed(items[1][1]); err != nil {
52✔
174
                                s.Errorf("LeafNode Remote Credentials file %q has malformed seed", creds)
×
175
                        } else if uc, err := jwt.DecodeUserClaims(string(items[0][1])); err != nil {
52✔
176
                                s.Errorf("LeafNode Remote Credentials file %q has malformed user jwt", creds)
×
177
                        } else if isSysAccRemote {
56✔
178
                                if !uc.Permissions.Pub.Empty() || !uc.Permissions.Sub.Empty() || uc.Permissions.Resp != nil {
5✔
179
                                        s.Noticef("LeafNode Remote for System Account uses credentials file %q with restricted permissions", creds)
1✔
180
                                }
1✔
181
                        } else {
48✔
182
                                if !uc.Permissions.Pub.Empty() || !uc.Permissions.Sub.Empty() || uc.Permissions.Resp != nil {
54✔
183
                                        s.Noticef("LeafNode Remote for Account %s uses credentials file %q with restricted permissions", accName, creds)
6✔
184
                                }
6✔
185
                        }
186
                }
187
                return remote
1,375✔
188
        }
189
        for _, r := range remotes {
2,603✔
190
                // We need to call this, even if the leaf is disabled. This is so that
1,375✔
191
                // the number of internal configuration matches the options' remote leaf
1,375✔
192
                // configuration required for configuration reload.
1,375✔
193
                remote := addRemote(r, r.LocalAccount == sysAccName)
1,375✔
194
                if !r.Disabled {
2,749✔
195
                        s.startGoRoutine(func() { s.connectToRemoteLeafNode(remote, true) })
2,748✔
196
                }
197
        }
198
}
199

200
func (s *Server) remoteLeafNodeStillValid(remote *leafNodeCfg) bool {
7,461✔
201
        if remote.Disabled {
7,462✔
202
                return false
1✔
203
        }
1✔
204
        for _, ri := range s.getOpts().LeafNode.Remotes {
15,303✔
205
                // FIXME(dlc) - What about auth changes?
7,843✔
206
                if reflect.DeepEqual(ri.URLs, remote.URLs) {
15,303✔
207
                        return true
7,460✔
208
                }
7,460✔
209
        }
210
        return false
×
211
}
212

213
// Ensure that leafnode is properly configured.
214
func validateLeafNode(o *Options) error {
8,318✔
215
        if err := validateLeafNodeAuthOptions(o); err != nil {
8,320✔
216
                return err
2✔
217
        }
2✔
218

219
        // Users can bind to any local account, if its empty we will assume the $G account.
220
        for _, r := range o.LeafNode.Remotes {
9,736✔
221
                if r.LocalAccount == _EMPTY_ {
1,860✔
222
                        r.LocalAccount = globalAccountName
440✔
223
                }
440✔
224
        }
225

226
        // In local config mode, check that leafnode configuration refers to accounts that exist.
227
        if len(o.TrustedOperators) == 0 {
16,311✔
228
                accNames := map[string]struct{}{}
7,995✔
229
                for _, a := range o.Accounts {
16,922✔
230
                        accNames[a.Name] = struct{}{}
8,927✔
231
                }
8,927✔
232
                // global account is always created
233
                accNames[DEFAULT_GLOBAL_ACCOUNT] = struct{}{}
7,995✔
234
                // in the context of leaf nodes, empty account means global account
7,995✔
235
                accNames[_EMPTY_] = struct{}{}
7,995✔
236
                // system account either exists or, if not disabled, will be created
7,995✔
237
                if o.SystemAccount == _EMPTY_ && !o.NoSystemAccount {
14,424✔
238
                        accNames[DEFAULT_SYSTEM_ACCOUNT] = struct{}{}
6,429✔
239
                }
6,429✔
240
                checkAccountExists := func(accName string, cfgType string) error {
17,416✔
241
                        if _, ok := accNames[accName]; !ok {
9,423✔
242
                                return fmt.Errorf("cannot find local account %q specified in leafnode %s", accName, cfgType)
2✔
243
                        }
2✔
244
                        return nil
9,419✔
245
                }
246
                if err := checkAccountExists(o.LeafNode.Account, "authorization"); err != nil {
7,996✔
247
                        return err
1✔
248
                }
1✔
249
                for _, lu := range o.LeafNode.Users {
8,011✔
250
                        if lu.Account == nil { // means global account
27✔
251
                                continue
10✔
252
                        }
253
                        if err := checkAccountExists(lu.Account.Name, "authorization"); err != nil {
7✔
254
                                return err
×
255
                        }
×
256
                }
257
                for _, r := range o.LeafNode.Remotes {
9,413✔
258
                        if err := checkAccountExists(r.LocalAccount, "remote"); err != nil {
1,420✔
259
                                return err
1✔
260
                        }
1✔
261
                }
262
        } else {
321✔
263
                if len(o.LeafNode.Users) != 0 {
322✔
264
                        return fmt.Errorf("operator mode does not allow specifying users in leafnode config")
1✔
265
                }
1✔
266
                for _, r := range o.LeafNode.Remotes {
321✔
267
                        if !nkeys.IsValidPublicAccountKey(r.LocalAccount) {
2✔
268
                                return fmt.Errorf(
1✔
269
                                        "operator mode requires account nkeys in remotes. " +
1✔
270
                                                "Please add an `account` key to each remote in your `leafnodes` section, to assign it to an account. " +
1✔
271
                                                "Each account value should be a 56 character public key, starting with the letter 'A'")
1✔
272
                        }
1✔
273
                }
274
                if o.LeafNode.Port != 0 && o.LeafNode.Account != "" && !nkeys.IsValidPublicAccountKey(o.LeafNode.Account) {
320✔
275
                        return fmt.Errorf("operator mode and non account nkeys are incompatible")
1✔
276
                }
1✔
277
        }
278

279
        // Validate compression settings
280
        if o.LeafNode.Compression.Mode != _EMPTY_ {
12,952✔
281
                if err := validateAndNormalizeCompressionOption(&o.LeafNode.Compression, CompressionS2Auto); err != nil {
4,646✔
282
                        return err
5✔
283
                }
5✔
284
        }
285

286
        // If a remote has a websocket scheme, all need to have it.
287
        for _, rcfg := range o.LeafNode.Remotes {
9,724✔
288
                // Validate proxy configuration
1,418✔
289
                if _, err := validateLeafNodeProxyOptions(rcfg); err != nil {
1,424✔
290
                        return err
6✔
291
                }
6✔
292

293
                if len(rcfg.URLs) >= 2 {
1,614✔
294
                        firstIsWS, ok := isWSURL(rcfg.URLs[0]), true
202✔
295
                        for i := 1; i < len(rcfg.URLs); i++ {
641✔
296
                                u := rcfg.URLs[i]
439✔
297
                                if isWS := isWSURL(u); isWS && !firstIsWS || !isWS && firstIsWS {
446✔
298
                                        ok = false
7✔
299
                                        break
7✔
300
                                }
301
                        }
302
                        if !ok {
209✔
303
                                return fmt.Errorf("remote leaf node configuration cannot have a mix of websocket and non-websocket urls: %q", redactURLList(rcfg.URLs))
7✔
304
                        }
7✔
305
                }
306
                // Validate compression settings
307
                if rcfg.Compression.Mode != _EMPTY_ {
2,806✔
308
                        if err := validateAndNormalizeCompressionOption(&rcfg.Compression, CompressionS2Auto); err != nil {
1,406✔
309
                                return err
5✔
310
                        }
5✔
311
                }
312
        }
313

314
        if o.LeafNode.Port == 0 {
12,533✔
315
                return nil
4,245✔
316
        }
4,245✔
317

318
        // If MinVersion is defined, check that it is valid.
319
        if mv := o.LeafNode.MinVersion; mv != _EMPTY_ {
4,047✔
320
                if err := checkLeafMinVersionConfig(mv); err != nil {
6✔
321
                        return err
2✔
322
                }
2✔
323
        }
324

325
        // The checks below will be done only when detecting that we are configured
326
        // with gateways. So if an option validation needs to be done regardless,
327
        // it MUST be done before this point!
328

329
        if o.Gateway.Name == _EMPTY_ && o.Gateway.Port == 0 {
7,397✔
330
                return nil
3,356✔
331
        }
3,356✔
332
        // If we are here we have both leaf nodes and gateways defined, make sure there
333
        // is a system account defined.
334
        if o.SystemAccount == _EMPTY_ {
686✔
335
                return fmt.Errorf("leaf nodes and gateways (both being defined) require a system account to also be configured")
1✔
336
        }
1✔
337
        if err := validatePinnedCerts(o.LeafNode.TLSPinnedCerts); err != nil {
684✔
338
                return fmt.Errorf("leafnode: %v", err)
×
339
        }
×
340
        return nil
684✔
341
}
342

343
func checkLeafMinVersionConfig(mv string) error {
8✔
344
        if ok, err := versionAtLeastCheckError(mv, 2, 8, 0); !ok || err != nil {
12✔
345
                if err != nil {
6✔
346
                        return fmt.Errorf("invalid leafnode's minimum version: %v", err)
2✔
347
                } else {
4✔
348
                        return fmt.Errorf("the minimum version should be at least 2.8.0")
2✔
349
                }
2✔
350
        }
351
        return nil
4✔
352
}
353

354
// Used to validate user names in LeafNode configuration.
355
// - rejects mix of single and multiple users.
356
// - rejects duplicate user names.
357
func validateLeafNodeAuthOptions(o *Options) error {
8,379✔
358
        if len(o.LeafNode.Users) == 0 {
16,731✔
359
                return nil
8,352✔
360
        }
8,352✔
361
        if o.LeafNode.Username != _EMPTY_ {
29✔
362
                return fmt.Errorf("can not have a single user/pass and a users array")
2✔
363
        }
2✔
364
        if o.LeafNode.Nkey != _EMPTY_ {
25✔
365
                return fmt.Errorf("can not have a single nkey and a users array")
×
366
        }
×
367
        users := map[string]struct{}{}
25✔
368
        for _, u := range o.LeafNode.Users {
66✔
369
                if _, exists := users[u.Username]; exists {
43✔
370
                        return fmt.Errorf("duplicate user %q detected in leafnode authorization", u.Username)
2✔
371
                }
2✔
372
                users[u.Username] = struct{}{}
39✔
373
        }
374
        return nil
23✔
375
}
376

377
func validateLeafNodeProxyOptions(remote *RemoteLeafOpts) ([]string, error) {
2,013✔
378
        var warnings []string
2,013✔
379

2,013✔
380
        if remote.Proxy.URL == _EMPTY_ {
4,000✔
381
                return warnings, nil
1,987✔
382
        }
1,987✔
383

384
        proxyURL, err := url.Parse(remote.Proxy.URL)
26✔
385
        if err != nil {
27✔
386
                return warnings, fmt.Errorf("invalid proxy URL: %v", err)
1✔
387
        }
1✔
388

389
        if proxyURL.Scheme != "http" && proxyURL.Scheme != "https" {
27✔
390
                return warnings, fmt.Errorf("proxy URL scheme must be http or https, got: %s", proxyURL.Scheme)
2✔
391
        }
2✔
392

393
        if proxyURL.Host == _EMPTY_ {
25✔
394
                return warnings, fmt.Errorf("proxy URL must specify a host")
2✔
395
        }
2✔
396

397
        if remote.Proxy.Timeout < 0 {
22✔
398
                return warnings, fmt.Errorf("proxy timeout must be >= 0")
1✔
399
        }
1✔
400

401
        if (remote.Proxy.Username == _EMPTY_) != (remote.Proxy.Password == _EMPTY_) {
24✔
402
                return warnings, fmt.Errorf("proxy username and password must both be specified or both be empty")
4✔
403
        }
4✔
404

405
        if len(remote.URLs) > 0 {
32✔
406
                hasWebSocketURL := false
16✔
407
                hasNonWebSocketURL := false
16✔
408

16✔
409
                for _, remoteURL := range remote.URLs {
33✔
410
                        if remoteURL.Scheme == wsSchemePrefix || remoteURL.Scheme == wsSchemePrefixTLS {
30✔
411
                                hasWebSocketURL = true
13✔
412
                                if (remoteURL.Scheme == wsSchemePrefixTLS) &&
13✔
413
                                        remote.TLSConfig == nil && !remote.TLS {
14✔
414
                                        return warnings, fmt.Errorf("proxy is configured but remote URL %s requires TLS and no TLS configuration is provided. When using proxy with TLS endpoints, ensure TLS is properly configured for the leafnode remote", remoteURL.String())
1✔
415
                                }
1✔
416
                        } else {
4✔
417
                                hasNonWebSocketURL = true
4✔
418
                        }
4✔
419
                }
420

421
                if !hasWebSocketURL {
18✔
422
                        warnings = append(warnings, "proxy configuration will be ignored: proxy settings only apply to WebSocket connections (ws:// or wss://), but all configured URLs use TCP connections (nats://)")
3✔
423
                } else if hasNonWebSocketURL {
16✔
424
                        warnings = append(warnings, "proxy configuration will only be used for WebSocket URLs: proxy settings do not apply to TCP connections (nats://)")
1✔
425
                }
1✔
426
        }
427

428
        return warnings, nil
15✔
429
}
430

431
// Update remote LeafNode TLS configurations after a config reload.
432
func (s *Server) updateRemoteLeafNodesTLSConfig(opts *Options) {
8✔
433
        max := len(opts.LeafNode.Remotes)
8✔
434
        if max == 0 {
8✔
435
                return
×
436
        }
×
437

438
        s.mu.RLock()
8✔
439
        defer s.mu.RUnlock()
8✔
440

8✔
441
        // Changes in the list of remote leaf nodes is not supported.
8✔
442
        // However, make sure that we don't go over the arrays.
8✔
443
        if len(s.leafRemoteCfgs) < max {
8✔
444
                max = len(s.leafRemoteCfgs)
×
445
        }
×
446
        for i := 0; i < max; i++ {
21✔
447
                ro := opts.LeafNode.Remotes[i]
13✔
448
                cfg := s.leafRemoteCfgs[i]
13✔
449
                if ro.TLSConfig != nil {
15✔
450
                        cfg.Lock()
2✔
451
                        cfg.TLSConfig = ro.TLSConfig.Clone()
2✔
452
                        cfg.TLSHandshakeFirst = ro.TLSHandshakeFirst
2✔
453
                        cfg.Unlock()
2✔
454
                }
2✔
455
        }
456
}
457

458
func (s *Server) reConnectToRemoteLeafNode(remote *leafNodeCfg) {
254✔
459
        delay := s.getOpts().LeafNode.ReconnectInterval
254✔
460
        select {
254✔
461
        case <-time.After(delay):
196✔
462
        case <-s.quitCh:
58✔
463
                s.grWG.Done()
58✔
464
                return
58✔
465
        }
466
        s.connectToRemoteLeafNode(remote, false)
196✔
467
}
468

469
// Creates a leafNodeCfg object that wraps the RemoteLeafOpts.
470
func newLeafNodeCfg(remote *RemoteLeafOpts) *leafNodeCfg {
1,375✔
471
        cfg := &leafNodeCfg{
1,375✔
472
                RemoteLeafOpts: remote,
1,375✔
473
                urls:           make([]*url.URL, 0, len(remote.URLs)),
1,375✔
474
        }
1,375✔
475
        if len(remote.DenyExports) > 0 || len(remote.DenyImports) > 0 {
1,383✔
476
                perms := &Permissions{}
8✔
477
                if len(remote.DenyExports) > 0 {
16✔
478
                        perms.Publish = &SubjectPermission{Deny: remote.DenyExports}
8✔
479
                }
8✔
480
                if len(remote.DenyImports) > 0 {
15✔
481
                        perms.Subscribe = &SubjectPermission{Deny: remote.DenyImports}
7✔
482
                }
7✔
483
                cfg.perms = perms
8✔
484
        }
485
        // Start with the one that is configured. We will add to this
486
        // array when receiving async leafnode INFOs.
487
        cfg.urls = append(cfg.urls, cfg.URLs...)
1,375✔
488
        // If allowed to randomize, do it on our copy of URLs
1,375✔
489
        if !remote.NoRandomize {
2,748✔
490
                rand.Shuffle(len(cfg.urls), func(i, j int) {
1,774✔
491
                        cfg.urls[i], cfg.urls[j] = cfg.urls[j], cfg.urls[i]
401✔
492
                })
401✔
493
        }
494
        // If we are TLS make sure we save off a proper servername if possible.
495
        // Do same for user/password since we may need them to connect to
496
        // a bare URL that we get from INFO protocol.
497
        for _, u := range cfg.urls {
3,181✔
498
                cfg.saveTLSHostname(u)
1,806✔
499
                cfg.saveUserPassword(u)
1,806✔
500
                // If the url(s) have the "wss://" scheme, and we don't have a TLS
1,806✔
501
                // config, mark that we should be using TLS anyway.
1,806✔
502
                if !cfg.TLS && isWSSURL(u) {
1,807✔
503
                        cfg.TLS = true
1✔
504
                }
1✔
505
        }
506
        return cfg
1,375✔
507
}
508

509
// Will pick an URL from the list of available URLs.
510
func (cfg *leafNodeCfg) pickNextURL() *url.URL {
6,642✔
511
        cfg.Lock()
6,642✔
512
        defer cfg.Unlock()
6,642✔
513
        // If the current URL is the first in the list and we have more than
6,642✔
514
        // one URL, then move that one to end of the list.
6,642✔
515
        if cfg.curURL != nil && len(cfg.urls) > 1 && urlsAreEqual(cfg.curURL, cfg.urls[0]) {
9,882✔
516
                first := cfg.urls[0]
3,240✔
517
                copy(cfg.urls, cfg.urls[1:])
3,240✔
518
                cfg.urls[len(cfg.urls)-1] = first
3,240✔
519
        }
3,240✔
520
        cfg.curURL = cfg.urls[0]
6,642✔
521
        return cfg.curURL
6,642✔
522
}
523

524
// Returns the current URL
525
func (cfg *leafNodeCfg) getCurrentURL() *url.URL {
77✔
526
        cfg.RLock()
77✔
527
        defer cfg.RUnlock()
77✔
528
        return cfg.curURL
77✔
529
}
77✔
530

531
// Returns how long the server should wait before attempting
532
// to solicit a remote leafnode connection.
533
func (cfg *leafNodeCfg) getConnectDelay() time.Duration {
1,572✔
534
        cfg.RLock()
1,572✔
535
        delay := cfg.connDelay
1,572✔
536
        cfg.RUnlock()
1,572✔
537
        return delay
1,572✔
538
}
1,572✔
539

540
// Sets the connect delay.
541
func (cfg *leafNodeCfg) setConnectDelay(delay time.Duration) {
152✔
542
        cfg.Lock()
152✔
543
        cfg.connDelay = delay
152✔
544
        cfg.Unlock()
152✔
545
}
152✔
546

547
// Ensure that non-exported options (used in tests) have
548
// been properly set.
549
func (s *Server) setLeafNodeNonExportedOptions() {
7,112✔
550
        opts := s.getOpts()
7,112✔
551
        s.leafNodeOpts.dialTimeout = opts.LeafNode.dialTimeout
7,112✔
552
        if s.leafNodeOpts.dialTimeout == 0 {
14,223✔
553
                // Use same timeouts as routes for now.
7,111✔
554
                s.leafNodeOpts.dialTimeout = DEFAULT_ROUTE_DIAL
7,111✔
555
        }
7,111✔
556
        s.leafNodeOpts.resolver = opts.LeafNode.resolver
7,112✔
557
        if s.leafNodeOpts.resolver == nil {
14,221✔
558
                s.leafNodeOpts.resolver = net.DefaultResolver
7,109✔
559
        }
7,109✔
560
}
561

562
const sharedSysAccDelay = 250 * time.Millisecond
563

564
// establishHTTPProxyTunnel establishes an HTTP CONNECT tunnel through a proxy server
565
func establishHTTPProxyTunnel(proxyURL, targetHost string, timeout time.Duration, username, password string) (net.Conn, error) {
11✔
566
        proxyAddr, err := url.Parse(proxyURL)
11✔
567
        if err != nil {
11✔
568
                // This should not happen since proxy URL is validated during configuration parsing
×
569
                return nil, fmt.Errorf("unexpected proxy URL parse error (URL was pre-validated): %v", err)
×
570
        }
×
571

572
        // Connect to the proxy server
573
        conn, err := natsDialTimeout("tcp", proxyAddr.Host, timeout)
11✔
574
        if err != nil {
11✔
575
                return nil, fmt.Errorf("failed to connect to proxy: %v", err)
×
576
        }
×
577

578
        // Set deadline for the entire proxy handshake
579
        if err := conn.SetDeadline(time.Now().Add(timeout)); err != nil {
11✔
580
                conn.Close()
×
581
                return nil, fmt.Errorf("failed to set deadline: %v", err)
×
582
        }
×
583

584
        req := &http.Request{
11✔
585
                Method: http.MethodConnect,
11✔
586
                URL:    &url.URL{Opaque: targetHost}, // Opaque is required for CONNECT
11✔
587
                Host:   targetHost,
11✔
588
                Header: make(http.Header),
11✔
589
        }
11✔
590

11✔
591
        // Add proxy authentication if provided
11✔
592
        if username != "" && password != "" {
13✔
593
                req.Header.Set("Proxy-Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(username+":"+password)))
2✔
594
        }
2✔
595

596
        if err := req.Write(conn); err != nil {
11✔
597
                conn.Close()
×
598
                return nil, fmt.Errorf("failed to write CONNECT request: %v", err)
×
599
        }
×
600

601
        resp, err := http.ReadResponse(bufio.NewReader(conn), req)
11✔
602
        if err != nil {
11✔
603
                conn.Close()
×
604
                return nil, fmt.Errorf("failed to read proxy response: %v", err)
×
605
        }
×
606

607
        if resp.StatusCode != http.StatusOK {
12✔
608
                resp.Body.Close()
1✔
609
                conn.Close()
1✔
610
                return nil, fmt.Errorf("proxy CONNECT failed: %s", resp.Status)
1✔
611
        }
1✔
612

613
        // Close the response body
614
        resp.Body.Close()
10✔
615

10✔
616
        // Clear the deadline now that we've finished the proxy handshake
10✔
617
        if err := conn.SetDeadline(time.Time{}); err != nil {
10✔
618
                conn.Close()
×
619
                return nil, fmt.Errorf("failed to clear deadline: %v", err)
×
620
        }
×
621

622
        return conn, nil
10✔
623
}
624

625
func (s *Server) connectToRemoteLeafNode(remote *leafNodeCfg, firstConnect bool) {
1,572✔
626
        defer s.grWG.Done()
1,572✔
627

1,572✔
628
        if remote == nil || len(remote.URLs) == 0 {
1,572✔
629
                s.Debugf("Empty remote leafnode definition, nothing to connect")
×
630
                return
×
631
        }
×
632

633
        opts := s.getOpts()
1,572✔
634
        reconnectDelay := opts.LeafNode.ReconnectInterval
1,572✔
635
        s.mu.RLock()
1,572✔
636
        dialTimeout := s.leafNodeOpts.dialTimeout
1,572✔
637
        resolver := s.leafNodeOpts.resolver
1,572✔
638
        var isSysAcc bool
1,572✔
639
        if s.eventsEnabled() {
3,102✔
640
                isSysAcc = remote.LocalAccount == s.sys.account.Name
1,530✔
641
        }
1,530✔
642
        jetstreamMigrateDelay := remote.JetStreamClusterMigrateDelay
1,572✔
643
        s.mu.RUnlock()
1,572✔
644

1,572✔
645
        // If we are sharing a system account and we are not standalone delay to gather some info prior.
1,572✔
646
        if firstConnect && isSysAcc && !s.standAloneMode() {
1,641✔
647
                s.Debugf("Will delay first leafnode connect to shared system account due to clustering")
69✔
648
                remote.setConnectDelay(sharedSysAccDelay)
69✔
649
        }
69✔
650

651
        if connDelay := remote.getConnectDelay(); connDelay > 0 {
1,652✔
652
                select {
80✔
653
                case <-time.After(connDelay):
71✔
654
                case <-s.quitCh:
9✔
655
                        return
9✔
656
                }
657
                remote.setConnectDelay(0)
71✔
658
        }
659

660
        var conn net.Conn
1,563✔
661

1,563✔
662
        const connErrFmt = "Error trying to connect as leafnode to remote server %q (attempt %v): %v"
1,563✔
663

1,563✔
664
        // Capture proxy configuration once before the loop with proper locking
1,563✔
665
        remote.RLock()
1,563✔
666
        proxyURL := remote.Proxy.URL
1,563✔
667
        proxyUsername := remote.Proxy.Username
1,563✔
668
        proxyPassword := remote.Proxy.Password
1,563✔
669
        proxyTimeout := remote.Proxy.Timeout
1,563✔
670
        remote.RUnlock()
1,563✔
671

1,563✔
672
        // Set default proxy timeout if not specified
1,563✔
673
        if proxyTimeout == 0 {
3,118✔
674
                proxyTimeout = dialTimeout
1,555✔
675
        }
1,555✔
676

677
        attempts := 0
1,563✔
678

1,563✔
679
        for s.isRunning() && s.remoteLeafNodeStillValid(remote) {
8,205✔
680
                rURL := remote.pickNextURL()
6,642✔
681
                url, err := s.getRandomIP(resolver, rURL.Host, nil)
6,642✔
682
                if err == nil {
13,277✔
683
                        var ipStr string
6,635✔
684
                        if url != rURL.Host {
6,706✔
685
                                ipStr = fmt.Sprintf(" (%s)", url)
71✔
686
                        }
71✔
687
                        // Some test may want to disable remotes from connecting
688
                        if s.isLeafConnectDisabled() {
6,759✔
689
                                s.Debugf("Will not attempt to connect to remote server on %q%s, leafnodes currently disabled", rURL.Host, ipStr)
124✔
690
                                err = ErrLeafNodeDisabled
124✔
691
                        } else {
6,635✔
692
                                s.Debugf("Trying to connect as leafnode to remote server on %q%s", rURL.Host, ipStr)
6,511✔
693

6,511✔
694
                                // Check if proxy is configured
6,511✔
695
                                if proxyURL != _EMPTY_ {
6,519✔
696
                                        targetHost := rURL.Host
8✔
697
                                        // If URL doesn't include port, add the default port for the scheme
8✔
698
                                        if rURL.Port() == _EMPTY_ {
8✔
699
                                                defaultPort := "80"
×
700
                                                if rURL.Scheme == wsSchemePrefixTLS {
×
701
                                                        defaultPort = "443"
×
702
                                                }
×
703
                                                targetHost = net.JoinHostPort(rURL.Hostname(), defaultPort)
×
704
                                        }
705

706
                                        conn, err = establishHTTPProxyTunnel(proxyURL, targetHost, proxyTimeout, proxyUsername, proxyPassword)
8✔
707
                                } else {
6,503✔
708
                                        // Direct connection
6,503✔
709
                                        conn, err = natsDialTimeout("tcp", url, dialTimeout)
6,503✔
710
                                }
6,503✔
711
                        }
712
                }
713
                if err != nil {
12,466✔
714
                        jitter := time.Duration(rand.Int63n(int64(reconnectDelay)))
5,824✔
715
                        delay := reconnectDelay + jitter
5,824✔
716
                        attempts++
5,824✔
717
                        if s.shouldReportConnectErr(firstConnect, attempts) {
9,809✔
718
                                s.Errorf(connErrFmt, rURL.Host, attempts, err)
3,985✔
719
                        } else {
5,824✔
720
                                s.Debugf(connErrFmt, rURL.Host, attempts, err)
1,839✔
721
                        }
1,839✔
722
                        remote.Lock()
5,824✔
723
                        // if we are using a delay to start migrating assets, kick off a migrate timer.
5,824✔
724
                        if remote.jsMigrateTimer == nil && jetstreamMigrateDelay > 0 {
5,832✔
725
                                remote.jsMigrateTimer = time.AfterFunc(jetstreamMigrateDelay, func() {
16✔
726
                                        s.checkJetStreamMigrate(remote)
8✔
727
                                })
8✔
728
                        }
729
                        remote.Unlock()
5,824✔
730
                        select {
5,824✔
731
                        case <-s.quitCh:
728✔
732
                                remote.cancelMigrateTimer()
728✔
733
                                return
728✔
734
                        case <-time.After(delay):
5,095✔
735
                                // Check if we should migrate any JetStream assets immediately while this remote is down.
5,095✔
736
                                // This will be used if JetStreamClusterMigrateDelay was not set
5,095✔
737
                                if jetstreamMigrateDelay == 0 {
10,116✔
738
                                        s.checkJetStreamMigrate(remote)
5,021✔
739
                                }
5,021✔
740
                                continue
5,095✔
741
                        }
742
                }
743
                remote.cancelMigrateTimer()
818✔
744
                if !s.remoteLeafNodeStillValid(remote) {
818✔
745
                        conn.Close()
×
746
                        return
×
747
                }
×
748

749
                // We have a connection here to a remote server.
750
                // Go ahead and create our leaf node and return.
751
                s.createLeafNode(conn, rURL, remote, nil)
818✔
752

818✔
753
                // Clear any observer states if we had them.
818✔
754
                s.clearObserverState(remote)
818✔
755

818✔
756
                return
818✔
757
        }
758
}
759

760
func (cfg *leafNodeCfg) cancelMigrateTimer() {
1,546✔
761
        cfg.Lock()
1,546✔
762
        stopAndClearTimer(&cfg.jsMigrateTimer)
1,546✔
763
        cfg.Unlock()
1,546✔
764
}
1,546✔
765

766
// This will clear any observer state such that stream or consumer assets on this server can become leaders again.
767
func (s *Server) clearObserverState(remote *leafNodeCfg) {
818✔
768
        s.mu.RLock()
818✔
769
        accName := remote.LocalAccount
818✔
770
        s.mu.RUnlock()
818✔
771

818✔
772
        acc, err := s.LookupAccount(accName)
818✔
773
        if err != nil {
820✔
774
                s.Warnf("Error looking up account [%s] checking for JetStream clear observer state on a leafnode", accName)
2✔
775
                return
2✔
776
        }
2✔
777

778
        acc.jscmMu.Lock()
816✔
779
        defer acc.jscmMu.Unlock()
816✔
780

816✔
781
        // Walk all streams looking for any clustered stream, skip otherwise.
816✔
782
        for _, mset := range acc.streams() {
862✔
783
                node := mset.raftNode()
46✔
784
                if node == nil {
84✔
785
                        // Not R>1
38✔
786
                        continue
38✔
787
                }
788
                // Check consumers
789
                for _, o := range mset.getConsumers() {
10✔
790
                        if n := o.raftNode(); n != nil {
4✔
791
                                // Ensure we can become a leader again.
2✔
792
                                n.SetObserver(false)
2✔
793
                        }
2✔
794
                }
795
                // Ensure we can not become a leader again.
796
                node.SetObserver(false)
8✔
797
        }
798
}
799

800
// Check to see if we should migrate any assets from this account.
801
func (s *Server) checkJetStreamMigrate(remote *leafNodeCfg) {
5,029✔
802
        s.mu.RLock()
5,029✔
803
        accName, shouldMigrate := remote.LocalAccount, remote.JetStreamClusterMigrate
5,029✔
804
        s.mu.RUnlock()
5,029✔
805

5,029✔
806
        if !shouldMigrate {
10,000✔
807
                return
4,971✔
808
        }
4,971✔
809

810
        acc, err := s.LookupAccount(accName)
58✔
811
        if err != nil {
58✔
812
                s.Warnf("Error looking up account [%s] checking for JetStream migration on a leafnode", accName)
×
813
                return
×
814
        }
×
815

816
        acc.jscmMu.Lock()
58✔
817
        defer acc.jscmMu.Unlock()
58✔
818

58✔
819
        // Walk all streams looking for any clustered stream, skip otherwise.
58✔
820
        // If we are the leader force stepdown.
58✔
821
        for _, mset := range acc.streams() {
87✔
822
                node := mset.raftNode()
29✔
823
                if node == nil {
29✔
824
                        // Not R>1
×
825
                        continue
×
826
                }
827
                // Collect any consumers
828
                for _, o := range mset.getConsumers() {
48✔
829
                        if n := o.raftNode(); n != nil {
38✔
830
                                n.StepDown()
19✔
831
                                // Ensure we can not become a leader while in this state.
19✔
832
                                n.SetObserver(true)
19✔
833
                        }
19✔
834
                }
835
                // Stepdown if this stream was leader.
836
                node.StepDown()
29✔
837
                // Ensure we can not become a leader while in this state.
29✔
838
                node.SetObserver(true)
29✔
839
        }
840
}
841

842
// Helper for checking.
843
func (s *Server) isLeafConnectDisabled() bool {
6,635✔
844
        s.mu.RLock()
6,635✔
845
        defer s.mu.RUnlock()
6,635✔
846
        return s.leafDisableConnect
6,635✔
847
}
6,635✔
848

849
// Save off the tlsName for when we use TLS and mix hostnames and IPs. IPs usually
850
// come from the server we connect to.
851
//
852
// We used to save the name only if there was a TLSConfig or scheme equal to "tls".
853
// However, this was causing failures for users that did not set the scheme (and
854
// their remote connections did not have a tls{} block).
855
// We now save the host name regardless in case the remote returns an INFO indicating
856
// that TLS is required.
857
func (cfg *leafNodeCfg) saveTLSHostname(u *url.URL) {
2,454✔
858
        if cfg.tlsName == _EMPTY_ && net.ParseIP(u.Hostname()) == nil {
2,474✔
859
                cfg.tlsName = u.Hostname()
20✔
860
        }
20✔
861
}
862

863
// Save off the username/password for when we connect using a bare URL
864
// that we get from the INFO protocol.
865
func (cfg *leafNodeCfg) saveUserPassword(u *url.URL) {
1,806✔
866
        if cfg.username == _EMPTY_ && u.User != nil {
2,114✔
867
                cfg.username = u.User.Username()
308✔
868
                cfg.password, _ = u.User.Password()
308✔
869
        }
308✔
870
}
871

872
// This starts the leafnode accept loop in a go routine, unless it
873
// is detected that the server has already been shutdown.
874
func (s *Server) startLeafNodeAcceptLoop() {
4,022✔
875
        // Snapshot server options.
4,022✔
876
        opts := s.getOpts()
4,022✔
877

4,022✔
878
        port := opts.LeafNode.Port
4,022✔
879
        if port == -1 {
7,868✔
880
                port = 0
3,846✔
881
        }
3,846✔
882

883
        if s.isShuttingDown() {
4,022✔
884
                return
×
885
        }
×
886

887
        s.mu.Lock()
4,022✔
888
        hp := net.JoinHostPort(opts.LeafNode.Host, strconv.Itoa(port))
4,022✔
889
        l, e := natsListen("tcp", hp)
4,022✔
890
        s.leafNodeListenerErr = e
4,022✔
891
        if e != nil {
4,022✔
892
                s.mu.Unlock()
×
893
                s.Fatalf("Error listening on leafnode port: %d - %v", opts.LeafNode.Port, e)
×
894
                return
×
895
        }
×
896

897
        s.Noticef("Listening for leafnode connections on %s",
4,022✔
898
                net.JoinHostPort(opts.LeafNode.Host, strconv.Itoa(l.Addr().(*net.TCPAddr).Port)))
4,022✔
899

4,022✔
900
        tlsRequired := opts.LeafNode.TLSConfig != nil
4,022✔
901
        tlsVerify := tlsRequired && opts.LeafNode.TLSConfig.ClientAuth == tls.RequireAndVerifyClientCert
4,022✔
902
        // Do not set compression in this Info object, it would possibly cause
4,022✔
903
        // issues when sending asynchronous INFO to the remote.
4,022✔
904
        info := Info{
4,022✔
905
                ID:            s.info.ID,
4,022✔
906
                Name:          s.info.Name,
4,022✔
907
                Version:       s.info.Version,
4,022✔
908
                GitCommit:     gitCommit,
4,022✔
909
                GoVersion:     runtime.Version(),
4,022✔
910
                AuthRequired:  true,
4,022✔
911
                TLSRequired:   tlsRequired,
4,022✔
912
                TLSVerify:     tlsVerify,
4,022✔
913
                MaxPayload:    s.info.MaxPayload, // TODO(dlc) - Allow override?
4,022✔
914
                Headers:       s.supportsHeaders(),
4,022✔
915
                JetStream:     opts.JetStream,
4,022✔
916
                Domain:        opts.JetStreamDomain,
4,022✔
917
                Proto:         s.getServerProto(),
4,022✔
918
                InfoOnConnect: true,
4,022✔
919
                JSApiLevel:    JSApiLevel,
4,022✔
920
        }
4,022✔
921
        // If we have selected a random port...
4,022✔
922
        if port == 0 {
7,868✔
923
                // Write resolved port back to options.
3,846✔
924
                opts.LeafNode.Port = l.Addr().(*net.TCPAddr).Port
3,846✔
925
        }
3,846✔
926

927
        s.leafNodeInfo = info
4,022✔
928
        // Possibly override Host/Port and set IP based on Cluster.Advertise
4,022✔
929
        if err := s.setLeafNodeInfoHostPortAndIP(); err != nil {
4,022✔
930
                s.Fatalf("Error setting leafnode INFO with LeafNode.Advertise value of %s, err=%v", opts.LeafNode.Advertise, err)
×
931
                l.Close()
×
932
                s.mu.Unlock()
×
933
                return
×
934
        }
×
935
        s.leafURLsMap[s.leafNodeInfo.IP]++
4,022✔
936
        s.generateLeafNodeInfoJSON()
4,022✔
937

4,022✔
938
        // Setup state that can enable shutdown
4,022✔
939
        s.leafNodeListener = l
4,022✔
940

4,022✔
941
        // As of now, a server that does not have remotes configured would
4,022✔
942
        // never solicit a connection, so we should not have to warn if
4,022✔
943
        // InsecureSkipVerify is set in main LeafNodes config (since
4,022✔
944
        // this TLS setting matters only when soliciting a connection).
4,022✔
945
        // Still, warn if insecure is set in any of LeafNode block.
4,022✔
946
        // We need to check remotes, even if tls is not required on accept.
4,022✔
947
        warn := tlsRequired && opts.LeafNode.TLSConfig.InsecureSkipVerify
4,022✔
948
        if !warn {
8,042✔
949
                for _, r := range opts.LeafNode.Remotes {
4,213✔
950
                        if r.TLSConfig != nil && r.TLSConfig.InsecureSkipVerify {
193✔
951
                                warn = true
×
952
                                break
×
953
                        }
954
                }
955
        }
956
        if warn {
4,024✔
957
                s.Warnf(leafnodeTLSInsecureWarning)
2✔
958
        }
2✔
959
        go s.acceptConnections(l, "Leafnode", func(conn net.Conn) { s.createLeafNode(conn, nil, nil, nil) }, nil)
4,876✔
960
        s.mu.Unlock()
4,022✔
961
}
962

963
// RegEx to match a creds file with user JWT and Seed.
964
var credsRe = regexp.MustCompile(`\s*(?:(?:[-]{3,}.*[-]{3,}\r?\n)([\w\-.=]+)(?:\r?\n[-]{3,}.*[-]{3,}(\r?\n|\z)))`)
965

966
// clusterName is provided as argument to avoid lock ordering issues with the locked client c
967
// Lock should be held entering here.
968
func (c *client) sendLeafConnect(clusterName string, headers bool) error {
685✔
969
        // We support basic user/pass and operator based user JWT with signatures.
685✔
970
        cinfo := leafConnectInfo{
685✔
971
                Version:       VERSION,
685✔
972
                ID:            c.srv.info.ID,
685✔
973
                Domain:        c.srv.info.Domain,
685✔
974
                Name:          c.srv.info.Name,
685✔
975
                Hub:           c.leaf.remote.Hub,
685✔
976
                Cluster:       clusterName,
685✔
977
                Headers:       headers,
685✔
978
                JetStream:     c.acc.jetStreamConfigured(),
685✔
979
                DenyPub:       c.leaf.remote.DenyImports,
685✔
980
                Compression:   c.leaf.compression,
685✔
981
                RemoteAccount: c.acc.GetName(),
685✔
982
                Proto:         c.srv.getServerProto(),
685✔
983
                Isolate:       c.leaf.remote.RequestIsolation,
685✔
984
        }
685✔
985

685✔
986
        // If a signature callback is specified, this takes precedence over anything else.
685✔
987
        if cb := c.leaf.remote.SignatureCB; cb != nil {
690✔
988
                nonce := c.nonce
5✔
989
                c.mu.Unlock()
5✔
990
                jwt, sigraw, err := cb(nonce)
5✔
991
                c.mu.Lock()
5✔
992
                if err == nil && c.isClosed() {
6✔
993
                        err = ErrConnectionClosed
1✔
994
                }
1✔
995
                if err != nil {
7✔
996
                        c.Errorf("Error signing the nonce: %v", err)
2✔
997
                        return err
2✔
998
                }
2✔
999
                sig := base64.RawURLEncoding.EncodeToString(sigraw)
3✔
1000
                cinfo.JWT, cinfo.Sig = jwt, sig
3✔
1001

1002
        } else if creds := c.leaf.remote.Credentials; creds != _EMPTY_ {
736✔
1003
                // Check for credentials first, that will take precedence..
56✔
1004
                c.Debugf("Authenticating with credentials file %q", c.leaf.remote.Credentials)
56✔
1005
                contents, err := os.ReadFile(creds)
56✔
1006
                if err != nil {
56✔
1007
                        c.Errorf("%v", err)
×
1008
                        return err
×
1009
                }
×
1010
                defer wipeSlice(contents)
56✔
1011
                items := credsRe.FindAllSubmatch(contents, -1)
56✔
1012
                if len(items) < 2 {
56✔
1013
                        c.Errorf("Credentials file malformed")
×
1014
                        return err
×
1015
                }
×
1016
                // First result should be the user JWT.
1017
                // We copy here so that the file containing the seed will be wiped appropriately.
1018
                raw := items[0][1]
56✔
1019
                tmp := make([]byte, len(raw))
56✔
1020
                copy(tmp, raw)
56✔
1021
                // Seed is second item.
56✔
1022
                kp, err := nkeys.FromSeed(items[1][1])
56✔
1023
                if err != nil {
56✔
1024
                        c.Errorf("Credentials file has malformed seed")
×
1025
                        return err
×
1026
                }
×
1027
                // Wipe our key on exit.
1028
                defer kp.Wipe()
56✔
1029

56✔
1030
                sigraw, _ := kp.Sign(c.nonce)
56✔
1031
                sig := base64.RawURLEncoding.EncodeToString(sigraw)
56✔
1032
                cinfo.JWT = bytesToString(tmp)
56✔
1033
                cinfo.Sig = sig
56✔
1034
        } else if nkey := c.leaf.remote.Nkey; nkey != _EMPTY_ {
629✔
1035
                kp, err := nkeys.FromSeed([]byte(nkey))
5✔
1036
                if err != nil {
5✔
1037
                        c.Errorf("Remote nkey has malformed seed")
×
1038
                        return err
×
1039
                }
×
1040
                // Wipe our key on exit.
1041
                defer kp.Wipe()
5✔
1042
                sigraw, _ := kp.Sign(c.nonce)
5✔
1043
                sig := base64.RawURLEncoding.EncodeToString(sigraw)
5✔
1044
                pkey, _ := kp.PublicKey()
5✔
1045
                cinfo.Nkey = pkey
5✔
1046
                cinfo.Sig = sig
5✔
1047
        }
1048
        // In addition, and this is to allow auth callout, set user/password or
1049
        // token if applicable.
1050
        if userInfo := c.leaf.remote.curURL.User; userInfo != nil {
1,018✔
1051
                cinfo.User = userInfo.Username()
335✔
1052
                var ok bool
335✔
1053
                cinfo.Pass, ok = userInfo.Password()
335✔
1054
                // For backward compatibility, if only username is provided, set both
335✔
1055
                // Token and User, not just Token.
335✔
1056
                if !ok {
344✔
1057
                        cinfo.Token = cinfo.User
9✔
1058
                }
9✔
1059
        } else if c.leaf.remote.username != _EMPTY_ {
354✔
1060
                cinfo.User = c.leaf.remote.username
6✔
1061
                cinfo.Pass = c.leaf.remote.password
6✔
1062
                // For backward compatibility, if only username is provided, set both
6✔
1063
                // Token and User, not just Token.
6✔
1064
                if cinfo.Pass == _EMPTY_ {
6✔
1065
                        cinfo.Token = cinfo.User
×
1066
                }
×
1067
        }
1068
        b, err := json.Marshal(cinfo)
683✔
1069
        if err != nil {
683✔
1070
                c.Errorf("Error marshaling CONNECT to remote leafnode: %v\n", err)
×
1071
                return err
×
1072
        }
×
1073
        // Although this call is made before the writeLoop is created,
1074
        // we don't really need to send in place. The protocol will be
1075
        // sent out by the writeLoop.
1076
        c.enqueueProto([]byte(fmt.Sprintf(ConProto, b)))
683✔
1077
        return nil
683✔
1078
}
1079

1080
// Makes a deep copy of the LeafNode Info structure.
1081
// The server lock is held on entry.
1082
func (s *Server) copyLeafNodeInfo() *Info {
2,765✔
1083
        clone := s.leafNodeInfo
2,765✔
1084
        // Copy the array of urls.
2,765✔
1085
        if len(s.leafNodeInfo.LeafNodeURLs) > 0 {
5,012✔
1086
                clone.LeafNodeURLs = append([]string(nil), s.leafNodeInfo.LeafNodeURLs...)
2,247✔
1087
        }
2,247✔
1088
        return &clone
2,765✔
1089
}
1090

1091
// Adds a LeafNode URL that we get when a route connects to the Info structure.
1092
// Regenerates the JSON byte array so that it can be sent to LeafNode connections.
1093
// Returns a boolean indicating if the URL was added or not.
1094
// Server lock is held on entry
1095
func (s *Server) addLeafNodeURL(urlStr string) bool {
8,011✔
1096
        if s.leafURLsMap.addUrl(urlStr) {
16,017✔
1097
                s.generateLeafNodeInfoJSON()
8,006✔
1098
                return true
8,006✔
1099
        }
8,006✔
1100
        return false
5✔
1101
}
1102

1103
// Removes a LeafNode URL of the route that is disconnecting from the Info structure.
1104
// Regenerates the JSON byte array so that it can be sent to LeafNode connections.
1105
// Returns a boolean indicating if the URL was removed or not.
1106
// Server lock is held on entry.
1107
func (s *Server) removeLeafNodeURL(urlStr string) bool {
8,005✔
1108
        // Don't need to do this if we are removing the route connection because
8,005✔
1109
        // we are shuting down...
8,005✔
1110
        if s.isShuttingDown() {
12,254✔
1111
                return false
4,249✔
1112
        }
4,249✔
1113
        if s.leafURLsMap.removeUrl(urlStr) {
7,508✔
1114
                s.generateLeafNodeInfoJSON()
3,752✔
1115
                return true
3,752✔
1116
        }
3,752✔
1117
        return false
4✔
1118
}
1119

1120
// Server lock is held on entry
1121
func (s *Server) generateLeafNodeInfoJSON() {
15,780✔
1122
        s.leafNodeInfo.Cluster = s.cachedClusterName()
15,780✔
1123
        s.leafNodeInfo.LeafNodeURLs = s.leafURLsMap.getAsStringSlice()
15,780✔
1124
        s.leafNodeInfo.WSConnectURLs = s.websocket.connectURLsMap.getAsStringSlice()
15,780✔
1125
        s.leafNodeInfoJSON = generateInfoJSON(&s.leafNodeInfo)
15,780✔
1126
}
15,780✔
1127

1128
// Sends an async INFO protocol so that the connected servers can update
1129
// their list of LeafNode urls.
1130
func (s *Server) sendAsyncLeafNodeInfo() {
11,758✔
1131
        for _, c := range s.leafs {
11,861✔
1132
                c.mu.Lock()
103✔
1133
                c.enqueueProto(s.leafNodeInfoJSON)
103✔
1134
                c.mu.Unlock()
103✔
1135
        }
103✔
1136
}
1137

1138
// Called when an inbound leafnode connection is accepted or we create one for a solicited leafnode.
1139
func (s *Server) createLeafNode(conn net.Conn, rURL *url.URL, remote *leafNodeCfg, ws *websocket) *client {
1,701✔
1140
        // Snapshot server options.
1,701✔
1141
        opts := s.getOpts()
1,701✔
1142

1,701✔
1143
        maxPay := int32(opts.MaxPayload)
1,701✔
1144
        maxSubs := int32(opts.MaxSubs)
1,701✔
1145
        // For system, maxSubs of 0 means unlimited, so re-adjust here.
1,701✔
1146
        if maxSubs == 0 {
3,401✔
1147
                maxSubs = -1
1,700✔
1148
        }
1,700✔
1149
        now := time.Now().UTC()
1,701✔
1150

1,701✔
1151
        c := &client{srv: s, nc: conn, kind: LEAF, opts: defaultOpts, mpay: maxPay, msubs: maxSubs, start: now, last: now}
1,701✔
1152
        // Do not update the smap here, we need to do it in initLeafNodeSmapAndSendSubs
1,701✔
1153
        c.leaf = &leaf{}
1,701✔
1154

1,701✔
1155
        // If the leafnode subject interest should be isolated, flag it here.
1,701✔
1156
        s.optsMu.RLock()
1,701✔
1157
        if c.leaf.isolated = s.opts.LeafNode.IsolateLeafnodeInterest; !c.leaf.isolated && remote != nil {
2,517✔
1158
                c.leaf.isolated = remote.LocalIsolation
816✔
1159
        }
816✔
1160
        s.optsMu.RUnlock()
1,701✔
1161

1,701✔
1162
        // For accepted LN connections, ws will be != nil if it was accepted
1,701✔
1163
        // through the Websocket port.
1,701✔
1164
        c.ws = ws
1,701✔
1165

1,701✔
1166
        // For remote, check if the scheme starts with "ws", if so, we will initiate
1,701✔
1167
        // a remote Leaf Node connection as a websocket connection.
1,701✔
1168
        if remote != nil && rURL != nil && isWSURL(rURL) {
1,751✔
1169
                remote.RLock()
50✔
1170
                c.ws = &websocket{compress: remote.Websocket.Compression, maskwrite: !remote.Websocket.NoMasking}
50✔
1171
                remote.RUnlock()
50✔
1172
        }
50✔
1173

1174
        // Determines if we are soliciting the connection or not.
1175
        var solicited bool
1,701✔
1176
        var acc *Account
1,701✔
1177
        var remoteSuffix string
1,701✔
1178
        if remote != nil {
2,519✔
1179
                // For now, if lookup fails, we will constantly try
818✔
1180
                // to recreate this LN connection.
818✔
1181
                lacc := remote.LocalAccount
818✔
1182
                var err error
818✔
1183
                acc, err = s.LookupAccount(lacc)
818✔
1184
                if err != nil {
820✔
1185
                        // An account not existing is something that can happen with nats/http account resolver and the account
2✔
1186
                        // has not yet been pushed, or the request failed for other reasons.
2✔
1187
                        // remote needs to be set or retry won't happen
2✔
1188
                        c.leaf.remote = remote
2✔
1189
                        c.closeConnection(MissingAccount)
2✔
1190
                        s.Errorf("Unable to lookup account %s for solicited leafnode connection: %v", lacc, err)
2✔
1191
                        return nil
2✔
1192
                }
2✔
1193
                remoteSuffix = fmt.Sprintf(" for account: %s", acc.traceLabel())
816✔
1194
        }
1195

1196
        c.mu.Lock()
1,699✔
1197
        c.initClient()
1,699✔
1198
        c.Noticef("Leafnode connection created%s %s", remoteSuffix, c.opts.Name)
1,699✔
1199

1,699✔
1200
        var (
1,699✔
1201
                tlsFirst         bool
1,699✔
1202
                tlsFirstFallback time.Duration
1,699✔
1203
                infoTimeout      time.Duration
1,699✔
1204
        )
1,699✔
1205
        if remote != nil {
2,515✔
1206
                solicited = true
816✔
1207
                remote.Lock()
816✔
1208
                c.leaf.remote = remote
816✔
1209
                c.setPermissions(remote.perms)
816✔
1210
                if !c.leaf.remote.Hub {
1,616✔
1211
                        c.leaf.isSpoke = true
800✔
1212
                }
800✔
1213
                tlsFirst = remote.TLSHandshakeFirst
816✔
1214
                infoTimeout = remote.FirstInfoTimeout
816✔
1215
                remote.Unlock()
816✔
1216
                c.acc = acc
816✔
1217
        } else {
883✔
1218
                c.flags.set(expectConnect)
883✔
1219
                if ws != nil {
912✔
1220
                        c.Debugf("Leafnode compression=%v", c.ws.compress)
29✔
1221
                }
29✔
1222
                tlsFirst = opts.LeafNode.TLSHandshakeFirst
883✔
1223
                if f := opts.LeafNode.TLSHandshakeFirstFallback; f > 0 {
884✔
1224
                        tlsFirstFallback = f
1✔
1225
                }
1✔
1226
        }
1227
        c.mu.Unlock()
1,699✔
1228

1,699✔
1229
        var nonce [nonceLen]byte
1,699✔
1230
        var info *Info
1,699✔
1231

1,699✔
1232
        // Grab this before the client lock below.
1,699✔
1233
        if !solicited {
2,582✔
1234
                // Grab server variables
883✔
1235
                s.mu.Lock()
883✔
1236
                info = s.copyLeafNodeInfo()
883✔
1237
                // For tests that want to simulate old servers, do not set the compression
883✔
1238
                // on the INFO protocol if configured with CompressionNotSupported.
883✔
1239
                if cm := opts.LeafNode.Compression.Mode; cm != CompressionNotSupported {
1,765✔
1240
                        info.Compression = cm
882✔
1241
                }
882✔
1242
                // We always send a nonce for LEAF connections. Do not change that without
1243
                // taking into account presence of proxy trusted keys.
1244
                s.generateNonce(nonce[:])
883✔
1245
                s.mu.Unlock()
883✔
1246
        }
1247

1248
        // Grab lock
1249
        c.mu.Lock()
1,699✔
1250

1,699✔
1251
        var preBuf []byte
1,699✔
1252
        if solicited {
2,515✔
1253
                // For websocket connection, we need to send an HTTP request,
816✔
1254
                // and get the response before starting the readLoop to get
816✔
1255
                // the INFO, etc..
816✔
1256
                if c.isWebsocket() {
866✔
1257
                        var err error
50✔
1258
                        var closeReason ClosedState
50✔
1259

50✔
1260
                        preBuf, closeReason, err = c.leafNodeSolicitWSConnection(opts, rURL, remote)
50✔
1261
                        if err != nil {
71✔
1262
                                c.Errorf("Error soliciting websocket connection: %v", err)
21✔
1263
                                c.mu.Unlock()
21✔
1264
                                if closeReason != 0 {
38✔
1265
                                        c.closeConnection(closeReason)
17✔
1266
                                }
17✔
1267
                                return nil
21✔
1268
                        }
1269
                } else {
766✔
1270
                        // If configured to do TLS handshake first
766✔
1271
                        if tlsFirst {
770✔
1272
                                if _, err := c.leafClientHandshakeIfNeeded(remote, opts); err != nil {
5✔
1273
                                        c.mu.Unlock()
1✔
1274
                                        return nil
1✔
1275
                                }
1✔
1276
                        }
1277
                        // We need to wait for the info, but not for too long.
1278
                        c.nc.SetReadDeadline(time.Now().Add(infoTimeout))
765✔
1279
                }
1280

1281
                // We will process the INFO from the readloop and finish by
1282
                // sending the CONNECT and finish registration later.
1283
        } else {
883✔
1284
                // Send our info to the other side.
883✔
1285
                // Remember the nonce we sent here for signatures, etc.
883✔
1286
                c.nonce = make([]byte, nonceLen)
883✔
1287
                copy(c.nonce, nonce[:])
883✔
1288
                info.Nonce = bytesToString(c.nonce)
883✔
1289
                info.CID = c.cid
883✔
1290
                proto := generateInfoJSON(info)
883✔
1291

883✔
1292
                var pre []byte
883✔
1293
                // We need first to check for "TLS First" fallback delay.
883✔
1294
                if tlsFirstFallback > 0 {
884✔
1295
                        // We wait and see if we are getting any data. Since we did not send
1✔
1296
                        // the INFO protocol yet, only clients that use TLS first should be
1✔
1297
                        // sending data (the TLS handshake). We don't really check the content:
1✔
1298
                        // if it is a rogue agent and not an actual client performing the
1✔
1299
                        // TLS handshake, the error will be detected when performing the
1✔
1300
                        // handshake on our side.
1✔
1301
                        pre = make([]byte, 4)
1✔
1302
                        c.nc.SetReadDeadline(time.Now().Add(tlsFirstFallback))
1✔
1303
                        n, _ := io.ReadFull(c.nc, pre[:])
1✔
1304
                        c.nc.SetReadDeadline(time.Time{})
1✔
1305
                        // If we get any data (regardless of possible timeout), we will proceed
1✔
1306
                        // with the TLS handshake.
1✔
1307
                        if n > 0 {
1✔
1308
                                pre = pre[:n]
×
1309
                        } else {
1✔
1310
                                // We did not get anything so we will send the INFO protocol.
1✔
1311
                                pre = nil
1✔
1312
                                // Set the boolean to false for the rest of the function.
1✔
1313
                                tlsFirst = false
1✔
1314
                        }
1✔
1315
                }
1316

1317
                if !tlsFirst {
1,761✔
1318
                        // We have to send from this go routine because we may
878✔
1319
                        // have to block for TLS handshake before we start our
878✔
1320
                        // writeLoop go routine. The other side needs to receive
878✔
1321
                        // this before it can initiate the TLS handshake..
878✔
1322
                        c.sendProtoNow(proto)
878✔
1323

878✔
1324
                        // The above call could have marked the connection as closed (due to TCP error).
878✔
1325
                        if c.isClosed() {
878✔
1326
                                c.mu.Unlock()
×
1327
                                c.closeConnection(WriteError)
×
1328
                                return nil
×
1329
                        }
×
1330
                }
1331

1332
                // Check to see if we need to spin up TLS.
1333
                if !c.isWebsocket() && info.TLSRequired {
958✔
1334
                        // If we have a prebuffer create a multi-reader.
75✔
1335
                        if len(pre) > 0 {
75✔
1336
                                c.nc = &tlsMixConn{c.nc, bytes.NewBuffer(pre)}
×
1337
                        }
×
1338
                        // Perform server-side TLS handshake.
1339
                        if err := c.doTLSServerHandshake(tlsHandshakeLeaf, opts.LeafNode.TLSConfig, opts.LeafNode.TLSTimeout, opts.LeafNode.TLSPinnedCerts); err != nil {
123✔
1340
                                c.mu.Unlock()
48✔
1341
                                return nil
48✔
1342
                        }
48✔
1343
                }
1344

1345
                // If the user wants the TLS handshake to occur first, now that it is
1346
                // done, send the INFO protocol.
1347
                if tlsFirst {
838✔
1348
                        c.flags.set(didTLSFirst)
3✔
1349
                        c.sendProtoNow(proto)
3✔
1350
                        if c.isClosed() {
3✔
1351
                                c.mu.Unlock()
×
1352
                                c.closeConnection(WriteError)
×
1353
                                return nil
×
1354
                        }
×
1355
                }
1356

1357
                // Leaf nodes will always require a CONNECT to let us know
1358
                // when we are properly bound to an account.
1359
                //
1360
                // If compression is configured, we can't set the authTimer here because
1361
                // it would cause the parser to fail any incoming protocol that is not a
1362
                // CONNECT (and we need to exchange INFO protocols for compression
1363
                // negotiation). So instead, use the ping timer until we are done with
1364
                // negotiation and can set the auth timer.
1365
                timeout := secondsToDuration(opts.LeafNode.AuthTimeout)
835✔
1366
                if needsCompression(opts.LeafNode.Compression.Mode) {
1,452✔
1367
                        c.ping.tmr = time.AfterFunc(timeout, func() {
626✔
1368
                                c.authTimeout()
9✔
1369
                        })
9✔
1370
                } else {
218✔
1371
                        c.setAuthTimer(timeout)
218✔
1372
                }
218✔
1373
        }
1374

1375
        // Keep track in case server is shutdown before we can successfully register.
1376
        if !s.addToTempClients(c.cid, c) {
1,630✔
1377
                c.mu.Unlock()
1✔
1378
                c.setNoReconnect()
1✔
1379
                c.closeConnection(ServerShutdown)
1✔
1380
                return nil
1✔
1381
        }
1✔
1382

1383
        // Spin up the read loop.
1384
        s.startGoRoutine(func() { c.readLoop(preBuf) })
3,256✔
1385

1386
        // We will spin the write loop for solicited connections only
1387
        // when processing the INFO and after switching to TLS if needed.
1388
        if !solicited {
2,463✔
1389
                s.startGoRoutine(func() { c.writeLoop() })
1,670✔
1390
        }
1391

1392
        c.mu.Unlock()
1,628✔
1393

1,628✔
1394
        return c
1,628✔
1395
}
1396

1397
// Will perform the client-side TLS handshake if needed. Assumes that this
1398
// is called by the solicit side (remote will be non nil). Returns `true`
1399
// if TLS is required, `false` otherwise.
1400
// Lock held on entry.
1401
func (c *client) leafClientHandshakeIfNeeded(remote *leafNodeCfg, opts *Options) (bool, error) {
1,969✔
1402
        // Check if TLS is required and gather TLS config variables.
1,969✔
1403
        tlsRequired, tlsConfig, tlsName, tlsTimeout := c.leafNodeGetTLSConfigForSolicit(remote)
1,969✔
1404
        if !tlsRequired {
3,861✔
1405
                return false, nil
1,892✔
1406
        }
1,892✔
1407

1408
        // If TLS required, peform handshake.
1409
        // Get the URL that was used to connect to the remote server.
1410
        rURL := remote.getCurrentURL()
77✔
1411

77✔
1412
        // Perform the client-side TLS handshake.
77✔
1413
        if resetTLSName, err := c.doTLSClientHandshake(tlsHandshakeLeaf, rURL, tlsConfig, tlsName, tlsTimeout, opts.LeafNode.TLSPinnedCerts); err != nil {
113✔
1414
                // Check if we need to reset the remote's TLS name.
36✔
1415
                if resetTLSName {
36✔
1416
                        remote.Lock()
×
1417
                        remote.tlsName = _EMPTY_
×
1418
                        remote.Unlock()
×
1419
                }
×
1420
                return false, err
36✔
1421
        }
1422
        return true, nil
41✔
1423
}
1424

1425
func (c *client) processLeafnodeInfo(info *Info) {
2,723✔
1426
        c.mu.Lock()
2,723✔
1427
        if c.leaf == nil || c.isClosed() {
2,724✔
1428
                c.mu.Unlock()
1✔
1429
                return
1✔
1430
        }
1✔
1431
        s := c.srv
2,722✔
1432
        opts := s.getOpts()
2,722✔
1433
        remote := c.leaf.remote
2,722✔
1434
        didSolicit := remote != nil
2,722✔
1435
        firstINFO := !c.flags.isSet(infoReceived)
2,722✔
1436

2,722✔
1437
        // In case of websocket, the TLS handshake has been already done.
2,722✔
1438
        // So check only for non websocket connections and for configurations
2,722✔
1439
        // where the TLS Handshake was not done first.
2,722✔
1440
        if didSolicit && !c.flags.isSet(handshakeComplete) && !c.isWebsocket() && !remote.TLSHandshakeFirst {
4,637✔
1441
                // If the server requires TLS, we need to set this in the remote
1,915✔
1442
                // otherwise if there is no TLS configuration block for the remote,
1,915✔
1443
                // the solicit side will not attempt to perform the TLS handshake.
1,915✔
1444
                if firstINFO && info.TLSRequired {
1,976✔
1445
                        // Check for TLS/proxy configuration mismatch
61✔
1446
                        if remote.Proxy.URL != _EMPTY_ && !remote.TLS && remote.TLSConfig == nil {
61✔
1447
                                c.mu.Unlock()
×
1448
                                c.Errorf("TLS configuration mismatch: Hub requires TLS but leafnode remote is not configured for TLS. When using a proxy, ensure TLS leafnode configuration matches the Hub requirements.")
×
1449
                                c.closeConnection(TLSHandshakeError)
×
1450
                                return
×
1451
                        }
×
1452
                        remote.TLS = true
61✔
1453
                }
1454
                if _, err := c.leafClientHandshakeIfNeeded(remote, opts); err != nil {
1,946✔
1455
                        c.mu.Unlock()
31✔
1456
                        return
31✔
1457
                }
31✔
1458
        }
1459

1460
        // Check for compression, unless already done.
1461
        if firstINFO && !c.flags.isSet(compressionNegotiated) {
4,030✔
1462
                // Prevent from getting back here.
1,339✔
1463
                c.flags.set(compressionNegotiated)
1,339✔
1464

1,339✔
1465
                var co *CompressionOpts
1,339✔
1466
                if !didSolicit {
1,927✔
1467
                        co = &opts.LeafNode.Compression
588✔
1468
                } else {
1,339✔
1469
                        co = &remote.Compression
751✔
1470
                }
751✔
1471
                if needsCompression(co.Mode) {
2,669✔
1472
                        // Release client lock since following function will need server lock.
1,330✔
1473
                        c.mu.Unlock()
1,330✔
1474
                        compress, err := s.negotiateLeafCompression(c, didSolicit, info.Compression, co)
1,330✔
1475
                        if err != nil {
1,330✔
1476
                                c.sendErrAndErr(err.Error())
×
1477
                                c.closeConnection(ProtocolViolation)
×
1478
                                return
×
1479
                        }
×
1480
                        if compress {
2,525✔
1481
                                // Done for now, will get back another INFO protocol...
1,195✔
1482
                                return
1,195✔
1483
                        }
1,195✔
1484
                        // No compression because one side does not want/can't, so proceed.
1485
                        c.mu.Lock()
135✔
1486
                        // Check that the connection did not close if the lock was released.
135✔
1487
                        if c.isClosed() {
135✔
1488
                                c.mu.Unlock()
×
1489
                                return
×
1490
                        }
×
1491
                } else {
9✔
1492
                        // Coming from an old server, the Compression field would be the empty
9✔
1493
                        // string. For servers that are configured with CompressionNotSupported,
9✔
1494
                        // this makes them behave as old servers.
9✔
1495
                        if info.Compression == _EMPTY_ || co.Mode == CompressionNotSupported {
11✔
1496
                                c.leaf.compression = CompressionNotSupported
2✔
1497
                        } else {
9✔
1498
                                c.leaf.compression = CompressionOff
7✔
1499
                        }
7✔
1500
                }
1501
                // Accepting side does not normally process an INFO protocol during
1502
                // initial connection handshake. So we keep it consistent by returning
1503
                // if we are not soliciting.
1504
                if !didSolicit {
145✔
1505
                        // If we had created the ping timer instead of the auth timer, we will
1✔
1506
                        // clear the ping timer and set the auth timer now that the compression
1✔
1507
                        // negotiation is done.
1✔
1508
                        if info.Compression != _EMPTY_ && c.ping.tmr != nil {
1✔
1509
                                clearTimer(&c.ping.tmr)
×
1510
                                c.setAuthTimer(secondsToDuration(opts.LeafNode.AuthTimeout))
×
1511
                        }
×
1512
                        c.mu.Unlock()
1✔
1513
                        return
1✔
1514
                }
1515
                // Fall through and process the INFO protocol as usual.
1516
        }
1517

1518
        // Note: For now, only the initial INFO has a nonce. We
1519
        // will probably do auto key rotation at some point.
1520
        if firstINFO {
2,286✔
1521
                // Mark that the INFO protocol has been received.
791✔
1522
                c.flags.set(infoReceived)
791✔
1523
                // Prevent connecting to non leafnode port. Need to do this only for
791✔
1524
                // the first INFO, not for async INFO updates...
791✔
1525
                //
791✔
1526
                // Content of INFO sent by the server when accepting a tcp connection.
791✔
1527
                // -------------------------------------------------------------------
791✔
1528
                // Listen Port Of | CID | ClientConnectURLs | LeafNodeURLs | Gateway |
791✔
1529
                // -------------------------------------------------------------------
791✔
1530
                //      CLIENT    |  X* |        X**        |              |         |
791✔
1531
                //      ROUTE     |     |        X**        |      X***    |         |
791✔
1532
                //     GATEWAY    |     |                   |              |    X    |
791✔
1533
                //     LEAFNODE   |  X  |                   |       X      |         |
791✔
1534
                // -------------------------------------------------------------------
791✔
1535
                // *   Not on older servers.
791✔
1536
                // **  Not if "no advertise" is enabled.
791✔
1537
                // *** Not if leafnode's "no advertise" is enabled.
791✔
1538
                //
791✔
1539
                // As seen from above, a solicited LeafNode connection should receive
791✔
1540
                // from the remote server an INFO with CID and LeafNodeURLs. Anything
791✔
1541
                // else should be considered an attempt to connect to a wrong port.
791✔
1542
                if didSolicit && (info.CID == 0 || info.LeafNodeURLs == nil) {
853✔
1543
                        c.mu.Unlock()
62✔
1544
                        c.Errorf(ErrConnectedToWrongPort.Error())
62✔
1545
                        c.closeConnection(WrongPort)
62✔
1546
                        return
62✔
1547
                }
62✔
1548
                // Reject a cluster that contains spaces.
1549
                if info.Cluster != _EMPTY_ && strings.Contains(info.Cluster, " ") {
730✔
1550
                        c.mu.Unlock()
1✔
1551
                        c.sendErrAndErr(ErrClusterNameHasSpaces.Error())
1✔
1552
                        c.closeConnection(ProtocolViolation)
1✔
1553
                        return
1✔
1554
                }
1✔
1555
                // Capture a nonce here.
1556
                c.nonce = []byte(info.Nonce)
728✔
1557
                if info.TLSRequired && didSolicit {
758✔
1558
                        remote.TLS = true
30✔
1559
                }
30✔
1560
                supportsHeaders := c.srv.supportsHeaders()
728✔
1561
                c.headers = supportsHeaders && info.Headers
728✔
1562

728✔
1563
                // Remember the remote server.
728✔
1564
                // Pre 2.2.0 servers are not sending their server name.
728✔
1565
                // In that case, use info.ID, which, for those servers, matches
728✔
1566
                // the content of the field `Name` in the leafnode CONNECT protocol.
728✔
1567
                if info.Name == _EMPTY_ {
728✔
1568
                        c.leaf.remoteServer = info.ID
×
1569
                } else {
728✔
1570
                        c.leaf.remoteServer = info.Name
728✔
1571
                }
728✔
1572
                c.leaf.remoteDomain = info.Domain
728✔
1573
                c.leaf.remoteCluster = info.Cluster
728✔
1574
                // We send the protocol version in the INFO protocol.
728✔
1575
                // Keep track of it, so we know if this connection supports message
728✔
1576
                // tracing for instance.
728✔
1577
                c.opts.Protocol = info.Proto
728✔
1578
        }
1579

1580
        // For both initial INFO and async INFO protocols, Possibly
1581
        // update our list of remote leafnode URLs we can connect to.
1582
        if didSolicit && (len(info.LeafNodeURLs) > 0 || len(info.WSConnectURLs) > 0) {
2,774✔
1583
                // Consider the incoming array as the most up-to-date
1,342✔
1584
                // representation of the remote cluster's list of URLs.
1,342✔
1585
                c.updateLeafNodeURLs(info)
1,342✔
1586
        }
1,342✔
1587

1588
        // Check to see if we have permissions updates here.
1589
        if info.Import != nil || info.Export != nil {
1,448✔
1590
                perms := &Permissions{
16✔
1591
                        Publish:   info.Export,
16✔
1592
                        Subscribe: info.Import,
16✔
1593
                }
16✔
1594
                // Check if we have local deny clauses that we need to merge.
16✔
1595
                if remote := c.leaf.remote; remote != nil {
32✔
1596
                        if len(remote.DenyExports) > 0 {
17✔
1597
                                if perms.Publish == nil {
1✔
1598
                                        perms.Publish = &SubjectPermission{}
×
1599
                                }
×
1600
                                perms.Publish.Deny = append(perms.Publish.Deny, remote.DenyExports...)
1✔
1601
                        }
1602
                        if len(remote.DenyImports) > 0 {
17✔
1603
                                if perms.Subscribe == nil {
1✔
1604
                                        perms.Subscribe = &SubjectPermission{}
×
1605
                                }
×
1606
                                perms.Subscribe.Deny = append(perms.Subscribe.Deny, remote.DenyImports...)
1✔
1607
                        }
1608
                }
1609
                c.setPermissions(perms)
16✔
1610
        }
1611

1612
        var resumeConnect bool
1,432✔
1613

1,432✔
1614
        // If this is a remote connection and this is the first INFO protocol,
1,432✔
1615
        // then we need to finish the connect process by sending CONNECT, etc..
1,432✔
1616
        if firstINFO && didSolicit {
2,117✔
1617
                // Clear deadline that was set in createLeafNode while waiting for the INFO.
685✔
1618
                c.nc.SetDeadline(time.Time{})
685✔
1619
                resumeConnect = true
685✔
1620
        } else if !firstINFO && didSolicit {
2,089✔
1621
                c.leaf.remoteAccName = info.RemoteAccount
657✔
1622
        }
657✔
1623

1624
        // Check if we have the remote account information and if so make sure it's stored.
1625
        if info.RemoteAccount != _EMPTY_ {
2,078✔
1626
                s.leafRemoteAccounts.Store(c.acc.Name, info.RemoteAccount)
646✔
1627
        }
646✔
1628
        c.mu.Unlock()
1,432✔
1629

1,432✔
1630
        finishConnect := info.ConnectInfo
1,432✔
1631
        if resumeConnect && s != nil {
2,117✔
1632
                s.leafNodeResumeConnectProcess(c)
685✔
1633
                if !info.InfoOnConnect {
685✔
1634
                        finishConnect = true
×
1635
                }
×
1636
        }
1637
        if finishConnect {
2,078✔
1638
                s.leafNodeFinishConnectProcess(c)
646✔
1639
        }
646✔
1640

1641
        // Check to see if we need to kick any internal source or mirror consumers.
1642
        // This will be a no-op if JetStream not enabled for this server or if the bound account
1643
        // does not have jetstream.
1644
        s.checkInternalSyncConsumers(c.acc)
1,432✔
1645
}
1646

1647
func (s *Server) negotiateLeafCompression(c *client, didSolicit bool, infoCompression string, co *CompressionOpts) (bool, error) {
1,330✔
1648
        // Negotiate the appropriate compression mode (or no compression)
1,330✔
1649
        cm, err := selectCompressionMode(co.Mode, infoCompression)
1,330✔
1650
        if err != nil {
1,330✔
1651
                return false, err
×
1652
        }
×
1653
        c.mu.Lock()
1,330✔
1654
        // For "auto" mode, set the initial compression mode based on RTT
1,330✔
1655
        if cm == CompressionS2Auto {
2,493✔
1656
                if c.rttStart.IsZero() {
2,326✔
1657
                        c.rtt = computeRTT(c.start)
1,163✔
1658
                }
1,163✔
1659
                cm = selectS2AutoModeBasedOnRTT(c.rtt, co.RTTThresholds)
1,163✔
1660
        }
1661
        // Keep track of the negotiated compression mode.
1662
        c.leaf.compression = cm
1,330✔
1663
        cid := c.cid
1,330✔
1664
        var nonce string
1,330✔
1665
        if !didSolicit {
1,917✔
1666
                nonce = bytesToString(c.nonce)
587✔
1667
        }
587✔
1668
        c.mu.Unlock()
1,330✔
1669

1,330✔
1670
        if !needsCompression(cm) {
1,465✔
1671
                return false, nil
135✔
1672
        }
135✔
1673

1674
        // If we end-up doing compression...
1675

1676
        // Generate an INFO with the chosen compression mode.
1677
        s.mu.Lock()
1,195✔
1678
        info := s.copyLeafNodeInfo()
1,195✔
1679
        info.Compression, info.CID, info.Nonce = compressionModeForInfoProtocol(co, cm), cid, nonce
1,195✔
1680
        infoProto := generateInfoJSON(info)
1,195✔
1681
        s.mu.Unlock()
1,195✔
1682

1,195✔
1683
        // If we solicited, then send this INFO protocol BEFORE switching
1,195✔
1684
        // to compression writer. However, if we did not, we send it after.
1,195✔
1685
        c.mu.Lock()
1,195✔
1686
        if didSolicit {
1,803✔
1687
                c.enqueueProto(infoProto)
608✔
1688
                // Make sure it is completely flushed (the pending bytes goes to
608✔
1689
                // 0) before proceeding.
608✔
1690
                for c.out.pb > 0 && !c.isClosed() {
1,215✔
1691
                        c.flushOutbound()
607✔
1692
                }
607✔
1693
        }
1694
        // This is to notify the readLoop that it should switch to a
1695
        // (de)compression reader.
1696
        c.in.flags.set(switchToCompression)
1,195✔
1697
        // Create the compress writer before queueing the INFO protocol for
1,195✔
1698
        // a route that did not solicit. It will make sure that that proto
1,195✔
1699
        // is sent with compression on.
1,195✔
1700
        c.out.cw = s2.NewWriter(nil, s2WriterOptions(cm)...)
1,195✔
1701
        if !didSolicit {
1,782✔
1702
                c.enqueueProto(infoProto)
587✔
1703
        }
587✔
1704
        c.mu.Unlock()
1,195✔
1705
        return true, nil
1,195✔
1706
}
1707

1708
// When getting a leaf node INFO protocol, use the provided
1709
// array of urls to update the list of possible endpoints.
1710
func (c *client) updateLeafNodeURLs(info *Info) {
1,342✔
1711
        cfg := c.leaf.remote
1,342✔
1712
        cfg.Lock()
1,342✔
1713
        defer cfg.Unlock()
1,342✔
1714

1,342✔
1715
        // We have ensured that if a remote has a WS scheme, then all are.
1,342✔
1716
        // So check if first is WS, then add WS URLs, otherwise, add non WS ones.
1,342✔
1717
        if len(cfg.URLs) > 0 && isWSURL(cfg.URLs[0]) {
1,400✔
1718
                // It does not really matter if we use "ws://" or "wss://" here since
58✔
1719
                // we will have already marked that the remote should use TLS anyway.
58✔
1720
                // But use proper scheme for log statements, etc...
58✔
1721
                proto := wsSchemePrefix
58✔
1722
                if cfg.TLS {
58✔
1723
                        proto = wsSchemePrefixTLS
×
1724
                }
×
1725
                c.doUpdateLNURLs(cfg, proto, info.WSConnectURLs)
58✔
1726
                return
58✔
1727
        }
1728
        c.doUpdateLNURLs(cfg, "nats-leaf", info.LeafNodeURLs)
1,284✔
1729
}
1730

1731
func (c *client) doUpdateLNURLs(cfg *leafNodeCfg, scheme string, URLs []string) {
1,342✔
1732
        cfg.urls = make([]*url.URL, 0, 1+len(URLs))
1,342✔
1733
        // Add the ones we receive in the protocol
1,342✔
1734
        for _, surl := range URLs {
3,708✔
1735
                url, err := url.Parse(fmt.Sprintf("%s://%s", scheme, surl))
2,366✔
1736
                if err != nil {
2,366✔
1737
                        // As per below, the URLs we receive should not have contained URL info, so this should be safe to log.
×
1738
                        c.Errorf("Error parsing url %q: %v", surl, err)
×
1739
                        continue
×
1740
                }
1741
                // Do not add if it's the same as what we already have configured.
1742
                var dup bool
2,366✔
1743
                for _, u := range cfg.URLs {
5,974✔
1744
                        // URLs that we receive never have user info, but the
3,608✔
1745
                        // ones that were configured may have. Simply compare
3,608✔
1746
                        // host and port to decide if they are equal or not.
3,608✔
1747
                        if url.Host == u.Host && url.Port() == u.Port() {
5,326✔
1748
                                dup = true
1,718✔
1749
                                break
1,718✔
1750
                        }
1751
                }
1752
                if !dup {
3,014✔
1753
                        cfg.urls = append(cfg.urls, url)
648✔
1754
                        cfg.saveTLSHostname(url)
648✔
1755
                }
648✔
1756
        }
1757
        // Add the configured one
1758
        cfg.urls = append(cfg.urls, cfg.URLs...)
1,342✔
1759
}
1760

1761
// Similar to setInfoHostPortAndGenerateJSON, but for leafNodeInfo.
1762
func (s *Server) setLeafNodeInfoHostPortAndIP() error {
4,022✔
1763
        opts := s.getOpts()
4,022✔
1764
        if opts.LeafNode.Advertise != _EMPTY_ {
4,033✔
1765
                advHost, advPort, err := parseHostPort(opts.LeafNode.Advertise, opts.LeafNode.Port)
11✔
1766
                if err != nil {
11✔
1767
                        return err
×
1768
                }
×
1769
                s.leafNodeInfo.Host = advHost
11✔
1770
                s.leafNodeInfo.Port = advPort
11✔
1771
        } else {
4,011✔
1772
                s.leafNodeInfo.Host = opts.LeafNode.Host
4,011✔
1773
                s.leafNodeInfo.Port = opts.LeafNode.Port
4,011✔
1774
                // If the host is "0.0.0.0" or "::" we need to resolve to a public IP.
4,011✔
1775
                // This will return at most 1 IP.
4,011✔
1776
                hostIsIPAny, ips, err := s.getNonLocalIPsIfHostIsIPAny(s.leafNodeInfo.Host, false)
4,011✔
1777
                if err != nil {
4,011✔
1778
                        return err
×
1779
                }
×
1780
                if hostIsIPAny {
4,316✔
1781
                        if len(ips) == 0 {
305✔
1782
                                s.Errorf("Could not find any non-local IP for leafnode's listen specification %q",
×
1783
                                        s.leafNodeInfo.Host)
×
1784
                        } else {
305✔
1785
                                // Take the first from the list...
305✔
1786
                                s.leafNodeInfo.Host = ips[0]
305✔
1787
                        }
305✔
1788
                }
1789
        }
1790
        // Use just host:port for the IP
1791
        s.leafNodeInfo.IP = net.JoinHostPort(s.leafNodeInfo.Host, strconv.Itoa(s.leafNodeInfo.Port))
4,022✔
1792
        if opts.LeafNode.Advertise != _EMPTY_ {
4,033✔
1793
                s.Noticef("Advertise address for leafnode is set to %s", s.leafNodeInfo.IP)
11✔
1794
        }
11✔
1795
        return nil
4,022✔
1796
}
1797

1798
// Add the connection to the map of leaf nodes.
1799
// If `checkForDup` is true (invoked when a leafnode is accepted), then we check
1800
// if a connection already exists for the same server name and account.
1801
// That can happen when the remote is attempting to reconnect while the accepting
1802
// side did not detect the connection as broken yet.
1803
// But it can also happen when there is a misconfiguration and the remote is
1804
// creating two (or more) connections that bind to the same account on the accept
1805
// side.
1806
// When a duplicate is found, the new connection is accepted and the old is closed
1807
// (this solves the stale connection situation). An error is returned to help the
1808
// remote detect the misconfiguration when the duplicate is the result of that
1809
// misconfiguration.
1810
func (s *Server) addLeafNodeConnection(c *client, srvName, clusterName string, checkForDup bool) {
1,330✔
1811
        var accName string
1,330✔
1812
        c.mu.Lock()
1,330✔
1813
        cid := c.cid
1,330✔
1814
        acc := c.acc
1,330✔
1815
        if acc != nil {
2,660✔
1816
                accName = acc.Name
1,330✔
1817
        }
1,330✔
1818
        myRemoteDomain := c.leaf.remoteDomain
1,330✔
1819
        mySrvName := c.leaf.remoteServer
1,330✔
1820
        remoteAccName := c.leaf.remoteAccName
1,330✔
1821
        myClustName := c.leaf.remoteCluster
1,330✔
1822
        solicited := c.leaf.remote != nil
1,330✔
1823
        c.mu.Unlock()
1,330✔
1824

1,330✔
1825
        var old *client
1,330✔
1826
        s.mu.Lock()
1,330✔
1827
        // We check for empty because in some test we may send empty CONNECT{}
1,330✔
1828
        if checkForDup && srvName != _EMPTY_ {
1,977✔
1829
                for _, ol := range s.leafs {
1,028✔
1830
                        ol.mu.Lock()
381✔
1831
                        // We care here only about non solicited Leafnode. This function
381✔
1832
                        // is more about replacing stale connections than detecting loops.
381✔
1833
                        // We have code for the loop detection elsewhere, which also delays
381✔
1834
                        // attempt to reconnect.
381✔
1835
                        if !ol.isSolicitedLeafNode() && ol.leaf.remoteServer == srvName &&
381✔
1836
                                ol.leaf.remoteCluster == clusterName && ol.acc.Name == accName &&
381✔
1837
                                remoteAccName != _EMPTY_ && ol.leaf.remoteAccName == remoteAccName {
384✔
1838
                                old = ol
3✔
1839
                        }
3✔
1840
                        ol.mu.Unlock()
381✔
1841
                        if old != nil {
384✔
1842
                                break
3✔
1843
                        }
1844
                }
1845
        }
1846
        // Store new connection in the map
1847
        s.leafs[cid] = c
1,330✔
1848
        s.mu.Unlock()
1,330✔
1849
        s.removeFromTempClients(cid)
1,330✔
1850

1,330✔
1851
        // If applicable, evict the old one.
1,330✔
1852
        if old != nil {
1,333✔
1853
                old.sendErrAndErr(DuplicateRemoteLeafnodeConnection.String())
3✔
1854
                old.closeConnection(DuplicateRemoteLeafnodeConnection)
3✔
1855
                c.Warnf("Replacing connection from same server")
3✔
1856
        }
3✔
1857

1858
        srvDecorated := func() string {
1,543✔
1859
                if myClustName == _EMPTY_ {
240✔
1860
                        return mySrvName
27✔
1861
                }
27✔
1862
                return fmt.Sprintf("%s/%s", mySrvName, myClustName)
186✔
1863
        }
1864

1865
        opts := s.getOpts()
1,330✔
1866
        sysAcc := s.SystemAccount()
1,330✔
1867
        js := s.getJetStream()
1,330✔
1868
        var meta *raft
1,330✔
1869
        if js != nil {
1,872✔
1870
                if mg := js.getMetaGroup(); mg != nil {
967✔
1871
                        meta = mg.(*raft)
425✔
1872
                }
425✔
1873
        }
1874
        blockMappingOutgoing := false
1,330✔
1875
        // Deny (non domain) JetStream API traffic unless system account is shared
1,330✔
1876
        // and domain names are identical and extending is not disabled
1,330✔
1877

1,330✔
1878
        // Check if backwards compatibility has been enabled and needs to be acted on
1,330✔
1879
        forceSysAccDeny := false
1,330✔
1880
        if len(opts.JsAccDefaultDomain) > 0 {
1,367✔
1881
                if acc == sysAcc {
48✔
1882
                        for _, d := range opts.JsAccDefaultDomain {
22✔
1883
                                if d == _EMPTY_ {
19✔
1884
                                        // Extending JetStream via leaf node is mutually exclusive with a domain mapping to the empty/default domain.
8✔
1885
                                        // As soon as one mapping to "" is found, disable the ability to extend JS via a leaf node.
8✔
1886
                                        c.Noticef("Not extending remote JetStream domain %q due to presence of empty default domain", myRemoteDomain)
8✔
1887
                                        forceSysAccDeny = true
8✔
1888
                                        break
8✔
1889
                                }
1890
                        }
1891
                } else if domain, ok := opts.JsAccDefaultDomain[accName]; ok && domain == _EMPTY_ {
41✔
1892
                        // for backwards compatibility with old setups that do not have a domain name set
15✔
1893
                        c.Debugf("Skipping deny %q for account %q due to default domain", jsAllAPI, accName)
15✔
1894
                        return
15✔
1895
                }
15✔
1896
        }
1897

1898
        // If the server has JS disabled, it may still be part of a JetStream that could be extended.
1899
        // This is either signaled by js being disabled and a domain set,
1900
        // or in cases where no domain name exists, an extension hint is set.
1901
        // However, this is only relevant in mixed setups.
1902
        //
1903
        // If the system account connects but default domains are present, JetStream can't be extended.
1904
        if opts.JetStreamDomain != myRemoteDomain || (!opts.JetStream && (opts.JetStreamDomain == _EMPTY_ && opts.JetStreamExtHint != jsWillExtend)) ||
1,315✔
1905
                sysAcc == nil || acc == nil || forceSysAccDeny {
2,472✔
1906
                // If domain names mismatch always deny. This applies to system accounts as well as non system accounts.
1,157✔
1907
                // Not having a system account, account or JetStream disabled is considered a mismatch as well.
1,157✔
1908
                if acc != nil && acc == sysAcc {
1,298✔
1909
                        c.Noticef("System account connected from %s", srvDecorated())
141✔
1910
                        c.Noticef("JetStream not extended, domains differ")
141✔
1911
                        c.mergeDenyPermissionsLocked(both, denyAllJs)
141✔
1912
                        // When a remote with a system account is present in a server, unless otherwise disabled, the server will be
141✔
1913
                        // started in observer mode. Now that it is clear that this not used, turn the observer mode off.
141✔
1914
                        if solicited && meta != nil && meta.IsObserver() {
170✔
1915
                                meta.setObserver(false, extNotExtended)
29✔
1916
                                c.Debugf("Turning JetStream metadata controller Observer Mode off")
29✔
1917
                                // Take note that the domain was not extended to avoid this state from startup.
29✔
1918
                                writePeerState(js.config.StoreDir, meta.currentPeerState())
29✔
1919
                                // Meta controller can't be leader yet.
29✔
1920
                                // Yet it is possible that due to observer mode every server already stopped campaigning.
29✔
1921
                                // Therefore this server needs to be kicked into campaigning gear explicitly.
29✔
1922
                                meta.Campaign()
29✔
1923
                        }
29✔
1924
                } else {
1,016✔
1925
                        c.Noticef("JetStream using domains: local %q, remote %q", opts.JetStreamDomain, myRemoteDomain)
1,016✔
1926
                        c.mergeDenyPermissionsLocked(both, denyAllClientJs)
1,016✔
1927
                }
1,016✔
1928
                blockMappingOutgoing = true
1,157✔
1929
        } else if acc == sysAcc {
230✔
1930
                // system account and same domain
72✔
1931
                s.sys.client.Noticef("Extending JetStream domain %q as System Account connected from server %s",
72✔
1932
                        myRemoteDomain, srvDecorated())
72✔
1933
                // In an extension use case, pin leadership to server remotes connect to.
72✔
1934
                // Therefore, server with a remote that are not already in observer mode, need to be put into it.
72✔
1935
                if solicited && meta != nil && !meta.IsObserver() {
76✔
1936
                        meta.setObserver(true, extExtended)
4✔
1937
                        c.Debugf("Turning JetStream metadata controller Observer Mode on - System Account Connected")
4✔
1938
                        // Take note that the domain was not extended to avoid this state next startup.
4✔
1939
                        writePeerState(js.config.StoreDir, meta.currentPeerState())
4✔
1940
                        // If this server is the leader already, step down so a new leader can be elected (that is not an observer)
4✔
1941
                        meta.StepDown()
4✔
1942
                }
4✔
1943
        } else {
86✔
1944
                // This deny is needed in all cases (system account shared or not)
86✔
1945
                // If the system account is shared, jsAllAPI traffic will go through the system account.
86✔
1946
                // So in order to prevent duplicate delivery (from system and actual account) suppress it on the account.
86✔
1947
                // If the system account is NOT shared, jsAllAPI traffic has no business
86✔
1948
                c.Debugf("Adding deny %+v for account %q", denyAllClientJs, accName)
86✔
1949
                c.mergeDenyPermissionsLocked(both, denyAllClientJs)
86✔
1950
        }
86✔
1951
        // If we have a specified JetStream domain we will want to add a mapping to
1952
        // allow access cross domain for each non-system account.
1953
        if opts.JetStreamDomain != _EMPTY_ && opts.JetStream && acc != nil && acc != sysAcc {
1,567✔
1954
                for src, dest := range generateJSMappingTable(opts.JetStreamDomain) {
2,520✔
1955
                        if err := acc.AddMapping(src, dest); err != nil {
2,268✔
1956
                                c.Debugf("Error adding JetStream domain mapping: %s", err.Error())
×
1957
                        } else {
2,268✔
1958
                                c.Debugf("Adding JetStream Domain Mapping %q -> %s to account %q", src, dest, accName)
2,268✔
1959
                        }
2,268✔
1960
                }
1961
                if blockMappingOutgoing {
473✔
1962
                        src := fmt.Sprintf(jsDomainAPI, opts.JetStreamDomain)
221✔
1963
                        // make sure that messages intended for this domain, do not leave the cluster via this leaf node connection
221✔
1964
                        // This is a guard against a miss-config with two identical domain names and will only cover some forms
221✔
1965
                        // of this issue, not all of them.
221✔
1966
                        // This guards against a hub and a spoke having the same domain name.
221✔
1967
                        // But not two spokes having the same one and the request coming from the hub.
221✔
1968
                        c.mergeDenyPermissionsLocked(pub, []string{src})
221✔
1969
                        c.Debugf("Adding deny %q for outgoing messages to account %q", src, accName)
221✔
1970
                }
221✔
1971
        }
1972
}
1973

1974
func (s *Server) removeLeafNodeConnection(c *client) {
1,701✔
1975
        c.mu.Lock()
1,701✔
1976
        cid := c.cid
1,701✔
1977
        if c.leaf != nil {
3,402✔
1978
                if c.leaf.tsubt != nil {
2,916✔
1979
                        c.leaf.tsubt.Stop()
1,215✔
1980
                        c.leaf.tsubt = nil
1,215✔
1981
                }
1,215✔
1982
                if c.leaf.gwSub != nil {
2,345✔
1983
                        s.gwLeafSubs.Remove(c.leaf.gwSub)
644✔
1984
                        // We need to set this to nil for GC to release the connection
644✔
1985
                        c.leaf.gwSub = nil
644✔
1986
                }
644✔
1987
        }
1988
        proxyKey := c.proxyKey
1,701✔
1989
        c.mu.Unlock()
1,701✔
1990
        s.mu.Lock()
1,701✔
1991
        delete(s.leafs, cid)
1,701✔
1992
        if proxyKey != _EMPTY_ {
1,705✔
1993
                s.removeProxiedConn(proxyKey, cid)
4✔
1994
        }
4✔
1995
        s.mu.Unlock()
1,701✔
1996
        s.removeFromTempClients(cid)
1,701✔
1997
}
1998

1999
// Connect information for solicited leafnodes.
2000
type leafConnectInfo struct {
2001
        Version   string   `json:"version,omitempty"`
2002
        Nkey      string   `json:"nkey,omitempty"`
2003
        JWT       string   `json:"jwt,omitempty"`
2004
        Sig       string   `json:"sig,omitempty"`
2005
        User      string   `json:"user,omitempty"`
2006
        Pass      string   `json:"pass,omitempty"`
2007
        Token     string   `json:"auth_token,omitempty"`
2008
        ID        string   `json:"server_id,omitempty"`
2009
        Domain    string   `json:"domain,omitempty"`
2010
        Name      string   `json:"name,omitempty"`
2011
        Hub       bool     `json:"is_hub,omitempty"`
2012
        Cluster   string   `json:"cluster,omitempty"`
2013
        Headers   bool     `json:"headers,omitempty"`
2014
        JetStream bool     `json:"jetstream,omitempty"`
2015
        DenyPub   []string `json:"deny_pub,omitempty"`
2016
        Isolate   bool     `json:"isolate,omitempty"`
2017

2018
        // There was an existing field called:
2019
        // >> Comp bool `json:"compression,omitempty"`
2020
        // that has never been used. With support for compression, we now need
2021
        // a field that is a string. So we use a different json tag:
2022
        Compression string `json:"compress_mode,omitempty"`
2023

2024
        // Just used to detect wrong connection attempts.
2025
        Gateway string `json:"gateway,omitempty"`
2026

2027
        // Tells the accept side which account the remote is binding to.
2028
        RemoteAccount string `json:"remote_account,omitempty"`
2029

2030
        // The accept side of a LEAF connection, unlike ROUTER and GATEWAY, receives
2031
        // only the CONNECT protocol, and no INFO. So we need to send the protocol
2032
        // version as part of the CONNECT. It will indicate if a connection supports
2033
        // some features, such as message tracing.
2034
        // We use `protocol` as the JSON tag, so this is automatically unmarshal'ed
2035
        // in the low level process CONNECT.
2036
        Proto int `json:"protocol,omitempty"`
2037
}
2038

2039
// processLeafNodeConnect will process the inbound connect args.
2040
// Once we are here we are bound to an account, so can send any interest that
2041
// we would have to the other side.
2042
func (c *client) processLeafNodeConnect(s *Server, arg []byte, lang string) error {
692✔
2043
        // Way to detect clients that incorrectly connect to the route listen
692✔
2044
        // port. Client provided "lang" in the CONNECT protocol while LEAFNODEs don't.
692✔
2045
        if lang != _EMPTY_ {
692✔
2046
                c.sendErrAndErr(ErrClientConnectedToLeafNodePort.Error())
×
2047
                c.closeConnection(WrongPort)
×
2048
                return ErrClientConnectedToLeafNodePort
×
2049
        }
×
2050

2051
        // Unmarshal as a leaf node connect protocol
2052
        proto := &leafConnectInfo{}
692✔
2053
        if err := json.Unmarshal(arg, proto); err != nil {
692✔
2054
                return err
×
2055
        }
×
2056

2057
        // Reject a cluster that contains spaces.
2058
        if proto.Cluster != _EMPTY_ && strings.Contains(proto.Cluster, " ") {
693✔
2059
                c.sendErrAndErr(ErrClusterNameHasSpaces.Error())
1✔
2060
                c.closeConnection(ProtocolViolation)
1✔
2061
                return ErrClusterNameHasSpaces
1✔
2062
        }
1✔
2063

2064
        // Check for cluster name collisions.
2065
        if cn := s.cachedClusterName(); cn != _EMPTY_ && proto.Cluster != _EMPTY_ && proto.Cluster == cn {
695✔
2066
                c.sendErrAndErr(ErrLeafNodeHasSameClusterName.Error())
4✔
2067
                c.closeConnection(ClusterNamesIdentical)
4✔
2068
                return ErrLeafNodeHasSameClusterName
4✔
2069
        }
4✔
2070

2071
        // Reject if this has Gateway which means that it would be from a gateway
2072
        // connection that incorrectly connects to the leafnode port.
2073
        if proto.Gateway != _EMPTY_ {
687✔
2074
                errTxt := fmt.Sprintf("Rejecting connection from gateway %q on the leafnode port", proto.Gateway)
×
2075
                c.Errorf(errTxt)
×
2076
                c.sendErr(errTxt)
×
2077
                c.closeConnection(WrongGateway)
×
2078
                return ErrWrongGateway
×
2079
        }
×
2080

2081
        if mv := s.getOpts().LeafNode.MinVersion; mv != _EMPTY_ {
689✔
2082
                major, minor, update, _ := versionComponents(mv)
2✔
2083
                if !versionAtLeast(proto.Version, major, minor, update) {
3✔
2084
                        // Send back an INFO so recent remote servers process the rejection
1✔
2085
                        // cleanly, then close immediately. The soliciting side applies the
1✔
2086
                        // reconnect delay when it processes the error.
1✔
2087
                        s.sendPermsAndAccountInfo(c)
1✔
2088
                        c.sendErrAndErr(fmt.Sprintf("%s %q", ErrLeafNodeMinVersionRejected, mv))
1✔
2089
                        c.closeConnection(MinimumVersionRequired)
1✔
2090
                        return ErrMinimumVersionRequired
1✔
2091
                }
1✔
2092
        }
2093

2094
        // Check if this server supports headers.
2095
        supportHeaders := c.srv.supportsHeaders()
686✔
2096

686✔
2097
        c.mu.Lock()
686✔
2098
        // Leaf Nodes do not do echo or verbose or pedantic.
686✔
2099
        c.opts.Verbose = false
686✔
2100
        c.opts.Echo = false
686✔
2101
        c.opts.Pedantic = false
686✔
2102
        // This inbound connection will be marked as supporting headers if this server
686✔
2103
        // support headers and the remote has sent in the CONNECT protocol that it does
686✔
2104
        // support headers too.
686✔
2105
        c.headers = supportHeaders && proto.Headers
686✔
2106
        // If the compression level is still not set, set it based on what has been
686✔
2107
        // given to us in the CONNECT protocol.
686✔
2108
        if c.leaf.compression == _EMPTY_ {
815✔
2109
                // But if proto.Compression is _EMPTY_, set it to CompressionNotSupported
129✔
2110
                if proto.Compression == _EMPTY_ {
168✔
2111
                        c.leaf.compression = CompressionNotSupported
39✔
2112
                } else {
129✔
2113
                        c.leaf.compression = proto.Compression
90✔
2114
                }
90✔
2115
        }
2116

2117
        // Remember the remote server.
2118
        c.leaf.remoteServer = proto.Name
686✔
2119
        // Remember the remote account name
686✔
2120
        c.leaf.remoteAccName = proto.RemoteAccount
686✔
2121
        // Remember if the leafnode requested isolation.
686✔
2122
        c.leaf.isolated = c.leaf.isolated || proto.Isolate
686✔
2123

686✔
2124
        // If the other side has declared itself a hub, so we will take on the spoke role.
686✔
2125
        if proto.Hub {
702✔
2126
                c.leaf.isSpoke = true
16✔
2127
        }
16✔
2128

2129
        // The soliciting side is part of a cluster.
2130
        if proto.Cluster != _EMPTY_ {
1,213✔
2131
                c.leaf.remoteCluster = proto.Cluster
527✔
2132
        }
527✔
2133

2134
        c.leaf.remoteDomain = proto.Domain
686✔
2135

686✔
2136
        // When a leaf solicits a connection to a hub, the perms that it will use on the soliciting leafnode's
686✔
2137
        // behalf are correct for them, but inside the hub need to be reversed since data is flowing in the opposite direction.
686✔
2138
        if !c.isSolicitedLeafNode() && c.perms != nil {
703✔
2139
                sp, pp := c.perms.sub, c.perms.pub
17✔
2140
                c.perms.sub, c.perms.pub = pp, sp
17✔
2141
                if c.opts.Import != nil {
33✔
2142
                        c.darray = c.opts.Import.Deny
16✔
2143
                } else {
17✔
2144
                        c.darray = nil
1✔
2145
                }
1✔
2146
        }
2147

2148
        // Set the Ping timer
2149
        c.setFirstPingTimer()
686✔
2150

686✔
2151
        // If we received pub deny permissions from the other end, merge with existing ones.
686✔
2152
        c.mergeDenyPermissions(pub, proto.DenyPub)
686✔
2153

686✔
2154
        acc := c.acc
686✔
2155
        c.mu.Unlock()
686✔
2156

686✔
2157
        // If the account is not set (e.g. connection was closed due to auth
686✔
2158
        // timeout while still being processed), bail out to avoid a panic.
686✔
2159
        if acc == nil {
686✔
2160
                c.closeConnection(MissingAccount)
×
2161
                return ErrMissingAccount
×
2162
        }
×
2163

2164
        // Register the cluster, even if empty, as long as we are acting as a hub.
2165
        if !proto.Hub {
1,356✔
2166
                acc.registerLeafNodeCluster(proto.Cluster)
670✔
2167
        }
670✔
2168

2169
        // Add in the leafnode here since we passed through auth at this point.
2170
        s.addLeafNodeConnection(c, proto.Name, proto.Cluster, true)
686✔
2171

686✔
2172
        // If we have permissions bound to this leafnode we need to send then back to the
686✔
2173
        // origin server for local enforcement.
686✔
2174
        s.sendPermsAndAccountInfo(c)
686✔
2175

686✔
2176
        // Create and initialize the smap since we know our bound account now.
686✔
2177
        // This will send all registered subs too.
686✔
2178
        s.initLeafNodeSmapAndSendSubs(c)
686✔
2179

686✔
2180
        // Announce the account connect event for a leaf node.
686✔
2181
        // This will be a no-op as needed.
686✔
2182
        s.sendLeafNodeConnect(c.acc)
686✔
2183

686✔
2184
        // Check to see if we need to kick any internal source or mirror consumers.
686✔
2185
        // This will be a no-op if JetStream not enabled for this server or if the bound account
686✔
2186
        // does not have jetstream.
686✔
2187
        s.checkInternalSyncConsumers(acc)
686✔
2188

686✔
2189
        return nil
686✔
2190
}
2191

2192
// checkInternalSyncConsumers
2193
func (s *Server) checkInternalSyncConsumers(acc *Account) {
2,118✔
2194
        // Grab our js
2,118✔
2195
        js := s.getJetStream()
2,118✔
2196

2,118✔
2197
        // Only applicable if we have JS and the leafnode has JS as well.
2,118✔
2198
        // We check for remote JS outside.
2,118✔
2199
        if !js.isEnabled() || acc == nil {
3,339✔
2200
                return
1,221✔
2201
        }
1,221✔
2202

2203
        // We will check all streams in our local account. They must be a leader and
2204
        // be sourcing or mirroring. We will check the external config on the stream itself
2205
        // if this is cross domain, or if the remote domain is empty, meaning we might be
2206
        // extending the system across this leafnode connection and hence we would be extending
2207
        // our own domain.
2208
        jsa := js.lookupAccount(acc)
897✔
2209
        if jsa == nil {
1,248✔
2210
                return
351✔
2211
        }
351✔
2212

2213
        var streams []*stream
546✔
2214
        jsa.mu.RLock()
546✔
2215
        for _, mset := range jsa.streams {
603✔
2216
                mset.cfgMu.RLock()
57✔
2217
                // We need to have a mirror or source defined.
57✔
2218
                // We do not want to force another lock here to look for leader status,
57✔
2219
                // so collect and after we release jsa will make sure.
57✔
2220
                if mset.cfg.Mirror != nil || len(mset.cfg.Sources) > 0 {
69✔
2221
                        streams = append(streams, mset)
12✔
2222
                }
12✔
2223
                mset.cfgMu.RUnlock()
57✔
2224
        }
2225
        jsa.mu.RUnlock()
546✔
2226

546✔
2227
        // Now loop through all candidates and check if we are the leader and have NOT
546✔
2228
        // created the sync up consumer.
546✔
2229
        for _, mset := range streams {
558✔
2230
                mset.retryDisconnectedSyncConsumers()
12✔
2231
        }
12✔
2232
}
2233

2234
// Returns the remote cluster name. This is set only once so does not require a lock.
2235
func (c *client) remoteCluster() string {
165,833✔
2236
        if c.leaf == nil {
165,833✔
2237
                return _EMPTY_
×
2238
        }
×
2239
        return c.leaf.remoteCluster
165,833✔
2240
}
2241

2242
// Sends back an info block to the soliciting leafnode to let it know about
2243
// its permission settings for local enforcement.
2244
func (s *Server) sendPermsAndAccountInfo(c *client) {
687✔
2245
        // Copy
687✔
2246
        s.mu.Lock()
687✔
2247
        info := s.copyLeafNodeInfo()
687✔
2248
        s.mu.Unlock()
687✔
2249
        c.mu.Lock()
687✔
2250
        info.CID = c.cid
687✔
2251
        info.Import = c.opts.Import
687✔
2252
        info.Export = c.opts.Export
687✔
2253
        info.RemoteAccount = c.acc.Name
687✔
2254
        // s.SystemAccount() uses an atomic operation and does not get the server lock, so this is safe.
687✔
2255
        info.IsSystemAccount = c.acc == s.SystemAccount()
687✔
2256
        info.ConnectInfo = true
687✔
2257
        c.enqueueProto(generateInfoJSON(info))
687✔
2258
        c.mu.Unlock()
687✔
2259
}
687✔
2260

2261
// Snapshot the current subscriptions from the sublist into our smap which
2262
// we will keep updated from now on.
2263
// Also send the registered subscriptions.
2264
func (s *Server) initLeafNodeSmapAndSendSubs(c *client) {
1,330✔
2265
        acc := c.acc
1,330✔
2266
        if acc == nil {
1,330✔
2267
                c.Debugf("Leafnode does not have an account bound")
×
2268
                return
×
2269
        }
×
2270
        // Collect all account subs here.
2271
        _subs := [1024]*subscription{}
1,330✔
2272
        subs := _subs[:0]
1,330✔
2273
        ims := []string{}
1,330✔
2274

1,330✔
2275
        // Hold the client lock otherwise there can be a race and miss some subs.
1,330✔
2276
        c.mu.Lock()
1,330✔
2277
        defer c.mu.Unlock()
1,330✔
2278

1,330✔
2279
        acc.mu.RLock()
1,330✔
2280
        accName := acc.Name
1,330✔
2281
        accNTag := acc.nameTag
1,330✔
2282

1,330✔
2283
        // To make printing look better when no friendly name present.
1,330✔
2284
        if accNTag != _EMPTY_ {
1,342✔
2285
                accNTag = "/" + accNTag
12✔
2286
        }
12✔
2287

2288
        // If we are solicited we only send interest for local clients.
2289
        if c.isSpokeLeafNode() {
1,974✔
2290
                acc.sl.localSubs(&subs, true)
644✔
2291
        } else {
1,330✔
2292
                acc.sl.All(&subs)
686✔
2293
        }
686✔
2294

2295
        // Check if we have an existing service import reply.
2296
        siReply := copyBytes(acc.siReply)
1,330✔
2297

1,330✔
2298
        // Since leaf nodes only send on interest, if the bound
1,330✔
2299
        // account has import services we need to send those over.
1,330✔
2300
        for isubj := range acc.imports.services {
6,288✔
2301
                if c.isSpokeLeafNode() && !c.canSubscribe(isubj) {
5,247✔
2302
                        c.Debugf("Not permitted to import service %q on behalf of %s%s", isubj, accName, accNTag)
289✔
2303
                        continue
289✔
2304
                }
2305
                ims = append(ims, isubj)
4,669✔
2306
        }
2307
        // Likewise for mappings.
2308
        for _, m := range acc.mappings {
3,719✔
2309
                if c.isSpokeLeafNode() && !c.canSubscribe(m.src) {
2,435✔
2310
                        c.Debugf("Not permitted to import mapping %q on behalf of %s%s", m.src, accName, accNTag)
46✔
2311
                        continue
46✔
2312
                }
2313
                ims = append(ims, m.src)
2,343✔
2314
        }
2315

2316
        // Create a unique subject that will be used for loop detection.
2317
        lds := acc.lds
1,330✔
2318
        acc.mu.RUnlock()
1,330✔
2319

1,330✔
2320
        // Check if we have to create the LDS.
1,330✔
2321
        if lds == _EMPTY_ {
2,377✔
2322
                lds = leafNodeLoopDetectionSubjectPrefix + nuid.Next()
1,047✔
2323
                acc.mu.Lock()
1,047✔
2324
                acc.lds = lds
1,047✔
2325
                acc.mu.Unlock()
1,047✔
2326
        }
1,047✔
2327

2328
        // Now check for gateway interest. Leafnodes will put this into
2329
        // the proper mode to propagate, but they are not held in the account.
2330
        gwsa := [16]*client{}
1,330✔
2331
        gws := gwsa[:0]
1,330✔
2332
        s.getOutboundGatewayConnections(&gws)
1,330✔
2333
        for _, cgw := range gws {
1,413✔
2334
                cgw.mu.Lock()
83✔
2335
                gw := cgw.gw
83✔
2336
                cgw.mu.Unlock()
83✔
2337
                if gw != nil {
166✔
2338
                        if ei, _ := gw.outsim.Load(accName); ei != nil {
166✔
2339
                                if e := ei.(*outsie); e != nil && e.sl != nil {
166✔
2340
                                        e.sl.All(&subs)
83✔
2341
                                }
83✔
2342
                        }
2343
                }
2344
        }
2345

2346
        applyGlobalRouting := s.gateway.enabled
1,330✔
2347
        if c.isSpokeLeafNode() {
1,974✔
2348
                // Add a fake subscription for this solicited leafnode connection
644✔
2349
                // so that we can send back directly for mapped GW replies.
644✔
2350
                // We need to keep track of this subscription so it can be removed
644✔
2351
                // when the connection is closed so that the GC can release it.
644✔
2352
                c.leaf.gwSub = &subscription{client: c, subject: []byte(gwReplyPrefix + ">")}
644✔
2353
                c.srv.gwLeafSubs.Insert(c.leaf.gwSub)
644✔
2354
        }
644✔
2355

2356
        // Now walk the results and add them to our smap
2357
        rc := c.leaf.remoteCluster
1,330✔
2358
        c.leaf.smap = make(map[string]int32)
1,330✔
2359
        for _, sub := range subs {
39,556✔
2360
                // Check perms regardless of role.
38,226✔
2361
                if c.perms != nil && !c.canSubscribe(string(sub.subject)) {
40,585✔
2362
                        c.Debugf("Not permitted to subscribe to %q on behalf of %s%s", sub.subject, accName, accNTag)
2,359✔
2363
                        continue
2,359✔
2364
                }
2365
                // Don't advertise interest from leafnodes to other isolated leafnodes.
2366
                if sub.client.kind == LEAF && c.isIsolatedLeafNode() {
35,882✔
2367
                        continue
15✔
2368
                }
2369
                // We ignore ourselves here.
2370
                // Also don't add the subscription if it has a origin cluster and the
2371
                // cluster name matches the one of the client we are sending to.
2372
                if c != sub.client && (sub.origin == nil || (bytesToString(sub.origin) != rc)) {
66,327✔
2373
                        count := int32(1)
30,475✔
2374
                        if len(sub.queue) > 0 && sub.qw > 0 {
30,484✔
2375
                                count = sub.qw
9✔
2376
                        }
9✔
2377
                        c.leaf.smap[keyFromSub(sub)] += count
30,475✔
2378
                        if c.leaf.tsub == nil {
31,728✔
2379
                                c.leaf.tsub = make(map[*subscription]struct{})
1,253✔
2380
                        }
1,253✔
2381
                        c.leaf.tsub[sub] = struct{}{}
30,475✔
2382
                }
2383
        }
2384
        // FIXME(dlc) - We need to update appropriately on an account claims update.
2385
        for _, isubj := range ims {
8,342✔
2386
                c.leaf.smap[isubj]++
7,012✔
2387
        }
7,012✔
2388
        // If we have gateways enabled we need to make sure the other side sends us responses
2389
        // that have been augmented from the original subscription.
2390
        // TODO(dlc) - Should we lock this down more?
2391
        if applyGlobalRouting {
1,434✔
2392
                c.leaf.smap[oldGWReplyPrefix+"*.>"]++
104✔
2393
                c.leaf.smap[gwReplyPrefix+">"]++
104✔
2394
        }
104✔
2395
        // Detect loops by subscribing to a specific subject and checking
2396
        // if this sub is coming back to us.
2397
        c.leaf.smap[lds]++
1,330✔
2398

1,330✔
2399
        // Check if we need to add an existing siReply to our map.
1,330✔
2400
        // This will be a prefix so add on the wildcard.
1,330✔
2401
        if siReply != nil {
1,348✔
2402
                wcsub := append(siReply, '>')
18✔
2403
                c.leaf.smap[string(wcsub)]++
18✔
2404
        }
18✔
2405
        // Queue all protocols. There is no max pending limit for LN connection,
2406
        // so we don't need chunking. The writes will happen from the writeLoop.
2407
        var b bytes.Buffer
1,330✔
2408
        for key, n := range c.leaf.smap {
28,373✔
2409
                c.writeLeafSub(&b, key, n)
27,043✔
2410
        }
27,043✔
2411
        if b.Len() > 0 {
2,660✔
2412
                c.enqueueProto(b.Bytes())
1,330✔
2413
        }
1,330✔
2414
        if c.leaf.tsub != nil {
2,584✔
2415
                // Clear the tsub map after 5 seconds.
1,254✔
2416
                c.leaf.tsubt = time.AfterFunc(5*time.Second, func() {
1,293✔
2417
                        c.mu.Lock()
39✔
2418
                        if c.leaf != nil {
78✔
2419
                                c.leaf.tsub = nil
39✔
2420
                                c.leaf.tsubt = nil
39✔
2421
                        }
39✔
2422
                        c.mu.Unlock()
39✔
2423
                })
2424
        }
2425
}
2426

2427
// updateInterestForAccountOnGateway called from gateway code when processing RS+ and RS-.
2428
func (s *Server) updateInterestForAccountOnGateway(accName string, sub *subscription, delta int32) {
203,806✔
2429
        // Since we're in the gateway's readLoop, and we would otherwise block, don't allow fetching.
203,806✔
2430
        acc, err := s.lookupOrFetchAccount(accName, false)
203,806✔
2431
        if acc == nil || err != nil {
204,059✔
2432
                s.Debugf("No or bad account for %q, failed to update interest from gateway", accName)
253✔
2433
                return
253✔
2434
        }
253✔
2435
        acc.updateLeafNodes(sub, delta)
203,553✔
2436
}
2437

2438
// updateLeafNodesEx will make sure to update the account smap for the subscription.
2439
// Will also forward to all leaf nodes as needed.
2440
// If `hubOnly` is true, then will update only leaf nodes that connect to this server
2441
// (that is, for which this server acts as a hub to them).
2442
func (acc *Account) updateLeafNodesEx(sub *subscription, delta int32, hubOnly bool) {
2,565,294✔
2443
        if acc == nil || sub == nil {
2,565,294✔
2444
                return
×
2445
        }
×
2446

2447
        // We will do checks for no leafnodes and same cluster here inline and under the
2448
        // general account read lock.
2449
        // If we feel we need to update the leafnodes we will do that out of line to avoid
2450
        // blocking routes or GWs.
2451

2452
        acc.mu.RLock()
2,565,294✔
2453
        // First check if we even have leafnodes here.
2,565,294✔
2454
        if acc.nleafs == 0 {
5,060,700✔
2455
                acc.mu.RUnlock()
2,495,406✔
2456
                return
2,495,406✔
2457
        }
2,495,406✔
2458

2459
        // Is this a loop detection subject.
2460
        isLDS := bytes.HasPrefix(sub.subject, []byte(leafNodeLoopDetectionSubjectPrefix))
69,888✔
2461

69,888✔
2462
        // Capture the cluster even if its empty.
69,888✔
2463
        var cluster string
69,888✔
2464
        if sub.origin != nil {
120,089✔
2465
                cluster = bytesToString(sub.origin)
50,201✔
2466
        }
50,201✔
2467

2468
        // If we have an isolated cluster we can return early, as long as it is not a loop detection subject.
2469
        // Empty clusters will return false for the check.
2470
        if !isLDS && acc.isLeafNodeClusterIsolated(cluster) {
92,144✔
2471
                acc.mu.RUnlock()
22,256✔
2472
                return
22,256✔
2473
        }
22,256✔
2474

2475
        // We can release the general account lock.
2476
        acc.mu.RUnlock()
47,632✔
2477

47,632✔
2478
        // We can hold the list lock here to avoid having to copy a large slice.
47,632✔
2479
        acc.lmu.RLock()
47,632✔
2480
        defer acc.lmu.RUnlock()
47,632✔
2481

47,632✔
2482
        // Do this once.
47,632✔
2483
        subject := string(sub.subject)
47,632✔
2484

47,632✔
2485
        // Walk the connected leafnodes.
47,632✔
2486
        for _, ln := range acc.lleafs {
106,576✔
2487
                if ln == sub.client {
89,611✔
2488
                        continue
30,667✔
2489
                }
2490
                ln.mu.Lock()
28,277✔
2491
                // Don't advertise interest from leafnodes to other isolated leafnodes.
28,277✔
2492
                if sub.client.kind == LEAF && ln.isIsolatedLeafNode() {
28,308✔
2493
                        ln.mu.Unlock()
31✔
2494
                        continue
31✔
2495
                }
2496
                // If `hubOnly` is true, it means that we want to update only leafnodes
2497
                // that connect to this server (so isHubLeafNode() would return `true`).
2498
                if hubOnly && !ln.isHubLeafNode() {
28,252✔
2499
                        ln.mu.Unlock()
6✔
2500
                        continue
6✔
2501
                }
2502
                // Check to make sure this sub does not have an origin cluster that matches the leafnode.
2503
                // If skipped, make sure that we still let go the "$LDS." subscription that allows
2504
                // the detection of loops as long as different cluster.
2505
                clusterDifferent := cluster != ln.remoteCluster()
28,240✔
2506
                if (isLDS && clusterDifferent) || ((cluster == _EMPTY_ || clusterDifferent) && (delta <= 0 || ln.canSubscribe(subject))) {
52,206✔
2507
                        ln.updateSmap(sub, delta, isLDS)
23,966✔
2508
                }
23,966✔
2509
                ln.mu.Unlock()
28,240✔
2510
        }
2511
}
2512

2513
// updateLeafNodes will make sure to update the account smap for the subscription.
2514
// Will also forward to all leaf nodes as needed.
2515
func (acc *Account) updateLeafNodes(sub *subscription, delta int32) {
2,565,271✔
2516
        acc.updateLeafNodesEx(sub, delta, false)
2,565,271✔
2517
}
2,565,271✔
2518

2519
// This will make an update to our internal smap and determine if we should send out
2520
// an interest update to the remote side.
2521
// Lock should be held.
2522
func (c *client) updateSmap(sub *subscription, delta int32, isLDS bool) {
23,966✔
2523
        if c.leaf.smap == nil {
23,973✔
2524
                return
7✔
2525
        }
7✔
2526

2527
        // If we are solicited make sure this is a local client or a non-solicited leaf node
2528
        skind := sub.client.kind
23,959✔
2529
        updateClient := skind == CLIENT || skind == SYSTEM || skind == JETSTREAM || skind == ACCOUNT
23,959✔
2530
        if !isLDS && c.isSpokeLeafNode() && !(updateClient || (skind == LEAF && !sub.client.isSpokeLeafNode())) {
32,087✔
2531
                return
8,128✔
2532
        }
8,128✔
2533

2534
        // For additions, check if that sub has just been processed during initLeafNodeSmapAndSendSubs
2535
        if delta > 0 && c.leaf.tsub != nil {
23,509✔
2536
                if _, present := c.leaf.tsub[sub]; present {
7,682✔
2537
                        delete(c.leaf.tsub, sub)
4✔
2538
                        if len(c.leaf.tsub) == 0 {
4✔
2539
                                c.leaf.tsub = nil
×
2540
                                c.leaf.tsubt.Stop()
×
2541
                                c.leaf.tsubt = nil
×
2542
                        }
×
2543
                        return
4✔
2544
                }
2545
        }
2546

2547
        key := keyFromSub(sub)
15,827✔
2548
        n, ok := c.leaf.smap[key]
15,827✔
2549
        if delta < 0 && !ok {
16,920✔
2550
                return
1,093✔
2551
        }
1,093✔
2552

2553
        // We will update if its a queue, if count is zero (or negative), or we were 0 and are N > 0.
2554
        update := sub.queue != nil || (n <= 0 && n+delta > 0) || (n > 0 && n+delta <= 0)
14,734✔
2555
        n += delta
14,734✔
2556
        if n > 0 {
25,636✔
2557
                c.leaf.smap[key] = n
10,902✔
2558
        } else {
14,734✔
2559
                delete(c.leaf.smap, key)
3,832✔
2560
        }
3,832✔
2561
        if update {
24,927✔
2562
                c.sendLeafNodeSubUpdate(key, n)
10,193✔
2563
        }
10,193✔
2564
}
2565

2566
// Used to force add subjects to the subject map.
2567
func (c *client) forceAddToSmap(subj string) {
13✔
2568
        c.mu.Lock()
13✔
2569
        defer c.mu.Unlock()
13✔
2570

13✔
2571
        if c.leaf.smap == nil {
13✔
2572
                return
×
2573
        }
×
2574
        n := c.leaf.smap[subj]
13✔
2575
        if n != 0 {
14✔
2576
                return
1✔
2577
        }
1✔
2578
        // Place into the map since it was not there.
2579
        c.leaf.smap[subj] = 1
12✔
2580
        c.sendLeafNodeSubUpdate(subj, 1)
12✔
2581
}
2582

2583
// Used to force remove a subject from the subject map.
2584
func (c *client) forceRemoveFromSmap(subj string) {
1✔
2585
        c.mu.Lock()
1✔
2586
        defer c.mu.Unlock()
1✔
2587

1✔
2588
        if c.leaf.smap == nil {
1✔
2589
                return
×
2590
        }
×
2591
        n := c.leaf.smap[subj]
1✔
2592
        if n == 0 {
1✔
2593
                return
×
2594
        }
×
2595
        n--
1✔
2596
        if n == 0 {
2✔
2597
                // Remove is now zero
1✔
2598
                delete(c.leaf.smap, subj)
1✔
2599
                c.sendLeafNodeSubUpdate(subj, 0)
1✔
2600
        } else {
1✔
2601
                c.leaf.smap[subj] = n
×
2602
        }
×
2603
}
2604

2605
// Send the subscription interest change to the other side.
2606
// Lock should be held.
2607
func (c *client) sendLeafNodeSubUpdate(key string, n int32) {
10,206✔
2608
        // If we are a spoke, we need to check if we are allowed to send this subscription over to the hub.
10,206✔
2609
        if c.isSpokeLeafNode() {
12,730✔
2610
                checkPerms := true
2,524✔
2611
                if len(key) > 0 && (key[0] == '$' || key[0] == '_') {
4,073✔
2612
                        if strings.HasPrefix(key, leafNodeLoopDetectionSubjectPrefix) ||
1,549✔
2613
                                strings.HasPrefix(key, oldGWReplyPrefix) ||
1,549✔
2614
                                strings.HasPrefix(key, gwReplyPrefix) {
1,638✔
2615
                                checkPerms = false
89✔
2616
                        }
89✔
2617
                }
2618
                if checkPerms {
4,959✔
2619
                        var subject string
2,435✔
2620
                        if sep := strings.IndexByte(key, ' '); sep != -1 {
2,928✔
2621
                                subject = key[:sep]
493✔
2622
                        } else {
2,435✔
2623
                                subject = key
1,942✔
2624
                        }
1,942✔
2625
                        if !c.canSubscribe(subject) {
2,444✔
2626
                                return
9✔
2627
                        }
9✔
2628
                }
2629
        }
2630
        // If we are here we can send over to the other side.
2631
        _b := [64]byte{}
10,197✔
2632
        b := bytes.NewBuffer(_b[:0])
10,197✔
2633
        c.writeLeafSub(b, key, n)
10,197✔
2634
        c.enqueueProto(b.Bytes())
10,197✔
2635
}
2636

2637
// Helper function to build the key.
2638
func keyFromSub(sub *subscription) string {
47,242✔
2639
        var sb strings.Builder
47,242✔
2640
        sb.Grow(len(sub.subject) + len(sub.queue) + 1)
47,242✔
2641
        sb.Write(sub.subject)
47,242✔
2642
        if sub.queue != nil {
50,972✔
2643
                // Just make the key subject spc group, e.g. 'foo bar'
3,730✔
2644
                sb.WriteByte(' ')
3,730✔
2645
                sb.Write(sub.queue)
3,730✔
2646
        }
3,730✔
2647
        return sb.String()
47,242✔
2648
}
2649

2650
const (
2651
        keyRoutedSub         = "R"
2652
        keyRoutedSubByte     = 'R'
2653
        keyRoutedLeafSub     = "L"
2654
        keyRoutedLeafSubByte = 'L'
2655
)
2656

2657
// Helper function to build the key that prevents collisions between normal
2658
// routed subscriptions and routed subscriptions on behalf of a leafnode.
2659
// Keys will look like this:
2660
// "R foo"          -> plain routed sub on "foo"
2661
// "R foo bar"      -> queue routed sub on "foo", queue "bar"
2662
// "L foo bar"      -> plain routed leaf sub on "foo", leaf "bar"
2663
// "L foo bar baz"  -> queue routed sub on "foo", queue "bar", leaf "baz"
2664
func keyFromSubWithOrigin(sub *subscription) string {
718,907✔
2665
        var sb strings.Builder
718,907✔
2666
        sb.Grow(2 + len(sub.origin) + 1 + len(sub.subject) + 1 + len(sub.queue))
718,907✔
2667
        leaf := len(sub.origin) > 0
718,907✔
2668
        if leaf {
735,583✔
2669
                sb.WriteByte(keyRoutedLeafSubByte)
16,676✔
2670
        } else {
718,907✔
2671
                sb.WriteByte(keyRoutedSubByte)
702,231✔
2672
        }
702,231✔
2673
        sb.WriteByte(' ')
718,907✔
2674
        sb.Write(sub.subject)
718,907✔
2675
        if sub.queue != nil {
746,603✔
2676
                sb.WriteByte(' ')
27,696✔
2677
                sb.Write(sub.queue)
27,696✔
2678
        }
27,696✔
2679
        if leaf {
735,583✔
2680
                sb.WriteByte(' ')
16,676✔
2681
                sb.Write(sub.origin)
16,676✔
2682
        }
16,676✔
2683
        return sb.String()
718,907✔
2684
}
2685

2686
// Lock should be held.
2687
func (c *client) writeLeafSub(w *bytes.Buffer, key string, n int32) {
37,240✔
2688
        if key == _EMPTY_ {
37,240✔
2689
                return
×
2690
        }
×
2691
        if n > 0 {
70,647✔
2692
                w.WriteString("LS+ " + key)
33,407✔
2693
                // Check for queue semantics, if found write n.
33,407✔
2694
                if strings.Contains(key, " ") {
35,713✔
2695
                        w.WriteString(" ")
2,306✔
2696
                        var b [12]byte
2,306✔
2697
                        var i = len(b)
2,306✔
2698
                        for l := n; l > 0; l /= 10 {
5,510✔
2699
                                i--
3,204✔
2700
                                b[i] = digits[l%10]
3,204✔
2701
                        }
3,204✔
2702
                        w.Write(b[i:])
2,306✔
2703
                        if c.trace {
2,306✔
2704
                                arg := fmt.Sprintf("%s %d", key, n)
×
2705
                                c.traceOutOp("LS+", []byte(arg))
×
2706
                        }
×
2707
                } else if c.trace {
31,299✔
2708
                        c.traceOutOp("LS+", []byte(key))
198✔
2709
                }
198✔
2710
        } else {
3,833✔
2711
                w.WriteString("LS- " + key)
3,833✔
2712
                if c.trace {
3,845✔
2713
                        c.traceOutOp("LS-", []byte(key))
12✔
2714
                }
12✔
2715
        }
2716
        w.WriteString(CR_LF)
37,240✔
2717
}
2718

2719
// processLeafSub will process an inbound sub request for the remote leaf node.
2720
func (c *client) processLeafSub(argo []byte) (err error) {
33,134✔
2721
        // Indicate activity.
33,134✔
2722
        c.in.subs++
33,134✔
2723

33,134✔
2724
        srv := c.srv
33,134✔
2725
        if srv == nil {
33,134✔
2726
                return nil
×
2727
        }
×
2728

2729
        // Copy so we do not reference a potentially large buffer
2730
        arg := make([]byte, len(argo))
33,134✔
2731
        copy(arg, argo)
33,134✔
2732

33,134✔
2733
        args := splitArg(arg)
33,134✔
2734
        sub := &subscription{client: c}
33,134✔
2735

33,134✔
2736
        delta := int32(1)
33,134✔
2737
        switch len(args) {
33,134✔
2738
        case 1:
30,850✔
2739
                sub.queue = nil
30,850✔
2740
        case 3:
2,284✔
2741
                sub.queue = args[1]
2,284✔
2742
                sub.qw = int32(parseSize(args[2]))
2,284✔
2743
                // TODO: (ik) We should have a non empty queue name and a queue
2,284✔
2744
                // weight >= 1. For 2.11, we may want to return an error if that
2,284✔
2745
                // is not the case, but for now just overwrite `delta` if queue
2,284✔
2746
                // weight is greater than 1 (it is possible after a reconnect/
2,284✔
2747
                // server restart to receive a queue weight > 1 for a new sub).
2,284✔
2748
                if sub.qw > 1 {
3,951✔
2749
                        delta = sub.qw
1,667✔
2750
                }
1,667✔
2751
        default:
×
2752
                return fmt.Errorf("processLeafSub Parse Error: '%s'", arg)
×
2753
        }
2754
        sub.subject = args[0]
33,134✔
2755

33,134✔
2756
        c.mu.Lock()
33,134✔
2757
        if c.isClosed() {
33,147✔
2758
                c.mu.Unlock()
13✔
2759
                return nil
13✔
2760
        }
13✔
2761

2762
        acc := c.acc
33,121✔
2763
        // Guard against LS+ arriving before CONNECT has been processed, which
33,121✔
2764
        // can happen when compression is enabled.
33,121✔
2765
        if acc == nil {
33,124✔
2766
                c.mu.Unlock()
3✔
2767
                c.sendErr("Authorization Violation")
3✔
2768
                c.closeConnection(ProtocolViolation)
3✔
2769
                return nil
3✔
2770
        }
3✔
2771
        // Check if we have a loop.
2772
        ldsPrefix := bytes.HasPrefix(sub.subject, []byte(leafNodeLoopDetectionSubjectPrefix))
33,118✔
2773

33,118✔
2774
        if ldsPrefix && bytesToString(sub.subject) == acc.getLDSubject() {
33,123✔
2775
                c.mu.Unlock()
5✔
2776
                c.handleLeafNodeLoop(true)
5✔
2777
                return nil
5✔
2778
        }
5✔
2779

2780
        // Check permissions if applicable. (but exclude the $LDS, $GR and _GR_)
2781
        checkPerms := true
33,113✔
2782
        if sub.subject[0] == '$' || sub.subject[0] == '_' {
63,238✔
2783
                if ldsPrefix ||
30,125✔
2784
                        bytes.HasPrefix(sub.subject, []byte(oldGWReplyPrefix)) ||
30,125✔
2785
                        bytes.HasPrefix(sub.subject, []byte(gwReplyPrefix)) {
32,168✔
2786
                        checkPerms = false
2,043✔
2787
                }
2,043✔
2788
        }
2789

2790
        // If we are a hub check that we can publish to this subject.
2791
        if checkPerms {
64,183✔
2792
                subj := string(sub.subject)
31,070✔
2793
                if subjectIsLiteral(subj) && !c.pubAllowedFullCheck(subj, true, true) {
31,396✔
2794
                        c.mu.Unlock()
326✔
2795
                        c.leafSubPermViolation(sub.subject)
326✔
2796
                        c.Debugf(fmt.Sprintf("Permissions Violation for Subscription to %q", sub.subject))
326✔
2797
                        return nil
326✔
2798
                }
326✔
2799
        }
2800

2801
        // Check if we have a maximum on the number of subscriptions.
2802
        if c.subsAtLimit() {
32,795✔
2803
                c.mu.Unlock()
8✔
2804
                c.maxSubsExceeded()
8✔
2805
                return nil
8✔
2806
        }
8✔
2807

2808
        // If we have an origin cluster associated mark that in the sub.
2809
        if rc := c.remoteCluster(); rc != _EMPTY_ {
61,655✔
2810
                sub.origin = []byte(rc)
28,876✔
2811
        }
28,876✔
2812

2813
        // Like Routes, we store local subs by account and subject and optionally queue name.
2814
        // If we have a queue it will have a trailing weight which we do not want.
2815
        if sub.queue != nil {
34,773✔
2816
                sub.sid = arg[:len(arg)-len(args[2])-1]
1,994✔
2817
        } else {
32,779✔
2818
                sub.sid = arg
30,785✔
2819
        }
30,785✔
2820
        key := bytesToString(sub.sid)
32,779✔
2821
        osub := c.subs[key]
32,779✔
2822
        if osub == nil {
64,023✔
2823
                c.subs[key] = sub
31,244✔
2824
                // Now place into the account sl.
31,244✔
2825
                if err := acc.sl.Insert(sub); err != nil {
31,244✔
2826
                        delete(c.subs, key)
×
2827
                        c.mu.Unlock()
×
2828
                        c.Errorf("Could not insert subscription: %v", err)
×
2829
                        c.sendErr("Invalid Subscription")
×
2830
                        return nil
×
2831
                }
×
2832
        } else if sub.queue != nil {
3,069✔
2833
                // For a queue we need to update the weight.
1,534✔
2834
                delta = sub.qw - atomic.LoadInt32(&osub.qw)
1,534✔
2835
                atomic.StoreInt32(&osub.qw, sub.qw)
1,534✔
2836
                acc.sl.UpdateRemoteQSub(osub)
1,534✔
2837
        }
1,534✔
2838
        spoke := c.isSpokeLeafNode()
32,779✔
2839
        c.mu.Unlock()
32,779✔
2840

32,779✔
2841
        // Only add in shadow subs if a new sub or qsub.
32,779✔
2842
        if osub == nil {
64,023✔
2843
                if err := c.addShadowSubscriptions(acc, sub); err != nil {
31,244✔
2844
                        c.Errorf(err.Error())
×
2845
                }
×
2846
        }
2847

2848
        // If we are not solicited, treat leaf node subscriptions similar to a
2849
        // client subscription, meaning we forward them to routes, gateways and
2850
        // other leaf nodes as needed.
2851
        if !spoke {
44,327✔
2852
                // If we are routing add to the route map for the associated account.
11,548✔
2853
                srv.updateRouteSubscriptionMap(acc, sub, delta)
11,548✔
2854
                if srv.gateway.enabled {
13,083✔
2855
                        srv.gatewayUpdateSubInterest(acc.Name, sub, delta)
1,535✔
2856
                }
1,535✔
2857
        }
2858
        // Now check on leafnode updates for other leaf nodes. We understand solicited
2859
        // and non-solicited state in this call so we will do the right thing.
2860
        acc.updateLeafNodes(sub, delta)
32,779✔
2861

32,779✔
2862
        return nil
32,779✔
2863
}
2864

2865
// If the leafnode is a solicited, set the connect delay based on default
2866
// or private option (for tests). Sends the error to the other side, log and
2867
// close the connection.
2868
func (c *client) handleLeafNodeLoop(sendErr bool) {
15✔
2869
        accName, delay := c.setLeafConnectDelayIfSoliciting(leafNodeReconnectDelayAfterLoopDetected)
15✔
2870
        errTxt := fmt.Sprintf("Loop detected for leafnode account=%q. Delaying attempt to reconnect for %v", accName, delay)
15✔
2871
        if sendErr {
22✔
2872
                c.sendErr(errTxt)
7✔
2873
        }
7✔
2874

2875
        c.Errorf(errTxt)
15✔
2876
        // If we are here with "sendErr" false, it means that this is the server
15✔
2877
        // that received the error. The other side will have closed the connection,
15✔
2878
        // but does not hurt to close here too.
15✔
2879
        c.closeConnection(ProtocolViolation)
15✔
2880
}
2881

2882
// processLeafUnsub will process an inbound unsub request for the remote leaf node.
2883
func (c *client) processLeafUnsub(arg []byte) error {
3,462✔
2884
        // Indicate any activity, so pub and sub or unsubs.
3,462✔
2885
        c.in.subs++
3,462✔
2886

3,462✔
2887
        srv := c.srv
3,462✔
2888

3,462✔
2889
        c.mu.Lock()
3,462✔
2890
        if c.isClosed() {
3,532✔
2891
                c.mu.Unlock()
70✔
2892
                return nil
70✔
2893
        }
70✔
2894

2895
        acc := c.acc
3,392✔
2896
        // Guard against LS- arriving before CONNECT has been processed.
3,392✔
2897
        if acc == nil {
3,393✔
2898
                c.mu.Unlock()
1✔
2899
                c.sendErr("Authorization Violation")
1✔
2900
                c.closeConnection(ProtocolViolation)
1✔
2901
                return nil
1✔
2902
        }
1✔
2903

2904
        spoke := c.isSpokeLeafNode()
3,391✔
2905
        // We store local subs by account and subject and optionally queue name.
3,391✔
2906
        // LS- will have the arg exactly as the key.
3,391✔
2907
        sub, ok := c.subs[string(arg)]
3,391✔
2908
        if !ok {
3,402✔
2909
                // If not found, don't try to update routes/gws/leaf nodes.
11✔
2910
                c.mu.Unlock()
11✔
2911
                return nil
11✔
2912
        }
11✔
2913
        delta := int32(1)
3,380✔
2914
        if len(sub.queue) > 0 {
3,800✔
2915
                delta = sub.qw
420✔
2916
        }
420✔
2917
        c.mu.Unlock()
3,380✔
2918

3,380✔
2919
        c.unsubscribe(acc, sub, true, true)
3,380✔
2920
        if !spoke {
4,427✔
2921
                // If we are routing subtract from the route map for the associated account.
1,047✔
2922
                srv.updateRouteSubscriptionMap(acc, sub, -delta)
1,047✔
2923
                // Gateways
1,047✔
2924
                if srv.gateway.enabled {
1,322✔
2925
                        srv.gatewayUpdateSubInterest(acc.Name, sub, -delta)
275✔
2926
                }
275✔
2927
        }
2928
        // Now check on leafnode updates for other leaf nodes.
2929
        acc.updateLeafNodes(sub, -delta)
3,380✔
2930
        return nil
3,380✔
2931
}
2932

2933
func (c *client) processLeafHeaderMsgArgs(arg []byte) error {
490✔
2934
        // Unroll splitArgs to avoid runtime/heap issues
490✔
2935
        args := c.argsa[:0]
490✔
2936
        start := -1
490✔
2937
        for i, b := range arg {
32,763✔
2938
                switch b {
32,273✔
2939
                case ' ', '\t', '\r', '\n':
1,404✔
2940
                        if start >= 0 {
2,808✔
2941
                                args = append(args, arg[start:i])
1,404✔
2942
                                start = -1
1,404✔
2943
                        }
1,404✔
2944
                default:
30,869✔
2945
                        if start < 0 {
32,763✔
2946
                                start = i
1,894✔
2947
                        }
1,894✔
2948
                }
2949
        }
2950
        if start >= 0 {
980✔
2951
                args = append(args, arg[start:])
490✔
2952
        }
490✔
2953

2954
        c.pa.arg = arg
490✔
2955
        switch len(args) {
490✔
2956
        case 0, 1, 2:
×
2957
                return fmt.Errorf("processLeafHeaderMsgArgs Parse Error: '%s'", args)
×
2958
        case 3:
84✔
2959
                c.pa.reply = nil
84✔
2960
                c.pa.queues = nil
84✔
2961
                c.pa.hdb = args[1]
84✔
2962
                c.pa.hdr = parseSize(args[1])
84✔
2963
                c.pa.szb = args[2]
84✔
2964
                c.pa.size = parseSize(args[2])
84✔
2965
        case 4:
392✔
2966
                c.pa.reply = args[1]
392✔
2967
                c.pa.queues = nil
392✔
2968
                c.pa.hdb = args[2]
392✔
2969
                c.pa.hdr = parseSize(args[2])
392✔
2970
                c.pa.szb = args[3]
392✔
2971
                c.pa.size = parseSize(args[3])
392✔
2972
        default:
14✔
2973
                // args[1] is our reply indicator. Should be + or | normally.
14✔
2974
                if len(args[1]) != 1 {
14✔
2975
                        return fmt.Errorf("processLeafHeaderMsgArgs Bad or Missing Reply Indicator: '%s'", args[1])
×
2976
                }
×
2977
                switch args[1][0] {
14✔
2978
                case '+':
4✔
2979
                        c.pa.reply = args[2]
4✔
2980
                case '|':
10✔
2981
                        c.pa.reply = nil
10✔
2982
                default:
×
2983
                        return fmt.Errorf("processLeafHeaderMsgArgs Bad or Missing Reply Indicator: '%s'", args[1])
×
2984
                }
2985
                // Grab header size.
2986
                c.pa.hdb = args[len(args)-2]
14✔
2987
                c.pa.hdr = parseSize(c.pa.hdb)
14✔
2988

14✔
2989
                // Grab size.
14✔
2990
                c.pa.szb = args[len(args)-1]
14✔
2991
                c.pa.size = parseSize(c.pa.szb)
14✔
2992

14✔
2993
                // Grab queue names.
14✔
2994
                if c.pa.reply != nil {
18✔
2995
                        c.pa.queues = args[3 : len(args)-2]
4✔
2996
                } else {
14✔
2997
                        c.pa.queues = args[2 : len(args)-2]
10✔
2998
                }
10✔
2999
        }
3000
        if c.pa.hdr < 0 {
490✔
3001
                return fmt.Errorf("processLeafHeaderMsgArgs Bad or Missing Header Size: '%s'", arg)
×
3002
        }
×
3003
        if c.pa.size < 0 {
490✔
3004
                return fmt.Errorf("processLeafHeaderMsgArgs Bad or Missing Size: '%s'", args)
×
3005
        }
×
3006
        if c.pa.hdr > c.pa.size {
490✔
3007
                return fmt.Errorf("processLeafHeaderMsgArgs Header Size larger then TotalSize: '%s'", arg)
×
3008
        }
×
3009

3010
        // Common ones processed after check for arg length
3011
        c.pa.subject = args[0]
490✔
3012

490✔
3013
        return nil
490✔
3014
}
3015

3016
func (c *client) processLeafMsgArgs(arg []byte) error {
83,274✔
3017
        // Unroll splitArgs to avoid runtime/heap issues
83,274✔
3018
        args := c.argsa[:0]
83,274✔
3019
        start := -1
83,274✔
3020
        for i, b := range arg {
2,694,178✔
3021
                switch b {
2,610,904✔
3022
                case ' ', '\t', '\r', '\n':
135,062✔
3023
                        if start >= 0 {
270,124✔
3024
                                args = append(args, arg[start:i])
135,062✔
3025
                                start = -1
135,062✔
3026
                        }
135,062✔
3027
                default:
2,475,842✔
3028
                        if start < 0 {
2,694,178✔
3029
                                start = i
218,336✔
3030
                        }
218,336✔
3031
                }
3032
        }
3033
        if start >= 0 {
166,548✔
3034
                args = append(args, arg[start:])
83,274✔
3035
        }
83,274✔
3036

3037
        c.pa.arg = arg
83,274✔
3038
        switch len(args) {
83,274✔
3039
        case 0, 1:
×
3040
                return fmt.Errorf("processLeafMsgArgs Parse Error: '%s'", args)
×
3041
        case 2:
54,207✔
3042
                c.pa.reply = nil
54,207✔
3043
                c.pa.queues = nil
54,207✔
3044
                c.pa.szb = args[1]
54,207✔
3045
                c.pa.size = parseSize(args[1])
54,207✔
3046
        case 3:
6,506✔
3047
                c.pa.reply = args[1]
6,506✔
3048
                c.pa.queues = nil
6,506✔
3049
                c.pa.szb = args[2]
6,506✔
3050
                c.pa.size = parseSize(args[2])
6,506✔
3051
        default:
22,561✔
3052
                // args[1] is our reply indicator. Should be + or | normally.
22,561✔
3053
                if len(args[1]) != 1 {
22,561✔
3054
                        return fmt.Errorf("processLeafMsgArgs Bad or Missing Reply Indicator: '%s'", args[1])
×
3055
                }
×
3056
                switch args[1][0] {
22,561✔
3057
                case '+':
160✔
3058
                        c.pa.reply = args[2]
160✔
3059
                case '|':
22,401✔
3060
                        c.pa.reply = nil
22,401✔
3061
                default:
×
3062
                        return fmt.Errorf("processLeafMsgArgs Bad or Missing Reply Indicator: '%s'", args[1])
×
3063
                }
3064
                // Grab size.
3065
                c.pa.szb = args[len(args)-1]
22,561✔
3066
                c.pa.size = parseSize(c.pa.szb)
22,561✔
3067

22,561✔
3068
                // Grab queue names.
22,561✔
3069
                if c.pa.reply != nil {
22,721✔
3070
                        c.pa.queues = args[3 : len(args)-1]
160✔
3071
                } else {
22,561✔
3072
                        c.pa.queues = args[2 : len(args)-1]
22,401✔
3073
                }
22,401✔
3074
        }
3075
        if c.pa.size < 0 {
83,274✔
3076
                return fmt.Errorf("processLeafMsgArgs Bad or Missing Size: '%s'", args)
×
3077
        }
×
3078

3079
        // Common ones processed after check for arg length
3080
        c.pa.subject = args[0]
83,274✔
3081

83,274✔
3082
        return nil
83,274✔
3083
}
3084

3085
// processInboundLeafMsg is called to process an inbound msg from a leaf node.
3086
func (c *client) processInboundLeafMsg(msg []byte) {
81,969✔
3087
        // Update statistics
81,969✔
3088
        // The msg includes the CR_LF, so pull back out for accounting.
81,969✔
3089
        c.in.msgs++
81,969✔
3090
        c.in.bytes += int32(len(msg) - LEN_CR_LF)
81,969✔
3091

81,969✔
3092
        srv, acc, subject := c.srv, c.acc, string(c.pa.subject)
81,969✔
3093

81,969✔
3094
        // Mostly under testing scenarios.
81,969✔
3095
        if srv == nil || acc == nil {
81,970✔
3096
                return
1✔
3097
        }
1✔
3098

3099
        // Match the subscriptions. We will use our own L1 map if
3100
        // it's still valid, avoiding contention on the shared sublist.
3101
        var r *SublistResult
81,968✔
3102
        var ok bool
81,968✔
3103

81,968✔
3104
        genid := atomic.LoadUint64(&c.acc.sl.genid)
81,968✔
3105
        if genid == c.in.genid && c.in.results != nil {
161,475✔
3106
                r, ok = c.in.results[subject]
79,507✔
3107
        } else {
81,968✔
3108
                // Reset our L1 completely.
2,461✔
3109
                c.in.results = make(map[string]*SublistResult)
2,461✔
3110
                c.in.genid = genid
2,461✔
3111
        }
2,461✔
3112

3113
        // Go back to the sublist data structure.
3114
        if !ok {
133,367✔
3115
                r = c.acc.sl.Match(subject)
51,399✔
3116
                // Prune the results cache. Keeps us from unbounded growth. Random delete.
51,399✔
3117
                if len(c.in.results) >= maxResultCacheSize {
52,776✔
3118
                        n := 0
1,377✔
3119
                        for subj := range c.in.results {
46,818✔
3120
                                delete(c.in.results, subj)
45,441✔
3121
                                if n++; n > pruneSize {
46,818✔
3122
                                        break
1,377✔
3123
                                }
3124
                        }
3125
                }
3126
                // Then add the new cache entry.
3127
                c.in.results[subject] = r
51,399✔
3128
        }
3129

3130
        // Collect queue names if needed.
3131
        var qnames [][]byte
81,968✔
3132

81,968✔
3133
        // Check for no interest, short circuit if so.
81,968✔
3134
        // This is the fanout scale.
81,968✔
3135
        if len(r.psubs)+len(r.qsubs) > 0 {
163,433✔
3136
                flag := pmrNoFlag
81,465✔
3137
                // If we have queue subs in this cluster, then if we run in gateway
81,465✔
3138
                // mode and the remote gateways have queue subs, then we need to
81,465✔
3139
                // collect the queue groups this message was sent to so that we
81,465✔
3140
                // exclude them when sending to gateways.
81,465✔
3141
                if len(r.qsubs) > 0 && c.srv.gateway.enabled &&
81,465✔
3142
                        atomic.LoadInt64(&c.srv.gateway.totalQSubs) > 0 {
93,763✔
3143
                        flag |= pmrCollectQueueNames
12,298✔
3144
                }
12,298✔
3145
                // If this is a mapped subject that means the mapped interest
3146
                // is what got us here, but this might not have a queue designation
3147
                // If that is the case, make sure we ignore to process local queue subscribers.
3148
                if len(c.pa.mapped) > 0 && len(c.pa.queues) == 0 {
81,800✔
3149
                        flag |= pmrIgnoreEmptyQueueFilter
335✔
3150
                }
335✔
3151
                _, qnames = c.processMsgResults(acc, r, msg, nil, c.pa.subject, c.pa.reply, flag)
81,465✔
3152
        }
3153

3154
        // Now deal with gateways
3155
        if c.srv.gateway.enabled {
95,370✔
3156
                c.sendMsgToGateways(acc, msg, c.pa.subject, c.pa.reply, qnames, true)
13,402✔
3157
        }
13,402✔
3158
}
3159

3160
// Handles a subscription permission violation.
3161
// See leafPermViolation() for details.
3162
func (c *client) leafSubPermViolation(subj []byte) {
326✔
3163
        c.leafPermViolation(false, subj)
326✔
3164
}
326✔
3165

3166
// Common function to process publish or subscribe leafnode permission violation.
3167
// Sends the permission violation error to the remote, logs it and closes the connection.
3168
// If this is from a server soliciting, the reconnection will be delayed.
3169
func (c *client) leafPermViolation(pub bool, subj []byte) {
326✔
3170
        if c.isSpokeLeafNode() {
652✔
3171
                // For spokes these are no-ops since the hub server told us our permissions.
326✔
3172
                // We just need to not send these over to the other side since we will get cutoff.
326✔
3173
                return
326✔
3174
        }
326✔
3175
        // FIXME(dlc) ?
3176
        c.setLeafConnectDelayIfSoliciting(leafNodeReconnectAfterPermViolation)
×
3177
        var action string
×
3178
        if pub {
×
3179
                c.sendErr(fmt.Sprintf("Permissions Violation for Publish to %q", subj))
×
3180
                action = "Publish"
×
3181
        } else {
×
3182
                c.sendErr(fmt.Sprintf("Permissions Violation for Subscription to %q", subj))
×
3183
                action = "Subscription"
×
3184
        }
×
3185
        c.Errorf("%s Violation on %q - Check other side configuration", action, subj)
×
3186
        // TODO: add a new close reason that is more appropriate?
×
3187
        c.closeConnection(ProtocolViolation)
×
3188
}
3189

3190
// Invoked from generic processErr() for LEAF connections.
3191
func (c *client) leafProcessErr(errStr string) {
50✔
3192
        // Check if we got a cluster name collision.
50✔
3193
        if strings.Contains(errStr, ErrLeafNodeHasSameClusterName.Error()) {
54✔
3194
                _, delay := c.setLeafConnectDelayIfSoliciting(leafNodeReconnectDelayAfterClusterNameSame)
4✔
3195
                c.Errorf("Leafnode connection dropped with same cluster name error. Delaying attempt to reconnect for %v", delay)
4✔
3196
                return
4✔
3197
        }
4✔
3198
        if strings.Contains(errStr, ErrLeafNodeMinVersionRejected.Error()) {
47✔
3199
                _, delay := c.setLeafConnectDelayIfSoliciting(leafNodeMinVersionReconnectDelay)
1✔
3200
                c.Errorf("Leafnode connection dropped due to minimum version requirement. Delaying attempt to reconnect for %v", delay)
1✔
3201
                return
1✔
3202
        }
1✔
3203

3204
        // We will look for Loop detected error coming from the other side.
3205
        // If we solicit, set the connect delay.
3206
        if !strings.Contains(errStr, "Loop detected") {
82✔
3207
                return
37✔
3208
        }
37✔
3209
        c.handleLeafNodeLoop(false)
8✔
3210
}
3211

3212
// If this leaf connection solicits, sets the connect delay to the given value,
3213
// or the one from the server option's LeafNode.connDelay if one is set (for tests).
3214
// Returns the connection's account name and delay.
3215
func (c *client) setLeafConnectDelayIfSoliciting(delay time.Duration) (string, time.Duration) {
20✔
3216
        c.mu.Lock()
20✔
3217
        if c.isSolicitedLeafNode() {
32✔
3218
                if s := c.srv; s != nil {
24✔
3219
                        if srvdelay := s.getOpts().LeafNode.connDelay; srvdelay != 0 {
16✔
3220
                                delay = srvdelay
4✔
3221
                        }
4✔
3222
                }
3223
                c.leaf.remote.setConnectDelay(delay)
12✔
3224
        }
3225
        var accName string
20✔
3226
        if c.acc != nil {
39✔
3227
                accName = c.acc.Name
19✔
3228
        }
19✔
3229
        c.mu.Unlock()
20✔
3230
        return accName, delay
20✔
3231
}
3232

3233
// For the given remote Leafnode configuration, this function returns
3234
// if TLS is required, and if so, will return a clone of the TLS Config
3235
// (since some fields will be changed during handshake), the TLS server
3236
// name that is remembered, and the TLS timeout.
3237
func (c *client) leafNodeGetTLSConfigForSolicit(remote *leafNodeCfg) (bool, *tls.Config, string, float64) {
1,969✔
3238
        var (
1,969✔
3239
                tlsConfig  *tls.Config
1,969✔
3240
                tlsName    string
1,969✔
3241
                tlsTimeout float64
1,969✔
3242
        )
1,969✔
3243

1,969✔
3244
        remote.RLock()
1,969✔
3245
        defer remote.RUnlock()
1,969✔
3246

1,969✔
3247
        tlsRequired := remote.TLS || remote.TLSConfig != nil
1,969✔
3248
        if tlsRequired {
2,046✔
3249
                if remote.TLSConfig != nil {
128✔
3250
                        tlsConfig = remote.TLSConfig.Clone()
51✔
3251
                } else {
77✔
3252
                        tlsConfig = &tls.Config{MinVersion: tls.VersionTLS12}
26✔
3253
                }
26✔
3254
                tlsName = remote.tlsName
77✔
3255
                tlsTimeout = remote.TLSTimeout
77✔
3256
                if tlsTimeout == 0 {
120✔
3257
                        tlsTimeout = float64(TLS_TIMEOUT / time.Second)
43✔
3258
                }
43✔
3259
        }
3260

3261
        return tlsRequired, tlsConfig, tlsName, tlsTimeout
1,969✔
3262
}
3263

3264
// Initiates the LeafNode Websocket connection by:
3265
// - doing the TLS handshake if needed
3266
// - sending the HTTP request
3267
// - waiting for the HTTP response
3268
//
3269
// Since some bufio reader is used to consume the HTTP response, this function
3270
// returns the slice of buffered bytes (if any) so that the readLoop that will
3271
// be started after that consume those first before reading from the socket.
3272
// The boolean
3273
//
3274
// Lock held on entry.
3275
func (c *client) leafNodeSolicitWSConnection(opts *Options, rURL *url.URL, remote *leafNodeCfg) ([]byte, ClosedState, error) {
50✔
3276
        remote.RLock()
50✔
3277
        compress := remote.Websocket.Compression
50✔
3278
        // By default the server will mask outbound frames, but it can be disabled with this option.
50✔
3279
        noMasking := remote.Websocket.NoMasking
50✔
3280
        infoTimeout := remote.FirstInfoTimeout
50✔
3281
        remote.RUnlock()
50✔
3282
        // Will do the client-side TLS handshake if needed.
50✔
3283
        tlsRequired, err := c.leafClientHandshakeIfNeeded(remote, opts)
50✔
3284
        if err != nil {
54✔
3285
                // 0 will indicate that the connection was already closed
4✔
3286
                return nil, 0, err
4✔
3287
        }
4✔
3288

3289
        // For http request, we need the passed URL to contain either http or https scheme.
3290
        scheme := "http"
46✔
3291
        if tlsRequired {
54✔
3292
                scheme = "https"
8✔
3293
        }
8✔
3294
        // We will use the `/leafnode` path to tell the accepting WS server that it should
3295
        // create a LEAF connection, not a CLIENT.
3296
        // In case we use the user's URL path in the future, make sure we append the user's
3297
        // path to our `/leafnode` path.
3298
        lpath := leafNodeWSPath
46✔
3299
        if curPath := rURL.EscapedPath(); curPath != _EMPTY_ {
67✔
3300
                if curPath[0] == '/' {
42✔
3301
                        curPath = curPath[1:]
21✔
3302
                }
21✔
3303
                lpath = path.Join(curPath, lpath)
21✔
3304
        } else {
25✔
3305
                lpath = lpath[1:]
25✔
3306
        }
25✔
3307
        ustr := fmt.Sprintf("%s://%s/%s", scheme, rURL.Host, lpath)
46✔
3308
        u, _ := url.Parse(ustr)
46✔
3309
        req := &http.Request{
46✔
3310
                Method:     "GET",
46✔
3311
                URL:        u,
46✔
3312
                Proto:      "HTTP/1.1",
46✔
3313
                ProtoMajor: 1,
46✔
3314
                ProtoMinor: 1,
46✔
3315
                Header:     make(http.Header),
46✔
3316
                Host:       u.Host,
46✔
3317
        }
46✔
3318
        wsKey, err := wsMakeChallengeKey()
46✔
3319
        if err != nil {
46✔
3320
                return nil, WriteError, err
×
3321
        }
×
3322

3323
        req.Header["Upgrade"] = []string{"websocket"}
46✔
3324
        req.Header["Connection"] = []string{"Upgrade"}
46✔
3325
        req.Header["Sec-WebSocket-Key"] = []string{wsKey}
46✔
3326
        req.Header["Sec-WebSocket-Version"] = []string{"13"}
46✔
3327
        if compress {
55✔
3328
                req.Header.Add("Sec-WebSocket-Extensions", wsPMCReqHeaderValue)
9✔
3329
        }
9✔
3330
        if noMasking {
56✔
3331
                req.Header.Add(wsNoMaskingHeader, wsNoMaskingValue)
10✔
3332
        }
10✔
3333
        c.nc.SetDeadline(time.Now().Add(infoTimeout))
46✔
3334
        if err := req.Write(c.nc); err != nil {
46✔
3335
                return nil, WriteError, err
×
3336
        }
×
3337

3338
        var resp *http.Response
46✔
3339

46✔
3340
        br := bufio.NewReaderSize(c.nc, MAX_CONTROL_LINE_SIZE)
46✔
3341
        resp, err = http.ReadResponse(br, req)
46✔
3342
        if err == nil &&
46✔
3343
                (resp.StatusCode != 101 ||
46✔
3344
                        !strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") ||
46✔
3345
                        !strings.EqualFold(resp.Header.Get("Connection"), "upgrade") ||
46✔
3346
                        resp.Header.Get("Sec-Websocket-Accept") != wsAcceptKey(wsKey)) {
47✔
3347

1✔
3348
                err = fmt.Errorf("invalid websocket connection")
1✔
3349
        }
1✔
3350
        // Check compression extension...
3351
        if err == nil && c.ws.compress {
55✔
3352
                // Check that not only permessage-deflate extension is present, but that
9✔
3353
                // we also have server and client no context take over.
9✔
3354
                srvCompress, noCtxTakeover := wsPMCExtensionSupport(resp.Header, false)
9✔
3355

9✔
3356
                // If server does not support compression, then simply disable it in our side.
9✔
3357
                if !srvCompress {
13✔
3358
                        c.ws.compress = false
4✔
3359
                } else if !noCtxTakeover {
9✔
3360
                        err = fmt.Errorf("compression negotiation error")
×
3361
                }
×
3362
        }
3363
        // Same for no masking...
3364
        if err == nil && noMasking {
56✔
3365
                // Check if server accepts no masking
10✔
3366
                if resp.Header.Get(wsNoMaskingHeader) != wsNoMaskingValue {
11✔
3367
                        // Nope, need to mask our writes as any client would do.
1✔
3368
                        c.ws.maskwrite = true
1✔
3369
                }
1✔
3370
        }
3371
        if resp != nil {
76✔
3372
                resp.Body.Close()
30✔
3373
        }
30✔
3374
        if err != nil {
63✔
3375
                return nil, ReadError, err
17✔
3376
        }
17✔
3377
        c.Debugf("Leafnode compression=%v masking=%v", c.ws.compress, c.ws.maskwrite)
29✔
3378

29✔
3379
        var preBuf []byte
29✔
3380
        // We have to slurp whatever is in the bufio reader and pass that to the readloop.
29✔
3381
        if n := br.Buffered(); n != 0 {
29✔
3382
                preBuf, _ = br.Peek(n)
×
3383
        }
×
3384
        return preBuf, 0, nil
29✔
3385
}
3386

3387
const connectProcessTimeout = 2 * time.Second
3388

3389
// This is invoked for remote LEAF remote connections after processing the INFO
3390
// protocol.
3391
func (s *Server) leafNodeResumeConnectProcess(c *client) {
685✔
3392
        clusterName := s.ClusterName()
685✔
3393

685✔
3394
        c.mu.Lock()
685✔
3395
        if c.isClosed() {
685✔
3396
                c.mu.Unlock()
×
3397
                return
×
3398
        }
×
3399
        if err := c.sendLeafConnect(clusterName, c.headers); err != nil {
687✔
3400
                c.mu.Unlock()
2✔
3401
                c.closeConnection(WriteError)
2✔
3402
                return
2✔
3403
        }
2✔
3404

3405
        // Spin up the write loop.
3406
        s.startGoRoutine(func() { c.writeLoop() })
1,366✔
3407

3408
        // timeout leafNodeFinishConnectProcess
3409
        c.ping.tmr = time.AfterFunc(connectProcessTimeout, func() {
683✔
3410
                c.mu.Lock()
×
3411
                // check if leafNodeFinishConnectProcess was called and prevent later leafNodeFinishConnectProcess
×
3412
                if !c.flags.setIfNotSet(connectProcessFinished) {
×
3413
                        c.mu.Unlock()
×
3414
                        return
×
3415
                }
×
3416
                clearTimer(&c.ping.tmr)
×
3417
                closed := c.isClosed()
×
3418
                c.mu.Unlock()
×
3419
                if !closed {
×
3420
                        c.sendErrAndDebug("Stale Leaf Node Connection - Closing")
×
3421
                        c.closeConnection(StaleConnection)
×
3422
                }
×
3423
        })
3424
        c.mu.Unlock()
683✔
3425
        c.Debugf("Remote leafnode connect msg sent")
683✔
3426
}
3427

3428
// This is invoked for remote LEAF connections after processing the INFO
3429
// protocol and leafNodeResumeConnectProcess.
3430
// This will send LS+ the CONNECT protocol and register the leaf node.
3431
func (s *Server) leafNodeFinishConnectProcess(c *client) {
646✔
3432
        c.mu.Lock()
646✔
3433
        if !c.flags.setIfNotSet(connectProcessFinished) {
646✔
3434
                c.mu.Unlock()
×
3435
                return
×
3436
        }
×
3437
        if c.isClosed() {
646✔
3438
                c.mu.Unlock()
×
3439
                s.removeLeafNodeConnection(c)
×
3440
                return
×
3441
        }
×
3442
        remote := c.leaf.remote
646✔
3443
        // Check if we will need to send the system connect event.
646✔
3444
        remote.RLock()
646✔
3445
        sendSysConnectEvent := remote.Hub
646✔
3446
        remote.RUnlock()
646✔
3447

646✔
3448
        // Capture account before releasing lock
646✔
3449
        acc := c.acc
646✔
3450
        // cancel connectProcessTimeout
646✔
3451
        clearTimer(&c.ping.tmr)
646✔
3452
        c.mu.Unlock()
646✔
3453

646✔
3454
        // Make sure we register with the account here.
646✔
3455
        if err := c.registerWithAccount(acc); err != nil {
648✔
3456
                if err == ErrTooManyAccountConnections {
2✔
3457
                        c.maxAccountConnExceeded()
×
3458
                        return
×
3459
                } else if err == ErrLeafNodeLoop {
4✔
3460
                        c.handleLeafNodeLoop(true)
2✔
3461
                        return
2✔
3462
                }
2✔
3463
                c.Errorf("Registering leaf with account %s resulted in error: %v", acc.Name, err)
×
3464
                c.closeConnection(ProtocolViolation)
×
3465
                return
×
3466
        }
3467
        s.addLeafNodeConnection(c, _EMPTY_, _EMPTY_, false)
644✔
3468
        s.initLeafNodeSmapAndSendSubs(c)
644✔
3469
        if sendSysConnectEvent {
660✔
3470
                s.sendLeafNodeConnect(acc)
16✔
3471
        }
16✔
3472
        s.accountConnectEvent(c)
644✔
3473

644✔
3474
        // The above functions are not atomically under the client
644✔
3475
        // lock doing those operations. It is possible - since we
644✔
3476
        // have started the read/write loops - that the connection
644✔
3477
        // is closed before or in between. This would leave the
644✔
3478
        // closed LN connection possible registered with the account
644✔
3479
        // and/or the server's leafs map. So check if connection
644✔
3480
        // is closed, and if so, manually cleanup.
644✔
3481
        c.mu.Lock()
644✔
3482
        closed := c.isClosed()
644✔
3483
        if !closed {
1,288✔
3484
                c.setFirstPingTimer()
644✔
3485
        }
644✔
3486
        c.mu.Unlock()
644✔
3487
        if closed {
644✔
3488
                s.removeLeafNodeConnection(c)
×
3489
                if prev := acc.removeClient(c); prev == 1 {
×
3490
                        s.decActiveAccounts()
×
3491
                }
×
3492
        }
3493
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc