ALL

For reading and verification only. You may not copy, use, modify or redistribute this code. Full notice on the main page.

tunnel/server.go

SHA-256: 1ad8c7feb651d90f1914641e55751deae687bd5dae5e5ba232ac6c8282f2c674
1894 lines · 67625 bytes · View on GitHub

1package main
2
3import (
4 "bufio"
5 "context"
6 "crypto/rand"
7 "crypto/subtle"
8 "crypto/tls"
9 "encoding/hex"
10 "errors"
11 "fmt"
12 "hash/maphash"
13 "io"
14 "log/slog"
15 "net"
16 "net/netip"
17 "os"
18 "strings"
19 "sync"
20 "sync/atomic"
21 "time"
22)
23
24const (
25 // TLS alert descriptions, sent (in the clear, as a fatal alert record) to a client we decide not to serve.
26 alertInternalError = 80
27 alertUnrecognizedName = 112
28)
29
30var (
31 errOffline = errors.New("device_offline")
32 errAttachTimeout = errors.New("attach_timeout")
33 errControlWrite = errors.New("control_write_failed")
34 errShuttingDown = errors.New("shutting_down")
35 errByteCap = errors.New("stream byte cap reached")
36 errHostTaken = errors.New("host_taken")
37 errGatedBytes = errors.New("gated byte budget spent")
38)
39
40// pendingStream is an OPEN waiting for its ATTACH: the secret is one use, and only the device that was sent the OPEN knows it.
41type pendingStream struct {
42 cc *ctrl
43 secret string
44 ch chan net.Conn // buffered(1): the ATTACHed data connection
45}
46
47// Server is the tunnel node. It never terminates a client's TLS: the only TLS it speaks is the one it holds for its own name, with devices.
48type Server struct {
49 cfg Config
50 log *slog.Logger
51 tlsCfg *tls.Config
52 routes *routeTable
53 replay *replayCache
54
55 nodeURL string // the htu of a proof: this node's URL
56
57 clk clock
58 health *cpHealth
59
60 ctx context.Context
61 cancel context.CancelFunc
62 bg sync.WaitGroup // the feed loop, the background tasks and the refreshes of connections: ended by cancel
63
64 mu sync.Mutex
65 pending map[string]*pendingStream
66 conns map[*ctrl]struct{} // every live control connection, for shutdown
67
68 streams atomic.Int64 // client connections being served, global
69 anonStreams atomic.Int64 // of them, the ones whose source is neither openai nor marked good: they may not use the share reserved for the others
70 unauth atomic.Int64 // connections that have not finished their first message
71 anonUnauth atomic.Int64 // of them, the ones whose source is neither ChatGPT's, nor a device's, nor marked good: they may not use the share that is kept for the others
72 authQ atomic.Int64 // exchanges that wait for, or are in, a call to authorize
73 connSeq atomic.Int64
74
75 authSem chan struct{} // the calls to authorize in flight: at most cfg.AuthorizeMax
76
77 watchMu sync.Mutex
78 watches map[*feedWatch]struct{} // the exchanges waiting for authorize, each told of the feed events applied meanwhile
79
80 killed atomic.Bool // the global kill switch is on: no device is served until it is cleared
81 draining atomic.Bool // SIGTERM: no new client and no new device connection, the open streams finish
82 drainFor atomic.Int64 // the quiet period this drain was started with, in nanoseconds (what a late device is told)
83
84 // hardening (limits.go, hints.go, gated.go, openai_prefixes.go, metrics.go, observer.go)
85 ips *ipLimits
86 hints *hintTable
87 openai *openaiPrefixes
88 gates *gateTable
89 met *metrics
90 sampler *logSampler
91 seed maphash.Seed // per boot: what Server.short hashes with, so two log lines of one source can be told apart and nothing can be read back
92 startedAt time.Time
93
94 httpsLn, httpLn net.Listener
95 metricsSrv *metricsServer
96 wg sync.WaitGroup
97 bufPool sync.Pool // the 32 KiB buffers a pump takes for bulk reads
98 smallBufPool sync.Pool // the 4 KiB buffers a pump waits in a read with
99 bigBuffersInUse atomic.Int64 // 32 KiB buffers held by a pump now (an idle stream holds none: tests and the memory check read it)
100}
101
102const (
103 pumpSmallBuf = 4 << 10
104 pumpBigBuf = 32 << 10
105)
106
107// NewServer validates cfg and builds the node. It does not listen yet.
108func NewServer(cfg Config) (*Server, error) {
109 if cfg.Logger == nil {
110 cfg.Logger = slog.New(slog.NewJSONHandler(io.Discard, nil))
111 }
112 if cfg.Now == nil {
113 cfg.Now = time.Now
114 }
115 if cfg.Health == nil {
116 cfg.Health = &cpHealth{}
117 }
118 if err := cfg.validate(); err != nil {
119 return nil, err
120 }
121 ctx, cancel := context.WithCancel(context.Background())
122 clk := newClock(cfg.Now, cfg.AfterFunc)
123 s := &Server{
124 cfg: cfg,
125 log: cfg.Logger,
126 routes: newRouteTable(),
127 replay: newReplayCache(replayLimit),
128 nodeURL: nodeProofURL(cfg.NodeHost, cfg.PublicPort),
129 clk: clk,
130 health: cfg.Health,
131 ctx: ctx,
132 cancel: cancel,
133 pending: map[string]*pendingStream{},
134 conns: map[*ctrl]struct{}{},
135 watches: map[*feedWatch]struct{}{},
136 authSem: make(chan struct{}, cfg.AuthorizeMax),
137 seed: maphash.MakeSeed(),
138 met: newMetrics(),
139 sampler: newLogSampler(clk),
140 hints: newHintTable(clk, cfg.HintTTL, cfg.HintsPerLabel),
141 gates: newGateTable(clk),
142 startedAt: clk.Now(),
143 }
144 s.ips = newIPLimits(&cfg, clk, s.seed)
145 s.openai = newOpenAIPrefixes(&cfg, clk, s.met.prefixRefresh.inc)
146 s.routes.lim = labelLimitsOf(&cfg)
147 if cfg.HTTPSAddr != "" {
148 s.tlsCfg = &tls.Config{
149 GetCertificate: cfg.CertSource,
150 MinVersion: tls.VersionTLS13,
151 NextProtos: []string{policyData.Protocol.ALPN},
152 }
153 }
154 s.bufPool.New = func() any { b := make([]byte, pumpBigBuf); return &b }
155 s.smallBufPool.New = func() any { b := make([]byte, pumpSmallBuf); return &b }
156 return s, nil
157}
158
159// replayLimit bounds the proofs remembered at once (a proof lives for the policy's proof TTL, so this is the number of proofs per minute the node
160// admits before it refuses more).
161const replayLimit = 1 << 18
162
163func (s *Server) now() time.Time { return s.clk.Now() }
164
165func addrOf(ln net.Listener) string {
166 if ln == nil {
167 return ""
168 }
169 return ln.Addr().String()
170}
171
172func (s *Server) HTTPSAddr() string { return addrOf(s.httpsLn) }
173func (s *Server) HTTPAddr() string { return addrOf(s.httpLn) }
174
175// Online reports whether the host has a live control connection that opens streams.
176func (s *Server) Online(host string) bool {
177 cc, _ := s.routes.lookup(host)
178 return cc != nil && cc.canOpen(s.now())
179}
180
181// Start opens the two listeners (443 and 80: there is no other) and serves until Shutdown.
182func (s *Server) Start() error {
183 // Every socket the node accepts gets TCP keepalive from the policy: a stream whose peer vanished (a computer that slept, a network that changed) ends
184 // by itself instead of holding its slot until the idle timeout.
185 lc := s.listenConfig()
186 listen := func(addr string) (net.Listener, error) {
187 if addr == "" {
188 return nil, nil
189 }
190 return lc.Listen(context.Background(), "tcp", addr)
191 }
192 var err error
193 if s.httpsLn, err = listen(s.cfg.HTTPSAddr); err != nil {
194 return fmt.Errorf("https listener: %w", err)
195 }
196 if s.httpLn, err = listen(s.cfg.HTTPAddr); err != nil {
197 _ = s.closeListeners()
198 return fmt.Errorf("http listener: %w", err)
199 }
200 if merr := s.startMetrics(); merr != nil {
201 // The node serves without its metrics rather than not at all: a port that is taken is not a reason for a fleet's devices to lose their node.
202 // The operator sees the line and the missing /healthz.
203 s.log.Warn("metrics_unavailable")
204 }
205 // What the node must already have stopped serving is known before the first device connects: the feed's first answer (a revoke that happened while
206 // the node was down, the kill switch) comes first, bounded so that a late control plane does not keep the node from starting.
207 s.startBackground()
208 for _, l := range []struct {
209 ln net.Listener
210 fn func(net.Conn)
211 }{{s.httpsLn, s.handleHTTPS}, {s.httpLn, s.handleHTTP}} {
212 if l.ln != nil {
213 s.wg.Add(1)
214 go s.acceptLoop(l.ln, l.fn)
215 }
216 }
217 s.log.Info("listening", "https", s.HTTPSAddr(), "http", s.HTTPAddr(), "node", s.cfg.NodeID)
218 return nil
219}
220
221// keepaliveProbes is how many unanswered keepalive probes end a connection.
222const keepaliveProbes = 4
223
224// listenConfig is how the node's listeners are made: the connections they accept have TCP keepalive on, with the policy's period.
225func (s *Server) listenConfig() net.ListenConfig {
226 return net.ListenConfig{KeepAliveConfig: net.KeepAliveConfig{Enable: true, Idle: s.cfg.TCPKeepalive, Interval: s.cfg.TCPKeepalive, Count: keepaliveProbes}}
227}
228
229func (s *Server) closeListeners() error {
230 var first error
231 for _, ln := range []net.Listener{s.httpsLn, s.httpLn} {
232 if ln != nil {
233 if err := ln.Close(); err != nil && first == nil {
234 first = err
235 }
236 }
237 }
238 return first
239}
240
241// Shutdown stops accepting, tells every device the node is going (CLOSE shutdown) and waits (up to grace) for streams to finish; the streams are
242// force-closed when it ends.
243func (s *Server) Shutdown(grace time.Duration) { s.shutdown(grace, closeShutdown) }
244
245func (s *Server) shutdown(grace time.Duration, reason string) {
246 _ = s.closeListeners()
247 s.stopMetrics()
248 s.mu.Lock()
249 live := make([]*ctrl, 0, len(s.conns))
250 for cc := range s.conns {
251 live = append(live, cc)
252 }
253 s.mu.Unlock()
254 for _, cc := range live {
255 cc.close(reason)
256 }
257 done := make(chan struct{})
258 go func() { s.wg.Wait(); close(done) }()
259 select {
260 case <-done:
261 case <-time.After(grace):
262 }
263 s.mu.Lock() // with goBackground: a task is either counted before the wait below starts, or sees the node ended and does not start
264 s.cancel()
265 s.mu.Unlock()
266 select {
267 case <-done:
268 case <-time.After(2 * time.Second):
269 }
270 bgDone := make(chan struct{})
271 go func() { s.bg.Wait(); close(bgDone) }()
272 select {
273 case <-bgDone:
274 case <-time.After(2 * time.Second):
275 }
276}
277
278func (s *Server) acceptLoop(ln net.Listener, handle func(net.Conn)) {
279 defer s.wg.Done()
280 var delay time.Duration
281 for {
282 c, err := ln.Accept()
283 if err != nil {
284 if s.ctx.Err() != nil || errors.Is(err, net.ErrClosed) {
285 return
286 }
287 delay = min(max(2*delay, 5*time.Millisecond), time.Second)
288 s.log.Warn("accept_error", "retry_in_ms", delay.Milliseconds())
289 select {
290 case <-time.After(delay):
291 case <-s.ctx.Done():
292 return
293 }
294 continue
295 }
296 delay = 0
297 s.wg.Add(1)
298 go handle(c)
299 }
300}
301
302func tryAcquire(counter *atomic.Int64, limit int64) bool {
303 for {
304 cur := counter.Load()
305 if cur >= limit {
306 return false
307 }
308 if counter.CompareAndSwap(cur, cur+1) {
309 return true
310 }
311 }
312}
313
314// anonUnauthMax is the most unauthenticated connections the sources nobody vouches for may hold together.
315func (s *Server) anonUnauthMax() int64 {
316 return int64(s.cfg.MaxUnauth - s.cfg.MaxUnauth*s.cfg.OpenAIReservedPct/100)
317}
318
319func newStreamID() string {
320 var b [16]byte
321 if _, err := rand.Read(b[:]); err != nil {
322 panic("crypto/rand failed: " + err.Error())
323 }
324 return hex.EncodeToString(b[:])
325}
326
327// newSecret is a 256-bit one-use attach secret (43 base64url characters).
328func newSecret() string {
329 var b [32]byte
330 if _, err := rand.Read(b[:]); err != nil {
331 panic("crypto/rand failed: " + err.Error())
332 }
333 return b64Encode(b[:])
334}
335
336// newNonce is the 128-bit CHALLENGE nonce (22 base64url characters).
337func newNonce() string {
338 var b [16]byte
339 if _, err := rand.Read(b[:]); err != nil {
340 panic("crypto/rand failed: " + err.Error())
341 }
342 return b64Encode(b[:])
343}
344
345func (s *Server) tapBytes(id, dir string, p []byte) {
346 if s.cfg.tap != nil {
347 s.cfg.tap(id, dir, p)
348 }
349}
350
351// emit records one protocol step for the contracts' transcript checker; it costs nothing when no trace is set.
352func (s *Server) emit(at time.Time, ev map[string]any) {
353 if s.cfg.trace != nil {
354 ev["t"] = epochSeconds(at)
355 s.cfg.trace(ev)
356 }
357}
358
359// sendAlert writes one fatal TLS alert record, then lets the peer drain for a moment so it is not reset before it can read the alert.
360func sendAlert(c net.Conn, desc byte) {
361 _ = c.SetWriteDeadline(time.Now().Add(time.Second))
362 _, _ = c.Write([]byte{21, 3, 3, 0, 2, 2, desc})
363 if cw, ok := c.(interface{ CloseWrite() error }); ok {
364 _ = cw.CloseWrite()
365 }
366 _ = c.SetReadDeadline(time.Now().Add(time.Second))
367 _, _ = io.Copy(io.Discard, io.LimitReader(c, 16<<10))
368}
369
370func helloReason(err error) string {
371 switch {
372 case errors.Is(err, errNotTLS):
373 return "not_tls"
374 case errors.Is(err, errNotClientHello), errors.Is(err, errHelloMalformed):
375 return "bad_hello"
376 case errors.Is(err, errHelloTooLarge):
377 return "hello_too_large"
378 case errors.Is(err, os.ErrDeadlineExceeded):
379 return "hello_timeout"
380 case errors.Is(err, io.EOF), errors.Is(err, io.ErrUnexpectedEOF):
381 return "closed_before_hello"
382 default:
383 return "read_error"
384 }
385}
386
387// peerAddr is the client's address as the contracts write it in an OPEN: no zone, an IPv4-mapped address as the IPv4 address it carries.
388func peerAddr(c net.Conn) (netip.Addr, bool) {
389 ap, err := netip.ParseAddrPort(c.RemoteAddr().String())
390 if err != nil {
391 return netip.Addr{}, false
392 }
393 return ap.Addr().WithZone("").Unmap(), true
394}
395
396// ---------------------------------------------------------------- routing table
397
398// hostEntry is what the node knows about one device host: the control connection that serves it (nil while the device is away) and how many client
399// streams are open on it.
400type hostEntry struct {
401 cc *ctrl
402 streams int64 // client streams open on the host, all slices together
403 anon int64 // of them, the ones in the anonymous slice (the rest are in the slice of sources the device marked good)
404 live map[*liveStream]struct{} // the client streams being served on this host: what a revoke, a pause or an expiry must be able to cut
405 gated atomic.Bool // the control plane's last status for the host's connection is gated: its streams are held to the day's budget (gated.go)
406 newOnes bucket // the host's budget of new streams per second, for sources that are not marked good
407}
408
409// liveStream is one client stream the node serves for a host, from the moment it is admitted (before its OPEN) to its end. A revoke, a pause, an
410// expired connection or the kill switch kills it: whichever connections it holds are closed, and one that is not attached yet is closed the moment it is.
411type liveStream struct {
412 mu sync.Mutex
413 client net.Conn
414 dev net.Conn
415 killed string // why it was killed, "" while it lives
416
417 he *hostEntry // the host's entry: what says, at every chunk, whether the host is gated now (a RENEW may have made it so since the stream began)
418 usage *dayUsage // the host's gated-day record
419 anon bool // the stream counts in the host's anonymous slice
420}
421
422// bind gives the stream its two connections. It reports false when the stream was killed before they were ready (the caller closes them).
423func (l *liveStream) bind(client, dev net.Conn) bool {
424 l.mu.Lock()
425 defer l.mu.Unlock()
426 if l.killed != "" {
427 return false
428 }
429 l.client, l.dev = client, dev
430 return true
431}
432
433func (l *liveStream) kill(reason string) {
434 l.mu.Lock()
435 if l.killed == "" {
436 l.killed = reason
437 }
438 client, dev := l.client, l.dev
439 l.mu.Unlock()
440 if client != nil {
441 _ = client.Close()
442 }
443 if dev != nil {
444 _ = dev.Close()
445 }
446}
447
448func (l *liveStream) killedBy() string {
449 l.mu.Lock()
450 defer l.mu.Unlock()
451 return l.killed
452}
453
454// routeTable maps a host to the control connection of the device that owns it. The host comes ONLY from the control plane's answer; the table
455// refuses to give one host to two devices and replaces a connection only by a newer one of the SAME subject. It is safe for concurrent use.
456type routeTable struct {
457 mu sync.Mutex
458 byHost map[string]*hostEntry
459 bySub map[string]*ctrl
460 lim labelLimits
461}
462
463// labelLimits are what one device host accepts: how many streams (a cap on the whole, and on what sources that are not marked good may hold of it),
464// and how fast new ones may begin when they come from such sources.
465type labelLimits struct {
466 total, anon int
467 rate, burst float64
468}
469
470func labelLimitsOf(cfg *Config) labelLimits {
471 return labelLimits{total: cfg.MaxStreamsPerHost, anon: min(cfg.LabelAnonSlice, cfg.MaxStreamsPerHost), rate: float64(cfg.LabelNewPerS), burst: float64(cfg.LabelNewBurst)}
472}
473
474func newRouteTable() *routeTable {
475 return &routeTable{byHost: map[string]*hostEntry{}, bySub: map[string]*ctrl{}}
476}
477
478// activate binds cc to host as subject sub. It returns the connection it replaced (the same subject's older one: the caller closes it) or
479// errHostTaken when another subject holds the host. bound, when set, runs under the table's lock once cc is bound, so whatever it records is
480// ordered like the routing decisions are.
481func (rt *routeTable) activate(cc *ctrl, sub, host string, bound func()) (*ctrl, error) {
482 rt.mu.Lock()
483 defer rt.mu.Unlock()
484 he := rt.byHost[host]
485 if he != nil && he.cc != nil && he.cc != cc && he.cc.subject() != sub {
486 return nil, errHostTaken
487 }
488 var old *ctrl
489 prevHost := ""
490 if prev := rt.bySub[sub]; prev != nil && prev != cc {
491 old, prevHost = prev, prev.hostName()
492 if pe := rt.byHost[prevHost]; pe != nil && pe.cc == prev {
493 pe.cc = nil
494 }
495 }
496 he = rt.byHost[host] // looked up again: unbinding the older connection must not have dropped it from under us
497 if he == nil {
498 he = &hostEntry{}
499 rt.byHost[host] = he
500 }
501 he.cc = cc
502 rt.bySub[sub] = cc
503 if prevHost != "" && prevHost != host {
504 if pe := rt.byHost[prevHost]; pe != nil {
505 rt.dropIfIdle(prevHost, pe)
506 }
507 }
508 cc.bind(sub, host)
509 if bound != nil {
510 bound()
511 }
512 return old, nil
513}
514
515// remove unbinds cc (it never touches a newer connection that replaced it).
516func (rt *routeTable) remove(cc *ctrl) {
517 rt.mu.Lock()
518 defer rt.mu.Unlock()
519 sub, host := cc.subject(), cc.hostName()
520 if rt.bySub[sub] == cc {
521 delete(rt.bySub, sub)
522 }
523 if he := rt.byHost[host]; he != nil && he.cc == cc {
524 he.cc = nil
525 rt.dropIfIdle(host, he)
526 }
527}
528
529func (rt *routeTable) dropIfIdle(host string, he *hostEntry) {
530 if he.cc == nil && he.streams == 0 {
531 delete(rt.byHost, host)
532 }
533}
534
535func (rt *routeTable) lookup(host string) (*ctrl, *hostEntry) {
536 rt.mu.Lock()
537 defer rt.mu.Unlock()
538 he := rt.byHost[host]
539 if he == nil {
540 return nil, nil
541 }
542 return he.cc, he
543}
544
545// The reasons routes.acquire gives for a stream it does not admit.
546const (
547 admitUnknown = "unknown_host" // no device is online under the name
548 admitHostLimit = "host_limit" // the host holds all the streams its slice allows
549 admitLabelRate = "label_rate" // the host is being asked for new streams faster than it accepts from sources that are not marked good
550)
551
552// acquire counts one more client stream on the host and registers it so that it can be killed. A source the device marked good (hint) may use any of
553// the host's cap; any other source only the anonymous slice, which is what leaves room for the good ones when an attacker holds streams, and its new
554// streams are also paced per host. It returns the connection to OPEN on, or why the stream is refused.
555func (rt *routeTable) acquire(host string, hint bool, now time.Time) (*ctrl, *liveStream, string) {
556 rt.mu.Lock()
557 defer rt.mu.Unlock()
558 he := rt.byHost[host]
559 if he == nil || he.cc == nil {
560 return nil, nil, admitUnknown
561 }
562 if he.streams >= int64(rt.lim.total) || (!hint && he.anon >= int64(rt.lim.anon)) {
563 return nil, nil, admitHostLimit
564 }
565 if !hint && !he.newOnes.take(now, rt.lim.rate, rt.lim.burst) {
566 return nil, nil, admitLabelRate
567 }
568 he.streams++
569 if !hint {
570 he.anon++
571 }
572 ls := &liveStream{he: he, anon: !hint}
573 if he.live == nil {
574 he.live = map[*liveStream]struct{}{}
575 }
576 he.live[ls] = struct{}{}
577 return he.cc, ls, ""
578}
579
580func (rt *routeTable) release(host string, ls *liveStream) {
581 rt.mu.Lock()
582 defer rt.mu.Unlock()
583 he := ls.he
584 he.streams--
585 if ls.anon {
586 he.anon--
587 }
588 delete(he.live, ls)
589 if rt.byHost[host] == he {
590 rt.dropIfIdle(host, he)
591 }
592}
593
594// setGated records, for the host cc serves, whether the control plane last called it gated. A connection that has been replaced says nothing.
595func (rt *routeTable) setGated(cc *ctrl, gated bool) {
596 rt.mu.Lock()
597 defer rt.mu.Unlock()
598 if he := rt.byHost[cc.hostName()]; he != nil && he.cc == cc {
599 he.gated.Store(gated)
600 }
601}
602
603// onlineCount is the number of device hosts with a control connection bound to them.
604func (rt *routeTable) onlineCount() int {
605 rt.mu.Lock()
606 defer rt.mu.Unlock()
607 n := 0
608 for _, he := range rt.byHost {
609 if he.cc != nil {
610 n++
611 }
612 }
613 return n
614}
615
616// killStreams kills the client streams being served on host. Unless force, it does nothing while a connection of the device is bound to the host (an
617// expiry that raced a newer connection must not cut what that one serves).
618func (rt *routeTable) killStreams(host, reason string, force bool) int {
619 rt.mu.Lock()
620 var doomed []*liveStream
621 if he := rt.byHost[host]; he != nil && (force || he.cc == nil) {
622 for ls := range he.live {
623 doomed = append(doomed, ls)
624 }
625 }
626 rt.mu.Unlock()
627 for _, ls := range doomed {
628 ls.kill(reason)
629 }
630 return len(doomed)
631}
632
633// killAllStreams kills every client stream on every host (the kill switch, a feed reset).
634func (rt *routeTable) killAllStreams(reason string) {
635 rt.mu.Lock()
636 var doomed []*liveStream
637 for _, he := range rt.byHost {
638 for ls := range he.live {
639 doomed = append(doomed, ls)
640 }
641 }
642 rt.mu.Unlock()
643 for _, ls := range doomed {
644 ls.kill(reason)
645 }
646}
647
648// hostHasLabel reports whether host is <label>.<something>.
649func hostHasLabel(host, label string) bool {
650 return len(host) > len(label) && host[len(label)] == '.' && host[:len(label)] == label
651}
652
653// hostsOfLabel are the hosts the table knows (a connection bound to them, or streams still open on them) whose label is label. A host is
654// <label>.<zone>, and the label is the first 20 characters: the control plane's answer is the only thing that ever put a host in the table.
655func (rt *routeTable) hostsOfLabel(label string) []string {
656 rt.mu.Lock()
657 defer rt.mu.Unlock()
658 var hosts []string
659 for host := range rt.byHost {
660 if hostHasLabel(host, label) {
661 hosts = append(hosts, host)
662 }
663 }
664 return hosts
665}
666
667// controls are the connections bound to a host of label, and every connection when label is "".
668func (rt *routeTable) controls(label string) []*ctrl {
669 rt.mu.Lock()
670 defer rt.mu.Unlock()
671 var out []*ctrl
672 for host, he := range rt.byHost {
673 if he.cc != nil && (label == "" || hostHasLabel(host, label)) {
674 out = append(out, he.cc)
675 }
676 }
677 return out
678}
679
680// ---------------------------------------------------------------- the control connection of one device
681
682// ctrl is one control connection of a device: from HELLO to its close.
683type ctrl struct {
684 s *Server
685 id string
686 conn net.Conn
687 wmu sync.Mutex
688
689 mu sync.Mutex
690 sub, host string
691 status string
692 plan string // paid or trial, as the control plane last said (the grace is for paid)
693 jti string // the session the device last authenticated with: what a refresh of this connection tells the control plane
694 authAt int64 // the control plane's own time (epoch s) at its last answer for this connection: a feed event older than it is already in it
695 validUntil time.Time // what the control plane granted, never changed by the node
696 graceUntil time.Time // a paid connection's end while the control plane does not answer (zero when it is not in its grace)
697 renewDown bool // the last RENEW got no answer from the control plane (a timeout, a 5xx, a 429)
698 refused bool // the last RENEW got an answer from the control plane, and it was no: the control plane being down later does not extend it
699 ready bool // an OK was sent: the connection is in the routing table
700 lastRenew time.Time
701 stop func() bool // the timer that ends the connection at validUntil (or at the end of its grace)
702
703 refreshing, refreshAgain bool // a refresh of this connection (feed pause/resume) is running, and another event came while it ran
704
705 done chan struct{}
706 once sync.Once
707 reason atomic.Value // string, why it was closed
708}
709
710func (s *Server) newCtrl(c net.Conn) *ctrl {
711 cc := &ctrl{s: s, id: fmt.Sprintf("c%d", s.connSeq.Add(1)), conn: c, done: make(chan struct{})}
712 s.mu.Lock()
713 s.conns[cc] = struct{}{}
714 s.mu.Unlock()
715 return cc
716}
717
718func (c *ctrl) subject() string { c.mu.Lock(); defer c.mu.Unlock(); return c.sub }
719func (c *ctrl) hostName() string { c.mu.Lock(); defer c.mu.Unlock(); return c.host }
720
721func (c *ctrl) bind(sub, host string) {
722 c.mu.Lock()
723 c.sub, c.host = sub, host
724 c.mu.Unlock()
725}
726
727// setReady records what the control plane decided: the connection serves streams from now on, until validUntil. A new decision ends any grace.
728func (c *ctrl) setReady(resp AuthorizeResponse, jti string, validUntil time.Time) {
729 c.mu.Lock()
730 c.ready, c.status, c.plan, c.jti, c.authAt, c.validUntil = true, resp.Status, resp.Plan, jti, resp.ServerTime, validUntil
731 c.graceUntil, c.renewDown, c.refused = time.Time{}, false, false
732 c.mu.Unlock()
733}
734
735// setStatus records a status the control plane gave in an answer that does not extend the connection (a refresh after a pause or a resume).
736func (c *ctrl) setStatus(resp AuthorizeResponse) {
737 c.mu.Lock()
738 c.status, c.plan, c.authAt = resp.Status, resp.Plan, resp.ServerTime
739 c.mu.Unlock()
740}
741
742// pause stops this connection from opening streams until the control plane says otherwise.
743func (c *ctrl) pause() {
744 c.mu.Lock()
745 c.status = statusPaused
746 c.mu.Unlock()
747}
748
749// markRenew records how the control plane came out of a RENEW that was not granted: it did not answer (down), or it answered no (refused).
750func (c *ctrl) markRenew(down, refused bool) {
751 c.mu.Lock()
752 c.renewDown, c.refused = down, refused
753 c.mu.Unlock()
754}
755
756// canOpen reports whether a client stream may be opened on this connection at now: an OK was sent, the device is not paused and valid_until has not
757// come. A paused connection stays up (a resume arrives as a statement) but opens nothing.
758func (c *ctrl) canOpen(now time.Time) bool {
759 c.mu.Lock()
760 defer c.mu.Unlock()
761 return c.ready && c.status != statusPaused && now.Before(c.validUntil)
762}
763
764// tooSoon records a RENEW at now and reports whether the previous one was less than gap ago.
765func (c *ctrl) tooSoon(now time.Time, gap time.Duration) bool {
766 c.mu.Lock()
767 defer c.mu.Unlock()
768 if !c.lastRenew.IsZero() && now.Sub(c.lastRenew) < gap {
769 return true
770 }
771 c.lastRenew = now
772 return false
773}
774
775func (c *ctrl) isReady() bool { c.mu.Lock(); defer c.mu.Unlock(); return c.ready }
776
777func (c *ctrl) writeLocked(line string, at time.Time) error {
778 err := c.sendLocked(line)
779 c.s.emit(at, map[string]any{"conn": c.id, "ev": "send", "line": line})
780 return err
781}
782
783// sendLocked puts one line on the wire without recording it, for a caller that records it itself.
784func (c *ctrl) sendLocked(line string) error {
785 _ = c.conn.SetWriteDeadline(time.Now().Add(c.s.cfg.WriteTimeout))
786 _, err := io.WriteString(c.conn, line+"\n")
787 return err
788}
789
790func (c *ctrl) write(line string) error { return c.writeAt(line, c.s.now()) }
791
792// writeAt writes one line; at is the moment recorded in the trace (the moment the decision to write it was taken).
793func (c *ctrl) writeAt(line string, at time.Time) error {
794 c.wmu.Lock()
795 defer c.wmu.Unlock()
796 return c.writeLocked(line, at)
797}
798
799// close ends the connection for reason (a CLOSE reason, or another word for the log). A reason the device is told (CLOSE <reason>) is sent first,
800// unless the writer is stuck.
801func (c *ctrl) close(reason string) {
802 c.once.Do(func() {
803 c.reason.Store(reason)
804 close(c.done)
805 c.mu.Lock()
806 if c.stop != nil {
807 c.stop()
808 }
809 c.mu.Unlock()
810 // The CLOSE line goes out under the write lock, so it follows an OK that is still being written; a writer that is stuck is not waited for.
811 told, ready := false, false
812 if c.lockWrite(closeWait) {
813 if ready = c.isReady(); ready && inList(policyData.Protocol.CloseReasons, reason) {
814 _ = c.writeLocked(closeLine(reason), c.s.now())
815 told = true
816 }
817 c.wmu.Unlock()
818 } else {
819 ready = c.isReady()
820 }
821 if ready && !told {
822 // The transport ended without a CLOSE line (the device went away, the write was stuck). The recording still has to know this
823 // connection is over, so a trace gets a marker that never went on the wire.
824 c.s.emit(c.s.now(), map[string]any{"conn": c.id, "ev": "send", "line": "CLOSE", "wire": false})
825 }
826 _ = c.conn.Close()
827 host := c.hostName()
828 c.s.routes.remove(c)
829 c.s.mu.Lock()
830 delete(c.s.conns, c)
831 c.s.mu.Unlock()
832 // A connection that ends because its grant ended (not because the device went away) takes the streams of its host with it, unless a newer
833 // connection of the device already serves them.
834 if ready && endsStreams(reason) {
835 c.s.routes.killStreams(host, reason, false)
836 }
837 })
838}
839
840// endsStreams says whether a connection closed for reason takes the host's open streams with it: the grant ended (expired, revoked, released) or the
841// kill switch is on. A device that went away, a replaced connection and a node going down leave them to their own limits.
842func endsStreams(reason string) bool {
843 switch reason {
844 case closeExpired, closeRevoked, closeReleased, closeKilled:
845 return true
846 }
847 return false
848}
849
850// closeWait is how long a close waits for a writer that holds the connection's write lock before it gives up on the CLOSE line.
851const closeWait = 250 * time.Millisecond
852
853func (c *ctrl) lockWrite(wait time.Duration) bool {
854 deadline := time.Now().Add(wait)
855 for !c.wmu.TryLock() {
856 if time.Now().After(deadline) {
857 return false
858 }
859 time.Sleep(time.Millisecond)
860 }
861 return true
862}
863
864func (c *ctrl) closeReason() string {
865 if r, ok := c.reason.Load().(string); ok {
866 return r
867 }
868 return ""
869}
870
871// arm schedules the end of the connection at validUntil (CLOSE expired); a RENEW arms it again for the new moment.
872func (c *ctrl) arm(validUntil time.Time) {
873 c.mu.Lock()
874 defer c.mu.Unlock()
875 if c.stop != nil {
876 c.stop()
877 }
878 c.stop = c.s.clk.AfterFunc(validUntil.Sub(c.s.now()), func() { c.expire(validUntil) })
879}
880
881// expire is the end of the grant the timer was armed for. A grant a RENEW replaced since is not this one, and nothing happens. Otherwise the
882// connection closes, except for a paid one while the control plane does not answer: it keeps its connection and its streams (but opens no new
883// stream: canOpen is false from valid_until) for the grace, and closes when that is over. A trial or a gated connection is never extended, and
884// neither is a paid one the control plane did answer (its RENEW was refused, or never came).
885func (c *ctrl) expire(validUntil time.Time) {
886 c.mu.Lock()
887 current := c.validUntil.Equal(validUntil)
888 inGrace := !c.graceUntil.IsZero()
889 paid := c.plan == "paid"
890 down := c.renewDown
891 refused := c.refused
892 c.mu.Unlock()
893 if !current {
894 return
895 }
896 now := c.s.now()
897 if !inGrace && paid && !refused && c.s.cfg.Grace > 0 && (down || c.s.health.down(now, c.s.cfg.DownQuiet)) {
898 graceUntil := validUntil.Add(c.s.cfg.Grace)
899 c.mu.Lock()
900 c.graceUntil = graceUntil
901 if c.stop != nil {
902 c.stop()
903 }
904 c.stop = c.s.clk.AfterFunc(graceUntil.Sub(now), func() { c.expire(validUntil) })
905 c.mu.Unlock()
906 c.s.log.Warn("control_grace", "conn", c.id, "grace_s", int64(c.s.cfg.Grace/time.Second))
907 return
908 }
909 c.close(closeExpired)
910}
911
912// bufConn serves reads through the bufio.Reader that parsed the first protocol line, so no byte the peer sent right after it is lost.
913type bufConn struct {
914 net.Conn
915 r *bufio.Reader
916}
917
918func (b *bufConn) Read(p []byte) (int, error) { return b.r.Read(p) }
919
920func (b *bufConn) CloseWrite() error {
921 if cw, ok := b.Conn.(interface{ CloseWrite() error }); ok {
922 return cw.CloseWrite()
923 }
924 return b.Conn.Close()
925}
926
927// prefixConn replays the bytes already read from a connection before reading on: the ClientHello that was peeked to learn the SNI.
928type prefixConn struct {
929 net.Conn
930 prefix []byte
931}
932
933func (p *prefixConn) Read(b []byte) (int, error) {
934 if len(p.prefix) > 0 {
935 n := copy(b, p.prefix)
936 p.prefix = p.prefix[n:]
937 return n, nil
938 }
939 return p.Conn.Read(b)
940}
941
942// ---------------------------------------------------------------- :443
943
944// sourceOf is the source address of an accepted connection and whether it is in the OpenAI list.
945func (s *Server) sourceOf(c net.Conn) (peer netip.Addr, ok, openai bool) {
946 if s.cfg.peerOf != nil {
947 peer, ok = s.cfg.peerOf(c)
948 } else {
949 peer, ok = peerAddr(c)
950 }
951 return peer, ok, ok && s.openai.contains(peer)
952}
953
954// short is the per-boot salted short hash of a word (an address, a name): how a log line tells two of them apart without holding either. The salt
955// lives in memory only, so the hash cannot be read back, and it changes at every start, so it cannot be followed from one boot to the next.
956func (s *Server) short(word string) string {
957 return fmt.Sprintf("%08x", uint32(maphash.String(s.seed, word)))
958}
959
960// refused counts a connection or stream the node did not serve and writes its log line, at most one a second per reason: a flood must not become a flood
961// of log lines (the count is in the metrics, and the line says how many were left out). The line never names a source or a name.
962func (s *Server) refused(level slog.Level, msg, reason string, attrs ...any) {
963 s.met.rejected.inc(reason)
964 if ok, cut := s.sampler.allow(msg + "/" + reason); ok {
965 attrs = append(attrs, "reason", reason)
966 if cut > 0 {
967 attrs = append(attrs, "suppressed", cut)
968 }
969 s.log.Log(context.Background(), level, msg, attrs...)
970 }
971}
972
973// handleHTTPS is the one public listener. It reads only the cleartext ClientHello for the SNI: the node's own name is the control handler (the
974// device speaks protocol v2 to it over TLS with ALPN all-tunnel/2), any other name is a client of a device and is spliced as raw bytes.
975//
976// Before a byte is read, the source is admitted: a source that is banned, that opens connections faster than the policy lets it, or that already holds
977// the most it may is dropped without an answer (limits.go).
978func (s *Server) handleHTTPS(c net.Conn) {
979 defer s.wg.Done()
980 handedOff := false // an ATTACHed data connection belongs to its stream from then on
981 defer func() {
982 if !handedOff {
983 _ = c.Close()
984 }
985 }()
986 id := newStreamID()
987 start := s.now()
988
989 peer, havePeer, fromOpenAI := s.sourceOf(c)
990 slotDone := func() {}
991 if havePeer {
992 rel, why := s.ips.admit(peer, fromOpenAI)
993 if why != "" {
994 s.refused(slog.LevelInfo, "conn_reject", why, "stream", id)
995 return
996 }
997 slotDone = rel
998 }
999 defer slotDone()
1000
1001 // The unauthenticated budget has a reserve, as the stream budget has: a source that is neither an address of the OpenAI list, nor one that holds (or just held) a device's
1002 // control connection, nor one a device marked good, may not take the share kept for those (policy node_limits.openai_reserved_pct). Twenty anonymous addresses holding the
1003 // most a source may hold fill the anonymous share, never the whole budget: ChatGPT's clients and the devices that reconnect (they are unauthenticated until their PROOF) go on.
1004 anon := !(havePeer && (fromOpenAI || s.ips.knownDevice(peer) || s.hints.goodAnywhere(peer)))
1005 if anon && !tryAcquire(&s.anonUnauth, s.anonUnauthMax()) {
1006 s.refused(slog.LevelWarn, "conn_reject", "unauth_anon_limit")
1007 return
1008 }
1009 if !tryAcquire(&s.unauth, int64(s.cfg.MaxUnauth)) {
1010 if anon {
1011 s.anonUnauth.Add(-1)
1012 }
1013 s.refused(slog.LevelWarn, "conn_reject", "unauth_limit")
1014 return
1015 }
1016 released := false
1017 release := func() {
1018 if !released {
1019 released = true
1020 s.unauth.Add(-1)
1021 if anon {
1022 s.anonUnauth.Add(-1)
1023 }
1024 }
1025 }
1026 defer release()
1027 var unmark func()
1028 // strike is a source that sent something no client of a device sends: a name that is not a device online, or a ClientHello that is no TLS. Too
1029 // many different ones and the source is dropped at accept for a while (an address of the OpenAI list is never struck: ChatGPT retrying an
1030 // offline device is not a scan).
1031 strike := func(name string) {
1032 // An address a device marked good (it just completed authenticated calls) is not a scanner, whatever names it asks for: at fleet scale a shared
1033 // egress address asks for many devices that are offline at that moment.
1034 if havePeer && !s.hints.goodAnywhere(peer) && s.ips.strike(peer, name, fromOpenAI) {
1035 s.met.bansTotal.Add(1)
1036 s.log.Info("source_banned", "source", s.short(peer.String()), "ban_s", int64(s.cfg.UnknownSNIBan/time.Second))
1037 }
1038 }
1039
1040 peeked, sni, err := readClientHello(c, s.cfg.HelloTimeout)
1041 if err != nil && !errors.Is(err, errBadSNI) {
1042 // A ClientHello that is truncated, malformed, too large or never finished never produces an OPEN.
1043 s.emit(start, map[string]any{"ev": "client_hello", "sni": "", "complete": false})
1044 reason := helloReason(err)
1045 s.refused(slog.LevelInfo, "conn_reject", reason, "stream", id, "peeked_bytes", len(peeked))
1046 if reason == "not_tls" || reason == "bad_hello" || reason == "hello_too_large" {
1047 strike("")
1048 }
1049 return
1050 }
1051 if err != nil || sni == "" {
1052 reason := "no_sni"
1053 if err != nil {
1054 reason = "bad_sni"
1055 }
1056 s.emit(s.now(), map[string]any{"ev": "tls_alert", "sni": ""})
1057 s.refused(slog.LevelInfo, "conn_reject", reason, "stream", id, "peeked_bytes", len(peeked))
1058 strike("")
1059 sendAlert(c, alertUnrecognizedName)
1060 return
1061 }
1062 if sni == s.cfg.NodeHost {
1063 // A connection of the node's own name that proves who it is (a device's control connection after its proof, a data connection after its ATTACH)
1064 // stops being an unknown source's: it gives back its slot and the token it took, and a control connection marks its address as a device's for as
1065 // long as it lives, so that the connections of the streams it serves are not held to what an anonymous source gets (limits.go).
1066 defer func() {
1067 if unmark != nil {
1068 unmark()
1069 }
1070 }()
1071 handedOff = s.serveOwnName(c, peeked, func(control bool) {
1072 release()
1073 if havePeer {
1074 s.ips.refund(peer, fromOpenAI)
1075 if control && unmark == nil {
1076 unmark = s.ips.markDevice(peer)
1077 }
1078 }
1079 slotDone()
1080 })
1081 return
1082 }
1083 release()
1084 s.met.observeClientHello(peeked)
1085 s.serveClient(c, id, sni, peeked, start, peer, havePeer, strike)
1086}
1087
1088// classOf names the class of a client stream's source for the OPEN: the configured classifier when there is one, otherwise openai for an address of
1089// the OpenAI list, hint for one the device marked, anon for the rest. marked says whether the device marked the address (what puts the stream in the
1090// host's hint slice, whatever class the OPEN says).
1091func (s *Server) classOf(peer netip.Addr, havePeer bool, host string) (class string, marked bool) {
1092 class = classAnon
1093 if havePeer {
1094 marked = s.hints.good(host, peer)
1095 }
1096 switch {
1097 case s.cfg.Classify != nil && havePeer:
1098 if got := s.cfg.Classify(peer, host); inList(policyData.Protocol.SourceClasses, got) {
1099 class = got
1100 }
1101 case havePeer && s.openai.contains(peer):
1102 class = classOpenAI
1103 case marked:
1104 class = classHint
1105 }
1106 return class, marked || class == classHint
1107}
1108
1109// serveClient splices one client of a device. Everything the node learned is the SNI and the ClientHello's bytes, and the OPEN is sent only now
1110// that the ClientHello is complete and well formed.
1111func (s *Server) serveClient(c net.Conn, id, sni string, peeked []byte, start time.Time, peer netip.Addr, havePeer bool, strike func(name string)) {
1112 if s.cfg.divert != nil && s.cfg.divert(c, peeked, s.log) {
1113 return
1114 }
1115 class, marked := s.classOf(peer, havePeer, sni)
1116 peerText := "0.0.0.0"
1117 if havePeer {
1118 peerText = peer.String()
1119 }
1120 hello := map[string]any{"ev": "client_hello", "sni": sni, "complete": true, "peer_ip": peerText, "class": class}
1121 s.emit(s.now(), hello)
1122
1123 if s.draining.Load() {
1124 // A draining node takes no new client: the TLS alert, never an OPEN. (The client's ClientHello is not forwarded anywhere.)
1125 s.refused(slog.LevelInfo, "stream_reject", "draining", "stream", id, "peeked_bytes", len(peeked))
1126 s.alert(c, sni, alertInternalError)
1127 return
1128 }
1129 // The global budget: every stream counts in it, and a stream whose source is neither ChatGPT's (the OpenAI list) nor marked good by a device may not
1130 // take the share that is reserved for those (policy node_limits.openai_reserved_pct).
1131 if !tryAcquire(&s.streams, int64(s.cfg.MaxStreams)) {
1132 s.refused(slog.LevelWarn, "stream_reject", "global_limit", "stream", id)
1133 s.alert(c, sni, alertInternalError)
1134 return
1135 }
1136 defer s.streams.Add(-1)
1137 if class == classAnon && !marked {
1138 if !tryAcquire(&s.anonStreams, int64(s.cfg.MaxStreams-s.cfg.MaxStreams*s.cfg.OpenAIReservedPct/100)) {
1139 s.refused(slog.LevelWarn, "stream_reject", "anon_global_limit", "stream", id)
1140 s.alert(c, sni, alertInternalError)
1141 return
1142 }
1143 defer s.anonStreams.Add(-1)
1144 }
1145
1146 cc, live, why := s.routes.acquire(sni, marked, s.now())
1147 if why != "" {
1148 desc := byte(alertInternalError)
1149 if why == admitUnknown {
1150 desc = alertUnrecognizedName
1151 strike(sni)
1152 }
1153 s.refused(slog.LevelInfo, "stream_reject", why, "stream", id, "peeked_bytes", len(peeked))
1154 s.alert(c, sni, desc)
1155 return
1156 }
1157 defer s.routes.release(sni, live)
1158 // The host's record of today's gated budget: every stream holds it, so that a host that turns gated while the stream is open is held to it too.
1159 live.usage = s.gates.acquire(sni)
1160 defer s.gates.release(live.usage)
1161 if live.he.gated.Load() {
1162 if ok, reason := live.usage.admit(s.now(), s.cfg.GatedBytesPerDay, s.cfg.GatedStreamsPerDay); !ok {
1163 s.refused(slog.LevelInfo, "stream_reject", reason, "stream", id, "peeked_bytes", len(peeked))
1164 s.alert(c, sni, alertInternalError)
1165 return
1166 }
1167 }
1168
1169 dc, err := s.openStream(cc, id, peerText, class)
1170 if err != nil {
1171 s.refused(slog.LevelInfo, "stream_reject", err.Error(), "stream", id, "peeked_bytes", len(peeked), "wait_ms", s.now().Sub(start).Milliseconds())
1172 s.alert(c, sni, alertInternalError)
1173 return
1174 }
1175 defer dc.Close()
1176 if !live.bind(c, dc) {
1177 // Cut (a revoke, a pause) between the OPEN and the ATTACH: nothing of the client's goes to the device.
1178 s.refused(slog.LevelInfo, "stream_reject", live.killedBy(), "stream", id, "peeked_bytes", len(peeked))
1179 s.alert(c, sni, alertInternalError)
1180 return
1181 }
1182 s.met.streamsOpened.Add(1)
1183 s.log.Info("stream_open", "stream", id, "hello_bytes", len(peeked), "setup_ms", s.now().Sub(start).Milliseconds(), "class", class)
1184 res := s.splice(id, c, dc, peeked, live)
1185 if why := live.killedBy(); why != "" {
1186 res.reason = why
1187 }
1188 s.met.streamEnd.inc(res.reason)
1189 s.log.Info("stream_close", "stream", id, "up_bytes", res.up, "down_bytes", res.down,
1190 "duration_ms", s.now().Sub(start).Milliseconds(), "reason", res.reason)
1191}
1192
1193func (s *Server) alert(c net.Conn, sni string, desc byte) {
1194 s.emit(s.now(), map[string]any{"ev": "tls_alert", "sni": sni})
1195 sendAlert(c, desc)
1196}
1197
1198// openStream registers a pending stream, tells the device (OPEN over its control connection) and waits for its ATTACH. The returned connection has
1199// had its ATTACH line consumed; the caller's first write is the first byte of the raw stream (there is deliberately no reply line in between, so a
1200// device can hand the connection straight to its TLS layer).
1201func (s *Server) openStream(cc *ctrl, id, peer, class string) (net.Conn, error) {
1202 at := s.now()
1203 if !cc.canOpen(at) {
1204 return nil, errOffline
1205 }
1206 secret := newSecret()
1207 p := &pendingStream{cc: cc, secret: secret, ch: make(chan net.Conn, 1)}
1208 s.mu.Lock()
1209 s.pending[id] = p
1210 s.mu.Unlock()
1211
1212 if err := cc.writeAt(formatLine(message{"verb": "OPEN", "stream_id": id, "secret": secret, "peer_ip": peer, "class": class}), at); err != nil {
1213 s.dropPending(id)
1214 cc.close("write_error")
1215 return nil, errControlWrite
1216 }
1217 timer := time.NewTimer(s.cfg.AttachTimeout)
1218 defer timer.Stop()
1219 done := cc.done
1220wait:
1221 for {
1222 select {
1223 case dc := <-p.ch:
1224 return dc, nil
1225 case <-timer.C:
1226 break wait
1227 case <-done:
1228 if cc.closeReason() != closeReplaced {
1229 break wait
1230 }
1231 // A newer connection of the same device took over: this OPEN was sent with the old one, and its attach secret is still good.
1232 done = nil
1233 case <-s.ctx.Done():
1234 break wait
1235 }
1236 }
1237 // Failed: unregister, but an ATTACH may have just landed.
1238 if !s.dropPending(id) {
1239 return <-p.ch, nil
1240 }
1241 switch {
1242 case s.ctx.Err() != nil:
1243 return nil, errShuttingDown
1244 case cc.closeReason() != "" && cc.closeReason() != closeReplaced:
1245 return nil, errOffline
1246 }
1247 return nil, errAttachTimeout
1248}
1249
1250// dropPending removes id; it reports whether the entry was still there.
1251func (s *Server) dropPending(id string) bool {
1252 s.mu.Lock()
1253 defer s.mu.Unlock()
1254 _, ok := s.pending[id]
1255 delete(s.pending, id)
1256 return ok
1257}
1258
1259// ---------------------------------------------------------------- splice
1260
1261type spliceResult struct {
1262 up, down int64 // client->device, device->client
1263 reason string
1264}
1265
1266func classifyErr(err error) string {
1267 switch {
1268 case err == nil:
1269 return "closed"
1270 case errors.Is(err, errByteCap):
1271 return "byte_cap"
1272 case errors.Is(err, errGatedBytes):
1273 return "gated_bytes"
1274 case errors.Is(err, os.ErrDeadlineExceeded):
1275 return "idle"
1276 default:
1277 return "conn_error"
1278 }
1279}
1280
1281func closeWrite(c net.Conn) {
1282 if cw, ok := c.(interface{ CloseWrite() error }); ok {
1283 _ = cw.CloseWrite()
1284 return
1285 }
1286 _ = c.Close()
1287}
1288
1289// pump copies src to dst with per-read idle deadlines and the shared byte budget. A clean EOF half-closes dst and returns nil. While the stream's host
1290// is gated, every chunk is also charged to the host's budget of the day, and the stream ends where the budget does. watch, when set, reads the
1291// device's first bytes for the handshake observer.
1292func (s *Server) pump(id, dir string, dst, src net.Conn, ctr, total *atomic.Int64, live *liveStream, watch *serverHelloWatch) error {
1293 // A pump spends the whole life of an idle stream blocked in a read, so the buffer it waits with is a small one (4 KiB); it takes a 32 KiB one only for a read that filled
1294 // the small one (a bulk transfer) and gives it back when a read comes back short. An idle stream then holds about 8 KiB of buffer instead of 64 KiB (the acceptance is under 64 KiB of memory per stream).
1295 sp := s.smallBufPool.Get().(*[]byte)
1296 small := *sp
1297 defer s.smallBufPool.Put(sp)
1298 var bp *[]byte // the big buffer, while this pump holds one
1299 giveBack := func() {
1300 if bp != nil {
1301 s.bigBuffersInUse.Add(-1)
1302 s.bufPool.Put(bp)
1303 bp = nil
1304 }
1305 }
1306 defer giveBack()
1307 buf := small
1308 for {
1309 _ = src.SetReadDeadline(time.Now().Add(s.cfg.StreamIdle))
1310 n, rerr := src.Read(buf)
1311 if n > 0 {
1312 chunk, spent := buf[:n], false
1313 if live != nil && live.he.gated.Load() {
1314 if got := live.usage.charge(s.now(), int64(n), s.cfg.GatedBytesPerDay); got < int64(n) {
1315 chunk, spent = buf[:got], true
1316 }
1317 }
1318 if total.Add(int64(len(chunk))) > s.cfg.MaxStreamBytes {
1319 return errByteCap
1320 }
1321 if len(chunk) > 0 {
1322 s.tapBytes(id, dir, chunk)
1323 if watch != nil {
1324 watch.feed(chunk)
1325 }
1326 _ = dst.SetWriteDeadline(time.Now().Add(s.cfg.StreamIdle))
1327 if _, werr := dst.Write(chunk); werr != nil {
1328 return werr
1329 }
1330 ctr.Add(int64(len(chunk)))
1331 s.met.bytes.add(dir, int64(len(chunk)))
1332 }
1333 if spent {
1334 s.met.gatedCut.Add(1)
1335 return errGatedBytes
1336 }
1337 }
1338 if rerr != nil {
1339 if rerr == io.EOF {
1340 closeWrite(dst)
1341 return nil
1342 }
1343 return rerr
1344 }
1345 switch { // the buffer for the next read: bulk traffic gets the big one, a trickle (and the wait of an idle stream) the small one
1346 case bp == nil && n == len(small):
1347 bp = s.bufPool.Get().(*[]byte)
1348 s.bigBuffersInUse.Add(1)
1349 buf = *bp
1350 case bp != nil && n < len(small):
1351 giveBack()
1352 buf = small
1353 }
1354 }
1355}
1356
1357// splice copies bytes both ways between client and dev until both sides are done, one fails, or a limit trips. preload (the peeked ClientHello) is
1358// written to dev before anything else from the client. Neither side's bytes are interpreted.
1359func (s *Server) splice(id string, client, dev net.Conn, preload []byte, live *liveStream) spliceResult {
1360 var up, down, total atomic.Int64
1361 var rmu sync.Mutex
1362 reason := ""
1363 setReason := func(r string) {
1364 rmu.Lock()
1365 if reason == "" {
1366 reason = r
1367 }
1368 rmu.Unlock()
1369 }
1370 closeBoth := func() { _ = client.Close(); _ = dev.Close() }
1371
1372 age := time.AfterFunc(s.cfg.StreamMaxAge, func() { setReason("max_age"); closeBoth() })
1373 defer age.Stop()
1374 stopWatch := make(chan struct{})
1375 defer close(stopWatch)
1376 go func() {
1377 select {
1378 case <-s.ctx.Done():
1379 setReason("shutdown")
1380 closeBoth()
1381 case <-stopWatch:
1382 }
1383 }()
1384
1385 results := make(chan error, 2)
1386 go func() { // client -> device
1387 if len(preload) > 0 {
1388 if live.he.gated.Load() && live.usage.charge(s.now(), int64(len(preload)), s.cfg.GatedBytesPerDay) < int64(len(preload)) {
1389 // a ClientHello cut short is of no use to anyone: nothing of it goes to the device
1390 s.met.gatedCut.Add(1)
1391 results <- errGatedBytes
1392 return
1393 }
1394 _ = dev.SetWriteDeadline(time.Now().Add(s.cfg.WriteTimeout))
1395 if _, err := dev.Write(preload); err != nil {
1396 results <- err
1397 return
1398 }
1399 s.tapBytes(id, "up", preload)
1400 up.Add(int64(len(preload)))
1401 total.Add(int64(len(preload)))
1402 s.met.bytes.add("up", int64(len(preload)))
1403 }
1404 results <- s.pump(id, "up", dev, client, &up, &total, live, nil)
1405 }()
1406 go func() { // device -> client
1407 results <- s.pump(id, "down", client, dev, &down, &total, live, newServerHelloWatch(s.met))
1408 }()
1409
1410 first := <-results
1411 if first == nil {
1412 // One direction ended cleanly (and was half-closed): the other gets a bounded time to finish.
1413 t := time.NewTimer(s.cfg.HalfCloseTimeout)
1414 select {
1415 case second := <-results:
1416 setReason(classifyErr(second))
1417 case <-t.C:
1418 setReason("half_close_timeout")
1419 closeBoth()
1420 <-results
1421 }
1422 t.Stop()
1423 } else {
1424 setReason(classifyErr(first))
1425 closeBoth()
1426 <-results
1427 }
1428 closeBoth()
1429 rmu.Lock()
1430 defer rmu.Unlock()
1431 return spliceResult{up: up.Load(), down: down.Load(), reason: reason}
1432}
1433
1434// ---------------------------------------------------------------- the node's own name: control and data connections of devices
1435
1436func readLine(br *bufio.Reader) (string, error) {
1437 b, err := br.ReadSlice('\n')
1438 if err != nil {
1439 return "", err
1440 }
1441 return string(b[:len(b)-1]), nil
1442}
1443
1444func writeLineThenClose(c net.Conn, line string) {
1445 _ = c.SetWriteDeadline(time.Now().Add(2 * time.Second))
1446 _, _ = io.WriteString(c, line+"\n")
1447 _ = c.Close()
1448}
1449
1450// serveOwnName terminates the DEVICE-facing TLS on the node's own name (its public-CA certificate, TLS 1.3, ALPN all-tunnel/2), reads the first line
1451// and dispatches HELLO or ATTACH. This is the tunnel's own connection to its own client, never a client's TLS. It reports whether the connection was
1452// handed to a stream (an ATTACH that matched), in which case the caller must not close it.
1453func (s *Server) serveOwnName(raw net.Conn, peeked []byte, release func(control bool)) bool {
1454 tc := tls.Server(&prefixConn{Conn: raw, prefix: peeked}, s.tlsCfg)
1455 _ = tc.SetDeadline(time.Now().Add(s.cfg.FirstLineTimeout))
1456 if err := tc.Handshake(); err != nil {
1457 s.log.Debug("control_tls_failed")
1458 return false
1459 }
1460 if tc.ConnectionState().NegotiatedProtocol != policyData.Protocol.ALPN {
1461 s.log.Info("control_reject", "reason", "bad_alpn")
1462 return false
1463 }
1464 br := bufio.NewReaderSize(tc, policyData.Protocol.LineMaxBytes)
1465 line, err := readLine(br)
1466 if err != nil {
1467 if errors.Is(err, bufio.ErrBufferFull) {
1468 writeLineThenClose(tc, errLine(codeLineTooLong))
1469 } else {
1470 s.log.Info("control_reject", "reason", "bad_first_line")
1471 }
1472 return false
1473 }
1474 first, perr := parseLine(line, dirD2N)
1475 if perr != nil {
1476 // An ATTACH or a HELLO with a bad field answers with its own word; anything else is not a command.
1477 code := errCode(perr)
1478 if v, _, _ := strings.Cut(line, " "); v != "HELLO" && v != "ATTACH" {
1479 code = codeBadCommand // contract section 2: a first line that is not HELLO or ATTACH is bad_command, whatever else is wrong with it
1480 }
1481 s.log.Info("control_reject", "reason", code)
1482 writeLineThenClose(tc, errLine(code))
1483 return false
1484 }
1485 switch first.str("verb") {
1486 case "HELLO":
1487 cc := s.newCtrl(tc)
1488 s.emit(s.now(), map[string]any{"ev": "recv", "conn": cc.id, "line": line})
1489 if s.draining.Load() {
1490 // A draining node takes no new device either: it is told to try the other node. (An ATTACH for a stream that already has its OPEN is served.)
1491 s.log.Info("control_reject", "conn", cc.id, "reason", "draining")
1492 _ = cc.write(errLine(codeControlUnavailable))
1493 cc.close("refused")
1494 return false
1495 }
1496 s.serveControl(cc, br, first, release)
1497 return false
1498 case "ATTACH":
1499 return s.serveAttach(tc, br, first, line, fmt.Sprintf("d%d", s.connSeq.Add(1)), release)
1500 }
1501 s.log.Info("control_reject", "reason", codeBadCommand)
1502 writeLineThenClose(tc, errLine(codeBadCommand))
1503 return false
1504}
1505
1506// exchange is one authentication in flight on a control connection: the HELLO that opened it, or a RENEW.
1507type exchange struct {
1508 kind string // "hello" or "renew"
1509 token string
1510 session *SessionClaims
1511 nonce string
1512 deadline time.Time
1513}
1514
1515// refuse answers a failed HELLO or RENEW (or PROOF) with ERR. A refused HELLO closes the connection; a refused RENEW keeps it, with its old
1516// valid_until.
1517func (s *Server) refuse(cc *ctrl, kind, code string) bool {
1518 _ = cc.write(errLine(code))
1519 if kind == "hello" {
1520 cc.close("refused")
1521 return false
1522 }
1523 return true
1524}
1525
1526// beginExchange verifies the session of a HELLO or RENEW and, when it holds, sends the CHALLENGE. A RENEW must be for the same subject as the
1527// connection. It reports whether the connection goes on.
1528func (s *Server) beginExchange(cc *ctrl, ex *exchange, kind, token string) bool {
1529 *ex = exchange{}
1530 now := s.now()
1531 session, err := verifySession(token, now, s.cfg.Keys, s.cfg.ClockSkew)
1532 if err != nil {
1533 code := errFor(tokenWord(err), codeAuth)
1534 if kh, ok := s.cfg.Keys.(keyHealth); ok && tokenWord(err) == tokUnknownKID && !kh.Healthy() {
1535 // The node cannot vouch for the control plane's keys (stale beyond the policy's window): not the device's fault, so not auth.
1536 code = codeControlUnavailable
1537 if kind == "renew" {
1538 // ... and it is the control plane being unreachable, which is what a paid device's grace is for: without this the connection closed at valid_until although the
1539 // device did everything right (its session was refused only because the node could not read the control plane's keys).
1540 cc.markRenew(true, false)
1541 }
1542 }
1543 s.log.Info("session_refused", "conn", cc.id, "kind", kind, "reason", tokenWord(err), "answer", code)
1544 return s.refuse(cc, kind, code)
1545 }
1546 if kind == "renew" && session.Subject != cc.subject() {
1547 s.log.Info("session_refused", "conn", cc.id, "kind", kind, "reason", "other_subject")
1548 return s.refuse(cc, kind, codeAuth)
1549 }
1550 *ex = exchange{kind: kind, token: token, session: session, nonce: newNonce(), deadline: now.Add(s.cfg.FirstLineTimeout)}
1551 if err := cc.write(formatLine(message{"verb": "CHALLENGE", "nonce": ex.nonce})); err != nil {
1552 cc.close("write_error")
1553 return false
1554 }
1555 return true
1556}
1557
1558// serveControl runs a control connection from its HELLO to its end. release gives back the unauthenticated slot once the device has authenticated.
1559func (s *Server) serveControl(cc *ctrl, br *bufio.Reader, hello message, release func(control bool)) {
1560 tc := cc.conn
1561 defer cc.close("closed")
1562 defer s.routes.remove(cc) // idempotent: a close that raced an activation must not leave a route behind
1563 start := s.now()
1564 ex := &exchange{}
1565 if !s.beginExchange(cc, ex, "hello", hello.str("session")) {
1566 return
1567 }
1568 reason := ""
1569 for {
1570 deadline := s.now().Add(s.cfg.DeadPeer)
1571 if !cc.isReady() || ex.nonce != "" {
1572 deadline = s.now().Add(s.cfg.FirstLineTimeout)
1573 if !ex.deadline.IsZero() && ex.deadline.Before(deadline) {
1574 deadline = ex.deadline
1575 }
1576 }
1577 _ = tc.SetReadDeadline(deadline)
1578 line, err := readLine(br)
1579 if err != nil {
1580 switch {
1581 case errors.Is(err, bufio.ErrBufferFull):
1582 _ = cc.write(errLine(codeLineTooLong))
1583 reason = closeProtocol
1584 case errors.Is(err, os.ErrDeadlineExceeded) && ex.nonce != "" && cc.isReady():
1585 // The device never answered a RENEW's CHALLENGE: the exchange is dropped, the connection and its old valid_until stay.
1586 *ex = exchange{}
1587 continue
1588 case errors.Is(err, os.ErrDeadlineExceeded):
1589 reason = closeIdle
1590 default:
1591 reason = "closed"
1592 }
1593 cc.close(reason)
1594 s.log.Info("control_down", "conn", cc.id, "duration_ms", s.now().Sub(start).Milliseconds(), "reason", cc.closeReason())
1595 return
1596 }
1597 s.emit(s.now(), map[string]any{"ev": "recv", "conn": cc.id, "line": line})
1598 m, perr := parseLine(line, dirD2N)
1599 if perr != nil {
1600 _ = cc.write(errLine(errCode(perr)))
1601 cc.close(closeProtocol)
1602 return
1603 }
1604 switch m.str("verb") {
1605 case "PROOF":
1606 if ex.nonce == "" {
1607 _ = cc.write(errLine(codeBadCommand))
1608 cc.close(closeProtocol)
1609 return
1610 }
1611 if !s.finishExchange(cc, ex, m.str("proof"), release) {
1612 return
1613 }
1614 case "RENEW":
1615 if !cc.isReady() || ex.nonce != "" {
1616 _ = cc.write(errLine(codeBadCommand))
1617 cc.close(closeProtocol)
1618 return
1619 }
1620 if cc.tooSoon(s.now(), s.cfg.MinRenewGap) {
1621 // a device cannot turn one connection into a stream of authorize calls: the connection and its old valid_until stay
1622 if err := cc.write(errLine(codeBusy)); err != nil {
1623 cc.close("write_error")
1624 return
1625 }
1626 continue
1627 }
1628 if !s.beginExchange(cc, ex, "renew", m.str("session")) {
1629 return
1630 }
1631 case "PING":
1632 if err := cc.write(formatLine(message{"verb": "PONG"})); err != nil {
1633 cc.close("write_error")
1634 return
1635 }
1636 case "HINT":
1637 if cc.isReady() {
1638 var addrs []netip.Addr
1639 for _, a := range m["addresses"].([]any) {
1640 if addr, err := netip.ParseAddr(a.(string)); err == nil {
1641 addrs = append(addrs, addr)
1642 }
1643 }
1644 // filed under the host the control plane gave THIS connection: a device can mark addresses for no other host than its own
1645 s.hints.mark(cc.hostName(), addrs)
1646 s.met.hintsMarked.Add(1)
1647 if s.cfg.OnHint != nil {
1648 s.cfg.OnHint(cc.hostName(), addrs)
1649 }
1650 }
1651 case "CLOSE":
1652 cc.close("device_close")
1653 s.log.Info("control_down", "conn", cc.id, "duration_ms", s.now().Sub(start).Milliseconds(), "reason", "device_close")
1654 return
1655 default: // HELLO again, or anything else the device must not send here
1656 _ = cc.write(errLine(codeBadCommand))
1657 cc.close(closeProtocol)
1658 return
1659 }
1660 }
1661}
1662
1663// finishExchange checks the PROOF of the exchange in flight and, when it holds, asks the control plane what the device may do and answers OK. It
1664// reports whether the connection goes on.
1665func (s *Server) finishExchange(cc *ctrl, ex *exchange, proof string, release func(control bool)) bool {
1666 cur := *ex
1667 *ex = exchange{} // the nonce is spent whatever the verdict
1668 now := s.now()
1669 pc, err := verifyProof(proof, now, cur.token, cur.session, cur.nonce, s.nodeURL, s.cfg.ClockSkew)
1670 if err == nil && !s.replay.spend(pc.JTI, pc.IssuedAt, now, s.cfg.ClockSkew) {
1671 err = tokFail(tokReplayedJTI)
1672 }
1673 if err != nil {
1674 s.log.Info("proof_refused", "conn", cc.id, "kind", cur.kind, "reason", tokenWord(err))
1675 return s.refuse(cc, cur.kind, errFor(tokenWord(err), codeBadProof))
1676 }
1677
1678 // The device has proven itself, so it no longer holds a pre-authentication slot: a slow control plane must not starve the ClientHellos of
1679 // clients and the ATTACHes of devices that are already connected. What waits for authorize is bounded on its own.
1680 if cur.kind == "hello" {
1681 release(true)
1682 }
1683 if s.killed.Load() {
1684 // The kill switch is on: nobody is served, and nothing is asked of the control plane until it is cleared.
1685 s.log.Info("authorize_skipped", "conn", cc.id, "kind", cur.kind, "reason", "kill_switch")
1686 return s.refuse(cc, cur.kind, codeControlUnavailable)
1687 }
1688
1689 // The control plane decides; nothing the device said names a host. Until its answer is acted on the feed is listened to: an event the feed
1690 // applies while the call is in flight is not in the answer, and cannot find a connection that is not yet in the routing table.
1691 watch := s.watchFeed()
1692 defer watch.stop()
1693 req := AuthorizeRequest{DeviceID: cur.session.Subject, SessionJTI: cur.session.JTI, Reason: cur.kind, Node: s.cfg.NodeID}
1694 s.emit(s.now(), map[string]any{"conn": cc.id, "ev": "cp", "call": "authorize", "body": req.toMap()})
1695 resp, err := s.authorize(req)
1696 if err == nil {
1697 if cerr := checkAuthorize(resp, req); cerr != nil {
1698 s.log.Warn("authorize_answer_refused", "conn", cc.id, "reason", cerr.Error())
1699 s.health.bad(s.now())
1700 err = &ControlError{Status: 502, Code: "bad_response"}
1701 }
1702 }
1703 if err != nil {
1704 status, word := 503, "control_unavailable"
1705 if ce, ok := err.(*ControlError); ok {
1706 status, word = ce.Status, ce.Code
1707 }
1708 s.emit(s.now(), map[string]any{"conn": cc.id, "ev": "cp_reply", "status": status, "body": map[string]any{"ok": false, "error": word}})
1709 code := errForControl(err)
1710 if cur.kind == "renew" && !isLocalFailure(err) {
1711 // A RENEW this node could not even send (its own queue was full or its turn did not come) says nothing about the control plane.
1712 cc.markRenew(code == codeControlUnavailable, code != codeControlUnavailable)
1713 }
1714 s.log.Info("authorize_refused", "conn", cc.id, "kind", cur.kind, "reason", code)
1715 return s.refuse(cc, cur.kind, code)
1716 }
1717 s.emit(s.now(), map[string]any{"conn": cc.id, "ev": "cp_reply", "status": 200, "body": resp.toMap()})
1718 if resp.E2EKill {
1719 // The device is told first (the switch closes every connection, this one included), then nobody is served until it is cleared.
1720 s.log.Warn("kill_switch_seen", "conn", cc.id, "via", "authorize")
1721 s.refuse(cc, cur.kind, codeControlUnavailable)
1722 s.setKilled(true)
1723 return false
1724 }
1725
1726 validUntil := time.Unix(resp.ValidUntil, 0)
1727 if !validUntil.After(s.now()) {
1728 // A grant that has already ended is never relayed.
1729 return s.refuse(cc, cur.kind, codeAuth)
1730 }
1731 if v := watch.verdict(resp.Host, resp.ServerTime); v.cut != "" {
1732 // A revoke, a release or the end of the license was applied while the call was in flight: the answer is older than it. Nothing is served.
1733 s.log.Info("authorize_overtaken", "conn", cc.id, "kind", cur.kind, "reason", v.cut)
1734 if cur.kind != "hello" {
1735 cc.close(v.cut)
1736 return false
1737 }
1738 return s.refuse(cc, cur.kind, v.err)
1739 }
1740 ok := okBody{Node: s.cfg.NodeID, Host: resp.Host, Status: resp.Status, ValidUntil: resp.ValidUntil, ServerTime: resp.ServerTime, Policy: boundWindows(resp.Policy)}
1741
1742 // Publish and answer under the write lock, so no OPEN can be sent before the OK line.
1743 cc.wmu.Lock()
1744 var old *ctrl
1745 recorded := false
1746 if cur.kind == "hello" {
1747 // The OK is recorded under the routing table's lock, so two HELLOs of one subject that race are recorded in the order they were routed.
1748 old, err = s.routes.activate(cc, cur.session.Subject, resp.Host, func() {
1749 recorded = true
1750 s.emit(s.now(), map[string]any{"conn": cc.id, "ev": "send", "line": ok.line()})
1751 })
1752 if err != nil {
1753 cc.wmu.Unlock()
1754 s.log.Warn("host_refused", "conn", cc.id, "reason", err.Error())
1755 return s.refuse(cc, cur.kind, codeAuth)
1756 }
1757 } else if cc.hostName() != resp.Host {
1758 // The control plane now says this device owns another host: the one this connection serves is no longer its own. Fail closed.
1759 cc.wmu.Unlock()
1760 s.log.Warn("renew_host_changed", "conn", cc.id)
1761 cc.close(closeRevoked)
1762 return false
1763 }
1764 cc.setReady(resp, cur.session.JTI, validUntil)
1765 s.routes.setGated(cc, resp.Status == statusGated) // every status the control plane gives, a RENEW's included, decides what the host's streams are held to
1766 // The connection is in the routing table now, so the feed reaches it directly; what it applied before that is taken from the watch. A pause is
1767 // put back at once (setReady wrote the answer's status over it), and the control plane is asked again.
1768 late := watch.verdict(resp.Host, resp.ServerTime)
1769 if late.paused {
1770 cc.pause()
1771 }
1772 var werr error
1773 if recorded {
1774 werr = cc.sendLocked(ok.line())
1775 } else {
1776 werr = cc.writeLocked(ok.line(), s.now())
1777 }
1778 if werr == nil {
1779 werr = s.followUp(cc, resp)
1780 }
1781 cc.wmu.Unlock()
1782 if old != nil {
1783 old.close(closeReplaced)
1784 }
1785 if werr != nil {
1786 cc.close("write_error")
1787 return false
1788 }
1789 if late.cut != "" {
1790 // Applied between the check above and the connection joining the routing table: the OK is out, and the CLOSE follows it.
1791 cc.close(late.cut)
1792 s.routes.killStreams(resp.Host, late.cut, true)
1793 return false
1794 }
1795 if late.refresh {
1796 if late.paused {
1797 s.routes.killStreams(resp.Host, closePaused, true)
1798 }
1799 s.refreshAsync(cc)
1800 }
1801 cc.arm(validUntil)
1802 if cur.kind == "hello" {
1803 s.log.Info("control_up", "conn", cc.id, "status", resp.Status, "plan", resp.Plan, "replaced_previous", old != nil)
1804 } else {
1805 s.log.Info("control_renewed", "conn", cc.id, "status", resp.Status, "plan", resp.Plan)
1806 }
1807 return true
1808}
1809
1810// followUp writes what goes right after an OK, under the connection's write lock so that no OPEN comes between: the control plane's signed access
1811// statement, forwarded as it is (the device verifies it, the node cannot change a letter of it without breaking it), and DRAIN when the node is
1812// already draining (a connection that was authenticating when SIGTERM came).
1813func (s *Server) followUp(cc *ctrl, resp AuthorizeResponse) error {
1814 if line := statementLine(resp.Access); resp.Access != "" && line != "" {
1815 if err := cc.writeLocked(line, s.now()); err != nil {
1816 return err
1817 }
1818 }
1819 if s.draining.Load() {
1820 return cc.writeLocked(drainLine(time.Duration(s.drainFor.Load())), s.now())
1821 }
1822 return nil
1823}
1824
1825// The two ways authorize fails without having asked the control plane anything: the node's own queue was full, or its turn did not come in time.
1826var (
1827 errAuthorizeFull = &ControlError{Status: 503, Code: codeControlUnavailable}
1828 errAuthorizeWait = &ControlError{Status: 503, Code: codeControlUnavailable}
1829)
1830
1831func isLocalFailure(err error) bool { return err == errAuthorizeFull || err == errAuthorizeWait }
1832
1833// authorize asks the control plane for the verdict on one exchange. At most cfg.AuthorizeMax calls run at once; the exchanges waiting for a turn
1834// are bounded by the pre-authentication limit and share the call's time limit, and beyond either the device is told control_unavailable. What the
1835// call itself came to is recorded, so the node knows whether its control plane answers.
1836func (s *Server) authorize(req AuthorizeRequest) (AuthorizeResponse, error) {
1837 if !tryAcquire(&s.authQ, int64(s.cfg.MaxUnauth)) {
1838 s.met.authorize.inc("unavailable")
1839 return AuthorizeResponse{}, errAuthorizeFull
1840 }
1841 defer s.authQ.Add(-1)
1842 ctx, cancel := context.WithTimeout(s.ctx, s.cfg.AuthorizeTimeout)
1843 defer cancel()
1844 select {
1845 case s.authSem <- struct{}{}:
1846 defer func() { <-s.authSem }()
1847 case <-ctx.Done():
1848 s.met.authorize.inc("unavailable")
1849 return AuthorizeResponse{}, errAuthorizeWait
1850 }
1851 resp, err := s.cfg.Control.Authorize(ctx, req)
1852 s.health.record(s.now(), err)
1853 switch {
1854 case err == nil:
1855 s.met.authorize.inc("ok")
1856 case isRefusal(err):
1857 s.met.authorize.inc("refused")
1858 default:
1859 s.met.authorize.inc("unavailable")
1860 }
1861 return resp, err
1862}
1863
1864// serveAttach hands a data connection to the stream its ATTACH names. The secret is one use: a wrong one is refused and does not consume the stream,
1865// a used or unknown id is unknown_stream. On success there is NO reply: the next byte the node sends is the first byte of the client's stream.
1866func (s *Server) serveAttach(tc *tls.Conn, br *bufio.Reader, m message, line, connName string, release func(control bool)) bool {
1867 id, secret := m.str("stream_id"), m.str("secret")
1868 verdict := func(result string) {
1869 s.emit(s.now(), map[string]any{"conn": connName, "ev": "attach", "line": line, "result": result})
1870 }
1871 s.mu.Lock()
1872 p := s.pending[id]
1873 switch {
1874 case p == nil:
1875 s.mu.Unlock()
1876 verdict("err")
1877 s.log.Info("attach_reject", "reason", codeUnknownStream)
1878 writeLineThenClose(tc, errLine(codeUnknownStream))
1879 return false
1880 case subtle.ConstantTimeCompare([]byte(p.secret), []byte(secret)) != 1:
1881 s.mu.Unlock()
1882 verdict("err")
1883 s.log.Info("attach_reject", "reason", codeAuth, "stream", id)
1884 writeLineThenClose(tc, errLine(codeAuth))
1885 return false
1886 }
1887 delete(s.pending, id) // single use
1888 _ = tc.SetDeadline(time.Time{})
1889 verdict("ok")
1890 p.ch <- &bufConn{Conn: tc, r: br} // buffered(1): never blocks; the stream goroutine owns tc from here
1891 s.mu.Unlock()
1892 release(false)
1893 return true
1894}