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

nats-io / nats-server / 30607740357

30 Jul 2026 01:36PM UTC coverage: 77.29% (+0.01%) from 77.276%
30607740357

push

github

neilalexander
[FIXED] Certificate users authenticatable by username on other listeners

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

73339 of 94888 relevant lines covered (77.29%)

543177.05 hits per line

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

86.59
/server/leafnode.go
1
// Copyright 2019-2026 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
        "regexp"
31
        "runtime"
32
        "strconv"
33
        "strings"
34
        "sync"
35
        "sync/atomic"
36
        "time"
37

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

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

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

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

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

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

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

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

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

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

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

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

133
func (c *client) isHubLeafNode() bool {
15,850✔
134
        return c.kind == LEAF && c.leaf != nil && !c.leaf.isSpoke
15,850✔
135
}
15,850✔
136

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

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

205
// Ensure that leafnode is properly configured.
206
func validateLeafNode(o *Options) error {
7,565✔
207
        if err := validateLeafNodeAuthOptions(o); err != nil {
7,567✔
208
                return err
2✔
209
        }
2✔
210

211
        if len(o.LeafNode.Remotes) > 0 {
7,950✔
212
                names := make(map[string]struct{})
387✔
213
                // Check for duplicate remotes, also, users can bind to any local account,
387✔
214
                // if its empty we will assume the $G account.
387✔
215
                for _, r := range o.LeafNode.Remotes {
888✔
216
                        if r.LocalAccount == _EMPTY_ {
806✔
217
                                r.LocalAccount = globalAccountName
305✔
218
                        }
305✔
219
                        if err := checkPermSubjectArray(r.DenyImports, false); err != nil {
501✔
220
                                return fmt.Errorf("invalid deny_imports for remote %s: %w", r.safeName(), err)
×
221
                        }
×
222
                        if err := checkPermSubjectArray(r.DenyExports, false); err != nil {
501✔
223
                                return fmt.Errorf("invalid deny_exports for remote %s: %w", r.safeName(), err)
×
224
                        }
×
225
                        rn := r.name()
501✔
226
                        if _, dup := names[rn]; dup {
504✔
227
                                return fmt.Errorf("duplicate remote %s", r.safeName())
3✔
228
                        }
3✔
229
                        names[rn] = struct{}{}
498✔
230
                }
231
        }
232

233
        // In local config mode, check that leafnode configuration refers to accounts that exist.
234
        if len(o.TrustedOperators) == 0 {
14,839✔
235
                accNames := map[string]struct{}{}
7,279✔
236
                for _, a := range o.Accounts {
15,784✔
237
                        accNames[a.Name] = struct{}{}
8,505✔
238
                }
8,505✔
239
                // global account is always created
240
                accNames[DEFAULT_GLOBAL_ACCOUNT] = struct{}{}
7,279✔
241
                // in the context of leaf nodes, empty account means global account
7,279✔
242
                accNames[_EMPTY_] = struct{}{}
7,279✔
243
                // system account either exists or, if not disabled, will be created
7,279✔
244
                if o.SystemAccount == _EMPTY_ && !o.NoSystemAccount {
13,156✔
245
                        accNames[DEFAULT_SYSTEM_ACCOUNT] = struct{}{}
5,877✔
246
                }
5,877✔
247
                checkAccountExists := func(accName string, cfgType string) error {
15,056✔
248
                        if _, ok := accNames[accName]; !ok {
7,779✔
249
                                return fmt.Errorf("cannot find local account %q specified in leafnode %s", accName, cfgType)
2✔
250
                        }
2✔
251
                        return nil
7,775✔
252
                }
253
                if err := checkAccountExists(o.LeafNode.Account, "authorization"); err != nil {
7,280✔
254
                        return err
1✔
255
                }
1✔
256
                for _, lu := range o.LeafNode.Users {
7,296✔
257
                        if lu.Account == nil { // means global account
29✔
258
                                continue
11✔
259
                        }
260
                        if err := checkAccountExists(lu.Account.Name, "authorization"); err != nil {
7✔
261
                                return err
×
262
                        }
×
263
                }
264
                for _, r := range o.LeafNode.Remotes {
7,769✔
265
                        if err := checkAccountExists(r.LocalAccount, "remote"); err != nil {
492✔
266
                                return err
1✔
267
                        }
1✔
268
                }
269
        } else {
281✔
270
                if len(o.LeafNode.Users) != 0 {
282✔
271
                        return fmt.Errorf("operator mode does not allow specifying users in leafnode config")
1✔
272
                }
1✔
273
                for _, r := range o.LeafNode.Remotes {
281✔
274
                        if !nkeys.IsValidPublicAccountKey(r.LocalAccount) {
2✔
275
                                return fmt.Errorf(
1✔
276
                                        "operator mode requires account nkeys in remotes. " +
1✔
277
                                                "Please add an `account` key to each remote in your `leafnodes` section, to assign it to an account. " +
1✔
278
                                                "Each account value should be a 56 character public key, starting with the letter 'A'")
1✔
279
                        }
1✔
280
                }
281
                if o.LeafNode.Port != 0 && o.LeafNode.Account != "" && !nkeys.IsValidPublicAccountKey(o.LeafNode.Account) {
280✔
282
                        return fmt.Errorf("operator mode and non account nkeys are incompatible")
1✔
283
                }
1✔
284
        }
285

286
        // Validate compression settings
287
        if o.LeafNode.Compression.Mode != _EMPTY_ {
11,951✔
288
                if err := validateAndNormalizeCompressionOption(&o.LeafNode.Compression, CompressionS2Auto); err != nil {
4,396✔
289
                        return err
×
290
                }
×
291
        }
292

293
        // If a remote has a websocket scheme, all need to have it.
294
        for _, rcfg := range o.LeafNode.Remotes {
8,045✔
295
                // Validate proxy configuration
490✔
296
                if _, err := validateLeafNodeProxyOptions(rcfg); err != nil {
496✔
297
                        return err
6✔
298
                }
6✔
299

300
                if len(rcfg.URLs) >= 2 {
645✔
301
                        firstIsWS, ok := isWSURL(rcfg.URLs[0]), true
161✔
302
                        for i := 1; i < len(rcfg.URLs); i++ {
474✔
303
                                u := rcfg.URLs[i]
313✔
304
                                if isWS := isWSURL(u); isWS && !firstIsWS || !isWS && firstIsWS {
320✔
305
                                        ok = false
7✔
306
                                        break
7✔
307
                                }
308
                        }
309
                        if !ok {
168✔
310
                                return fmt.Errorf("remote leaf node configuration cannot have a mix of websocket and non-websocket urls: %q", redactURLList(rcfg.URLs))
7✔
311
                        }
7✔
312
                }
313
                if !wsAllowedFIPS() {
477✔
314
                        for _, u := range rcfg.URLs {
×
315
                                if isWSURL(u) {
×
316
                                        return fmt.Errorf("remote leaf node URL %q cannot be used in FIPS-140 mode when built with this Go version, use Go 1.26 or later", redactURLString(u.String()))
×
317
                                }
×
318
                        }
319
                }
320
                // Validate compression settings
321
                if rcfg.Compression.Mode != _EMPTY_ {
948✔
322
                        if err := validateAndNormalizeCompressionOption(&rcfg.Compression, CompressionS2Auto); err != nil {
471✔
323
                                return err
×
324
                        }
×
325
                }
326
        }
327

328
        if o.LeafNode.Port == 0 {
11,265✔
329
                return nil
3,723✔
330
        }
3,723✔
331

332
        // If MinVersion is defined, check that it is valid.
333
        if mv := o.LeafNode.MinVersion; mv != _EMPTY_ {
3,823✔
334
                if err := checkLeafMinVersionConfig(mv); err != nil {
6✔
335
                        return err
2✔
336
                }
2✔
337
        }
338

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

343
        if o.Gateway.Name == _EMPTY_ && o.Gateway.Port == 0 {
6,995✔
344
                return nil
3,178✔
345
        }
3,178✔
346
        // If we are here we have both leaf nodes and gateways defined, make sure there
347
        // is a system account defined.
348
        if o.SystemAccount == _EMPTY_ {
640✔
349
                return fmt.Errorf("leaf nodes and gateways (both being defined) require a system account to also be configured")
1✔
350
        }
1✔
351
        if err := validatePinnedCerts(o.LeafNode.TLSPinnedCerts); err != nil {
638✔
352
                return fmt.Errorf("leafnode: %v", err)
×
353
        }
×
354
        return nil
638✔
355
}
356

357
func checkLeafMinVersionConfig(mv string) error {
8✔
358
        if ok, err := versionAtLeastCheckError(mv, 2, 8, 0); !ok || err != nil {
12✔
359
                if err != nil {
6✔
360
                        return fmt.Errorf("invalid leafnode's minimum version: %v", err)
2✔
361
                } else {
4✔
362
                        return fmt.Errorf("the minimum version should be at least 2.8.0")
2✔
363
                }
2✔
364
        }
365
        return nil
4✔
366
}
367

368
// Used to validate user names in LeafNode configuration.
369
// - rejects mix of single and multiple users.
370
// - rejects duplicate user names.
371
func validateLeafNodeAuthOptions(o *Options) error {
7,612✔
372
        if len(o.LeafNode.Users) == 0 {
15,196✔
373
                return nil
7,584✔
374
        }
7,584✔
375
        if o.LeafNode.Username != _EMPTY_ {
30✔
376
                return fmt.Errorf("can not have a single user/pass and a users array")
2✔
377
        }
2✔
378
        if o.LeafNode.Nkey != _EMPTY_ {
26✔
379
                return fmt.Errorf("can not have a single nkey and a users array")
×
380
        }
×
381
        users := map[string]struct{}{}
26✔
382
        for _, u := range o.LeafNode.Users {
66✔
383
                if _, exists := users[u.Username]; exists {
42✔
384
                        return fmt.Errorf("duplicate user %q detected in leafnode authorization", u.Username)
2✔
385
                }
2✔
386
                users[u.Username] = struct{}{}
38✔
387
        }
388
        return nil
24✔
389
}
390

391
func validateLeafNodeProxyOptions(remote *RemoteLeafOpts) ([]string, error) {
871✔
392
        var warnings []string
871✔
393

871✔
394
        if remote.Proxy.URL == _EMPTY_ {
1,716✔
395
                return warnings, nil
845✔
396
        }
845✔
397

398
        proxyURL, err := url.Parse(remote.Proxy.URL)
26✔
399
        if err != nil {
27✔
400
                return warnings, fmt.Errorf("invalid proxy URL: %v", err)
1✔
401
        }
1✔
402

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

407
        if proxyURL.Host == _EMPTY_ {
25✔
408
                return warnings, fmt.Errorf("proxy URL must specify a host")
2✔
409
        }
2✔
410

411
        if remote.Proxy.Timeout < 0 {
22✔
412
                return warnings, fmt.Errorf("proxy timeout must be >= 0")
1✔
413
        }
1✔
414

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

419
        if len(remote.URLs) > 0 {
32✔
420
                hasWebSocketURL := false
16✔
421
                hasNonWebSocketURL := false
16✔
422

16✔
423
                for _, remoteURL := range remote.URLs {
33✔
424
                        if remoteURL.Scheme == wsSchemePrefix || remoteURL.Scheme == wsSchemePrefixTLS {
30✔
425
                                hasWebSocketURL = true
13✔
426
                                if (remoteURL.Scheme == wsSchemePrefixTLS) &&
13✔
427
                                        remote.TLSConfig == nil && !remote.TLS {
14✔
428
                                        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✔
429
                                }
1✔
430
                        } else {
4✔
431
                                hasNonWebSocketURL = true
4✔
432
                        }
4✔
433
                }
434

435
                if !hasWebSocketURL {
18✔
436
                        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✔
437
                } else if hasNonWebSocketURL {
16✔
438
                        warnings = append(warnings, "proxy configuration will only be used for WebSocket URLs: proxy settings do not apply to TCP connections (nats://)")
1✔
439
                }
1✔
440
        }
441

442
        return warnings, nil
15✔
443
}
444

445
// Wait for the configured reconnect interval before attempting to connect
446
// again to the remote leafnode.
447
func (s *Server) reConnectToRemoteLeafNode(remote *leafNodeCfg) {
232✔
448
        clearInProgress := true
232✔
449
        defer func() {
462✔
450
                s.grWG.Done()
230✔
451
                if clearInProgress {
288✔
452
                        remote.setConnectInProgress(false)
58✔
453
                }
58✔
454
        }()
455
        delay := s.getOpts().LeafNode.ReconnectInterval
232✔
456
        select {
232✔
457
        case <-time.After(delay):
180✔
458
        case <-remote.quitCh:
×
459
                return
×
460
        case <-s.quitCh:
51✔
461
                return
51✔
462
        }
463
        clearInProgress = !connectToRemoteLeafNode(s, remote, false)
180✔
464
}
465

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

507
// Notifies the quit channel without blocking.
508
// No lock is needed to invoke this function.
509
func (cfg *leafNodeCfg) notifyQuitChannel() {
2✔
510
        select {
2✔
511
        case cfg.quitCh <- struct{}{}:
2✔
512
        default:
×
513
        }
514
}
515

516
// Sets the connect-in-progress status for this remote leaf configuration.
517
func (cfg *leafNodeCfg) setConnectInProgress(inProgress bool) {
1,639✔
518
        cfg.Lock()
1,639✔
519
        defer cfg.Unlock()
1,639✔
520
        // In both cases we want to drain the "quit" channel.
1,639✔
521
        select {
1,639✔
522
        case <-cfg.quitCh:
×
523
        default:
1,639✔
524
        }
525
        cfg.connInProgress = inProgress
1,639✔
526
}
527

528
// Returns `true` if this remote is in the middle of a connect, `false` otherwise.
529
func (cfg *leafNodeCfg) isConnectInProgress() bool {
2✔
530
        cfg.RLock()
2✔
531
        defer cfg.RUnlock()
2✔
532
        return cfg.connInProgress
2✔
533
}
2✔
534

535
// Mark that this remote is being removed from the configuration.
536
func (cfg *leafNodeCfg) markAsRemoved() {
2✔
537
        cfg.Lock()
2✔
538
        defer cfg.Unlock()
2✔
539
        // This function should be invoked only once, but protect.
2✔
540
        if cfg.removed {
2✔
541
                return
×
542
        }
×
543
        cfg.removed = true
2✔
544
        cfg.notifyQuitChannel()
2✔
545
}
546

547
// Returns false if it has been disabled or removed.
548
func (cfg *leafNodeCfg) stillValid() bool {
4,692✔
549
        cfg.RLock()
4,692✔
550
        defer cfg.RUnlock()
4,692✔
551
        return !cfg.Disabled && !cfg.removed
4,692✔
552
}
4,692✔
553

554
// Will pick an URL from the list of available URLs.
555
func (cfg *leafNodeCfg) pickNextURL() *url.URL {
2,943✔
556
        cfg.Lock()
2,943✔
557
        defer cfg.Unlock()
2,943✔
558
        // If the current URL is the first in the list and we have more than
2,943✔
559
        // one URL, then move that one to end of the list.
2,943✔
560
        if cfg.curURL != nil && len(cfg.urls) > 1 && urlsAreEqual(cfg.curURL, cfg.urls[0]) {
5,223✔
561
                first := cfg.urls[0]
2,280✔
562
                copy(cfg.urls, cfg.urls[1:])
2,280✔
563
                cfg.urls[len(cfg.urls)-1] = first
2,280✔
564
        }
2,280✔
565
        cfg.curURL = cfg.urls[0]
2,943✔
566
        return cfg.curURL
2,943✔
567
}
568

569
// Returns the current URL
570
func (cfg *leafNodeCfg) getCurrentURL() *url.URL {
73✔
571
        cfg.RLock()
73✔
572
        defer cfg.RUnlock()
73✔
573
        return cfg.curURL
73✔
574
}
73✔
575

576
// Returns how long the server should wait before attempting
577
// to solicit a remote leafnode connection.
578
func (cfg *leafNodeCfg) getConnectDelay() time.Duration {
650✔
579
        cfg.RLock()
650✔
580
        delay := cfg.connDelay
650✔
581
        cfg.RUnlock()
650✔
582
        return delay
650✔
583
}
650✔
584

585
// Sets the connect delay.
586
func (cfg *leafNodeCfg) setConnectDelay(delay time.Duration) {
135✔
587
        cfg.Lock()
135✔
588
        cfg.connDelay = delay
135✔
589
        cfg.Unlock()
135✔
590
}
135✔
591

592
// Ensure that non-exported options (used in tests) have
593
// been properly set.
594
func (s *Server) setLeafNodeNonExportedOptions() {
6,400✔
595
        opts := s.getOpts()
6,400✔
596
        s.leafNodeOpts.dialTimeout = opts.LeafNode.dialTimeout
6,400✔
597
        if s.leafNodeOpts.dialTimeout == 0 {
12,799✔
598
                // Use same timeouts as routes for now.
6,399✔
599
                s.leafNodeOpts.dialTimeout = DEFAULT_ROUTE_DIAL
6,399✔
600
        }
6,399✔
601
        s.leafNodeOpts.resolver = opts.LeafNode.resolver
6,400✔
602
        if s.leafNodeOpts.resolver == nil {
12,797✔
603
                s.leafNodeOpts.resolver = net.DefaultResolver
6,397✔
604
        }
6,397✔
605
}
606

607
const sharedSysAccDelay = 250 * time.Millisecond
608

609
// establishHTTPProxyTunnel establishes an HTTP CONNECT tunnel through a proxy server
610
func establishHTTPProxyTunnel(proxyURL, targetHost string, timeout time.Duration, username, password string) (net.Conn, error) {
11✔
611
        proxyAddr, err := url.Parse(proxyURL)
11✔
612
        if err != nil {
11✔
613
                // This should not happen since proxy URL is validated during configuration parsing
×
614
                return nil, fmt.Errorf("unexpected proxy URL parse error (URL was pre-validated): %v", err)
×
615
        }
×
616

617
        // Connect to the proxy server
618
        conn, err := natsDialTimeout("tcp", proxyAddr.Host, timeout)
11✔
619
        if err != nil {
11✔
620
                return nil, fmt.Errorf("failed to connect to proxy: %v", err)
×
621
        }
×
622

623
        // Set deadline for the entire proxy handshake
624
        if err := conn.SetDeadline(time.Now().Add(timeout)); err != nil {
11✔
625
                conn.Close()
×
626
                return nil, fmt.Errorf("failed to set deadline: %v", err)
×
627
        }
×
628

629
        req := &http.Request{
11✔
630
                Method: http.MethodConnect,
11✔
631
                URL:    &url.URL{Opaque: targetHost}, // Opaque is required for CONNECT
11✔
632
                Host:   targetHost,
11✔
633
                Header: make(http.Header),
11✔
634
        }
11✔
635

11✔
636
        // Add proxy authentication if provided
11✔
637
        if username != "" && password != "" {
13✔
638
                req.Header.Set("Proxy-Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(username+":"+password)))
2✔
639
        }
2✔
640

641
        if err := req.Write(conn); err != nil {
11✔
642
                conn.Close()
×
643
                return nil, fmt.Errorf("failed to write CONNECT request: %v", err)
×
644
        }
×
645

646
        resp, err := http.ReadResponse(bufio.NewReader(conn), req)
11✔
647
        if err != nil {
11✔
648
                conn.Close()
×
649
                return nil, fmt.Errorf("failed to read proxy response: %v", err)
×
650
        }
×
651

652
        if resp.StatusCode != http.StatusOK {
12✔
653
                resp.Body.Close()
1✔
654
                conn.Close()
1✔
655
                return nil, fmt.Errorf("proxy CONNECT failed: %s", resp.Status)
1✔
656
        }
1✔
657

658
        // Close the response body
659
        resp.Body.Close()
10✔
660

10✔
661
        // Clear the deadline now that we've finished the proxy handshake
10✔
662
        if err := conn.SetDeadline(time.Time{}); err != nil {
10✔
663
                conn.Close()
×
664
                return nil, fmt.Errorf("failed to clear deadline: %v", err)
×
665
        }
×
666

667
        return conn, nil
10✔
668
}
669

670
// Connect to a remote leaf node asynchronously (that is, this function will do
671
// the connect in a go routine).
672
func (s *Server) connectToRemoteLeafNodeAsynchronously(remote *leafNodeCfg, firstConnect bool) {
470✔
673
        remote.setConnectInProgress(true)
470✔
674
        s.startGoRoutine(func() {
940✔
675
                defer s.grWG.Done()
470✔
676
                if !connectToRemoteLeafNode(s, remote, firstConnect) {
481✔
677
                        remote.setConnectInProgress(false)
11✔
678
                }
11✔
679
        })
680
}
681

682
// Connect to a remote leaf node. Should only be invoked from
683
// `s.connectToRemoteLeafNodeAsynchronously()` or `s.reConnectToRemoteLeafNode()`.
684
// Returns `true` if this function invoked `s.createLeafNode()`, false otherwise.
685
func connectToRemoteLeafNode(s *Server, remote *leafNodeCfg, firstConnect bool) bool {
650✔
686

650✔
687
        if remote == nil || len(remote.URLs) == 0 {
650✔
688
                s.Debugf("Empty remote leafnode definition, nothing to connect")
×
689
                return false
×
690
        }
×
691

692
        // If this remote is no longer valid by the time we return (e.g. it was
693
        // removed or disabled through a configuration reload), we will never
694
        // reconnect, so clear any JetStream observer state. Otherwise, the raft
695
        // nodes of this account's assets would remain observers.
696
        defer func() {
1,299✔
697
                if remote.stillValid() {
1,296✔
698
                        return
647✔
699
                }
647✔
700
                s.mu.RLock()
2✔
701
                shouldMigrate := remote.JetStreamClusterMigrate
2✔
702
                s.mu.RUnlock()
2✔
703
                if shouldMigrate {
4✔
704
                        s.clearObserverState(remote)
2✔
705
                }
2✔
706
        }()
707

708
        opts := s.getOpts()
650✔
709
        reconnectDelay := opts.LeafNode.ReconnectInterval
650✔
710
        s.mu.RLock()
650✔
711
        dialTimeout := s.leafNodeOpts.dialTimeout
650✔
712
        resolver := s.leafNodeOpts.resolver
650✔
713
        var isSysAcc bool
650✔
714
        if s.eventsEnabled() {
1,272✔
715
                isSysAcc = remote.LocalAccount == s.sys.account.Name
622✔
716
        }
622✔
717
        jetstreamMigrateDelay := remote.JetStreamClusterMigrateDelay
650✔
718
        s.mu.RUnlock()
650✔
719

650✔
720
        // If we are sharing a system account and we are not standalone delay to gather some info prior.
650✔
721
        if firstConnect && isSysAcc && !s.standAloneMode() {
712✔
722
                s.Debugf("Will delay first leafnode connect to shared system account due to clustering")
62✔
723
                remote.setConnectDelay(sharedSysAccDelay)
62✔
724
        }
62✔
725

726
        if connDelay := remote.getConnectDelay(); connDelay > 0 {
719✔
727
                select {
69✔
728
                case <-time.After(connDelay):
63✔
729
                case <-remote.quitCh:
×
730
                        return false
×
731
                case <-s.quitCh:
6✔
732
                        return false
6✔
733
                }
734
                remote.setConnectDelay(0)
63✔
735
        }
736

737
        var conn net.Conn
644✔
738

644✔
739
        const connErrFmt = "Error trying to connect as leafnode to remote server %q (attempt %v): %v"
644✔
740

644✔
741
        // Capture proxy configuration once before the loop with proper locking
644✔
742
        remote.RLock()
644✔
743
        proxyURL := remote.Proxy.URL
644✔
744
        proxyUsername := remote.Proxy.Username
644✔
745
        proxyPassword := remote.Proxy.Password
644✔
746
        proxyTimeout := remote.Proxy.Timeout
644✔
747
        remote.RUnlock()
644✔
748

644✔
749
        // Set default proxy timeout if not specified
644✔
750
        if proxyTimeout == 0 {
1,280✔
751
                proxyTimeout = dialTimeout
636✔
752
        }
636✔
753

754
        attempts := 0
644✔
755

644✔
756
        // In case the migrate timer was created but not canceled, do it when
644✔
757
        // this function exits. Note that the timer would not be created if
644✔
758
        // `jetstreamMigrateDelay == 0`.
644✔
759
        if jetstreamMigrateDelay > 0 {
652✔
760
                defer remote.cancelMigrateTimer()
8✔
761
        }
8✔
762

763
        reconnectTimer := time.NewTimer(reconnectDelay)
644✔
764
        reconnectTimer.Stop()
644✔
765
        defer stopAndClearTimer(&reconnectTimer)
644✔
766

644✔
767
        for s.isRunning() && remote.stillValid() {
3,587✔
768
                rURL := remote.pickNextURL()
2,943✔
769
                url, err := s.getRandomIP(resolver, rURL.Host, nil)
2,943✔
770
                if err == nil {
5,881✔
771
                        var ipStr string
2,938✔
772
                        if url != rURL.Host {
3,004✔
773
                                ipStr = fmt.Sprintf(" (%s)", url)
66✔
774
                        }
66✔
775
                        // Some test may want to disable remotes from connecting
776
                        if s.isLeafConnectDisabled() {
3,073✔
777
                                s.Debugf("Will not attempt to connect to remote server on %q%s, leafnodes currently disabled", rURL.Host, ipStr)
135✔
778
                                err = ErrLeafNodeDisabled
135✔
779
                        } else {
2,938✔
780
                                s.Debugf("Trying to connect as leafnode to remote server on %q%s", rURL.Host, ipStr)
2,803✔
781

2,803✔
782
                                // Check if proxy is configured
2,803✔
783
                                if proxyURL != _EMPTY_ {
2,811✔
784
                                        targetHost := rURL.Host
8✔
785
                                        // If URL doesn't include port, add the default port for the scheme
8✔
786
                                        if rURL.Port() == _EMPTY_ {
8✔
787
                                                defaultPort := "80"
×
788
                                                if rURL.Scheme == wsSchemePrefixTLS {
×
789
                                                        defaultPort = "443"
×
790
                                                }
×
791
                                                targetHost = net.JoinHostPort(rURL.Hostname(), defaultPort)
×
792
                                        }
793

794
                                        conn, err = establishHTTPProxyTunnel(proxyURL, targetHost, proxyTimeout, proxyUsername, proxyPassword)
8✔
795
                                } else {
2,795✔
796
                                        // Direct connection
2,795✔
797
                                        conn, err = natsDialTimeout("tcp", url, dialTimeout)
2,795✔
798
                                }
2,795✔
799
                        }
800
                }
801
                if err != nil {
5,255✔
802
                        jitter := time.Duration(rand.Int63n(int64(reconnectDelay)))
2,312✔
803
                        delay := reconnectDelay + jitter
2,312✔
804
                        attempts++
2,312✔
805
                        if s.shouldReportConnectErr(firstConnect, attempts) {
4,617✔
806
                                s.Errorf(connErrFmt, rURL.Host, attempts, err)
2,305✔
807
                        } else {
2,312✔
808
                                s.Debugf(connErrFmt, rURL.Host, attempts, err)
7✔
809
                        }
7✔
810
                        remote.Lock()
2,312✔
811
                        // if we are using a delay to start migrating assets, kick off a migrate timer.
2,312✔
812
                        if remote.jsMigrateTimer == nil && jetstreamMigrateDelay > 0 {
2,320✔
813
                                remote.jsMigrateTimer = time.AfterFunc(jetstreamMigrateDelay, func() {
16✔
814
                                        s.checkJetStreamMigrate(remote)
8✔
815
                                })
8✔
816
                        }
817
                        remote.Unlock()
2,312✔
818
                        reconnectTimer.Reset(delay)
2,312✔
819
                        select {
2,312✔
820
                        case <-s.quitCh:
10✔
821
                                return false
10✔
822
                        case <-remote.quitCh:
2✔
823
                                return false
2✔
824
                        case <-reconnectTimer.C:
2,299✔
825
                                // Check if we should migrate any JetStream assets immediately while this remote is down.
2,299✔
826
                                // This will be used if JetStreamClusterMigrateDelay was not set
2,299✔
827
                                if jetstreamMigrateDelay == 0 {
4,520✔
828
                                        s.checkJetStreamMigrate(remote)
2,221✔
829
                                }
2,221✔
830
                                continue
2,299✔
831
                        }
832
                }
833
                remote.cancelMigrateTimer()
631✔
834
                // We can check here, but really we will have to check again when the server
631✔
835
                // is about to add to the `s.leafs` map later in the process.
631✔
836
                if !remote.stillValid() {
631✔
837
                        conn.Close()
×
838
                        return false
×
839
                }
×
840

841
                // We have a connection here to a remote server.
842
                // Go ahead and create our leaf node and return.
843
                s.createLeafNode(conn, rURL, remote, nil)
631✔
844

631✔
845
                // Clear any observer states if we had them.
631✔
846
                s.clearObserverState(remote)
631✔
847

631✔
848
                return true
631✔
849
        }
850

851
        return false
×
852
}
853

854
func (cfg *leafNodeCfg) cancelMigrateTimer() {
639✔
855
        cfg.Lock()
639✔
856
        stopAndClearTimer(&cfg.jsMigrateTimer)
639✔
857
        cfg.Unlock()
639✔
858
}
639✔
859

860
// This will clear any observer state such that stream or consumer assets on this server can become leaders again.
861
func (s *Server) clearObserverState(remote *leafNodeCfg) {
633✔
862
        s.mu.RLock()
633✔
863
        accName := remote.LocalAccount
633✔
864
        s.mu.RUnlock()
633✔
865

633✔
866
        acc, err := s.LookupAccount(accName)
633✔
867
        if err != nil {
635✔
868
                s.Warnf("Error looking up account [%s] checking for JetStream clear observer state on a leafnode", accName)
2✔
869
                return
2✔
870
        }
2✔
871

872
        acc.jscmMu.Lock()
631✔
873
        defer acc.jscmMu.Unlock()
631✔
874

631✔
875
        // Walk all streams looking for any clustered stream, skip otherwise.
631✔
876
        for _, mset := range acc.streams() {
652✔
877
                node := mset.raftNode()
21✔
878
                if node == nil {
33✔
879
                        // Not R>1
12✔
880
                        continue
12✔
881
                }
882
                // Check consumers
883
                for _, o := range mset.getConsumers() {
11✔
884
                        if n := o.raftNode(); n != nil {
4✔
885
                                // Ensure we can become a leader again.
2✔
886
                                n.SetObserver(false)
2✔
887
                        }
2✔
888
                }
889
                // Ensure we can not become a leader again.
890
                node.SetObserver(false)
9✔
891
        }
892
}
893

894
// Check to see if we should migrate any assets from this account.
895
func (s *Server) checkJetStreamMigrate(remote *leafNodeCfg) {
2,229✔
896
        s.mu.RLock()
2,229✔
897
        accName, shouldMigrate := remote.LocalAccount, remote.JetStreamClusterMigrate
2,229✔
898
        s.mu.RUnlock()
2,229✔
899

2,229✔
900
        if !shouldMigrate {
4,395✔
901
                return
2,166✔
902
        }
2,166✔
903

904
        acc, err := s.LookupAccount(accName)
63✔
905
        if err != nil {
63✔
906
                s.Warnf("Error looking up account [%s] checking for JetStream migration on a leafnode", accName)
×
907
                return
×
908
        }
×
909

910
        acc.jscmMu.Lock()
63✔
911
        defer acc.jscmMu.Unlock()
63✔
912

63✔
913
        // Walk all streams looking for any clustered stream, skip otherwise.
63✔
914
        // If we are the leader force stepdown.
63✔
915
        for _, mset := range acc.streams() {
93✔
916
                node := mset.raftNode()
30✔
917
                if node == nil {
30✔
918
                        // Not R>1
×
919
                        continue
×
920
                }
921
                // Collect any consumers
922
                for _, o := range mset.getConsumers() {
48✔
923
                        if n := o.raftNode(); n != nil {
36✔
924
                                n.StepDown()
18✔
925
                                // Ensure we can not become a leader while in this state.
18✔
926
                                n.SetObserver(true)
18✔
927
                        }
18✔
928
                }
929
                // Stepdown if this stream was leader.
930
                node.StepDown()
30✔
931
                // Ensure we can not become a leader while in this state.
30✔
932
                node.SetObserver(true)
30✔
933
        }
934
}
935

936
// Helper for checking.
937
func (s *Server) isLeafConnectDisabled() bool {
2,938✔
938
        s.mu.RLock()
2,938✔
939
        defer s.mu.RUnlock()
2,938✔
940
        return s.leafDisableConnect
2,938✔
941
}
2,938✔
942

943
// Save off the tlsName for when we use TLS and mix hostnames and IPs. IPs usually
944
// come from the server we connect to.
945
//
946
// We used to save the name only if there was a TLSConfig or scheme equal to "tls".
947
// However, this was causing failures for users that did not set the scheme (and
948
// their remote connections did not have a tls{} block).
949
// We now save the host name regardless in case the remote returns an INFO indicating
950
// that TLS is required.
951
//
952
// Lock held on entry.
953
func (cfg *leafNodeCfg) saveTLSHostname(u *url.URL) {
1,171✔
954
        if cfg.tlsName == _EMPTY_ && net.ParseIP(u.Hostname()) == nil {
1,184✔
955
                cfg.tlsName = u.Hostname()
13✔
956
        }
13✔
957
}
958

959
// Save off the username/password for when we connect using a bare URL
960
// that we get from the INFO protocol.
961
//
962
// Lock held on entry.
963
func (cfg *leafNodeCfg) saveUserPassword(u *url.URL) {
775✔
964
        if cfg.username == _EMPTY_ && u.User != nil {
990✔
965
                cfg.username = u.User.Username()
215✔
966
                cfg.password, _ = u.User.Password()
215✔
967
        }
215✔
968
}
969

970
// This starts the leafnode accept loop in a go routine, unless it
971
// is detected that the server has already been shutdown.
972
func (s *Server) startLeafNodeAcceptLoop() {
3,803✔
973
        // Snapshot server options.
3,803✔
974
        opts := s.getOpts()
3,803✔
975

3,803✔
976
        port := opts.LeafNode.Port
3,803✔
977
        if port == -1 {
7,531✔
978
                port = 0
3,728✔
979
        }
3,728✔
980

981
        if s.isShuttingDown() {
3,803✔
982
                return
×
983
        }
×
984

985
        s.mu.Lock()
3,803✔
986
        hp := net.JoinHostPort(opts.LeafNode.Host, strconv.Itoa(port))
3,803✔
987
        l, e := natsListen("tcp", hp)
3,803✔
988
        s.leafNodeListenerErr = e
3,803✔
989
        if e != nil {
3,803✔
990
                s.mu.Unlock()
×
991
                s.Fatalf("Error listening on leafnode port: %d - %v", opts.LeafNode.Port, e)
×
992
                return
×
993
        }
×
994

995
        s.Noticef("Listening for leafnode connections on %s",
3,803✔
996
                net.JoinHostPort(opts.LeafNode.Host, strconv.Itoa(l.Addr().(*net.TCPAddr).Port)))
3,803✔
997

3,803✔
998
        tlsRequired := opts.LeafNode.TLSConfig != nil
3,803✔
999
        tlsVerify := tlsRequired && opts.LeafNode.TLSConfig.ClientAuth == tls.RequireAndVerifyClientCert
3,803✔
1000
        // Do not set compression in this Info object, it would possibly cause
3,803✔
1001
        // issues when sending asynchronous INFO to the remote.
3,803✔
1002
        info := Info{
3,803✔
1003
                ID:            s.info.ID,
3,803✔
1004
                Name:          s.info.Name,
3,803✔
1005
                Version:       s.info.Version,
3,803✔
1006
                GitCommit:     gitCommit,
3,803✔
1007
                GoVersion:     runtime.Version(),
3,803✔
1008
                AuthRequired:  true,
3,803✔
1009
                TLSRequired:   tlsRequired,
3,803✔
1010
                TLSVerify:     tlsVerify,
3,803✔
1011
                MaxPayload:    s.info.MaxPayload, // TODO(dlc) - Allow override?
3,803✔
1012
                Headers:       s.supportsHeaders(),
3,803✔
1013
                JetStream:     opts.JetStream,
3,803✔
1014
                Domain:        opts.JetStreamDomain,
3,803✔
1015
                Proto:         s.getServerProto(),
3,803✔
1016
                InfoOnConnect: true,
3,803✔
1017
                JSApiLevel:    JSApiLevel,
3,803✔
1018
        }
3,803✔
1019
        // If we have selected a random port...
3,803✔
1020
        if port == 0 {
7,531✔
1021
                // Write resolved port back to options.
3,728✔
1022
                opts.LeafNode.Port = l.Addr().(*net.TCPAddr).Port
3,728✔
1023
        }
3,728✔
1024

1025
        s.leafNodeInfo = info
3,803✔
1026
        // Possibly override Host/Port and set IP based on Cluster.Advertise
3,803✔
1027
        if err := s.setLeafNodeInfoHostPortAndIP(); err != nil {
3,803✔
1028
                s.Fatalf("Error setting leafnode INFO with LeafNode.Advertise value of %s, err=%v", opts.LeafNode.Advertise, err)
×
1029
                l.Close()
×
1030
                s.mu.Unlock()
×
1031
                return
×
1032
        }
×
1033
        s.leafURLsMap[s.leafNodeInfo.IP]++
3,803✔
1034
        s.generateLeafNodeInfoJSON()
3,803✔
1035

3,803✔
1036
        // Setup state that can enable shutdown
3,803✔
1037
        s.leafNodeListener = l
3,803✔
1038

3,803✔
1039
        // As of now, a server that does not have remotes configured would
3,803✔
1040
        // never solicit a connection, so we should not have to warn if
3,803✔
1041
        // InsecureSkipVerify is set in main LeafNodes config (since
3,803✔
1042
        // this TLS setting matters only when soliciting a connection).
3,803✔
1043
        // Still, warn if insecure is set in any of LeafNode block.
3,803✔
1044
        // We need to check remotes, even if tls is not required on accept.
3,803✔
1045
        warn := tlsRequired && opts.LeafNode.TLSConfig.InsecureSkipVerify
3,803✔
1046
        if !warn {
7,604✔
1047
                for _, r := range opts.LeafNode.Remotes {
3,845✔
1048
                        if r.TLSConfig != nil && r.TLSConfig.InsecureSkipVerify {
44✔
1049
                                warn = true
×
1050
                                break
×
1051
                        }
1052
                }
1053
        }
1054
        if warn {
3,805✔
1055
                s.Warnf(leafnodeTLSInsecureWarning)
2✔
1056
        }
2✔
1057
        go s.acceptConnections(l, "Leafnode", func(conn net.Conn) { s.createLeafNode(conn, nil, nil, nil) }, nil)
4,479✔
1058
        s.mu.Unlock()
3,803✔
1059
}
1060

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

1064
// clusterName is provided as argument to avoid lock ordering issues with the locked client c
1065
// Lock should be held entering here.
1066
func (c *client) sendLeafConnect(clusterName string, headers bool) error {
508✔
1067
        // We support basic user/pass and operator based user JWT with signatures.
508✔
1068
        cinfo := leafConnectInfo{
508✔
1069
                Version:       VERSION,
508✔
1070
                ID:            c.srv.info.ID,
508✔
1071
                Domain:        c.srv.info.Domain,
508✔
1072
                Name:          c.srv.info.Name,
508✔
1073
                Hub:           c.leaf.remote.Hub,
508✔
1074
                Cluster:       clusterName,
508✔
1075
                Headers:       headers,
508✔
1076
                JetStream:     c.acc.jetStreamConfigured(),
508✔
1077
                DenyPub:       c.leaf.remote.DenyImports,
508✔
1078
                Compression:   c.leaf.compression,
508✔
1079
                RemoteAccount: c.acc.GetName(),
508✔
1080
                Proto:         c.srv.getServerProto(),
508✔
1081
                Isolate:       c.leaf.remote.RequestIsolation,
508✔
1082
        }
508✔
1083

508✔
1084
        // If a signature callback is specified, this takes precedence over anything else.
508✔
1085
        if cb := c.leaf.remote.SignatureCB; cb != nil {
513✔
1086
                nonce := c.nonce
5✔
1087
                c.mu.Unlock()
5✔
1088
                jwt, sigraw, err := cb(nonce)
5✔
1089
                c.mu.Lock()
5✔
1090
                if err == nil && c.isClosed() {
6✔
1091
                        err = ErrConnectionClosed
1✔
1092
                }
1✔
1093
                if err != nil {
7✔
1094
                        c.Errorf("Error signing the nonce: %v", err)
2✔
1095
                        return err
2✔
1096
                }
2✔
1097
                sig := base64.RawURLEncoding.EncodeToString(sigraw)
3✔
1098
                cinfo.JWT, cinfo.Sig = jwt, sig
3✔
1099

1100
        } else if creds := c.leaf.remote.Credentials; creds != _EMPTY_ {
547✔
1101
                // Check for credentials first, that will take precedence..
44✔
1102
                c.Debugf("Authenticating with credentials file %q", c.leaf.remote.Credentials)
44✔
1103
                contents, err := os.ReadFile(creds)
44✔
1104
                if err != nil {
44✔
1105
                        c.Errorf("%v", err)
×
1106
                        return err
×
1107
                }
×
1108
                defer wipeSlice(contents)
44✔
1109
                items := credsRe.FindAllSubmatch(contents, -1)
44✔
1110
                if len(items) < 2 {
44✔
1111
                        c.Errorf("Credentials file malformed")
×
1112
                        return err
×
1113
                }
×
1114
                // First result should be the user JWT.
1115
                // We copy here so that the file containing the seed will be wiped appropriately.
1116
                raw := items[0][1]
44✔
1117
                tmp := make([]byte, len(raw))
44✔
1118
                copy(tmp, raw)
44✔
1119
                // Seed is second item.
44✔
1120
                kp, err := nkeys.FromSeed(items[1][1])
44✔
1121
                if err != nil {
44✔
1122
                        c.Errorf("Credentials file has malformed seed")
×
1123
                        return err
×
1124
                }
×
1125
                // Wipe our key on exit.
1126
                defer kp.Wipe()
44✔
1127

44✔
1128
                sigraw, _ := kp.Sign(c.nonce)
44✔
1129
                sig := base64.RawURLEncoding.EncodeToString(sigraw)
44✔
1130
                cinfo.JWT = bytesToString(tmp)
44✔
1131
                cinfo.Sig = sig
44✔
1132
        } else if nkey := c.leaf.remote.Nkey; nkey != _EMPTY_ {
462✔
1133
                kp, err := nkeys.FromSeed([]byte(nkey))
3✔
1134
                if err != nil {
3✔
1135
                        c.Errorf("Remote nkey has malformed seed")
×
1136
                        return err
×
1137
                }
×
1138
                // Wipe our key on exit.
1139
                defer kp.Wipe()
3✔
1140
                sigraw, _ := kp.Sign(c.nonce)
3✔
1141
                sig := base64.RawURLEncoding.EncodeToString(sigraw)
3✔
1142
                pkey, _ := kp.PublicKey()
3✔
1143
                cinfo.Nkey = pkey
3✔
1144
                cinfo.Sig = sig
3✔
1145
        }
1146
        // In addition, and this is to allow auth callout, set user/password or
1147
        // token if applicable.
1148
        if userInfo := c.leaf.remote.curURL.User; userInfo != nil {
743✔
1149
                cinfo.User = userInfo.Username()
237✔
1150
                var ok bool
237✔
1151
                cinfo.Pass, ok = userInfo.Password()
237✔
1152
                // For backward compatibility, if only username is provided, set both
237✔
1153
                // Token and User, not just Token.
237✔
1154
                if !ok {
245✔
1155
                        cinfo.Token = cinfo.User
8✔
1156
                }
8✔
1157
        } else if c.leaf.remote.username != _EMPTY_ {
275✔
1158
                cinfo.User = c.leaf.remote.username
6✔
1159
                cinfo.Pass = c.leaf.remote.password
6✔
1160
                // For backward compatibility, if only username is provided, set both
6✔
1161
                // Token and User, not just Token.
6✔
1162
                if cinfo.Pass == _EMPTY_ {
6✔
1163
                        cinfo.Token = cinfo.User
×
1164
                }
×
1165
        }
1166
        b, err := json.Marshal(cinfo)
506✔
1167
        if err != nil {
506✔
1168
                c.Errorf("Error marshaling CONNECT to remote leafnode: %v\n", err)
×
1169
                return err
×
1170
        }
×
1171
        // Although this call is made before the writeLoop is created,
1172
        // we don't really need to send in place. The protocol will be
1173
        // sent out by the writeLoop.
1174
        c.enqueueProto([]byte(fmt.Sprintf(ConProto, b)))
506✔
1175
        return nil
506✔
1176
}
1177

1178
// Makes a deep copy of the LeafNode Info structure.
1179
// The server lock is held on entry.
1180
func (s *Server) copyLeafNodeInfo() *Info {
2,059✔
1181
        clone := s.leafNodeInfo
2,059✔
1182
        // Copy the array of urls.
2,059✔
1183
        if len(s.leafNodeInfo.LeafNodeURLs) > 0 {
3,736✔
1184
                clone.LeafNodeURLs = append([]string(nil), s.leafNodeInfo.LeafNodeURLs...)
1,677✔
1185
        }
1,677✔
1186
        return &clone
2,059✔
1187
}
1188

1189
// Adds a LeafNode URL that we get when a route connects to the Info structure.
1190
// Regenerates the JSON byte array so that it can be sent to LeafNode connections.
1191
// Returns a boolean indicating if the URL was added or not.
1192
// Server lock is held on entry
1193
func (s *Server) addLeafNodeURL(urlStr string) bool {
8,158✔
1194
        if s.leafURLsMap.addUrl(urlStr) {
16,311✔
1195
                s.generateLeafNodeInfoJSON()
8,153✔
1196
                return true
8,153✔
1197
        }
8,153✔
1198
        return false
5✔
1199
}
1200

1201
// Removes a LeafNode URL of the route that is disconnecting from the Info structure.
1202
// Regenerates the JSON byte array so that it can be sent to LeafNode connections.
1203
// Returns a boolean indicating if the URL was removed or not.
1204
// Server lock is held on entry.
1205
func (s *Server) removeLeafNodeURL(urlStr string) bool {
8,158✔
1206
        // Don't need to do this if we are removing the route connection because
8,158✔
1207
        // we are shuting down...
8,158✔
1208
        if s.isShuttingDown() {
12,559✔
1209
                return false
4,401✔
1210
        }
4,401✔
1211
        if s.leafURLsMap.removeUrl(urlStr) {
7,510✔
1212
                s.generateLeafNodeInfoJSON()
3,753✔
1213
                return true
3,753✔
1214
        }
3,753✔
1215
        return false
4✔
1216
}
1217

1218
// Server lock is held on entry
1219
func (s *Server) generateLeafNodeInfoJSON() {
15,709✔
1220
        s.leafNodeInfo.Cluster = s.cachedClusterName()
15,709✔
1221
        s.leafNodeInfo.LeafNodeURLs = s.leafURLsMap.getAsStringSlice()
15,709✔
1222
        s.leafNodeInfo.WSConnectURLs = s.websocket.connectURLsMap.getAsStringSlice()
15,709✔
1223
        s.leafNodeInfoJSON = generateInfoJSON(&s.leafNodeInfo)
15,709✔
1224
}
15,709✔
1225

1226
// Sends an async INFO protocol so that the connected servers can update
1227
// their list of LeafNode urls.
1228
func (s *Server) sendAsyncLeafNodeInfo() {
11,906✔
1229
        for _, c := range s.leafs {
11,963✔
1230
                c.mu.Lock()
57✔
1231
                c.enqueueProto(s.leafNodeInfoJSON)
57✔
1232
                c.mu.Unlock()
57✔
1233
        }
57✔
1234
}
1235

1236
// Called when an inbound leafnode connection is accepted or we create one for a solicited leafnode.
1237
func (s *Server) createLeafNode(conn net.Conn, rURL *url.URL, remote *leafNodeCfg, ws *websocket) *client {
1,333✔
1238
        // Snapshot server options.
1,333✔
1239
        opts := s.getOpts()
1,333✔
1240

1,333✔
1241
        maxPay := int32(opts.MaxPayload)
1,333✔
1242
        maxSubs := int32(opts.MaxSubs)
1,333✔
1243
        // For system, maxSubs of 0 means unlimited, so re-adjust here.
1,333✔
1244
        if maxSubs == 0 {
2,665✔
1245
                maxSubs = -1
1,332✔
1246
        }
1,332✔
1247
        now := time.Now().UTC()
1,333✔
1248

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

1,333✔
1253
        // If the leafnode subject interest should be isolated, flag it here.
1,333✔
1254
        s.optsMu.RLock()
1,333✔
1255
        if c.leaf.isolated = s.opts.LeafNode.IsolateLeafnodeInterest; !c.leaf.isolated && remote != nil {
1,964✔
1256
                c.leaf.isolated = remote.LocalIsolation
631✔
1257
        }
631✔
1258
        s.optsMu.RUnlock()
1,333✔
1259

1,333✔
1260
        // For accepted LN connections, ws will be != nil if it was accepted
1,333✔
1261
        // through the Websocket port.
1,333✔
1262
        c.ws = ws
1,333✔
1263

1,333✔
1264
        // For remote, check if the scheme starts with "ws", if so, we will initiate
1,333✔
1265
        // a remote Leaf Node connection as a websocket connection.
1,333✔
1266
        if remote != nil && rURL != nil && isWSURL(rURL) {
1,379✔
1267
                remote.RLock()
46✔
1268
                c.ws = &websocket{compress: remote.Websocket.Compression, maskwrite: !remote.Websocket.NoMasking}
46✔
1269
                remote.RUnlock()
46✔
1270
        }
46✔
1271

1272
        // Determines if we are soliciting the connection or not.
1273
        var solicited bool
1,333✔
1274
        var acc *Account
1,333✔
1275
        var remoteSuffix string
1,333✔
1276
        if remote != nil {
1,964✔
1277
                // For now, if lookup fails, we will constantly try
631✔
1278
                // to recreate this LN connection.
631✔
1279
                lacc := remote.LocalAccount
631✔
1280
                var err error
631✔
1281
                acc, err = s.LookupAccount(lacc)
631✔
1282
                if err != nil {
633✔
1283
                        // An account not existing is something that can happen with nats/http account resolver and the account
2✔
1284
                        // has not yet been pushed, or the request failed for other reasons.
2✔
1285
                        // remote needs to be set or retry won't happen
2✔
1286
                        c.leaf.remote = remote
2✔
1287
                        c.closeConnection(MissingAccount)
2✔
1288
                        s.Errorf("Unable to lookup account %s for solicited leafnode connection: %v", lacc, err)
2✔
1289
                        return nil
2✔
1290
                }
2✔
1291
                remoteSuffix = fmt.Sprintf(" for account: %s", acc.traceLabel())
629✔
1292
        }
1293

1294
        c.mu.Lock()
1,331✔
1295
        c.initClient()
1,331✔
1296
        c.Noticef("Leafnode connection created%s %s", remoteSuffix, c.opts.Name)
1,331✔
1297

1,331✔
1298
        var (
1,331✔
1299
                tlsFirst         bool
1,331✔
1300
                tlsFirstFallback time.Duration
1,331✔
1301
                infoTimeout      time.Duration
1,331✔
1302
        )
1,331✔
1303
        if remote != nil {
1,960✔
1304
                solicited = true
629✔
1305
                remote.Lock()
629✔
1306
                c.leaf.remote = remote
629✔
1307
                c.setPermissions(remote.perms)
629✔
1308
                if !c.leaf.remote.Hub {
1,246✔
1309
                        c.leaf.isSpoke = true
617✔
1310
                }
617✔
1311
                tlsFirst = remote.TLSHandshakeFirst
629✔
1312
                infoTimeout = remote.FirstInfoTimeout
629✔
1313
                remote.Unlock()
629✔
1314
                c.acc = acc
629✔
1315
        } else {
702✔
1316
                c.flags.set(expectConnect)
702✔
1317
                if ws != nil {
728✔
1318
                        c.Debugf("Leafnode compression=%v", c.ws.compress)
26✔
1319
                }
26✔
1320
                tlsFirst = opts.LeafNode.TLSHandshakeFirst
702✔
1321
                if f := opts.LeafNode.TLSHandshakeFirstFallback; f > 0 {
702✔
1322
                        tlsFirstFallback = f
×
1323
                }
×
1324
        }
1325
        c.mu.Unlock()
1,331✔
1326

1,331✔
1327
        var nonce [nonceLen]byte
1,331✔
1328
        var info *Info
1,331✔
1329

1,331✔
1330
        // Grab this before the client lock below.
1,331✔
1331
        if !solicited {
2,033✔
1332
                // Grab server variables
702✔
1333
                s.mu.Lock()
702✔
1334
                info = s.copyLeafNodeInfo()
702✔
1335
                // For tests that want to simulate old servers, do not set the compression
702✔
1336
                // on the INFO protocol if configured with CompressionNotSupported.
702✔
1337
                // Also suppress it if WebSocket compression is already in use, otherwise
702✔
1338
                // an old soliciting peer would honor the advertised mode, switch to S2,
702✔
1339
                // and then wait forever for a compressed INFO response from us.
702✔
1340
                if cm := opts.LeafNode.Compression.Mode; cm != CompressionNotSupported && (ws == nil || !ws.compress) {
1,400✔
1341
                        info.Compression = cm
698✔
1342
                }
698✔
1343
                // We always send a nonce for LEAF connections. Do not change that without
1344
                // taking into account presence of proxy trusted keys.
1345
                s.generateNonce(nonce[:])
702✔
1346
                s.mu.Unlock()
702✔
1347
        }
1348

1349
        // Grab lock
1350
        c.mu.Lock()
1,331✔
1351

1,331✔
1352
        var preBuf []byte
1,331✔
1353
        if solicited {
1,960✔
1354
                // For websocket connection, we need to send an HTTP request,
629✔
1355
                // and get the response before starting the readLoop to get
629✔
1356
                // the INFO, etc..
629✔
1357
                if c.isWebsocket() {
675✔
1358
                        var err error
46✔
1359
                        var closeReason ClosedState
46✔
1360

46✔
1361
                        preBuf, closeReason, err = c.leafNodeSolicitWSConnection(opts, rURL, remote)
46✔
1362
                        if err != nil {
66✔
1363
                                c.Errorf("Error soliciting websocket connection: %v", err)
20✔
1364
                                c.mu.Unlock()
20✔
1365
                                if closeReason != 0 {
36✔
1366
                                        c.closeConnection(closeReason)
16✔
1367
                                }
16✔
1368
                                return nil
20✔
1369
                        }
1370
                } else {
583✔
1371
                        // If configured to do TLS handshake first
583✔
1372
                        if tlsFirst {
583✔
1373
                                if _, err := c.leafClientHandshakeIfNeeded(remote, opts); err != nil {
×
1374
                                        c.mu.Unlock()
×
1375
                                        return nil
×
1376
                                }
×
1377
                        }
1378
                        // We need to wait for the info, but not for too long.
1379
                        c.nc.SetReadDeadline(time.Now().Add(infoTimeout))
583✔
1380
                }
1381

1382
                // We will process the INFO from the readloop and finish by
1383
                // sending the CONNECT and finish registration later.
1384
        } else {
702✔
1385
                // Send our info to the other side.
702✔
1386
                // Remember the nonce we sent here for signatures, etc.
702✔
1387
                c.nonce = make([]byte, nonceLen)
702✔
1388
                copy(c.nonce, nonce[:])
702✔
1389
                info.Nonce = bytesToString(c.nonce)
702✔
1390
                info.CID = c.cid
702✔
1391
                proto := generateInfoJSON(info)
702✔
1392

702✔
1393
                var pre []byte
702✔
1394
                // We need first to check for "TLS First" fallback delay.
702✔
1395
                if tlsFirstFallback > 0 {
702✔
1396
                        // We wait and see if we are getting any data. Since we did not send
×
1397
                        // the INFO protocol yet, only clients that use TLS first should be
×
1398
                        // sending data (the TLS handshake). We don't really check the content:
×
1399
                        // if it is a rogue agent and not an actual client performing the
×
1400
                        // TLS handshake, the error will be detected when performing the
×
1401
                        // handshake on our side.
×
1402
                        pre = make([]byte, 4)
×
1403
                        c.nc.SetReadDeadline(time.Now().Add(tlsFirstFallback))
×
1404
                        n, _ := io.ReadFull(c.nc, pre[:])
×
1405
                        c.nc.SetReadDeadline(time.Time{})
×
1406
                        // If we get any data (regardless of possible timeout), we will proceed
×
1407
                        // with the TLS handshake.
×
1408
                        if n > 0 {
×
1409
                                pre = pre[:n]
×
1410
                        } else {
×
1411
                                // We did not get anything so we will send the INFO protocol.
×
1412
                                pre = nil
×
1413
                                // Set the boolean to false for the rest of the function.
×
1414
                                tlsFirst = false
×
1415
                        }
×
1416
                }
1417

1418
                if !tlsFirst {
1,404✔
1419
                        // We have to send from this go routine because we may
702✔
1420
                        // have to block for TLS handshake before we start our
702✔
1421
                        // writeLoop go routine. The other side needs to receive
702✔
1422
                        // this before it can initiate the TLS handshake..
702✔
1423
                        c.sendProtoNow(proto)
702✔
1424

702✔
1425
                        // The above call could have marked the connection as closed (due to TCP error).
702✔
1426
                        if c.isClosed() {
702✔
1427
                                c.mu.Unlock()
×
1428
                                c.closeConnection(WriteError)
×
1429
                                return nil
×
1430
                        }
×
1431
                }
1432

1433
                // Check to see if we need to spin up TLS.
1434
                if !c.isWebsocket() && info.TLSRequired {
771✔
1435
                        // If we have a prebuffer create a multi-reader.
69✔
1436
                        if len(pre) > 0 {
69✔
1437
                                c.nc = &tlsMixConn{c.nc, bytes.NewBuffer(pre)}
×
1438
                        }
×
1439
                        // Perform server-side TLS handshake.
1440
                        if err := c.doTLSServerHandshake(tlsHandshakeLeaf, opts.LeafNode.TLSConfig, opts.LeafNode.TLSTimeout, opts.LeafNode.TLSPinnedCerts); err != nil {
116✔
1441
                                c.mu.Unlock()
47✔
1442
                                return nil
47✔
1443
                        }
47✔
1444
                }
1445

1446
                // If the user wants the TLS handshake to occur first, now that it is
1447
                // done, send the INFO protocol.
1448
                if tlsFirst {
655✔
1449
                        c.flags.set(didTLSFirst)
×
1450
                        c.sendProtoNow(proto)
×
1451
                        if c.isClosed() {
×
1452
                                c.mu.Unlock()
×
1453
                                c.closeConnection(WriteError)
×
1454
                                return nil
×
1455
                        }
×
1456
                }
1457

1458
                // Leaf nodes will always require a CONNECT to let us know
1459
                // when we are properly bound to an account.
1460
                //
1461
                // If compression is configured, we can't set the authTimer here because
1462
                // it would cause the parser to fail any incoming protocol that is not a
1463
                // CONNECT (and we need to exchange INFO protocols for compression
1464
                // negotiation). So instead, use the ping timer until we are done with
1465
                // negotiation and can set the auth timer.
1466
                timeout := secondsToDuration(opts.LeafNode.AuthTimeout)
655✔
1467
                if needsCompression(opts.LeafNode.Compression.Mode) {
1,100✔
1468
                        c.ping.tmr = time.AfterFunc(timeout, func() {
449✔
1469
                                c.authTimeout()
4✔
1470
                        })
4✔
1471
                } else {
210✔
1472
                        c.setAuthTimer(timeout)
210✔
1473
                }
210✔
1474
        }
1475

1476
        // Keep track in case server is shutdown before we can successfully register.
1477
        if !s.addToTempClients(c.cid, c) {
1,265✔
1478
                c.mu.Unlock()
1✔
1479
                c.setNoReconnect()
1✔
1480
                c.closeConnection(ServerShutdown)
1✔
1481
                return nil
1✔
1482
        }
1✔
1483

1484
        // Spin up the read loop.
1485
        s.startGoRoutine(func() { c.readLoop(preBuf) })
2,526✔
1486

1487
        // We will spin the write loop for solicited connections only
1488
        // when processing the INFO and after switching to TLS if needed.
1489
        if !solicited {
1,918✔
1490
                s.startGoRoutine(func() { c.writeLoop() })
1,310✔
1491
        }
1492

1493
        c.mu.Unlock()
1,263✔
1494

1,263✔
1495
        return c
1,263✔
1496
}
1497

1498
// Will perform the client-side TLS handshake if needed. Assumes that this
1499
// is called by the solicit side (remote will be non nil). Returns `true`
1500
// if TLS is required, `false` otherwise.
1501
// Lock held on entry.
1502
func (c *client) leafClientHandshakeIfNeeded(remote *leafNodeCfg, opts *Options) (bool, error) {
1,445✔
1503
        // Check if TLS is required and gather TLS config variables.
1,445✔
1504
        tlsRequired, tlsConfig, tlsName, tlsTimeout := c.leafNodeGetTLSConfigForSolicit(remote)
1,445✔
1505
        if !tlsRequired {
2,817✔
1506
                return false, nil
1,372✔
1507
        }
1,372✔
1508

1509
        // If TLS required, peform handshake.
1510
        // Get the URL that was used to connect to the remote server.
1511
        rURL := remote.getCurrentURL()
73✔
1512

73✔
1513
        // Perform the client-side TLS handshake.
73✔
1514
        if resetTLSName, err := c.doTLSClientHandshake(tlsHandshakeLeaf, rURL, tlsConfig, tlsName, tlsTimeout, opts.LeafNode.TLSPinnedCerts); err != nil {
110✔
1515
                // Check if we need to reset the remote's TLS name.
37✔
1516
                if resetTLSName {
37✔
1517
                        remote.Lock()
×
1518
                        remote.tlsName = _EMPTY_
×
1519
                        remote.Unlock()
×
1520
                }
×
1521
                return false, err
37✔
1522
        }
1523
        return true, nil
36✔
1524
}
1525

1526
func (c *client) processLeafnodeInfo(info *Info) {
1,970✔
1527
        c.mu.Lock()
1,970✔
1528
        if c.leaf == nil || c.isClosed() {
1,970✔
1529
                c.mu.Unlock()
×
1530
                return
×
1531
        }
×
1532
        s := c.srv
1,970✔
1533
        opts := s.getOpts()
1,970✔
1534
        remote := c.leaf.remote
1,970✔
1535
        didSolicit := remote != nil
1,970✔
1536
        firstINFO := !c.flags.isSet(infoReceived)
1,970✔
1537

1,970✔
1538
        // In case of websocket, the TLS handshake has been already done.
1,970✔
1539
        // So check only for non websocket connections and for configurations
1,970✔
1540
        // where the TLS Handshake was not done first.
1,970✔
1541
        if didSolicit && !c.flags.isSet(handshakeComplete) && !c.isWebsocket() && !remote.TLSHandshakeFirst {
3,369✔
1542
                // If the server requires TLS, we need to set this in the remote
1,399✔
1543
                // otherwise if there is no TLS configuration block for the remote,
1,399✔
1544
                // the solicit side will not attempt to perform the TLS handshake.
1,399✔
1545
                if firstINFO && info.TLSRequired {
1,460✔
1546
                        // Check for TLS/proxy configuration mismatch
61✔
1547
                        if remote.Proxy.URL != _EMPTY_ && !remote.TLS && remote.TLSConfig == nil {
61✔
1548
                                c.mu.Unlock()
×
1549
                                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.")
×
1550
                                c.closeConnection(TLSHandshakeError)
×
1551
                                return
×
1552
                        }
×
1553
                        remote.TLS = true
61✔
1554
                }
1555
                if _, err := c.leafClientHandshakeIfNeeded(remote, opts); err != nil {
1,432✔
1556
                        c.mu.Unlock()
33✔
1557
                        return
33✔
1558
                }
33✔
1559
        }
1560

1561
        // Check for compression, unless already done.
1562
        if firstINFO && !c.flags.isSet(compressionNegotiated) {
2,925✔
1563
                // A solicited leafnode connection must first receive a leafnode INFO.
988✔
1564
                // Classify wrong-port connections before any leaf-specific negotiation.
988✔
1565
                if didSolicit && (info.CID == 0 || info.LeafNodeURLs == nil) {
1,042✔
1566
                        c.mu.Unlock()
54✔
1567
                        c.Errorf(ErrConnectedToWrongPort.Error())
54✔
1568
                        c.closeConnection(WrongPort)
54✔
1569
                        return
54✔
1570
                }
54✔
1571

1572
                // Prevent from getting back here.
1573
                c.flags.set(compressionNegotiated)
934✔
1574

934✔
1575
                var co *CompressionOpts
934✔
1576
                if !didSolicit {
1,356✔
1577
                        co = &opts.LeafNode.Compression
422✔
1578
                } else {
934✔
1579
                        co = &remote.Compression
512✔
1580
                }
512✔
1581
                if needsCompression(co.Mode) {
1,866✔
1582
                        // Release client lock since following function will need server lock.
932✔
1583
                        c.mu.Unlock()
932✔
1584
                        compress, err := s.negotiateLeafCompression(c, didSolicit, info.Compression, co)
932✔
1585
                        if err != nil {
932✔
1586
                                c.sendErrAndErr(err.Error())
×
1587
                                c.closeConnection(ProtocolViolation)
×
1588
                                return
×
1589
                        }
×
1590
                        if compress {
1,778✔
1591
                                // Done for now, will get back another INFO protocol...
846✔
1592
                                return
846✔
1593
                        }
846✔
1594
                        // No compression because one side does not want/can't, so proceed.
1595
                        c.mu.Lock()
86✔
1596
                        // Check that the connection did not close if the lock was released.
86✔
1597
                        if c.isClosed() {
86✔
1598
                                c.mu.Unlock()
×
1599
                                return
×
1600
                        }
×
1601
                } else {
2✔
1602
                        // Coming from an old server, the Compression field would be the empty
2✔
1603
                        // string. For servers that are configured with CompressionNotSupported,
2✔
1604
                        // this makes them behave as old servers.
2✔
1605
                        if info.Compression == _EMPTY_ || co.Mode == CompressionNotSupported {
3✔
1606
                                c.leaf.compression = CompressionNotSupported
1✔
1607
                        } else {
2✔
1608
                                c.leaf.compression = CompressionOff
1✔
1609
                        }
1✔
1610
                }
1611
                // Accepting side does not normally process an INFO protocol during
1612
                // initial connection handshake. So we keep it consistent by returning
1613
                // if we are not soliciting.
1614
                if !didSolicit {
89✔
1615
                        // If we had created the ping timer instead of the auth timer, we will
1✔
1616
                        // clear the ping timer and set the auth timer now that the compression
1✔
1617
                        // negotiation is done.
1✔
1618
                        if info.Compression != _EMPTY_ && c.ping.tmr != nil {
1✔
1619
                                clearTimer(&c.ping.tmr)
×
1620
                                c.setAuthTimer(secondsToDuration(opts.LeafNode.AuthTimeout))
×
1621
                        }
×
1622
                        c.mu.Unlock()
1✔
1623
                        return
1✔
1624
                }
1625
                // Fall through and process the INFO protocol as usual.
1626
        }
1627

1628
        // Note: For now, only the initial INFO has a nonce. We
1629
        // will probably do auto key rotation at some point.
1630
        if firstINFO {
1,565✔
1631
                // Mark that the INFO protocol has been received.
529✔
1632
                c.flags.set(infoReceived)
529✔
1633
                // Prevent connecting to non leafnode port. Need to do this only for
529✔
1634
                // the first INFO, not for async INFO updates...
529✔
1635
                //
529✔
1636
                // Content of INFO sent by the server when accepting a tcp connection.
529✔
1637
                // -------------------------------------------------------------------
529✔
1638
                // Listen Port Of | CID | ClientConnectURLs | LeafNodeURLs | Gateway |
529✔
1639
                // -------------------------------------------------------------------
529✔
1640
                //      CLIENT    |  X* |        X**        |              |         |
529✔
1641
                //      ROUTE     |     |        X**        |      X***    |         |
529✔
1642
                //     GATEWAY    |     |                   |              |    X    |
529✔
1643
                //     LEAFNODE   |  X  |                   |       X      |         |
529✔
1644
                // -------------------------------------------------------------------
529✔
1645
                // *   Not on older servers.
529✔
1646
                // **  Not if "no advertise" is enabled.
529✔
1647
                // *** Not if leafnode's "no advertise" is enabled.
529✔
1648
                //
529✔
1649
                // Reject a cluster that contains spaces.
529✔
1650
                if info.Cluster != _EMPTY_ && strings.Contains(info.Cluster, " ") {
530✔
1651
                        c.mu.Unlock()
1✔
1652
                        c.sendErrAndErr(ErrClusterNameHasSpaces.Error())
1✔
1653
                        c.closeConnection(ProtocolViolation)
1✔
1654
                        return
1✔
1655
                }
1✔
1656
                // For solicited outbound leaf connections, capture the remote's nonce.
1657
                // For inbound leaf connections, keep using the server-issued nonce that
1658
                // was sent in our initial INFO and must be signed in CONNECT.
1659
                if didSolicit {
1,036✔
1660
                        c.nonce = []byte(info.Nonce)
508✔
1661
                }
508✔
1662
                if info.TLSRequired && didSolicit {
553✔
1663
                        remote.TLS = true
25✔
1664
                }
25✔
1665
                supportsHeaders := c.srv.supportsHeaders()
528✔
1666
                c.headers = supportsHeaders && info.Headers
528✔
1667

528✔
1668
                // Remember the remote server.
528✔
1669
                // Pre 2.2.0 servers are not sending their server name.
528✔
1670
                // In that case, use info.ID, which, for those servers, matches
528✔
1671
                // the content of the field `Name` in the leafnode CONNECT protocol.
528✔
1672
                if info.Name == _EMPTY_ {
528✔
1673
                        c.leaf.remoteServer = info.ID
×
1674
                } else {
528✔
1675
                        c.leaf.remoteServer = info.Name
528✔
1676
                }
528✔
1677
                c.leaf.remoteDomain = info.Domain
528✔
1678
                c.leaf.remoteCluster = info.Cluster
528✔
1679
                // We send the protocol version in the INFO protocol.
528✔
1680
                // Keep track of it, so we know if this connection supports message
528✔
1681
                // tracing for instance.
528✔
1682
                c.opts.Protocol = info.Proto
528✔
1683
        }
1684

1685
        // For both initial INFO and async INFO protocols, Possibly
1686
        // update our list of remote leafnode URLs we can connect to,
1687
        // unless we are instructed not to.
1688
        if didSolicit && !remote.IgnoreDiscoveredServers &&
1,035✔
1689
                (len(info.LeafNodeURLs) > 0 || len(info.WSConnectURLs) > 0) {
2,026✔
1690
                // Consider the incoming array as the most up-to-date
991✔
1691
                // representation of the remote cluster's list of URLs.
991✔
1692
                c.updateLeafNodeURLs(info)
991✔
1693
        }
991✔
1694

1695
        // Only solicited leafnode connections trust permission updates from INFO.
1696
        if didSolicit && (info.Import != nil || info.Export != nil) {
1,041✔
1697
                perms := &Permissions{
6✔
1698
                        Publish:   info.Export,
6✔
1699
                        Subscribe: info.Import,
6✔
1700
                }
6✔
1701
                // Check if we have local deny clauses that we need to merge.
6✔
1702
                if remote := c.leaf.remote; remote != nil {
12✔
1703
                        if len(remote.DenyExports) > 0 {
7✔
1704
                                if perms.Publish == nil {
1✔
1705
                                        perms.Publish = &SubjectPermission{}
×
1706
                                }
×
1707
                                perms.Publish.Deny = append(perms.Publish.Deny, remote.DenyExports...)
1✔
1708
                        }
1709
                        if len(remote.DenyImports) > 0 {
7✔
1710
                                if perms.Subscribe == nil {
1✔
1711
                                        perms.Subscribe = &SubjectPermission{}
×
1712
                                }
×
1713
                                perms.Subscribe.Deny = append(perms.Subscribe.Deny, remote.DenyImports...)
1✔
1714
                        }
1715
                }
1716
                c.setPermissions(perms)
6✔
1717
        }
1718

1719
        var resumeConnect bool
1,035✔
1720

1,035✔
1721
        // If this is a remote connection and this is the first INFO protocol,
1,035✔
1722
        // then we need to finish the connect process by sending CONNECT, etc..
1,035✔
1723
        if firstINFO && didSolicit {
1,543✔
1724
                // Clear deadline that was set in createLeafNode while waiting for the INFO.
508✔
1725
                c.nc.SetDeadline(time.Time{})
508✔
1726
                resumeConnect = true
508✔
1727
        } else if !firstINFO && didSolicit {
1,519✔
1728
                c.leaf.remoteAccName = info.RemoteAccount
484✔
1729
        }
484✔
1730

1731
        // Check if we have the remote account information and if so make sure it's stored.
1732
        if info.RemoteAccount != _EMPTY_ {
1,506✔
1733
                if c.acc == nil {
471✔
1734
                        c.mu.Unlock()
×
1735
                        c.sendErr("Authorization Violation")
×
1736
                        c.closeConnection(ProtocolViolation)
×
1737
                        return
×
1738
                }
×
1739
                s.leafRemoteAccounts.Store(c.acc.Name, info.RemoteAccount)
471✔
1740
        }
1741
        c.mu.Unlock()
1,035✔
1742

1,035✔
1743
        finishConnect := info.ConnectInfo
1,035✔
1744
        if resumeConnect && s != nil {
1,543✔
1745
                s.leafNodeResumeConnectProcess(c)
508✔
1746
                if !info.InfoOnConnect {
508✔
1747
                        finishConnect = true
×
1748
                }
×
1749
        }
1750
        if finishConnect {
1,506✔
1751
                s.leafNodeFinishConnectProcess(c)
471✔
1752
        }
471✔
1753

1754
        // Check to see if we need to kick any internal source or mirror consumers.
1755
        // This will be a no-op if JetStream not enabled for this server or if the bound account
1756
        // does not have jetstream.
1757
        s.checkInternalSyncConsumers(c.acc)
1,035✔
1758
}
1759

1760
func (s *Server) negotiateLeafCompression(c *client, didSolicit bool, infoCompression string, co *CompressionOpts) (bool, error) {
932✔
1761
        // If WebSocket compression is already negotiated on this connection then
932✔
1762
        // we shouldn't layer S2 compression on top of it.
932✔
1763
        c.mu.Lock()
932✔
1764
        if c.ws != nil && c.ws.compress {
936✔
1765
                c.leaf.compression = CompressionOff
4✔
1766
                c.mu.Unlock()
4✔
1767
                return false, nil
4✔
1768
        }
4✔
1769
        c.mu.Unlock()
928✔
1770
        // Negotiate the appropriate compression mode (or no compression)
928✔
1771
        cm, err := selectCompressionMode(co.Mode, infoCompression)
928✔
1772
        if err != nil {
928✔
1773
                return false, err
×
1774
        }
×
1775
        c.mu.Lock()
928✔
1776
        // For "auto" mode, set the initial compression mode based on RTT
928✔
1777
        if cm == CompressionS2Auto {
1,774✔
1778
                if c.rttStart.IsZero() {
1,692✔
1779
                        c.rtt = computeRTT(c.start)
846✔
1780
                }
846✔
1781
                cm = selectS2AutoModeBasedOnRTT(c.rtt, co.RTTThresholds)
846✔
1782
        }
1783
        // Keep track of the negotiated compression mode.
1784
        c.leaf.compression = cm
928✔
1785
        cid := c.cid
928✔
1786
        var nonce string
928✔
1787
        if !didSolicit {
1,349✔
1788
                nonce = bytesToString(c.nonce)
421✔
1789
        }
421✔
1790
        c.mu.Unlock()
928✔
1791

928✔
1792
        if !needsCompression(cm) {
1,010✔
1793
                return false, nil
82✔
1794
        }
82✔
1795

1796
        // If we end-up doing compression...
1797

1798
        // Generate an INFO with the chosen compression mode.
1799
        s.mu.Lock()
846✔
1800
        info := s.copyLeafNodeInfo()
846✔
1801
        info.Compression, info.CID, info.Nonce = compressionModeForInfoProtocol(co, cm), cid, nonce
846✔
1802
        infoProto := generateInfoJSON(info)
846✔
1803
        s.mu.Unlock()
846✔
1804

846✔
1805
        // If we solicited, then send this INFO protocol BEFORE switching
846✔
1806
        // to compression writer. However, if we did not, we send it after.
846✔
1807
        c.mu.Lock()
846✔
1808
        if didSolicit {
1,271✔
1809
                c.enqueueProto(infoProto)
425✔
1810
                // Make sure it is completely flushed (the pending bytes goes to
425✔
1811
                // 0) before proceeding.
425✔
1812
                for c.out.pb > 0 && !c.isClosed() {
849✔
1813
                        c.flushOutbound()
424✔
1814
                }
424✔
1815
        }
1816
        // This is to notify the readLoop that it should switch to a
1817
        // (de)compression reader.
1818
        c.in.flags.set(switchToCompression)
846✔
1819
        // Create the compress writer before queueing the INFO protocol for
846✔
1820
        // a route that did not solicit. It will make sure that that proto
846✔
1821
        // is sent with compression on.
846✔
1822
        c.out.cw = s2.NewWriter(nil, s2WriterOptions(cm)...)
846✔
1823
        if !didSolicit {
1,267✔
1824
                c.enqueueProto(infoProto)
421✔
1825
        }
421✔
1826
        c.mu.Unlock()
846✔
1827
        return true, nil
846✔
1828
}
1829

1830
// When getting a leaf node INFO protocol, use the provided
1831
// array of urls to update the list of possible endpoints.
1832
func (c *client) updateLeafNodeURLs(info *Info) {
991✔
1833
        cfg := c.leaf.remote
991✔
1834
        cfg.Lock()
991✔
1835
        defer cfg.Unlock()
991✔
1836

991✔
1837
        // We have ensured that if a remote has a WS scheme, then all are.
991✔
1838
        // So check if first is WS, then add WS URLs, otherwise, add non WS ones.
991✔
1839
        if len(cfg.URLs) > 0 && isWSURL(cfg.URLs[0]) {
1,043✔
1840
                // It does not really matter if we use "ws://" or "wss://" here since
52✔
1841
                // we will have already marked that the remote should use TLS anyway.
52✔
1842
                // But use proper scheme for log statements, etc...
52✔
1843
                proto := wsSchemePrefix
52✔
1844
                if cfg.TLS {
52✔
1845
                        proto = wsSchemePrefixTLS
×
1846
                }
×
1847
                c.doUpdateLNURLs(cfg, proto, info.WSConnectURLs)
52✔
1848
                return
52✔
1849
        }
1850
        c.doUpdateLNURLs(cfg, "nats-leaf", info.LeafNodeURLs)
939✔
1851
}
1852

1853
func (c *client) doUpdateLNURLs(cfg *leafNodeCfg, scheme string, URLs []string) {
991✔
1854
        cfg.urls = make([]*url.URL, 0, 1+len(URLs))
991✔
1855
        // Add the ones we receive in the protocol
991✔
1856
        for _, surl := range URLs {
2,759✔
1857
                url, err := url.Parse(fmt.Sprintf("%s://%s", scheme, surl))
1,768✔
1858
                if err != nil {
1,768✔
1859
                        // As per below, the URLs we receive should not have contained URL info, so this should be safe to log.
×
1860
                        c.Errorf("Error parsing url %q: %v", surl, err)
×
1861
                        continue
×
1862
                }
1863
                // Do not add if it's the same as what we already have configured.
1864
                var dup bool
1,768✔
1865
                for _, u := range cfg.URLs {
4,417✔
1866
                        // URLs that we receive never have user info, but the
2,649✔
1867
                        // ones that were configured may have. Simply compare
2,649✔
1868
                        // host and port to decide if they are equal or not.
2,649✔
1869
                        if url.Host == u.Host && url.Port() == u.Port() {
4,021✔
1870
                                dup = true
1,372✔
1871
                                break
1,372✔
1872
                        }
1873
                }
1874
                if !dup {
2,164✔
1875
                        cfg.urls = append(cfg.urls, url)
396✔
1876
                        cfg.saveTLSHostname(url)
396✔
1877
                }
396✔
1878
        }
1879
        // Add the configured one
1880
        cfg.urls = append(cfg.urls, cfg.URLs...)
991✔
1881
}
1882

1883
// Similar to setInfoHostPortAndGenerateJSON, but for leafNodeInfo.
1884
func (s *Server) setLeafNodeInfoHostPortAndIP() error {
3,803✔
1885
        opts := s.getOpts()
3,803✔
1886
        if opts.LeafNode.Advertise != _EMPTY_ {
3,814✔
1887
                advHost, advPort, err := parseHostPort(opts.LeafNode.Advertise, opts.LeafNode.Port)
11✔
1888
                if err != nil {
11✔
1889
                        return err
×
1890
                }
×
1891
                s.leafNodeInfo.Host = advHost
11✔
1892
                s.leafNodeInfo.Port = advPort
11✔
1893
        } else {
3,792✔
1894
                s.leafNodeInfo.Host = opts.LeafNode.Host
3,792✔
1895
                s.leafNodeInfo.Port = opts.LeafNode.Port
3,792✔
1896
                // If the host is "0.0.0.0" or "::" we need to resolve to a public IP.
3,792✔
1897
                // This will return at most 1 IP.
3,792✔
1898
                hostIsIPAny, ips, err := s.getNonLocalIPsIfHostIsIPAny(s.leafNodeInfo.Host, false)
3,792✔
1899
                if err != nil {
3,792✔
1900
                        return err
×
1901
                }
×
1902
                if hostIsIPAny {
3,844✔
1903
                        if len(ips) == 0 {
52✔
1904
                                s.Errorf("Could not find any non-local IP for leafnode's listen specification %q",
×
1905
                                        s.leafNodeInfo.Host)
×
1906
                        } else {
52✔
1907
                                // Take the first from the list...
52✔
1908
                                s.leafNodeInfo.Host = ips[0]
52✔
1909
                        }
52✔
1910
                }
1911
        }
1912
        // Use just host:port for the IP
1913
        s.leafNodeInfo.IP = net.JoinHostPort(s.leafNodeInfo.Host, strconv.Itoa(s.leafNodeInfo.Port))
3,803✔
1914
        if opts.LeafNode.Advertise != _EMPTY_ {
3,814✔
1915
                s.Noticef("Advertise address for leafnode is set to %s", s.leafNodeInfo.IP)
11✔
1916
        }
11✔
1917
        return nil
3,803✔
1918
}
1919

1920
// Add the connection to the map of leaf nodes.
1921
// If `checkForDup` is true (invoked when a leafnode is accepted), then we check
1922
// if a connection already exists for the same server name and account.
1923
// That can happen when the remote is attempting to reconnect while the accepting
1924
// side did not detect the connection as broken yet.
1925
// But it can also happen when there is a misconfiguration and the remote is
1926
// creating two (or more) connections that bind to the same account on the accept
1927
// side.
1928
// When a duplicate is found, the new connection is accepted and the old is closed
1929
// (this solves the stale connection situation). An error is returned to help the
1930
// remote detect the misconfiguration when the duplicate is the result of that
1931
// misconfiguration.
1932
func (s *Server) addLeafNodeConnection(c *client, srvName, clusterName string, checkForDup bool) bool {
979✔
1933
        var accName string
979✔
1934
        c.mu.Lock()
979✔
1935
        cid := c.cid
979✔
1936
        acc := c.acc
979✔
1937
        if acc != nil {
1,958✔
1938
                accName = acc.Name
979✔
1939
        }
979✔
1940
        myRemoteDomain := c.leaf.remoteDomain
979✔
1941
        mySrvName := c.leaf.remoteServer
979✔
1942
        remoteAccName := c.leaf.remoteAccName
979✔
1943
        myClustName := c.leaf.remoteCluster
979✔
1944
        remote := c.leaf.remote
979✔
1945
        solicited := remote != nil
979✔
1946
        c.mu.Unlock()
979✔
1947

979✔
1948
        var old *client
979✔
1949
        s.mu.Lock()
979✔
1950
        // We check for empty because in some test we may send empty CONNECT{}
979✔
1951
        if checkForDup && srvName != _EMPTY_ {
1,451✔
1952
                for _, ol := range s.leafs {
792✔
1953
                        ol.mu.Lock()
320✔
1954
                        // We care here only about non solicited Leafnode. This function
320✔
1955
                        // is more about replacing stale connections than detecting loops.
320✔
1956
                        // We have code for the loop detection elsewhere, which also delays
320✔
1957
                        // attempt to reconnect.
320✔
1958
                        if !ol.isSolicitedLeafNode() && ol.leaf.remoteServer == srvName &&
320✔
1959
                                ol.leaf.remoteCluster == clusterName && ol.acc.Name == accName &&
320✔
1960
                                remoteAccName != _EMPTY_ && ol.leaf.remoteAccName == remoteAccName {
322✔
1961
                                old = ol
2✔
1962
                        }
2✔
1963
                        ol.mu.Unlock()
320✔
1964
                        if old != nil {
322✔
1965
                                break
2✔
1966
                        }
1967
                }
1968
        }
1969
        // Now that we are under the server lock and before adding it to the map,
1970
        // for a solicited leaf, we need to make sure that it has not been removed
1971
        // from the config or disabled.
1972
        if solicited {
1,448✔
1973
                // If no longer valid, do not add to the server map. The connection
469✔
1974
                // should have been marked so that it can't reconnect. When the caller
469✔
1975
                // calls closeConnection(), cleanup (including clearing the connect-
469✔
1976
                // in-progress flag) will occur at the appropriate time.
469✔
1977
                if !remote.stillValid() {
469✔
1978
                        // Prevent reconnect in case it was not yet done.
×
1979
                        c.setNoReconnect()
×
1980
                        s.mu.Unlock()
×
1981
                        s.removeFromTempClients(cid)
×
1982
                        return false
×
1983
                }
×
1984
                remote.setConnectInProgress(false)
469✔
1985
        }
1986
        // Store new connection in the map
1987
        s.leafs[cid] = c
979✔
1988
        s.mu.Unlock()
979✔
1989
        s.removeFromTempClients(cid)
979✔
1990

979✔
1991
        // If applicable, evict the old one.
979✔
1992
        if old != nil {
981✔
1993
                old.sendErrAndErr(DuplicateRemoteLeafnodeConnection.String())
2✔
1994
                old.closeConnection(DuplicateRemoteLeafnodeConnection)
2✔
1995
                c.Warnf("Replacing connection from same server")
2✔
1996
        }
2✔
1997

1998
        srvDecorated := func() string {
1,172✔
1999
                if myClustName == _EMPTY_ {
215✔
2000
                        return mySrvName
22✔
2001
                }
22✔
2002
                return fmt.Sprintf("%s/%s", mySrvName, myClustName)
171✔
2003
        }
2004

2005
        opts := s.getOpts()
979✔
2006
        sysAcc := s.SystemAccount()
979✔
2007
        js := s.getJetStream()
979✔
2008
        var meta *raft
979✔
2009
        if js != nil {
1,438✔
2010
                if mg := js.getMetaGroup(); mg != nil {
802✔
2011
                        meta = mg.(*raft)
343✔
2012
                }
343✔
2013
        }
2014
        blockMappingOutgoing := false
979✔
2015
        // Deny (non domain) JetStream API traffic unless system account is shared
979✔
2016
        // and domain names are identical and extending is not disabled
979✔
2017

979✔
2018
        // Check if backwards compatibility has been enabled and needs to be acted on
979✔
2019
        forceSysAccDeny := false
979✔
2020
        if len(opts.JsAccDefaultDomain) > 0 {
1,012✔
2021
                if acc == sysAcc {
44✔
2022
                        for _, d := range opts.JsAccDefaultDomain {
22✔
2023
                                if d == _EMPTY_ {
19✔
2024
                                        // Extending JetStream via leaf node is mutually exclusive with a domain mapping to the empty/default domain.
8✔
2025
                                        // As soon as one mapping to "" is found, disable the ability to extend JS via a leaf node.
8✔
2026
                                        c.Noticef("Not extending remote JetStream domain %q due to presence of empty default domain", myRemoteDomain)
8✔
2027
                                        forceSysAccDeny = true
8✔
2028
                                        break
8✔
2029
                                }
2030
                        }
2031
                } else if domain, ok := opts.JsAccDefaultDomain[accName]; ok && domain == _EMPTY_ {
35✔
2032
                        // for backwards compatibility with old setups that do not have a domain name set
13✔
2033
                        c.Debugf("Skipping deny %q for account %q due to default domain", jsAllAPI, accName)
13✔
2034
                        return true
13✔
2035
                }
13✔
2036
        }
2037

2038
        // If the server has JS disabled, it may still be part of a JetStream that could be extended.
2039
        // This is either signaled by js being disabled and a domain set,
2040
        // or in cases where no domain name exists, an extension hint is set.
2041
        // However, this is only relevant in mixed setups.
2042
        //
2043
        // If the system account connects but default domains are present, JetStream can't be extended.
2044
        if opts.JetStreamDomain != myRemoteDomain || (!opts.JetStream && (opts.JetStreamDomain == _EMPTY_ && opts.JetStreamExtHint != jsWillExtend)) ||
966✔
2045
                sysAcc == nil || acc == nil || forceSysAccDeny {
1,797✔
2046
                // If domain names mismatch always deny. This applies to system accounts as well as non system accounts.
831✔
2047
                // Not having a system account, account or JetStream disabled is considered a mismatch as well.
831✔
2048
                if acc != nil && acc == sysAcc {
958✔
2049
                        c.Noticef("System account connected from %s", srvDecorated())
127✔
2050
                        c.Noticef("JetStream not extended, domains differ")
127✔
2051
                        c.mergeDenyPermissionsLocked(both, denyAllJs)
127✔
2052
                        // When a remote with a system account is present in a server, unless otherwise disabled, the server will be
127✔
2053
                        // started in observer mode. Now that it is clear that this not used, turn the observer mode off.
127✔
2054
                        if solicited && meta != nil && meta.IsObserver() {
153✔
2055
                                meta.setObserver(false, extNotExtended)
26✔
2056
                                c.Debugf("Turning JetStream metadata controller Observer Mode off")
26✔
2057
                                // Take note that the domain was not extended to avoid this state from startup.
26✔
2058
                                writePeerState(c.srv.diskIOSemaphore(), js.config.StoreDir, meta.currentPeerState())
26✔
2059
                                // Meta controller can't be leader yet.
26✔
2060
                                // Yet it is possible that due to observer mode every server already stopped campaigning.
26✔
2061
                                // Therefore this server needs to be kicked into campaigning gear explicitly.
26✔
2062
                                meta.Campaign()
26✔
2063
                        }
26✔
2064
                } else {
704✔
2065
                        c.Noticef("JetStream using domains: local %q, remote %q", opts.JetStreamDomain, myRemoteDomain)
704✔
2066
                        c.mergeDenyPermissionsLocked(both, denyAllClientJs)
704✔
2067
                }
704✔
2068
                blockMappingOutgoing = true
831✔
2069
        } else if acc == sysAcc {
201✔
2070
                // system account and same domain
66✔
2071
                s.sys.client.Noticef("Extending JetStream domain %q as System Account connected from server %s",
66✔
2072
                        myRemoteDomain, srvDecorated())
66✔
2073
                // In an extension use case, pin leadership to server remotes connect to.
66✔
2074
                // Therefore, server with a remote that are not already in observer mode, need to be put into it.
66✔
2075
                if solicited && meta != nil && !meta.IsObserver() {
70✔
2076
                        c.Debugf("Turning JetStream metadata controller Observer Mode on - System Account Connected")
4✔
2077
                        // Discard any local metagroup state accumulated before the SYS-account
4✔
2078
                        // leaf came up (e.g. the wrong-hint case where this server bootstrapped
4✔
2079
                        // its own metagroup). The parent's view is now authoritative; without
4✔
2080
                        // this reset the two raft logs stay forked because the standalone log's
4✔
2081
                        // commit prefix short-circuits the follower's AE handling.
4✔
2082
                        meta.setObserver(true, extExtended)
4✔
2083
                        meta.Reset()
4✔
2084
                }
4✔
2085
        } else {
69✔
2086
                // This deny is needed in all cases (system account shared or not)
69✔
2087
                // If the system account is shared, jsAllAPI traffic will go through the system account.
69✔
2088
                // So in order to prevent duplicate delivery (from system and actual account) suppress it on the account.
69✔
2089
                // If the system account is NOT shared, jsAllAPI traffic has no business
69✔
2090
                c.Debugf("Adding deny %+v for account %q", denyAllClientJs, accName)
69✔
2091
                c.mergeDenyPermissionsLocked(both, denyAllClientJs)
69✔
2092
        }
69✔
2093
        // If we have a specified JetStream domain we will want to add a mapping to
2094
        // allow access cross domain for each non-system account.
2095
        if opts.JetStreamDomain != _EMPTY_ && opts.JetStream && acc != nil && acc != sysAcc {
1,193✔
2096
                for src, dest := range generateJSMappingTable(opts.JetStreamDomain) {
2,270✔
2097
                        if err := acc.AddMapping(src, dest); err != nil {
2,043✔
2098
                                c.Debugf("Error adding JetStream domain mapping: %s", err.Error())
×
2099
                        } else {
2,043✔
2100
                                c.Debugf("Adding JetStream Domain Mapping %q -> %s to account %q", src, dest, accName)
2,043✔
2101
                        }
2,043✔
2102
                }
2103
                if blockMappingOutgoing {
423✔
2104
                        src := fmt.Sprintf(jsDomainAPI, opts.JetStreamDomain)
196✔
2105
                        // make sure that messages intended for this domain, do not leave the cluster via this leaf node connection
196✔
2106
                        // This is a guard against a miss-config with two identical domain names and will only cover some forms
196✔
2107
                        // of this issue, not all of them.
196✔
2108
                        // This guards against a hub and a spoke having the same domain name.
196✔
2109
                        // But not two spokes having the same one and the request coming from the hub.
196✔
2110
                        c.mergeDenyPermissionsLocked(pub, []string{src})
196✔
2111
                        c.Debugf("Adding deny %q for outgoing messages to account %q", src, accName)
196✔
2112
                }
196✔
2113
        }
2114
        return true
966✔
2115
}
2116

2117
func (s *Server) removeLeafNodeConnection(c *client) {
1,334✔
2118
        s.mu.Lock()
1,334✔
2119
        c.mu.Lock()
1,334✔
2120
        cid := c.cid
1,334✔
2121
        if c.leaf != nil {
2,667✔
2122
                if c.leaf.tsubt != nil {
2,216✔
2123
                        c.leaf.tsubt.Stop()
883✔
2124
                        c.leaf.tsubt = nil
883✔
2125
                }
883✔
2126
                if c.leaf.gwSub != nil {
1,802✔
2127
                        s.gwLeafSubs.Remove(c.leaf.gwSub)
469✔
2128
                        // We need to set this to nil for GC to release the connection
469✔
2129
                        c.leaf.gwSub = nil
469✔
2130
                }
469✔
2131
                if remote := c.leaf.remote; remote != nil {
1,964✔
2132
                        // If "noReconnect" is true, then we won't attempt to reconnect, so
631✔
2133
                        // we will clear the "connect-in-progress" flag. However, if we can
631✔
2134
                        // reconnect, then we should set "connect-in-progress" to true while
631✔
2135
                        // we are under the server/client lock. The go routine that performs
631✔
2136
                        // the reconnect will be started later and there would be a gap with
631✔
2137
                        // the wrong flag value otherwise.
631✔
2138
                        remote.setConnectInProgress(!c.flags.isSet(noReconnect))
631✔
2139
                }
631✔
2140
        }
2141
        proxyKey := c.proxyKey
1,334✔
2142
        c.mu.Unlock()
1,334✔
2143
        delete(s.leafs, cid)
1,334✔
2144
        if proxyKey != _EMPTY_ {
1,338✔
2145
                s.removeProxiedConn(proxyKey, cid)
4✔
2146
        }
4✔
2147
        s.mu.Unlock()
1,334✔
2148
        s.removeFromTempClients(cid)
1,334✔
2149
}
2150

2151
// Connect information for solicited leafnodes.
2152
type leafConnectInfo struct {
2153
        Version   string   `json:"version,omitempty"`
2154
        Nkey      string   `json:"nkey,omitempty"`
2155
        JWT       string   `json:"jwt,omitempty"`
2156
        Sig       string   `json:"sig,omitempty"`
2157
        User      string   `json:"user,omitempty"`
2158
        Pass      string   `json:"pass,omitempty"`
2159
        Token     string   `json:"auth_token,omitempty"`
2160
        ID        string   `json:"server_id,omitempty"`
2161
        Domain    string   `json:"domain,omitempty"`
2162
        Name      string   `json:"name,omitempty"`
2163
        Hub       bool     `json:"is_hub,omitempty"`
2164
        Cluster   string   `json:"cluster,omitempty"`
2165
        Headers   bool     `json:"headers,omitempty"`
2166
        JetStream bool     `json:"jetstream,omitempty"`
2167
        DenyPub   []string `json:"deny_pub,omitempty"`
2168
        Isolate   bool     `json:"isolate,omitempty"`
2169

2170
        // There was an existing field called:
2171
        // >> Comp bool `json:"compression,omitempty"`
2172
        // that has never been used. With support for compression, we now need
2173
        // a field that is a string. So we use a different json tag:
2174
        Compression string `json:"compress_mode,omitempty"`
2175

2176
        // Just used to detect wrong connection attempts.
2177
        Gateway string `json:"gateway,omitempty"`
2178

2179
        // Tells the accept side which account the remote is binding to.
2180
        RemoteAccount string `json:"remote_account,omitempty"`
2181

2182
        // The accept side of a LEAF connection, unlike ROUTER and GATEWAY, receives
2183
        // only the CONNECT protocol, and no INFO. So we need to send the protocol
2184
        // version as part of the CONNECT. It will indicate if a connection supports
2185
        // some features, such as message tracing.
2186
        // We use `protocol` as the JSON tag, so this is automatically unmarshal'ed
2187
        // in the low level process CONNECT.
2188
        Proto int `json:"protocol,omitempty"`
2189
}
2190

2191
// processLeafNodeConnect will process the inbound connect args.
2192
// Once we are here we are bound to an account, so can send any interest that
2193
// we would have to the other side.
2194
func (c *client) processLeafNodeConnect(s *Server, arg []byte, lang string) error {
515✔
2195
        // Way to detect clients that incorrectly connect to the route listen
515✔
2196
        // port. Client provided "lang" in the CONNECT protocol while LEAFNODEs don't.
515✔
2197
        if lang != _EMPTY_ {
515✔
2198
                c.sendErrAndErr(ErrClientConnectedToLeafNodePort.Error())
×
2199
                c.closeConnection(WrongPort)
×
2200
                return ErrClientConnectedToLeafNodePort
×
2201
        }
×
2202

2203
        // Unmarshal as a leaf node connect protocol
2204
        proto := &leafConnectInfo{}
515✔
2205
        if err := json.Unmarshal(arg, proto); err != nil {
515✔
2206
                return err
×
2207
        }
×
2208

2209
        // Reject a cluster that contains spaces.
2210
        if proto.Cluster != _EMPTY_ && strings.Contains(proto.Cluster, " ") {
516✔
2211
                c.sendErrAndErr(ErrClusterNameHasSpaces.Error())
1✔
2212
                c.closeConnection(ProtocolViolation)
1✔
2213
                return ErrClusterNameHasSpaces
1✔
2214
        }
1✔
2215

2216
        // Check for cluster name collisions.
2217
        if cn := s.cachedClusterName(); cn != _EMPTY_ && proto.Cluster != _EMPTY_ && proto.Cluster == cn {
517✔
2218
                c.sendErrAndErr(ErrLeafNodeHasSameClusterName.Error())
3✔
2219
                c.closeConnection(ClusterNamesIdentical)
3✔
2220
                return ErrLeafNodeHasSameClusterName
3✔
2221
        }
3✔
2222

2223
        // Reject if this has Gateway which means that it would be from a gateway
2224
        // connection that incorrectly connects to the leafnode port.
2225
        if proto.Gateway != _EMPTY_ {
511✔
2226
                errTxt := fmt.Sprintf("Rejecting connection from gateway %q on the leafnode port", proto.Gateway)
×
2227
                c.Errorf(errTxt)
×
2228
                c.sendErr(errTxt)
×
2229
                c.closeConnection(WrongGateway)
×
2230
                return ErrWrongGateway
×
2231
        }
×
2232

2233
        if mv := s.getOpts().LeafNode.MinVersion; mv != _EMPTY_ {
513✔
2234
                major, minor, update, _ := versionComponents(mv)
2✔
2235
                if !versionAtLeast(proto.Version, major, minor, update) {
3✔
2236
                        // Send back an INFO so recent remote servers process the rejection
1✔
2237
                        // cleanly, then close immediately. The soliciting side applies the
1✔
2238
                        // reconnect delay when it processes the error.
1✔
2239
                        s.sendPermsAndAccountInfo(c)
1✔
2240
                        c.sendErrAndErr(fmt.Sprintf("%s %q", ErrLeafNodeMinVersionRejected, mv))
1✔
2241
                        c.closeConnection(MinimumVersionRequired)
1✔
2242
                        return ErrMinimumVersionRequired
1✔
2243
                }
1✔
2244
        }
2245

2246
        // Check if this server supports headers.
2247
        supportHeaders := c.srv.supportsHeaders()
510✔
2248

510✔
2249
        c.mu.Lock()
510✔
2250
        // Leaf Nodes do not do echo or verbose or pedantic.
510✔
2251
        c.opts.Verbose = false
510✔
2252
        c.opts.Echo = false
510✔
2253
        c.opts.Pedantic = false
510✔
2254
        // This inbound connection will be marked as supporting headers if this server
510✔
2255
        // support headers and the remote has sent in the CONNECT protocol that it does
510✔
2256
        // support headers too.
510✔
2257
        c.headers = supportHeaders && proto.Headers
510✔
2258
        // If the compression level is still not set, set it based on what has been
510✔
2259
        // given to us in the CONNECT protocol.
510✔
2260
        if c.leaf.compression == _EMPTY_ {
628✔
2261
                // But if proto.Compression is _EMPTY_, set it to CompressionNotSupported
118✔
2262
                if proto.Compression == _EMPTY_ {
156✔
2263
                        c.leaf.compression = CompressionNotSupported
38✔
2264
                } else {
118✔
2265
                        c.leaf.compression = proto.Compression
80✔
2266
                }
80✔
2267
        }
2268

2269
        // Remember the remote server.
2270
        c.leaf.remoteServer = proto.Name
510✔
2271
        // Remember the remote account name
510✔
2272
        c.leaf.remoteAccName = proto.RemoteAccount
510✔
2273
        // Remember if the leafnode requested isolation.
510✔
2274
        c.leaf.isolated = c.leaf.isolated || proto.Isolate
510✔
2275

510✔
2276
        // If the other side has declared itself a hub, so we will take on the spoke role.
510✔
2277
        if proto.Hub {
522✔
2278
                c.leaf.isSpoke = true
12✔
2279
        }
12✔
2280

2281
        // The soliciting side is part of a cluster.
2282
        if proto.Cluster != _EMPTY_ {
881✔
2283
                c.leaf.remoteCluster = proto.Cluster
371✔
2284
        }
371✔
2285

2286
        c.leaf.remoteDomain = proto.Domain
510✔
2287

510✔
2288
        // When a leaf solicits a connection to a hub, the perms that it will use on the soliciting leafnode's
510✔
2289
        // behalf are correct for them, but inside the hub need to be reversed since data is flowing in the opposite direction.
510✔
2290
        if !c.isSolicitedLeafNode() && c.perms != nil {
516✔
2291
                sp, pp := c.perms.sub, c.perms.pub
6✔
2292
                c.perms.sub, c.perms.pub = pp, sp
6✔
2293
                // setPermissions populated darray from the subscribe permissions,
6✔
2294
                // which are the import permissions advertised to the spoke. Keep
6✔
2295
                // those parsed denies after reversing the live permission directions.
6✔
2296
                if c.opts.Import == nil {
7✔
2297
                        c.darray = nil
1✔
2298
                }
1✔
2299
        }
2300

2301
        // Set the Ping timer
2302
        c.setFirstPingTimer()
510✔
2303

510✔
2304
        // If we received pub deny permissions from the other end, merge with existing ones.
510✔
2305
        c.mergeDenyPermissions(pub, proto.DenyPub)
510✔
2306

510✔
2307
        acc := c.acc
510✔
2308
        c.mu.Unlock()
510✔
2309

510✔
2310
        // If the account is not set (e.g. connection was closed due to auth
510✔
2311
        // timeout while still being processed), bail out to avoid a panic.
510✔
2312
        if acc == nil {
510✔
2313
                c.closeConnection(MissingAccount)
×
2314
                return ErrMissingAccount
×
2315
        }
×
2316

2317
        // Register the cluster, even if empty, as long as we are acting as a hub.
2318
        if !proto.Hub {
1,008✔
2319
                acc.registerLeafNodeCluster(proto.Cluster)
498✔
2320
        }
498✔
2321

2322
        // Add in the leafnode here since we passed through auth at this point.
2323
        s.addLeafNodeConnection(c, proto.Name, proto.Cluster, true)
510✔
2324

510✔
2325
        // If we have permissions bound to this leafnode we need to send then back to the
510✔
2326
        // origin server for local enforcement.
510✔
2327
        s.sendPermsAndAccountInfo(c)
510✔
2328

510✔
2329
        // Create and initialize the smap since we know our bound account now.
510✔
2330
        // This will send all registered subs too.
510✔
2331
        s.initLeafNodeSmapAndSendSubs(c)
510✔
2332

510✔
2333
        // Announce the account connect event for a leaf node.
510✔
2334
        // This will be a no-op as needed.
510✔
2335
        s.sendLeafNodeConnect(c.acc)
510✔
2336

510✔
2337
        // Check to see if we need to kick any internal source or mirror consumers.
510✔
2338
        // This will be a no-op if JetStream not enabled for this server or if the bound account
510✔
2339
        // does not have jetstream.
510✔
2340
        s.checkInternalSyncConsumers(acc)
510✔
2341

510✔
2342
        return nil
510✔
2343
}
2344

2345
// checkInternalSyncConsumers
2346
func (s *Server) checkInternalSyncConsumers(acc *Account) {
1,545✔
2347
        // Grab our js
1,545✔
2348
        js := s.getJetStream()
1,545✔
2349

1,545✔
2350
        // Only applicable if we have JS and the leafnode has JS as well.
1,545✔
2351
        // We check for remote JS outside.
1,545✔
2352
        if !js.isEnabled() || acc == nil {
2,352✔
2353
                return
807✔
2354
        }
807✔
2355

2356
        // We will check all streams in our local account. They must be a leader and
2357
        // be sourcing or mirroring. We will check the external config on the stream itself
2358
        // if this is cross domain, or if the remote domain is empty, meaning we might be
2359
        // extending the system across this leafnode connection and hence we would be extending
2360
        // our own domain.
2361
        jsa := js.lookupAccount(acc)
738✔
2362
        if jsa == nil {
1,018✔
2363
                return
280✔
2364
        }
280✔
2365

2366
        var streams []*stream
458✔
2367
        jsa.mu.RLock()
458✔
2368
        for _, mset := range jsa.streams {
518✔
2369
                mset.cfgMu.RLock()
60✔
2370
                // We need to have a mirror or source defined.
60✔
2371
                // We do not want to force another lock here to look for leader status,
60✔
2372
                // so collect and after we release jsa will make sure.
60✔
2373
                if mset.cfg.Mirror != nil || len(mset.cfg.Sources) > 0 {
73✔
2374
                        streams = append(streams, mset)
13✔
2375
                }
13✔
2376
                mset.cfgMu.RUnlock()
60✔
2377
        }
2378
        jsa.mu.RUnlock()
458✔
2379

458✔
2380
        // Now loop through all candidates and check if we are the leader and have NOT
458✔
2381
        // created the sync up consumer.
458✔
2382
        for _, mset := range streams {
471✔
2383
                mset.retryDisconnectedSyncConsumers()
13✔
2384
        }
13✔
2385
}
2386

2387
// Returns the remote cluster name. This is set only once so does not require a lock.
2388
func (c *client) remoteCluster() string {
122,219✔
2389
        if c.leaf == nil {
122,219✔
2390
                return _EMPTY_
×
2391
        }
×
2392
        return c.leaf.remoteCluster
122,219✔
2393
}
2394

2395
// Sends back an info block to the soliciting leafnode to let it know about
2396
// its permission settings for local enforcement.
2397
func (s *Server) sendPermsAndAccountInfo(c *client) {
511✔
2398
        // Copy
511✔
2399
        s.mu.Lock()
511✔
2400
        info := s.copyLeafNodeInfo()
511✔
2401
        s.mu.Unlock()
511✔
2402
        c.mu.Lock()
511✔
2403
        info.CID = c.cid
511✔
2404
        info.Import = c.opts.Import
511✔
2405
        info.Export = c.opts.Export
511✔
2406
        info.RemoteAccount = c.acc.Name
511✔
2407
        // s.SystemAccount() uses an atomic operation and does not get the server lock, so this is safe.
511✔
2408
        info.IsSystemAccount = c.acc == s.SystemAccount()
511✔
2409
        info.ConnectInfo = true
511✔
2410
        c.enqueueProto(generateInfoJSON(info))
511✔
2411
        c.mu.Unlock()
511✔
2412
}
511✔
2413

2414
// Snapshot the current subscriptions from the sublist into our smap which
2415
// we will keep updated from now on.
2416
// Also send the registered subscriptions.
2417
func (s *Server) initLeafNodeSmapAndSendSubs(c *client) {
979✔
2418
        acc := c.acc
979✔
2419
        if acc == nil {
979✔
2420
                c.Debugf("Leafnode does not have an account bound")
×
2421
                return
×
2422
        }
×
2423
        // Collect all account subs here.
2424
        _subs := [1024]*subscription{}
979✔
2425
        subs := _subs[:0]
979✔
2426
        ims := []string{}
979✔
2427

979✔
2428
        // Hold the client lock otherwise there can be a race and miss some subs.
979✔
2429
        c.mu.Lock()
979✔
2430
        defer c.mu.Unlock()
979✔
2431

979✔
2432
        acc.mu.RLock()
979✔
2433
        accName := acc.Name
979✔
2434
        accNTag := acc.nameTag
979✔
2435

979✔
2436
        // To make printing look better when no friendly name present.
979✔
2437
        if accNTag != _EMPTY_ {
982✔
2438
                accNTag = "/" + accNTag
3✔
2439
        }
3✔
2440

2441
        // If we are solicited we only send interest for local clients.
2442
        if c.isSpokeLeafNode() {
1,448✔
2443
                acc.sl.localSubs(&subs, true)
469✔
2444
        } else {
979✔
2445
                acc.sl.All(&subs)
510✔
2446
        }
510✔
2447

2448
        // Check if we have an existing service import reply.
2449
        siReply := copyBytes(acc.siReply)
979✔
2450

979✔
2451
        // Since leaf nodes only send on interest, if the bound
979✔
2452
        // account has import services we need to send those over.
979✔
2453
        for isubj := range acc.imports.services {
4,516✔
2454
                if c.isSpokeLeafNode() && !c.canSubscribe(isubj) {
3,765✔
2455
                        c.Debugf("Not permitted to import service %q on behalf of %s%s", isubj, accName, accNTag)
228✔
2456
                        continue
228✔
2457
                }
2458
                ims = append(ims, isubj)
3,309✔
2459
        }
2460
        // Likewise for mappings.
2461
        for _, m := range acc.mappings {
3,112✔
2462
                if c.isSpokeLeafNode() && !c.canSubscribe(m.src) {
2,151✔
2463
                        c.Debugf("Not permitted to import mapping %q on behalf of %s%s", m.src, accName, accNTag)
18✔
2464
                        continue
18✔
2465
                }
2466
                ims = append(ims, m.src)
2,115✔
2467
        }
2468

2469
        // Create a unique subject that will be used for loop detection.
2470
        lds := acc.lds
979✔
2471
        acc.mu.RUnlock()
979✔
2472

979✔
2473
        // Check if we have to create the LDS.
979✔
2474
        if lds == _EMPTY_ {
1,738✔
2475
                lds = leafNodeLoopDetectionSubjectPrefix + nuid.Next()
759✔
2476
                acc.mu.Lock()
759✔
2477
                acc.lds = lds
759✔
2478
                acc.mu.Unlock()
759✔
2479
        }
759✔
2480

2481
        // Now check for gateway interest. Leafnodes will put this into
2482
        // the proper mode to propagate, but they are not held in the account.
2483
        gwsa := [16]*client{}
979✔
2484
        gws := gwsa[:0]
979✔
2485
        s.getOutboundGatewayConnections(&gws)
979✔
2486
        for _, cgw := range gws {
1,039✔
2487
                cgw.mu.Lock()
60✔
2488
                gw := cgw.gw
60✔
2489
                cgw.mu.Unlock()
60✔
2490
                if gw != nil {
120✔
2491
                        if ei, _ := gw.outsim.Load(accName); ei != nil {
120✔
2492
                                if e := ei.(*outsie); e != nil && e.sl != nil {
120✔
2493
                                        e.sl.All(&subs)
60✔
2494
                                }
60✔
2495
                        }
2496
                }
2497
        }
2498

2499
        applyGlobalRouting := s.gateway.enabled
979✔
2500
        if c.isSpokeLeafNode() {
1,448✔
2501
                // Add a fake subscription for this solicited leafnode connection
469✔
2502
                // so that we can send back directly for mapped GW replies.
469✔
2503
                // We need to keep track of this subscription so it can be removed
469✔
2504
                // when the connection is closed so that the GC can release it.
469✔
2505
                c.leaf.gwSub = &subscription{client: c, subject: []byte(gwReplyPrefix + ">")}
469✔
2506
                c.srv.gwLeafSubs.Insert(c.leaf.gwSub)
469✔
2507
        }
469✔
2508

2509
        // Now walk the results and add them to our smap
2510
        rc := c.leaf.remoteCluster
979✔
2511
        c.leaf.smap = make(map[string]int32)
979✔
2512
        for _, sub := range subs {
34,740✔
2513
                // Check perms regardless of role.
33,761✔
2514
                if c.perms != nil && !c.canSubscribe(string(sub.subject)) {
35,882✔
2515
                        c.Debugf("Not permitted to subscribe to %q on behalf of %s%s", sub.subject, accName, accNTag)
2,121✔
2516
                        continue
2,121✔
2517
                }
2518
                // Don't advertise interest from leafnodes to other isolated leafnodes.
2519
                if sub.client.kind == LEAF && c.isIsolatedLeafNode() {
31,640✔
2520
                        continue
×
2521
                }
2522
                // We ignore ourselves here.
2523
                // Also don't add the subscription if it has a origin cluster and the
2524
                // cluster name matches the one of the client we are sending to.
2525
                if c != sub.client && (sub.origin == nil || (bytesToString(sub.origin) != rc)) {
58,587✔
2526
                        count := int32(1)
26,947✔
2527
                        if len(sub.queue) > 0 && sub.qw > 0 {
26,957✔
2528
                                count = sub.qw
10✔
2529
                        }
10✔
2530
                        c.leaf.smap[keyFromSub(sub)] += count
26,947✔
2531
                        if c.leaf.tsub == nil {
27,864✔
2532
                                c.leaf.tsub = make(map[*subscription]struct{})
917✔
2533
                        }
917✔
2534
                        c.leaf.tsub[sub] = struct{}{}
26,947✔
2535
                }
2536
        }
2537
        // FIXME(dlc) - We need to update appropriately on an account claims update.
2538
        for _, isubj := range ims {
6,403✔
2539
                c.leaf.smap[isubj]++
5,424✔
2540
        }
5,424✔
2541
        // If we have gateways enabled we need to make sure the other side sends us responses
2542
        // that have been augmented from the original subscription.
2543
        // TODO(dlc) - Should we lock this down more?
2544
        if applyGlobalRouting {
1,052✔
2545
                c.leaf.smap[oldGWReplyPrefix+"*.>"]++
73✔
2546
                c.leaf.smap[gwReplyPrefix+">"]++
73✔
2547
        }
73✔
2548
        // Detect loops by subscribing to a specific subject and checking
2549
        // if this sub is coming back to us.
2550
        c.leaf.smap[lds]++
979✔
2551

979✔
2552
        // Check if we need to add an existing siReply to our map.
979✔
2553
        // This will be a prefix so add on the wildcard.
979✔
2554
        if siReply != nil {
998✔
2555
                wcsub := append(siReply, '>')
19✔
2556
                c.leaf.smap[string(wcsub)]++
19✔
2557
        }
19✔
2558
        // Queue all protocols. There is no max pending limit for LN connection,
2559
        // so we don't need chunking. The writes will happen from the writeLoop.
2560
        var b bytes.Buffer
979✔
2561
        for key, n := range c.leaf.smap {
24,217✔
2562
                c.writeLeafSub(&b, key, n)
23,238✔
2563
        }
23,238✔
2564
        if b.Len() > 0 {
1,958✔
2565
                c.enqueueProto(b.Bytes())
979✔
2566
        }
979✔
2567
        if c.leaf.tsub != nil {
1,897✔
2568
                // Clear the tsub map after 5 seconds.
918✔
2569
                c.leaf.tsubt = time.AfterFunc(5*time.Second, func() {
953✔
2570
                        c.mu.Lock()
35✔
2571
                        if c.leaf != nil {
70✔
2572
                                c.leaf.tsub = nil
35✔
2573
                                c.leaf.tsubt = nil
35✔
2574
                        }
35✔
2575
                        c.mu.Unlock()
35✔
2576
                })
2577
        }
2578
}
2579

2580
// updateInterestForAccountOnGateway called from gateway code when processing RS+ and RS-.
2581
func (s *Server) updateInterestForAccountOnGateway(accName string, sub *subscription, delta int32) {
194,992✔
2582
        // Since we're in the gateway's readLoop, and we would otherwise block, don't allow fetching.
194,992✔
2583
        acc, err := s.lookupOrFetchAccount(accName, false)
194,992✔
2584
        if acc == nil || err != nil {
195,370✔
2585
                s.Debugf("No or bad account for %q, failed to update interest from gateway", accName)
378✔
2586
                return
378✔
2587
        }
378✔
2588
        acc.updateLeafNodes(sub, delta)
194,614✔
2589
}
2590

2591
// updateLeafNodesEx will make sure to update the account smap for the subscription.
2592
// Will also forward to all leaf nodes as needed.
2593
// If `hubOnly` is true, then will update only leaf nodes that connect to this server
2594
// (that is, for which this server acts as a hub to them).
2595
func (acc *Account) updateLeafNodesEx(sub *subscription, delta int32, hubOnly bool) {
2,498,525✔
2596
        if acc == nil || sub == nil {
2,498,525✔
2597
                return
×
2598
        }
×
2599

2600
        // We will do checks for no leafnodes and same cluster here inline and under the
2601
        // general account read lock.
2602
        // If we feel we need to update the leafnodes we will do that out of line to avoid
2603
        // blocking routes or GWs.
2604

2605
        acc.mu.RLock()
2,498,525✔
2606
        // First check if we even have leafnodes here.
2,498,525✔
2607
        if acc.nleafs == 0 {
4,940,431✔
2608
                acc.mu.RUnlock()
2,441,906✔
2609
                return
2,441,906✔
2610
        }
2,441,906✔
2611

2612
        // Is this a loop detection subject.
2613
        isLDS := bytes.HasPrefix(sub.subject, []byte(leafNodeLoopDetectionSubjectPrefix))
56,619✔
2614

56,619✔
2615
        // Capture the cluster even if its empty.
56,619✔
2616
        var cluster string
56,619✔
2617
        if sub.origin != nil {
98,899✔
2618
                cluster = bytesToString(sub.origin)
42,280✔
2619
        }
42,280✔
2620

2621
        // If we have an isolated cluster we can return early, as long as it is not a loop detection subject.
2622
        // Empty clusters will return false for the check.
2623
        if !isLDS && acc.isLeafNodeClusterIsolated(cluster) {
74,261✔
2624
                acc.mu.RUnlock()
17,642✔
2625
                return
17,642✔
2626
        }
17,642✔
2627

2628
        // We can release the general account lock.
2629
        acc.mu.RUnlock()
38,977✔
2630

38,977✔
2631
        // We can hold the list lock here to avoid having to copy a large slice.
38,977✔
2632
        acc.lmu.RLock()
38,977✔
2633
        defer acc.lmu.RUnlock()
38,977✔
2634

38,977✔
2635
        // Do this once.
38,977✔
2636
        subject := string(sub.subject)
38,977✔
2637

38,977✔
2638
        // Walk the connected leafnodes from a random starting point to avoid
38,977✔
2639
        // concurrent callers all contending over leafs in the same order.
38,977✔
2640
        nleafs := len(acc.lleafs)
38,977✔
2641
        start := 0
38,977✔
2642
        if nleafs > 1 {
43,997✔
2643
                start = rand.Intn(nleafs)
5,020✔
2644
        }
5,020✔
2645
        for i := 0; i < nleafs; i++ {
85,395✔
2646
                ln := acc.lleafs[(start+i)%nleafs]
46,418✔
2647
                if ln == sub.client {
72,716✔
2648
                        continue
26,298✔
2649
                }
2650
                ln.mu.RLock()
20,120✔
2651
                // Don't advertise interest from leafnodes to other isolated leafnodes.
20,120✔
2652
                if sub.client.kind == LEAF && ln.isIsolatedLeafNode() {
20,120✔
2653
                        ln.mu.RUnlock()
×
2654
                        continue
×
2655
                }
2656
                // If `hubOnly` is true, it means that we want to update only leafnodes
2657
                // that connect to this server (so isHubLeafNode() would return `true`).
2658
                if hubOnly && !ln.isHubLeafNode() {
20,126✔
2659
                        ln.mu.RUnlock()
6✔
2660
                        continue
6✔
2661
                }
2662
                // Check to make sure this sub does not have an origin cluster that matches the leafnode.
2663
                // If skipped, make sure that we still let go the "$LDS." subscription that allows
2664
                // the detection of loops as long as different cluster.
2665
                clusterDifferent := cluster != ln.remoteCluster()
20,114✔
2666
                update := (isLDS && clusterDifferent) ||
20,114✔
2667
                        ((cluster == _EMPTY_ || clusterDifferent) && (delta <= 0 || ln.canSubscribeInternal(subject)))
20,114✔
2668
                ln.mu.RUnlock()
20,114✔
2669
                if update {
37,202✔
2670
                        ln.mu.Lock()
17,088✔
2671
                        // The leaf role, isolation mode, and remote cluster are stable
17,088✔
2672
                        // for the connection. Recheck canSubscribe here since permissions
17,088✔
2673
                        // can change, and to initializes mperms for wildcard subscriptions
17,088✔
2674
                        // that collide with deny rules.
17,088✔
2675
                        if isLDS || delta <= 0 || ln.canSubscribe(subject) {
34,176✔
2676
                                ln.updateSmap(sub, delta, isLDS)
17,088✔
2677
                        }
17,088✔
2678
                        ln.mu.Unlock()
17,088✔
2679
                }
2680
        }
2681
}
2682

2683
// updateLeafNodes will make sure to update the account smap for the subscription.
2684
// Will also forward to all leaf nodes as needed.
2685
func (acc *Account) updateLeafNodes(sub *subscription, delta int32) {
2,498,512✔
2686
        acc.updateLeafNodesEx(sub, delta, false)
2,498,512✔
2687
}
2,498,512✔
2688

2689
// This will make an update to our internal smap and determine if we should send out
2690
// an interest update to the remote side.
2691
// Lock should be held.
2692
func (c *client) updateSmap(sub *subscription, delta int32, isLDS bool) {
17,088✔
2693
        if c.leaf.smap == nil {
17,103✔
2694
                return
15✔
2695
        }
15✔
2696

2697
        // If we are solicited make sure this is a local client or a non-solicited leaf node
2698
        skind := sub.client.kind
17,073✔
2699
        updateClient := skind == CLIENT || skind == SYSTEM || skind == JETSTREAM || skind == ACCOUNT
17,073✔
2700
        if !isLDS && c.isSpokeLeafNode() && !(updateClient || (skind == LEAF && !sub.client.isSpokeLeafNode())) {
22,214✔
2701
                return
5,141✔
2702
        }
5,141✔
2703

2704
        // For additions, check if that sub has just been processed during initLeafNodeSmapAndSendSubs
2705
        if delta > 0 && c.leaf.tsub != nil {
17,679✔
2706
                if _, present := c.leaf.tsub[sub]; present {
5,751✔
2707
                        delete(c.leaf.tsub, sub)
4✔
2708
                        if len(c.leaf.tsub) == 0 {
4✔
2709
                                c.leaf.tsub = nil
×
2710
                                c.leaf.tsubt.Stop()
×
2711
                                c.leaf.tsubt = nil
×
2712
                        }
×
2713
                        return
4✔
2714
                }
2715
        }
2716

2717
        key := keyFromSub(sub)
11,928✔
2718
        n, ok := c.leaf.smap[key]
11,928✔
2719
        if delta < 0 && !ok {
12,718✔
2720
                return
790✔
2721
        }
790✔
2722

2723
        // We will update if its a queue, if count is zero (or negative), or we were 0 and are N > 0.
2724
        update := sub.queue != nil || (n <= 0 && n+delta > 0) || (n > 0 && n+delta <= 0)
11,138✔
2725
        n += delta
11,138✔
2726
        if n > 0 {
19,018✔
2727
                c.leaf.smap[key] = n
7,880✔
2728
        } else {
11,138✔
2729
                delete(c.leaf.smap, key)
3,258✔
2730
        }
3,258✔
2731
        if update {
18,472✔
2732
                c.sendLeafNodeSubUpdate(key, n)
7,334✔
2733
        }
7,334✔
2734
}
2735

2736
// Used to force add subjects to the subject map.
2737
func (c *client) forceAddToSmap(subj string) {
2✔
2738
        c.mu.Lock()
2✔
2739
        defer c.mu.Unlock()
2✔
2740

2✔
2741
        if c.leaf.smap == nil {
2✔
2742
                return
×
2743
        }
×
2744
        n := c.leaf.smap[subj]
2✔
2745
        if n != 0 {
2✔
2746
                return
×
2747
        }
×
2748
        // Place into the map since it was not there.
2749
        c.leaf.smap[subj] = 1
2✔
2750
        c.sendLeafNodeSubUpdate(subj, 1)
2✔
2751
}
2752

2753
// Used to force remove a subject from the subject map.
2754
func (c *client) forceRemoveFromSmap(subj string) {
×
2755
        c.mu.Lock()
×
2756
        defer c.mu.Unlock()
×
2757

×
2758
        if c.leaf.smap == nil {
×
2759
                return
×
2760
        }
×
2761
        n := c.leaf.smap[subj]
×
2762
        if n == 0 {
×
2763
                return
×
2764
        }
×
2765
        n--
×
2766
        if n == 0 {
×
2767
                // Remove is now zero
×
2768
                delete(c.leaf.smap, subj)
×
2769
                c.sendLeafNodeSubUpdate(subj, 0)
×
2770
        } else {
×
2771
                c.leaf.smap[subj] = n
×
2772
        }
×
2773
}
2774

2775
// Send the subscription interest change to the other side.
2776
// Lock should be held.
2777
func (c *client) sendLeafNodeSubUpdate(key string, n int32) {
7,336✔
2778
        // If we are a spoke, we need to check if we are allowed to send this subscription over to the hub.
7,336✔
2779
        if c.isSpokeLeafNode() {
9,271✔
2780
                checkPerms := true
1,935✔
2781
                if len(key) > 0 && (key[0] == '$' || key[0] == '_') {
3,109✔
2782
                        if strings.HasPrefix(key, leafNodeLoopDetectionSubjectPrefix) ||
1,174✔
2783
                                strings.HasPrefix(key, oldGWReplyPrefix) ||
1,174✔
2784
                                strings.HasPrefix(key, gwReplyPrefix) {
1,234✔
2785
                                checkPerms = false
60✔
2786
                        }
60✔
2787
                }
2788
                if checkPerms {
3,810✔
2789
                        var subject string
1,875✔
2790
                        if sep := strings.IndexByte(key, ' '); sep != -1 {
2,301✔
2791
                                subject = key[:sep]
426✔
2792
                        } else {
1,875✔
2793
                                subject = key
1,449✔
2794
                        }
1,449✔
2795
                        if !c.canSubscribe(subject) {
1,875✔
2796
                                return
×
2797
                        }
×
2798
                }
2799
        }
2800
        // If we are here we can send over to the other side.
2801
        _b := [64]byte{}
7,336✔
2802
        b := bytes.NewBuffer(_b[:0])
7,336✔
2803
        c.writeLeafSub(b, key, n)
7,336✔
2804
        c.enqueueProto(b.Bytes())
7,336✔
2805
}
2806

2807
// Helper function to build the key.
2808
func keyFromSub(sub *subscription) string {
39,863✔
2809
        var sb strings.Builder
39,863✔
2810
        sb.Grow(len(sub.subject) + len(sub.queue) + 1)
39,863✔
2811
        sb.Write(sub.subject)
39,863✔
2812
        if sub.queue != nil {
42,011✔
2813
                // Just make the key subject spc group, e.g. 'foo bar'
2,148✔
2814
                sb.WriteByte(' ')
2,148✔
2815
                sb.Write(sub.queue)
2,148✔
2816
        }
2,148✔
2817
        return sb.String()
39,863✔
2818
}
2819

2820
const (
2821
        keyRoutedSub         = "R"
2822
        keyRoutedSubByte     = 'R'
2823
        keyRoutedLeafSub     = "L"
2824
        keyRoutedLeafSubByte = 'L'
2825
)
2826

2827
// Helper function to build the key that prevents collisions between normal
2828
// routed subscriptions and routed subscriptions on behalf of a leafnode.
2829
// Keys will look like this:
2830
// "R foo"          -> plain routed sub on "foo"
2831
// "R foo bar"      -> queue routed sub on "foo", queue "bar"
2832
// "L foo bar"      -> plain routed leaf sub on "foo", leaf "bar"
2833
// "L foo bar baz"  -> queue routed sub on "foo", queue "bar", leaf "baz"
2834
func keyFromSubWithOrigin(sub *subscription) string {
719,647✔
2835
        var sb strings.Builder
719,647✔
2836
        sb.Grow(2 + len(sub.origin) + 1 + len(sub.subject) + 1 + len(sub.queue))
719,647✔
2837
        leaf := len(sub.origin) > 0
719,647✔
2838
        if leaf {
733,959✔
2839
                sb.WriteByte(keyRoutedLeafSubByte)
14,312✔
2840
        } else {
719,647✔
2841
                sb.WriteByte(keyRoutedSubByte)
705,335✔
2842
        }
705,335✔
2843
        sb.WriteByte(' ')
719,647✔
2844
        sb.Write(sub.subject)
719,647✔
2845
        if sub.queue != nil {
746,746✔
2846
                sb.WriteByte(' ')
27,099✔
2847
                sb.Write(sub.queue)
27,099✔
2848
        }
27,099✔
2849
        if leaf {
733,959✔
2850
                sb.WriteByte(' ')
14,312✔
2851
                sb.Write(sub.origin)
14,312✔
2852
        }
14,312✔
2853
        return sb.String()
719,647✔
2854
}
2855

2856
// Lock should be held.
2857
func (c *client) writeLeafSub(w *bytes.Buffer, key string, n int32) {
30,574✔
2858
        if key == _EMPTY_ {
30,574✔
2859
                return
×
2860
        }
×
2861
        if n > 0 {
57,890✔
2862
                w.WriteString("LS+ " + key)
27,316✔
2863
                // Check for queue semantics, if found write n.
27,316✔
2864
                if strings.Contains(key, " ") {
28,057✔
2865
                        w.WriteString(" ")
741✔
2866
                        var b [12]byte
741✔
2867
                        var i = len(b)
741✔
2868
                        for l := n; l > 0; l /= 10 {
1,482✔
2869
                                i--
741✔
2870
                                b[i] = digits[l%10]
741✔
2871
                        }
741✔
2872
                        w.Write(b[i:])
741✔
2873
                        if c.trace {
742✔
2874
                                arg := fmt.Sprintf("%s %d", key, n)
1✔
2875
                                c.traceOutOp("LS+", []byte(arg))
1✔
2876
                        }
1✔
2877
                } else if c.trace {
26,599✔
2878
                        c.traceOutOp("LS+", []byte(key))
24✔
2879
                }
24✔
2880
        } else {
3,258✔
2881
                w.WriteString("LS- " + key)
3,258✔
2882
                if c.trace {
3,259✔
2883
                        c.traceOutOp("LS-", []byte(key))
1✔
2884
                }
1✔
2885
        }
2886
        w.WriteString(CR_LF)
30,574✔
2887
}
2888

2889
// processLeafSub will process an inbound sub request for the remote leaf node.
2890
func (c *client) processLeafSub(argo []byte) (err error) {
27,059✔
2891
        // Indicate activity.
27,059✔
2892
        c.in.subs++
27,059✔
2893

27,059✔
2894
        srv := c.srv
27,059✔
2895
        if srv == nil {
27,059✔
2896
                return nil
×
2897
        }
×
2898

2899
        // Copy so we do not reference a potentially large buffer
2900
        arg := make([]byte, len(argo))
27,059✔
2901
        copy(arg, argo)
27,059✔
2902

27,059✔
2903
        args := splitArg(arg)
27,059✔
2904
        sub := &subscription{client: c}
27,059✔
2905

27,059✔
2906
        delta := int32(1)
27,059✔
2907
        switch len(args) {
27,059✔
2908
        case 1:
26,330✔
2909
                sub.queue = nil
26,330✔
2910
        case 3:
729✔
2911
                sub.queue = args[1]
729✔
2912
                sub.qw = int32(parseSize(args[2]))
729✔
2913
                // TODO: (ik) We should have a non empty queue name and a queue
729✔
2914
                // weight >= 1. For 2.11, we may want to return an error if that
729✔
2915
                // is not the case, but for now just overwrite `delta` if queue
729✔
2916
                // weight is greater than 1 (it is possible after a reconnect/
729✔
2917
                // server restart to receive a queue weight > 1 for a new sub).
729✔
2918
                if sub.qw > 1 {
949✔
2919
                        delta = sub.qw
220✔
2920
                }
220✔
2921
        default:
×
2922
                return fmt.Errorf("processLeafSub Parse Error: '%s'", arg)
×
2923
        }
2924
        sub.subject = args[0]
27,059✔
2925

27,059✔
2926
        c.mu.Lock()
27,059✔
2927
        if c.isClosed() {
27,071✔
2928
                c.mu.Unlock()
12✔
2929
                return nil
12✔
2930
        }
12✔
2931

2932
        acc := c.acc
27,047✔
2933
        // Guard against LS+ arriving before CONNECT has been processed, which
27,047✔
2934
        // can happen when compression is enabled.
27,047✔
2935
        if acc == nil {
27,047✔
2936
                c.mu.Unlock()
×
2937
                c.sendErr("Authorization Violation")
×
2938
                c.closeConnection(ProtocolViolation)
×
2939
                return nil
×
2940
        }
×
2941
        // Check if we have a loop.
2942
        ldsPrefix := bytes.HasPrefix(sub.subject, []byte(leafNodeLoopDetectionSubjectPrefix))
27,047✔
2943

27,047✔
2944
        if ldsPrefix && bytesToString(sub.subject) == acc.getLDSubject() {
27,051✔
2945
                c.mu.Unlock()
4✔
2946
                c.handleLeafNodeLoop(true)
4✔
2947
                return nil
4✔
2948
        }
4✔
2949

2950
        // Check permissions if applicable. (but exclude the $LDS, $GR and _GR_)
2951
        checkPerms := true
27,043✔
2952
        if sub.subject[0] == '$' || sub.subject[0] == '_' {
52,893✔
2953
                if ldsPrefix ||
25,850✔
2954
                        bytes.HasPrefix(sub.subject, []byte(oldGWReplyPrefix)) ||
25,850✔
2955
                        bytes.HasPrefix(sub.subject, []byte(gwReplyPrefix)) {
27,426✔
2956
                        checkPerms = false
1,576✔
2957
                }
1,576✔
2958
        }
2959

2960
        // If we are a hub check that we can publish to this subject.
2961
        if checkPerms {
52,510✔
2962
                subj := string(sub.subject)
25,467✔
2963
                if subjectIsLiteral(subj) && !c.pubAllowedFullCheck(subj, true, true) {
25,480✔
2964
                        c.mu.Unlock()
13✔
2965
                        c.leafSubPermViolation(sub.subject)
13✔
2966
                        c.Debugf(fmt.Sprintf("Permissions Violation for Subscription to %q", sub.subject))
13✔
2967
                        return nil
13✔
2968
                }
13✔
2969
                c.loadMsgDenyFilterIfNeeded(subj, len(sub.queue) > 0)
25,454✔
2970
        }
2971

2972
        // Check if we have a maximum on the number of subscriptions.
2973
        if c.subsAtLimit() {
27,038✔
2974
                c.mu.Unlock()
8✔
2975
                c.maxSubsExceeded()
8✔
2976
                return nil
8✔
2977
        }
8✔
2978

2979
        // If we have an origin cluster associated mark that in the sub.
2980
        if rc := c.remoteCluster(); rc != _EMPTY_ {
50,942✔
2981
                sub.origin = []byte(rc)
23,920✔
2982
        }
23,920✔
2983

2984
        // Like Routes, we store local subs by account and subject and optionally queue name.
2985
        // If we have a queue it will have a trailing weight which we do not want.
2986
        if sub.queue != nil {
27,751✔
2987
                sub.sid = arg[:len(arg)-len(args[2])-1]
729✔
2988
        } else {
27,022✔
2989
                sub.sid = arg
26,293✔
2990
        }
26,293✔
2991
        key := bytesToString(sub.sid)
27,022✔
2992
        osub := c.subs[key]
27,022✔
2993
        if osub == nil {
53,731✔
2994
                c.subs[key] = sub
26,709✔
2995
                // Now place into the account sl.
26,709✔
2996
                if err := acc.sl.Insert(sub); err != nil {
26,709✔
2997
                        delete(c.subs, key)
×
2998
                        c.mu.Unlock()
×
2999
                        c.Errorf("Could not insert subscription: %v", err)
×
3000
                        c.sendErr("Invalid Subscription")
×
3001
                        return nil
×
3002
                }
×
3003
        } else if sub.queue != nil {
625✔
3004
                // For a queue we need to update the weight.
312✔
3005
                delta = sub.qw - atomic.LoadInt32(&osub.qw)
312✔
3006
                atomic.StoreInt32(&osub.qw, sub.qw)
312✔
3007
                acc.sl.UpdateRemoteQSub(osub)
312✔
3008
        }
312✔
3009
        spoke := c.isSpokeLeafNode()
27,022✔
3010
        c.mu.Unlock()
27,022✔
3011

27,022✔
3012
        // Only add in shadow subs if a new sub or qsub.
27,022✔
3013
        if osub == nil {
53,731✔
3014
                if err := c.addShadowSubscriptions(acc, sub); err != nil {
26,709✔
3015
                        c.Errorf(err.Error())
×
3016
                }
×
3017
        }
3018

3019
        // If we are not solicited, treat leaf node subscriptions similar to a
3020
        // client subscription, meaning we forward them to routes, gateways and
3021
        // other leaf nodes as needed.
3022
        if !spoke {
36,605✔
3023
                // If we are routing add to the route map for the associated account.
9,583✔
3024
                srv.updateRouteSubscriptionMap(acc, sub, delta)
9,583✔
3025
                if srv.gateway.enabled {
10,488✔
3026
                        srv.gatewayUpdateSubInterest(acc.Name, sub, delta)
905✔
3027
                }
905✔
3028
        }
3029
        // Now check on leafnode updates for other leaf nodes. We understand solicited
3030
        // and non-solicited state in this call so we will do the right thing.
3031
        acc.updateLeafNodes(sub, delta)
27,022✔
3032

27,022✔
3033
        return nil
27,022✔
3034
}
3035

3036
// If the leafnode is a solicited, set the connect delay based on default
3037
// or private option (for tests). Sends the error to the other side, log and
3038
// close the connection.
3039
func (c *client) handleLeafNodeLoop(sendErr bool) {
12✔
3040
        accName, delay := c.setLeafConnectDelayIfSoliciting(leafNodeReconnectDelayAfterLoopDetected)
12✔
3041
        errTxt := fmt.Sprintf("Loop detected for leafnode account=%q. Delaying attempt to reconnect for %v", accName, delay)
12✔
3042
        if sendErr {
18✔
3043
                c.sendErr(errTxt)
6✔
3044
        }
6✔
3045

3046
        c.Errorf(errTxt)
12✔
3047
        // If we are here with "sendErr" false, it means that this is the server
12✔
3048
        // that received the error. The other side will have closed the connection,
12✔
3049
        // but does not hurt to close here too.
12✔
3050
        c.closeConnection(ProtocolViolation)
12✔
3051
}
3052

3053
// processLeafUnsub will process an inbound unsub request for the remote leaf node.
3054
func (c *client) processLeafUnsub(arg []byte) error {
2,960✔
3055
        // Indicate any activity, so pub and sub or unsubs.
2,960✔
3056
        c.in.subs++
2,960✔
3057

2,960✔
3058
        srv := c.srv
2,960✔
3059

2,960✔
3060
        c.mu.Lock()
2,960✔
3061
        if c.isClosed() {
2,999✔
3062
                c.mu.Unlock()
39✔
3063
                return nil
39✔
3064
        }
39✔
3065

3066
        acc := c.acc
2,921✔
3067
        // Guard against LS- arriving before CONNECT has been processed.
2,921✔
3068
        if acc == nil {
2,921✔
3069
                c.mu.Unlock()
×
3070
                c.sendErr("Authorization Violation")
×
3071
                c.closeConnection(ProtocolViolation)
×
3072
                return nil
×
3073
        }
×
3074

3075
        spoke := c.isSpokeLeafNode()
2,921✔
3076
        // We store local subs by account and subject and optionally queue name.
2,921✔
3077
        // LS- will have the arg exactly as the key.
2,921✔
3078
        sub, ok := c.subs[string(arg)]
2,921✔
3079
        if !ok {
2,921✔
3080
                // If not found, don't try to update routes/gws/leaf nodes.
×
3081
                c.mu.Unlock()
×
3082
                return nil
×
3083
        }
×
3084
        delta := int32(1)
2,921✔
3085
        if len(sub.queue) > 0 {
3,304✔
3086
                delta = sub.qw
383✔
3087
        }
383✔
3088
        c.mu.Unlock()
2,921✔
3089

2,921✔
3090
        c.unsubscribe(acc, sub, true, true)
2,921✔
3091
        if !spoke {
3,752✔
3092
                // If we are routing subtract from the route map for the associated account.
831✔
3093
                srv.updateRouteSubscriptionMap(acc, sub, -delta)
831✔
3094
                // Gateways
831✔
3095
                if srv.gateway.enabled {
1,014✔
3096
                        srv.gatewayUpdateSubInterest(acc.Name, sub, -delta)
183✔
3097
                }
183✔
3098
        }
3099
        // Now check on leafnode updates for other leaf nodes.
3100
        acc.updateLeafNodes(sub, -delta)
2,921✔
3101
        return nil
2,921✔
3102
}
3103

3104
func (c *client) processLeafHeaderMsgArgs(arg []byte) error {
228✔
3105
        // Unroll splitArgs to avoid runtime/heap issues
228✔
3106
        args := c.argsa[:0]
228✔
3107
        start := -1
228✔
3108
        for i, b := range arg {
12,646✔
3109
                switch b {
12,418✔
3110
                case ' ', '\t', '\r', '\n':
668✔
3111
                        if start >= 0 {
1,336✔
3112
                                args = append(args, arg[start:i])
668✔
3113
                                start = -1
668✔
3114
                        }
668✔
3115
                default:
11,750✔
3116
                        if start < 0 {
12,646✔
3117
                                start = i
896✔
3118
                        }
896✔
3119
                }
3120
        }
3121
        if start >= 0 {
456✔
3122
                args = append(args, arg[start:])
228✔
3123
        }
228✔
3124

3125
        c.pa.arg = arg
228✔
3126
        switch len(args) {
228✔
3127
        case 0, 1, 2:
×
3128
                return fmt.Errorf("processLeafHeaderMsgArgs Parse Error: '%s'", args)
×
3129
        case 3:
21✔
3130
                c.pa.reply = nil
21✔
3131
                c.pa.queues = nil
21✔
3132
                c.pa.hdb = args[1]
21✔
3133
                c.pa.hdr = parseSize(args[1])
21✔
3134
                c.pa.szb = args[2]
21✔
3135
                c.pa.size = parseSize(args[2])
21✔
3136
        case 4:
204✔
3137
                c.pa.reply = args[1]
204✔
3138
                c.pa.queues = nil
204✔
3139
                c.pa.hdb = args[2]
204✔
3140
                c.pa.hdr = parseSize(args[2])
204✔
3141
                c.pa.szb = args[3]
204✔
3142
                c.pa.size = parseSize(args[3])
204✔
3143
        default:
3✔
3144
                // args[1] is our reply indicator. Should be + or | normally.
3✔
3145
                if len(args[1]) != 1 {
3✔
3146
                        return fmt.Errorf("processLeafHeaderMsgArgs Bad or Missing Reply Indicator: '%s'", args[1])
×
3147
                }
×
3148
                switch args[1][0] {
3✔
3149
                case '+':
2✔
3150
                        c.pa.reply = args[2]
2✔
3151
                case '|':
1✔
3152
                        c.pa.reply = nil
1✔
3153
                default:
×
3154
                        return fmt.Errorf("processLeafHeaderMsgArgs Bad or Missing Reply Indicator: '%s'", args[1])
×
3155
                }
3156
                // Grab header size.
3157
                c.pa.hdb = args[len(args)-2]
3✔
3158
                c.pa.hdr = parseSize(c.pa.hdb)
3✔
3159

3✔
3160
                // Grab size.
3✔
3161
                c.pa.szb = args[len(args)-1]
3✔
3162
                c.pa.size = parseSize(c.pa.szb)
3✔
3163

3✔
3164
                // Grab queue names.
3✔
3165
                if c.pa.reply != nil {
5✔
3166
                        c.pa.queues = args[3 : len(args)-2]
2✔
3167
                } else {
3✔
3168
                        c.pa.queues = args[2 : len(args)-2]
1✔
3169
                }
1✔
3170
        }
3171
        if c.pa.hdr < 0 {
228✔
3172
                return fmt.Errorf("processLeafHeaderMsgArgs Bad or Missing Header Size: '%s'", arg)
×
3173
        }
×
3174
        if c.pa.size < 0 {
228✔
3175
                return fmt.Errorf("processLeafHeaderMsgArgs Bad or Missing Size: '%s'", args)
×
3176
        }
×
3177
        if c.pa.hdr > c.pa.size {
228✔
3178
                return fmt.Errorf("processLeafHeaderMsgArgs Header Size larger then TotalSize: '%s'", arg)
×
3179
        }
×
3180
        maxPayload := atomic.LoadInt32(&c.mpay)
228✔
3181
        if maxPayload != jwt.NoLimit && int64(c.pa.size) > int64(maxPayload) {
228✔
3182
                c.maxPayloadViolation(c.pa.size, maxPayload)
×
3183
                return ErrMaxPayload
×
3184
        }
×
3185

3186
        // Common ones processed after check for arg length
3187
        c.pa.subject = args[0]
228✔
3188

228✔
3189
        return nil
228✔
3190
}
3191

3192
func (c *client) processLeafMsgArgs(arg []byte) error {
63,145✔
3193
        // Unroll splitArgs to avoid runtime/heap issues
63,145✔
3194
        args := c.argsa[:0]
63,145✔
3195
        start := -1
63,145✔
3196
        for i, b := range arg {
2,224,215✔
3197
                switch b {
2,161,070✔
3198
                case ' ', '\t', '\r', '\n':
100,040✔
3199
                        if start >= 0 {
200,080✔
3200
                                args = append(args, arg[start:i])
100,040✔
3201
                                start = -1
100,040✔
3202
                        }
100,040✔
3203
                default:
2,061,030✔
3204
                        if start < 0 {
2,224,215✔
3205
                                start = i
163,185✔
3206
                        }
163,185✔
3207
                }
3208
        }
3209
        if start >= 0 {
126,290✔
3210
                args = append(args, arg[start:])
63,145✔
3211
        }
63,145✔
3212

3213
        c.pa.arg = arg
63,145✔
3214
        switch len(args) {
63,145✔
3215
        case 0, 1:
×
3216
                return fmt.Errorf("processLeafMsgArgs Parse Error: '%s'", args)
×
3217
        case 2:
41,613✔
3218
                c.pa.reply = nil
41,613✔
3219
                c.pa.queues = nil
41,613✔
3220
                c.pa.szb = args[1]
41,613✔
3221
                c.pa.size = parseSize(args[1])
41,613✔
3222
        case 3:
6,328✔
3223
                c.pa.reply = args[1]
6,328✔
3224
                c.pa.queues = nil
6,328✔
3225
                c.pa.szb = args[2]
6,328✔
3226
                c.pa.size = parseSize(args[2])
6,328✔
3227
        default:
15,204✔
3228
                // args[1] is our reply indicator. Should be + or | normally.
15,204✔
3229
                if len(args[1]) != 1 {
15,204✔
3230
                        return fmt.Errorf("processLeafMsgArgs Bad or Missing Reply Indicator: '%s'", args[1])
×
3231
                }
×
3232
                switch args[1][0] {
15,204✔
3233
                case '+':
159✔
3234
                        c.pa.reply = args[2]
159✔
3235
                case '|':
15,045✔
3236
                        c.pa.reply = nil
15,045✔
3237
                default:
×
3238
                        return fmt.Errorf("processLeafMsgArgs Bad or Missing Reply Indicator: '%s'", args[1])
×
3239
                }
3240
                // Grab size.
3241
                c.pa.szb = args[len(args)-1]
15,204✔
3242
                c.pa.size = parseSize(c.pa.szb)
15,204✔
3243

15,204✔
3244
                // Grab queue names.
15,204✔
3245
                if c.pa.reply != nil {
15,363✔
3246
                        c.pa.queues = args[3 : len(args)-1]
159✔
3247
                } else {
15,204✔
3248
                        c.pa.queues = args[2 : len(args)-1]
15,045✔
3249
                }
15,045✔
3250
        }
3251
        if c.pa.size < 0 {
63,145✔
3252
                return fmt.Errorf("processLeafMsgArgs Bad or Missing Size: '%s'", args)
×
3253
        }
×
3254
        maxPayload := atomic.LoadInt32(&c.mpay)
63,145✔
3255
        if maxPayload != jwt.NoLimit && int64(c.pa.size) > int64(maxPayload) {
63,145✔
3256
                c.maxPayloadViolation(c.pa.size, maxPayload)
×
3257
                return ErrMaxPayload
×
3258
        }
×
3259

3260
        // Common ones processed after check for arg length
3261
        c.pa.subject = args[0]
63,145✔
3262

63,145✔
3263
        return nil
63,145✔
3264
}
3265

3266
// processInboundLeafMsg is called to process an inbound msg from a leaf node.
3267
func (c *client) processInboundLeafMsg(msg []byte) {
62,117✔
3268
        // Update statistics
62,117✔
3269
        // The msg includes the CR_LF, so pull back out for accounting.
62,117✔
3270
        c.in.msgs++
62,117✔
3271
        c.in.bytes += int32(len(msg) - LEN_CR_LF)
62,117✔
3272

62,117✔
3273
        srv, acc, subject := c.srv, c.acc, string(c.pa.subject)
62,117✔
3274

62,117✔
3275
        // Mostly under testing scenarios.
62,117✔
3276
        if srv == nil || acc == nil {
62,117✔
3277
                return
×
3278
        }
×
3279

3280
        // Check that leaf messages respect the subject permissions.
3281
        if c.perms != nil && !c.leafMsgAllowed() {
62,117✔
3282
                c.leafPubPermViolation(c.pa.subject)
×
3283
                return
×
3284
        }
×
3285

3286
        // Match the subscriptions. We will use our own L1 map if
3287
        // it's still valid, avoiding contention on the shared sublist.
3288
        var r *SublistResult
62,117✔
3289
        var ok bool
62,117✔
3290

62,117✔
3291
        genid := atomic.LoadUint64(&c.acc.sl.genid)
62,117✔
3292
        if genid == c.in.genid && c.in.results != nil {
122,338✔
3293
                r, ok = c.in.results[subject]
60,221✔
3294
        } else {
62,117✔
3295
                // Reset our L1 completely.
1,896✔
3296
                c.in.results = make(map[string]*SublistResult)
1,896✔
3297
                c.in.genid = genid
1,896✔
3298
        }
1,896✔
3299

3300
        // Go back to the sublist data structure.
3301
        if !ok {
101,709✔
3302
                r = c.acc.sl.Match(subject)
39,592✔
3303
                // Prune the results cache. Keeps us from unbounded growth. Random delete.
39,592✔
3304
                if len(c.in.results) >= maxResultCacheSize {
40,642✔
3305
                        n := 0
1,050✔
3306
                        for subj := range c.in.results {
35,700✔
3307
                                delete(c.in.results, subj)
34,650✔
3308
                                if n++; n > pruneSize {
35,700✔
3309
                                        break
1,050✔
3310
                                }
3311
                        }
3312
                }
3313
                // Then add the new cache entry.
3314
                c.in.results[subject] = r
39,592✔
3315
        }
3316

3317
        // Collect queue names if needed.
3318
        var qnames [][]byte
62,117✔
3319

62,117✔
3320
        // Check for no interest, short circuit if so.
62,117✔
3321
        // This is the fanout scale.
62,117✔
3322
        if len(r.psubs)+len(r.qsubs) > 0 {
123,912✔
3323
                flag := pmrNoFlag
61,795✔
3324
                // If we have queue subs in this cluster, then if we run in gateway
61,795✔
3325
                // mode and the remote gateways have queue subs, then we need to
61,795✔
3326
                // collect the queue groups this message was sent to so that we
61,795✔
3327
                // exclude them when sending to gateways.
61,795✔
3328
                if len(r.qsubs) > 0 && c.srv.gateway.enabled &&
61,795✔
3329
                        atomic.LoadInt64(&c.srv.gateway.totalQSubs) > 0 {
66,995✔
3330
                        flag |= pmrCollectQueueNames
5,200✔
3331
                }
5,200✔
3332
                // If this is a mapped subject that means the mapped interest
3333
                // is what got us here, but this might not have a queue designation
3334
                // If that is the case, make sure we ignore to process local queue subscribers.
3335
                if len(c.pa.mapped) > 0 && len(c.pa.queues) == 0 {
62,039✔
3336
                        flag |= pmrIgnoreEmptyQueueFilter
244✔
3337
                }
244✔
3338
                _, qnames = c.processMsgResults(acc, r, msg, nil, c.pa.subject, c.pa.reply, flag)
61,795✔
3339
        }
3340

3341
        // Now deal with gateways
3342
        if c.srv.gateway.enabled {
68,128✔
3343
                c.sendMsgToGateways(acc, msg, c.pa.subject, c.pa.reply, qnames, true)
6,011✔
3344
        }
6,011✔
3345
}
3346

3347
// Checks whether the inbound leaf message is allowed by the
3348
// connection's permissions. On the hub side this enforces what
3349
// the remote leaf may publish. On the spoke side this enforces
3350
// import restrictions such as deny_imports.
3351
func (c *client) leafMsgAllowed() bool {
58,337✔
3352
        wireSubject := c.pa.subject
58,337✔
3353
        if len(c.pa.mapped) > 0 {
58,581✔
3354
                // Mappings rewrite c.pa.subject to the internal
244✔
3355
                // destination. For leaf ACLs, need to check
244✔
3356
                // the original wire subject from the remote side.
244✔
3357
                wireSubject = c.pa.mapped
244✔
3358
        }
244✔
3359
        // Strip any gateway routing prefix for the permission check.
3360
        subjectToCheck, isGW := getGWRoutedSubjectOrSelf(wireSubject)
58,337✔
3361

58,337✔
3362
        // Service-import replies (_R_), JS ack subjects ($JS.ACK.)
58,337✔
3363
        // are internal routing subjects forwarded via LS+ without
58,337✔
3364
        // permission checks.
58,337✔
3365
        if isServiceReply(subjectToCheck) || isJSAckSubject(subjectToCheck) {
58,366✔
3366
                return true
29✔
3367
        }
29✔
3368

3369
        c.mu.RLock()
58,308✔
3370
        if c.isSpokeLeafNode() {
87,016✔
3371
                // Gateway routed replies are forwarded without
28,708✔
3372
                // permission checks.
28,708✔
3373
                if isGW || c.leafReceiveAllowed(subjectToCheck) {
57,416✔
3374
                        c.mu.RUnlock()
28,708✔
3375
                        return true
28,708✔
3376
                }
28,708✔
3377
        } else if c.leafSendAllowed(subjectToCheck) {
59,200✔
3378
                c.mu.RUnlock()
29,600✔
3379
                return true
29,600✔
3380
        }
29,600✔
3381

3382
        // If allow_responses is not configured, or there is no tracked reply for
3383
        // this subject, the answer is "denied" and we can return it while still
3384
        // holding only the read lock.
3385
        replySubject := bytesToString(wireSubject)
×
3386
        if c.perms == nil || c.perms.resp == nil || c.replies[replySubject] == nil {
×
3387
                c.mu.RUnlock()
×
3388
                return false
×
3389
        }
×
3390
        c.mu.RUnlock()
×
3391

×
3392
        // Check tracked reply permissions (allow_responses).
×
3393
        // Use the pre-strip subject since deliverMsg tracks
×
3394
        // replies under the original form, which includes
×
3395
        // the GW routing prefix for routed requests.
×
3396
        c.mu.Lock()
×
3397
        defer c.mu.Unlock()
×
3398
        return c.responseAllowed(replySubject)
×
3399
}
3400

3401
// Returns true if the leaf side ACLs allow importing this subject,
3402
// based on the permissions received over INFO and any local deny_imports.
3403
// At least a read lock must be held.
3404
func (c *client) leafReceiveAllowed(subject []byte) bool {
28,708✔
3405
        return c.canSubscribeInternal(bytesToString(subject))
28,708✔
3406
}
28,708✔
3407

3408
// Returns true if the hub side ACLs allow the remote leaf to send
3409
// this subject.
3410
// At least a read lock must be held.
3411
func (c *client) leafSendAllowed(bsubject []byte) bool {
29,600✔
3412
        // Use the original export ACL captured for this accepted leaf.
29,600✔
3413
        // The live perms also contain additional JetStream denies used by
29,600✔
3414
        // the normal forwarding path, and applying them here would reject
29,600✔
3415
        // legitimate inbound JS API requests.
29,600✔
3416
        subject := bytesToString(bsubject)
29,600✔
3417
        perms := c.opts.Export
29,600✔
3418
        if perms == nil || (perms.Allow == nil && perms.Deny == nil) {
59,197✔
3419
                return true
29,597✔
3420
        }
29,597✔
3421

3422
        allowed := true
3✔
3423
        if perms.Allow != nil && !strings.HasPrefix(subject, mqttPrefix) {
5✔
3424
                allowed = false
2✔
3425
                for _, allowSubj := range perms.Allow {
5✔
3426
                        if matchLiteral(subject, allowSubj) {
5✔
3427
                                allowed = true
2✔
3428
                                break
2✔
3429
                        }
3430
                }
3431
        }
3432

3433
        if allowed && len(perms.Deny) > 0 {
4✔
3434
                for _, denySubj := range perms.Deny {
2✔
3435
                        if matchLiteral(subject, denySubj) {
1✔
3436
                                allowed = false
×
3437
                                break
×
3438
                        }
3439
                }
3440
        }
3441
        return allowed
3✔
3442
}
3443

3444
// Handles a subscription permission violation.
3445
// See leafPermViolation() for details.
3446
func (c *client) leafSubPermViolation(subj []byte) {
13✔
3447
        c.leafPermViolation(false, subj)
13✔
3448
}
13✔
3449

3450
// Handles a publish permission violation.
3451
// See leafPermViolation() for details.
3452
func (c *client) leafPubPermViolation(subj []byte) {
×
3453
        c.leafPermViolation(true, subj)
×
3454
}
×
3455

3456
// Common function to process publish or subscribe leafnode permission violation.
3457
// Sends the permission violation error to the remote, logs it and closes the connection.
3458
// If this is from a server soliciting, the reconnection will be delayed.
3459
func (c *client) leafPermViolation(pub bool, subj []byte) {
13✔
3460
        if c.isSpokeLeafNode() {
26✔
3461
                // For spokes these are no-ops since the hub server told us our permissions.
13✔
3462
                // We just need to not send these over to the other side since we will get cutoff.
13✔
3463
                return
13✔
3464
        }
13✔
3465
        // FIXME(dlc) ?
3466
        c.setLeafConnectDelayIfSoliciting(leafNodeReconnectAfterPermViolation)
×
3467
        var action string
×
3468
        if pub {
×
3469
                c.sendErr(fmt.Sprintf("Permissions Violation for Publish to %q", subj))
×
3470
                action = "Publish"
×
3471
        } else {
×
3472
                c.sendErr(fmt.Sprintf("Permissions Violation for Subscription to %q", subj))
×
3473
                action = "Subscription"
×
3474
        }
×
3475
        c.Errorf("%s Violation on %q - Check other side configuration", action, subj)
×
3476
        // TODO: add a new close reason that is more appropriate?
×
3477
        c.closeConnection(ProtocolViolation)
×
3478
}
3479

3480
// Invoked from generic processErr() for LEAF connections.
3481
func (c *client) leafProcessErr(errStr string) {
45✔
3482
        // Check if we got a cluster name collision.
45✔
3483
        if strings.Contains(errStr, ErrLeafNodeHasSameClusterName.Error()) {
48✔
3484
                _, delay := c.setLeafConnectDelayIfSoliciting(leafNodeReconnectDelayAfterClusterNameSame)
3✔
3485
                c.Errorf("Leafnode connection dropped with same cluster name error. Delaying attempt to reconnect for %v", delay)
3✔
3486
                return
3✔
3487
        }
3✔
3488
        if strings.Contains(errStr, ErrLeafNodeMinVersionRejected.Error()) {
43✔
3489
                _, delay := c.setLeafConnectDelayIfSoliciting(leafNodeMinVersionReconnectDelay)
1✔
3490
                c.Errorf("Leafnode connection dropped due to minimum version requirement. Delaying attempt to reconnect for %v", delay)
1✔
3491
                return
1✔
3492
        }
1✔
3493

3494
        // We will look for Loop detected error coming from the other side.
3495
        // If we solicit, set the connect delay.
3496
        if !strings.Contains(errStr, "Loop detected") {
76✔
3497
                return
35✔
3498
        }
35✔
3499
        c.handleLeafNodeLoop(false)
6✔
3500
}
3501

3502
// If this leaf connection solicits, sets the connect delay to the given value,
3503
// or the one from the server option's LeafNode.connDelay if one is set (for tests).
3504
// Returns the connection's account name and delay.
3505
func (c *client) setLeafConnectDelayIfSoliciting(delay time.Duration) (string, time.Duration) {
16✔
3506
        c.mu.Lock()
16✔
3507
        if c.isSolicitedLeafNode() {
26✔
3508
                if s := c.srv; s != nil {
20✔
3509
                        if srvdelay := s.getOpts().LeafNode.connDelay; srvdelay != 0 {
14✔
3510
                                delay = srvdelay
4✔
3511
                        }
4✔
3512
                }
3513
                c.leaf.remote.setConnectDelay(delay)
10✔
3514
        }
3515
        var accName string
16✔
3516
        if c.acc != nil {
32✔
3517
                accName = c.acc.Name
16✔
3518
        }
16✔
3519
        c.mu.Unlock()
16✔
3520
        return accName, delay
16✔
3521
}
3522

3523
// For the given remote Leafnode configuration, this function returns
3524
// if TLS is required, and if so, will return a clone of the TLS Config
3525
// (since some fields will be changed during handshake), the TLS server
3526
// name that is remembered, and the TLS timeout.
3527
func (c *client) leafNodeGetTLSConfigForSolicit(remote *leafNodeCfg) (bool, *tls.Config, string, float64) {
1,445✔
3528
        var (
1,445✔
3529
                tlsConfig  *tls.Config
1,445✔
3530
                tlsName    string
1,445✔
3531
                tlsTimeout float64
1,445✔
3532
        )
1,445✔
3533

1,445✔
3534
        remote.RLock()
1,445✔
3535
        defer remote.RUnlock()
1,445✔
3536

1,445✔
3537
        tlsRequired := remote.TLS || remote.TLSConfig != nil
1,445✔
3538
        if tlsRequired {
1,518✔
3539
                if remote.TLSConfig != nil {
118✔
3540
                        tlsConfig = remote.TLSConfig.Clone()
45✔
3541
                } else {
73✔
3542
                        tlsConfig = &tls.Config{MinVersion: tls.VersionTLS12}
28✔
3543
                }
28✔
3544
                tlsName = remote.tlsName
73✔
3545
                tlsTimeout = remote.TLSTimeout
73✔
3546
                if tlsTimeout == 0 {
118✔
3547
                        tlsTimeout = float64(TLS_TIMEOUT / time.Second)
45✔
3548
                }
45✔
3549
        }
3550

3551
        return tlsRequired, tlsConfig, tlsName, tlsTimeout
1,445✔
3552
}
3553

3554
// Initiates the LeafNode Websocket connection by:
3555
// - doing the TLS handshake if needed
3556
// - sending the HTTP request
3557
// - waiting for the HTTP response
3558
//
3559
// Since some bufio reader is used to consume the HTTP response, this function
3560
// returns the slice of buffered bytes (if any) so that the readLoop that will
3561
// be started after that consume those first before reading from the socket.
3562
// The boolean
3563
//
3564
// Lock held on entry.
3565
func (c *client) leafNodeSolicitWSConnection(opts *Options, rURL *url.URL, remote *leafNodeCfg) ([]byte, ClosedState, error) {
46✔
3566
        remote.RLock()
46✔
3567
        compress := remote.Websocket.Compression
46✔
3568
        // By default the server will mask outbound frames, but it can be disabled with this option.
46✔
3569
        noMasking := remote.Websocket.NoMasking
46✔
3570
        infoTimeout := remote.FirstInfoTimeout
46✔
3571
        remote.RUnlock()
46✔
3572
        // Will do the client-side TLS handshake if needed.
46✔
3573
        tlsRequired, err := c.leafClientHandshakeIfNeeded(remote, opts)
46✔
3574
        if err != nil {
50✔
3575
                // 0 will indicate that the connection was already closed
4✔
3576
                return nil, 0, err
4✔
3577
        }
4✔
3578

3579
        // For http request, we need the passed URL to contain either http or https scheme.
3580
        scheme := "http"
42✔
3581
        if tlsRequired {
50✔
3582
                scheme = "https"
8✔
3583
        }
8✔
3584
        // We will use the `/leafnode` path to tell the accepting WS server that it should
3585
        // create a LEAF connection, not a CLIENT.
3586
        // In case we use the user's URL path in the future, make sure we append the user's
3587
        // path to our `/leafnode` path.
3588
        lpath := leafNodeWSPath
42✔
3589
        if curPath := rURL.EscapedPath(); curPath != _EMPTY_ {
63✔
3590
                if curPath[0] == '/' {
42✔
3591
                        curPath = curPath[1:]
21✔
3592
                }
21✔
3593
                lpath = path.Join(curPath, lpath)
21✔
3594
        } else {
21✔
3595
                lpath = lpath[1:]
21✔
3596
        }
21✔
3597
        ustr := fmt.Sprintf("%s://%s/%s", scheme, rURL.Host, lpath)
42✔
3598
        u, _ := url.Parse(ustr)
42✔
3599
        req := &http.Request{
42✔
3600
                Method:     "GET",
42✔
3601
                URL:        u,
42✔
3602
                Proto:      "HTTP/1.1",
42✔
3603
                ProtoMajor: 1,
42✔
3604
                ProtoMinor: 1,
42✔
3605
                Header:     make(http.Header),
42✔
3606
                Host:       u.Host,
42✔
3607
        }
42✔
3608
        wsKey, err := wsMakeChallengeKey()
42✔
3609
        if err != nil {
42✔
3610
                return nil, WriteError, err
×
3611
        }
×
3612

3613
        req.Header["Upgrade"] = []string{"websocket"}
42✔
3614
        req.Header["Connection"] = []string{"Upgrade"}
42✔
3615
        req.Header["Sec-WebSocket-Key"] = []string{wsKey}
42✔
3616
        req.Header["Sec-WebSocket-Version"] = []string{"13"}
42✔
3617
        if compress {
50✔
3618
                req.Header.Add("Sec-WebSocket-Extensions", wsPMCReqHeaderValue)
8✔
3619
        }
8✔
3620
        if noMasking {
51✔
3621
                req.Header.Add(wsNoMaskingHeader, wsNoMaskingValue)
9✔
3622
        }
9✔
3623
        c.nc.SetDeadline(time.Now().Add(infoTimeout))
42✔
3624
        if err := req.Write(c.nc); err != nil {
42✔
3625
                return nil, WriteError, err
×
3626
        }
×
3627

3628
        var resp *http.Response
42✔
3629

42✔
3630
        br := bufio.NewReaderSize(c.nc, MAX_CONTROL_LINE_SIZE)
42✔
3631
        resp, err = http.ReadResponse(br, req)
42✔
3632
        if err == nil &&
42✔
3633
                (resp.StatusCode != 101 ||
42✔
3634
                        !strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") ||
42✔
3635
                        !strings.EqualFold(resp.Header.Get("Connection"), "upgrade") ||
42✔
3636
                        resp.Header.Get("Sec-Websocket-Accept") != wsAcceptKey(wsKey)) {
43✔
3637

1✔
3638
                err = fmt.Errorf("invalid websocket connection")
1✔
3639
        }
1✔
3640
        // Check compression extension...
3641
        if err == nil && c.ws.compress {
50✔
3642
                // Check that not only permessage-deflate extension is present, but that
8✔
3643
                // we also have server and client no context take over.
8✔
3644
                srvCompress, noCtxTakeover := wsPMCExtensionSupport(resp.Header, false)
8✔
3645

8✔
3646
                // If server does not support compression, then simply disable it in our side.
8✔
3647
                if !srvCompress {
12✔
3648
                        c.ws.compress = false
4✔
3649
                } else if !noCtxTakeover {
8✔
3650
                        err = fmt.Errorf("compression negotiation error")
×
3651
                }
×
3652
        }
3653
        // Same for no masking...
3654
        if err == nil && noMasking {
51✔
3655
                // Check if server accepts no masking
9✔
3656
                if resp.Header.Get(wsNoMaskingHeader) != wsNoMaskingValue {
10✔
3657
                        // Nope, need to mask our writes as any client would do.
1✔
3658
                        c.ws.maskwrite = true
1✔
3659
                }
1✔
3660
        }
3661
        if resp != nil {
69✔
3662
                resp.Body.Close()
27✔
3663
        }
27✔
3664
        if err != nil {
58✔
3665
                return nil, ReadError, err
16✔
3666
        }
16✔
3667
        c.Debugf("Leafnode compression=%v masking=%v", c.ws.compress, c.ws.maskwrite)
26✔
3668

26✔
3669
        var preBuf []byte
26✔
3670
        // We have to slurp whatever is in the bufio reader and pass that to the readloop.
26✔
3671
        if n := br.Buffered(); n != 0 {
28✔
3672
                preBuf, _ = br.Peek(n)
2✔
3673
        }
2✔
3674
        return preBuf, 0, nil
26✔
3675
}
3676

3677
const connectProcessTimeout = 2 * time.Second
3678

3679
// This is invoked for remote LEAF remote connections after processing the INFO
3680
// protocol.
3681
func (s *Server) leafNodeResumeConnectProcess(c *client) {
508✔
3682
        clusterName := s.ClusterName()
508✔
3683

508✔
3684
        c.mu.Lock()
508✔
3685
        if c.isClosed() {
508✔
3686
                c.mu.Unlock()
×
3687
                return
×
3688
        }
×
3689
        if err := c.sendLeafConnect(clusterName, c.headers); err != nil {
510✔
3690
                c.mu.Unlock()
2✔
3691
                c.closeConnection(WriteError)
2✔
3692
                return
2✔
3693
        }
2✔
3694

3695
        // Spin up the write loop.
3696
        s.startGoRoutine(func() { c.writeLoop() })
1,012✔
3697

3698
        // timeout leafNodeFinishConnectProcess
3699
        c.ping.tmr = time.AfterFunc(connectProcessTimeout, func() {
506✔
3700
                c.mu.Lock()
×
3701
                // check if leafNodeFinishConnectProcess was called and prevent later leafNodeFinishConnectProcess
×
3702
                if !c.flags.setIfNotSet(connectProcessFinished) {
×
3703
                        c.mu.Unlock()
×
3704
                        return
×
3705
                }
×
3706
                clearTimer(&c.ping.tmr)
×
3707
                closed := c.isClosed()
×
3708
                c.mu.Unlock()
×
3709
                if !closed {
×
3710
                        c.sendErrAndDebug("Stale Leaf Node Connection - Closing")
×
3711
                        c.closeConnection(StaleConnection)
×
3712
                }
×
3713
        })
3714
        c.mu.Unlock()
506✔
3715
        c.Debugf("Remote leafnode connect msg sent")
506✔
3716
}
3717

3718
// This is invoked for remote LEAF connections after processing the INFO
3719
// protocol and leafNodeResumeConnectProcess.
3720
// This will send LS+ the CONNECT protocol and register the leaf node.
3721
func (s *Server) leafNodeFinishConnectProcess(c *client) {
471✔
3722
        c.mu.Lock()
471✔
3723
        if !c.flags.setIfNotSet(connectProcessFinished) {
471✔
3724
                c.mu.Unlock()
×
3725
                return
×
3726
        }
×
3727
        if c.isClosed() {
471✔
3728
                c.mu.Unlock()
×
3729
                s.removeLeafNodeConnection(c)
×
3730
                return
×
3731
        }
×
3732
        remote := c.leaf.remote
471✔
3733
        if remote == nil || c.acc == nil {
471✔
3734
                c.mu.Unlock()
×
3735
                c.sendErr("Authorization Violation")
×
3736
                c.closeConnection(ProtocolViolation)
×
3737
                return
×
3738
        }
×
3739
        // Check if we will need to send the system connect event.
3740
        remote.RLock()
471✔
3741
        sendSysConnectEvent := remote.Hub
471✔
3742
        remote.RUnlock()
471✔
3743

471✔
3744
        // Capture account before releasing lock
471✔
3745
        acc := c.acc
471✔
3746
        // cancel connectProcessTimeout
471✔
3747
        clearTimer(&c.ping.tmr)
471✔
3748
        c.mu.Unlock()
471✔
3749

471✔
3750
        // Make sure we register with the account here.
471✔
3751
        if err := c.registerWithAccount(acc); err != nil {
473✔
3752
                if err == ErrTooManyAccountConnections {
2✔
3753
                        c.maxAccountConnExceeded()
×
3754
                        return
×
3755
                } else if err == ErrLeafNodeLoop {
4✔
3756
                        c.handleLeafNodeLoop(true)
2✔
3757
                        return
2✔
3758
                }
2✔
3759
                c.Errorf("Registering leaf with account %s resulted in error: %v", acc.Name, err)
×
3760
                c.closeConnection(ProtocolViolation)
×
3761
                return
×
3762
        }
3763
        if !s.addLeafNodeConnection(c, _EMPTY_, _EMPTY_, false) {
469✔
3764
                // Was not added, could be because the remote configuration has been removed.
×
3765
                c.closeConnection(ClientClosed)
×
3766
                return
×
3767
        }
×
3768
        s.initLeafNodeSmapAndSendSubs(c)
469✔
3769
        if sendSysConnectEvent {
481✔
3770
                s.sendLeafNodeConnect(acc)
12✔
3771
        }
12✔
3772
        s.accountConnectEvent(c)
469✔
3773

469✔
3774
        // The above functions are not running under the client lock, so it is
469✔
3775
        // possible that between the time we have started the read/write loops
469✔
3776
        // and now, that the connection was closed. This would leave the closed
469✔
3777
        // LN connection possibly registered with the account and/or the server's
469✔
3778
        // leafs map. So check if connection is closed, and if so, manually cleanup.
469✔
3779
        c.mu.Lock()
469✔
3780
        closed := c.isClosed()
469✔
3781
        if !closed {
938✔
3782
                c.setFirstPingTimer()
469✔
3783
        }
469✔
3784
        c.mu.Unlock()
469✔
3785
        if closed {
469✔
3786
                s.removeLeafNodeConnection(c)
×
3787
                if prev := acc.removeClient(c); prev == 1 {
×
3788
                        s.decActiveAccounts()
×
3789
                }
×
3790
        }
3791
}
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