carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package relay
2
3import (
4 "encoding/binary"
5 "errors"
6 "fmt"
7 "math/rand/v2"
8 "net"
9 "sort"
10 "strconv"
11 "sync"
12 "time"
13
14 "github.com/pion/logging"
15 "github.com/pion/stun/v3"
16 "github.com/pion/turn/v4"
17)
18
19// Realm is the TURN realm every credential is minted and verified under.
20const Realm = "carlos"
21
22// pendingLife is how long a socket or an admission may sit unclaimed
23// before tick reclaims it. Both are consumed within one request in the
24// normal path; this only catches an allocation that failed after pion had
25// already asked us for a socket.
26const pendingLife = 30 * time.Second
27
28// authMemory is how long the last parsed username for a typed source is
29// kept once that source has no allocation, so a client that authenticates
30// and never allocates cannot grow the map without bound.
31const authMemory = 10 * time.Minute
32
33const (
34 // maxRefusalIdentities and maxLoggedRefusals bound the two maps an
35 // unauthenticated client can otherwise grow: pion calls AuthHandler
36 // with the raw username before it checks MESSAGE-INTEGRITY, so every
37 // counter keyed on something the caller chose is remote memory. Only
38 // identities in the authority record get their own row; everything
39 // else shares the zero identity, and these caps are the backstop.
40 maxRefusalIdentities = 4096
41 maxLoggedRefusals = 4096
42
43 // refusalLogEvery is how often one identity's one refusal reason may
44 // put a line in the log.
45 refusalLogEvery = time.Minute
46
47 // minLastAuthEntries is the floor under Limits.BoxLive*2 for how many
48 // typed sources may hold a remembered username at once.
49 minLastAuthEntries = 256
50)
51
52var errTCPAllocUnsupported = errors.New("relay: TCP relay allocations are not supported")
53
54// ServerConfig is everything the relay needs from its process: where to
55// listen, what to advertise, and the four live views (masters, authority,
56// lease, limits) it reads on every request.
57type ServerConfig struct {
58 Region string
59 RelayIP net.IP // the EIP advertised to clients
60 UDP, TCP string // listen addresses, e.g. ":3478"; empty disables
61 MinPort, MaxPort uint16 // 49152, 65535
62 DenyCIDRs []*net.IPNet // from CARLOS_RELAY_DENY_CIDRS, on top of the built-in private ranges
63 ExtraAllow []*net.IPNet // peer ranges allowed despite the built-in list; empty in production
64 Masters func() *Masters
65 Authority func() *Authority
66 Lease func(now time.Time) bool
67 Limits Limits
68 Now func() time.Time
69 Log func(string, ...any)
70
71 // Meter is the byte meter (Task 7); nil disables metering. It is an
72 // interface so this file does not depend on the meter's shape.
73 Meter interface {
74 Add(id Identity, in, out, rejected int64)
75 }
76}
77
78// Server is the relay: a pion TURN server whose every socket we own, so
79// that an allocation can be ended, attributed and bounded from outside
80// pion, which exposes no way to do any of it.
81type Server struct {
82 cfg ServerConfig
83 now func() time.Time
84 log func(string, ...any)
85
86 adm *Admission
87 box *Bucket
88 ports *portSet
89 deny []*net.IPNet
90 allow []*net.IPNet
91 local []net.IP // our listeners' own addresses, when not wildcard
92 maxLastAuth int
93
94 turn *turn.Server
95 udp net.PacketConn
96 tcp net.Listener
97 listeners []string
98
99 mu sync.Mutex
100 seq int64
101 pending map[int]pendingConn // relay port -> socket awaiting OnAllocationCreated
102 lastAuth map[string]authRec // typed source -> the username it last authenticated with
103 admits map[string]admitRec // typed source -> an admission not yet owned by an allocation
104 bound map[string]*Binding // typed source -> the identity it is bound to
105 allocs map[string]*allocEntry // typed source -> its live allocation
106 owners map[*relayConn]*allocEntry // socket -> what closing it must undo
107 byPort map[int]*relayConn // relay port -> its socket, however far the allocation got
108 refusals map[Identity]map[string]int
109 logged map[logKey]time.Time
110 shedding map[Identity]bool
111}
112
113type logKey struct {
114 id Identity
115 reason string
116}
117
118type allocEntry struct {
119 conn *relayConn
120 id Identity
121 key string // the typed source this allocation was created for
122 seq int64
123 timer *time.Timer
124 flow *flow
125}
126
127type pendingConn struct {
128 conn *relayConn
129 flow *flow
130 at time.Time
131}
132
133// flow is one allocation's peer-to-client accounting. pion's allocation
134// reads a peer packet and then either forwards it to the client or drops
135// it for want of a permission, both on the allocation's own goroutine, so
136// a packet that is still unforwarded when the next one arrives (or when
137// the socket closes) is a packet that was dropped. That is what spec §3
138// counts as `rejected`, and it is never charged to a budget.
139type flow struct {
140 mu sync.Mutex
141 awaiting int64 // size of the peer packet read and not yet forwarded
142}
143
144// read records a peer packet and returns the bytes of the previous one if
145// it was never forwarded.
146func (f *flow) read(n int) int64 {
147 f.mu.Lock()
148 defer f.mu.Unlock()
149 dropped := f.awaiting
150 f.awaiting = int64(n)
151 return dropped
152}
153
154// forwarded clears the packet the listener has just written to the client.
155func (f *flow) forwarded() {
156 f.mu.Lock()
157 f.awaiting = 0
158 f.mu.Unlock()
159}
160
161// flush is the last packet's verdict when the socket closes.
162func (f *flow) flush() int64 {
163 f.mu.Lock()
164 defer f.mu.Unlock()
165 dropped := f.awaiting
166 f.awaiting = 0
167 return dropped
168}
169
170// readCounter is the relay socket's peer-ingress leg. It sits under
171// relayConn so that relayConn keeps counting only what it forwards.
172type readCounter struct {
173 net.PacketConn
174 f *flow
175 onDrop func(int64)
176}
177
178func (r *readCounter) ReadFrom(p []byte) (int, net.Addr, error) {
179 n, addr, err := r.PacketConn.ReadFrom(p)
180 if n > 0 {
181 if dropped := r.f.read(n); dropped > 0 && r.onDrop != nil {
182 r.onDrop(dropped)
183 }
184 }
185 return n, addr, err
186}
187
188type authRec struct {
189 u Username
190 at time.Time
191}
192
193type admitRec struct {
194 id Identity
195 at time.Time
196}
197
198// builtinDeny is the peer policy every relay carries whatever the operator
199// configures: loopback, link-local (the IMDS address included), RFC 1918,
200// unique-local and carrier-grade NAT space; "this network" (0.0.0.0/8, which
201// Linux delivers to the local host, not only the unspecified address), the
202// IETF protocol-assignment block (192.0.0.0/24) and the benchmarking range
203// (198.18.0.0/15), none of which is a public peer (discussion #220).
204//
205// IPv6 forms that carry an IPv4 address — NAT64 (64:ff9b::/96) and 6to4
206// (2002::/16) — are not listed: they are judged by the address they embed
207// (see embeddedV4), so a NAT64 gateway cannot be used to reach 10.0.0.1 and
208// a public address behind either one still works.
209var builtinDeny = mustCIDRs(
210 "127.0.0.0/8", "::1/128",
211 "169.254.0.0/16", "fe80::/10",
212 "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16",
213 "fc00::/7", "100.64.0.0/10",
214 "0.0.0.0/8", "192.0.0.0/24", "198.18.0.0/15",
215)
216
217// Prefixes whose addresses embed an IPv4 address: the NAT64 well-known
218// prefix carries it in the last 32 bits (RFC 6052), 6to4 in bits 16-47
219// (RFC 3056).
220var (
221 nat64Prefix = mustCIDRs("64:ff9b::/96")[0]
222 sixToFour = mustCIDRs("2002::/16")[0]
223)
224
225// embeddedV4 returns the IPv4 address a NAT64 or 6to4 peer stands for, or
226// nil for any other address. An IPv4-mapped address (::ffff:a.b.c.d) needs
227// no help: net.IPNet.Contains already matches it against the IPv4 ranges.
228func embeddedV4(ip net.IP) net.IP {
229 ip16 := ip.To16()
230 if ip16 == nil || ip.To4() != nil {
231 return nil
232 }
233 switch {
234 case nat64Prefix.Contains(ip16):
235 return net.IPv4(ip16[12], ip16[13], ip16[14], ip16[15])
236 case sixToFour.Contains(ip16):
237 return net.IPv4(ip16[2], ip16[3], ip16[4], ip16[5])
238 }
239 return nil
240}
241
242func mustCIDRs(ss ...string) []*net.IPNet {
243 out := make([]*net.IPNet, 0, len(ss))
244 for _, s := range ss {
245 _, n, err := net.ParseCIDR(s)
246 if err != nil {
247 panic("relay: bad built-in CIDR " + s + ": " + err.Error())
248 }
249 out = append(out, n)
250 }
251 return out
252}
253
254func NewServer(cfg ServerConfig) (*Server, error) {
255 if cfg.Now == nil {
256 cfg.Now = time.Now
257 }
258 if cfg.Log == nil {
259 cfg.Log = func(string, ...any) {}
260 }
261 if cfg.MinPort == 0 {
262 cfg.MinPort = 49152
263 }
264 if cfg.MaxPort == 0 {
265 cfg.MaxPort = 65535
266 }
267 switch {
268 case cfg.RelayIP == nil:
269 return nil, errors.New("relay: RelayIP is required")
270 case cfg.MinPort > cfg.MaxPort:
271 return nil, fmt.Errorf("relay: port range %d-%d is empty", cfg.MinPort, cfg.MaxPort)
272 case cfg.Masters == nil || cfg.Authority == nil || cfg.Lease == nil:
273 return nil, errors.New("relay: Masters, Authority and Lease are required")
274 case cfg.UDP == "" && cfg.TCP == "":
275 return nil, errors.New("relay: no listener configured")
276 }
277
278 maxLastAuth := cfg.Limits.BoxLive * 2
279 if maxLastAuth < minLastAuthEntries {
280 maxLastAuth = minLastAuthEntries
281 }
282 s := &Server{
283 cfg: cfg,
284 now: cfg.Now,
285 log: cfg.Log,
286 adm: NewAdmission(cfg.Limits, cfg.Now),
287 box: NewBucket(cfg.Limits.BoxBitsPerSec, cfg.Now),
288 ports: &portSet{m: map[int]bool{}},
289 deny: append(append([]*net.IPNet{}, builtinDeny...), cfg.DenyCIDRs...),
290 allow: append([]*net.IPNet{}, cfg.ExtraAllow...),
291 maxLastAuth: maxLastAuth,
292 pending: map[int]pendingConn{},
293 lastAuth: map[string]authRec{},
294 admits: map[string]admitRec{},
295 bound: map[string]*Binding{},
296 allocs: map[string]*allocEntry{},
297 owners: map[*relayConn]*allocEntry{},
298 byPort: map[int]*relayConn{},
299 refusals: map[Identity]map[string]int{},
300 logged: map[logKey]time.Time{},
301 shedding: map[Identity]bool{},
302 }
303
304 var packets []turn.PacketConnConfig
305 var listeners []turn.ListenerConfig
306 if cfg.UDP != "" {
307 pc, err := net.ListenPacket("udp", cfg.UDP)
308 if err != nil {
309 return nil, fmt.Errorf("relay: listen udp %s: %w", cfg.UDP, err)
310 }
311 s.udp = pc
312 s.noteLocal(pc.LocalAddr())
313 s.listeners = append(s.listeners, "udp:"+portOf(pc.LocalAddr()))
314 packets = append(packets, turn.PacketConnConfig{
315 PacketConn: &listenerConn{PacketConn: pc, s: s},
316 RelayAddressGenerator: s,
317 PermissionHandler: s.permit,
318 })
319 }
320 if cfg.TCP != "" {
321 l, err := net.Listen("tcp", cfg.TCP)
322 if err != nil {
323 if s.udp != nil {
324 s.udp.Close()
325 }
326 return nil, fmt.Errorf("relay: listen tcp %s: %w", cfg.TCP, err)
327 }
328 s.tcp = l
329 s.noteLocal(l.Addr())
330 s.listeners = append(s.listeners, "tcp:"+portOf(l.Addr()))
331 listeners = append(listeners, turn.ListenerConfig{
332 Listener: &countingListener{Listener: l, s: s},
333 RelayAddressGenerator: s,
334 PermissionHandler: s.permit,
335 })
336 }
337
338 ts, err := turn.NewServer(turn.ServerConfig{
339 Realm: Realm,
340 AuthHandler: s.auth,
341 QuotaHandler: s.quota,
342 EventHandler: turn.EventHandler{OnAllocationCreated: s.created},
343 PacketConnConfigs: packets,
344 ListenerConfigs: listeners,
345 LoggerFactory: logging.NewDefaultLoggerFactory(),
346 })
347 if err != nil {
348 if s.udp != nil {
349 s.udp.Close()
350 }
351 if s.tcp != nil {
352 s.tcp.Close()
353 }
354 return nil, fmt.Errorf("relay: pion server: %w", err)
355 }
356 s.turn = ts
357 return s, nil
358}
359
360func (s *Server) noteLocal(a net.Addr) {
361 var ip net.IP
362 switch v := a.(type) {
363 case *net.UDPAddr:
364 ip = v.IP
365 case *net.TCPAddr:
366 ip = v.IP
367 }
368 if ip != nil && !ip.IsUnspecified() {
369 s.local = append(s.local, ip)
370 }
371}
372
373func portOf(a net.Addr) string {
374 switch v := a.(type) {
375 case *net.UDPAddr:
376 return strconv.Itoa(v.Port)
377 case *net.TCPAddr:
378 return strconv.Itoa(v.Port)
379 }
380 _, p, err := net.SplitHostPort(a.String())
381 if err != nil {
382 return a.String()
383 }
384 return p
385}
386
387// Listeners names the transports that are up, e.g. ["udp:3478","tcp:3478"],
388// for the heartbeat.
389func (s *Server) Listeners() []string { return append([]string{}, s.listeners...) }
390
391// UDPAddr is the UDP listener's bound address, or nil if UDP is disabled.
392func (s *Server) UDPAddr() net.Addr {
393 if s.udp == nil {
394 return nil
395 }
396 return s.udp.LocalAddr()
397}
398
399// Live is the number of allocations the relay is carrying.
400func (s *Server) Live() int {
401 s.mu.Lock()
402 defer s.mu.Unlock()
403 return len(s.allocs)
404}
405
406// LiveByIdentity is Live broken down per app, for the meter's `allocs`
407// gauge (spec §3, "Metering"). The Meter the server holds is narrowed to
408// Add, deliberately — the server counts bytes as it forwards them and knows
409// nothing about sample boundaries — so the gauge is the serve loop's job to
410// set, and this is where it reads it from.
411func (s *Server) LiveByIdentity() map[Identity]int {
412 s.mu.Lock()
413 defer s.mu.Unlock()
414 out := make(map[Identity]int, len(s.allocs))
415 for _, e := range s.allocs {
416 out[e.id]++
417 }
418 return out
419}
420
421// Seed replays metered bytes into the rolling-hour windows, so a restart
422// resumes its budgets from the metrics objects it wrote.
423func (s *Server) Seed(id Identity, at time.Time, n int64) { s.adm.Seed(id, at, n) }
424
425// Close ends every allocation and stops listening.
426func (s *Server) Close() error {
427 s.EndAll("server closing")
428 s.mu.Lock()
429 pending := make([]*relayConn, 0, len(s.pending))
430 for _, p := range s.pending {
431 pending = append(pending, p.conn)
432 }
433 s.mu.Unlock()
434 for _, c := range pending {
435 c.Close()
436 }
437 if s.turn == nil {
438 return nil
439 }
440 return s.turn.Close()
441}
442
443// EndAll ends every allocation: a lease lapse, or shutdown.
444func (s *Server) EndAll(reason string) {
445 s.EndWhere(func(*Binding) bool { return true }, reason)
446}
447
448// EndWhere ends every allocation whose binding matches: a disable, an
449// incarnation change, this region leaving an app's list, a byte budget, a
450// retired master. The predicate is called on a copy of the binding, off
451// the server's lock.
452func (s *Server) EndWhere(match func(*Binding) bool, reason string) {
453 type candidate struct {
454 conn *relayConn
455 b Binding
456 }
457 s.mu.Lock()
458 cands := make([]candidate, 0, len(s.allocs))
459 for _, e := range s.allocs {
460 cands = append(cands, candidate{conn: e.conn, b: *e.conn.b})
461 }
462 s.mu.Unlock()
463
464 n := 0
465 for _, c := range cands {
466 b := c.b
467 if match(&b) {
468 c.conn.Close()
469 n++
470 }
471 }
472 if n > 0 {
473 s.log("relay: ended %d allocation(s): %s", n, reason)
474 }
475}
476
477// ShedOldest ends the n oldest allocations, for memory pressure.
478func (s *Server) ShedOldest(n int) {
479 if n <= 0 {
480 return
481 }
482 s.mu.Lock()
483 entries := make([]*allocEntry, 0, len(s.allocs))
484 for _, e := range s.allocs {
485 entries = append(entries, e)
486 }
487 s.mu.Unlock()
488 sort.Slice(entries, func(i, j int) bool { return entries[i].seq < entries[j].seq })
489 if n > len(entries) {
490 n = len(entries)
491 }
492 for _, e := range entries[:n] {
493 e.conn.Close()
494 }
495 if n > 0 {
496 s.log("relay: shed %d oldest allocation(s)", n)
497 }
498}
499
500// tick is the sweep: it ends allocations whose credential has expired
501// (belt for the per-allocation timers), whose master has retired, whose app
502// or account is over budget, and every allocation at all if the serving
503// lease has lapsed. It also reclaims sockets and admissions that a failed
504// allocation left behind.
505func (s *Server) tick() {
506 now := s.now()
507 masters := s.cfg.Masters()
508 leaseDead := !s.cfg.Lease(now)
509
510 type victim struct {
511 conn *relayConn
512 reason string
513 }
514 var victims []victim
515 var stale []*relayConn
516
517 // A masters file that failed to reload leaves masters nil; that is a
518 // reason to keep serving on the keys we have, not to end anything.
519 retired := func(kid string) bool {
520 if masters == nil || kid == "" {
521 return false
522 }
523 _, ok := masters.Lookup(kid, now)
524 return !ok
525 }
526
527 s.mu.Lock()
528 for _, e := range s.allocs {
529 b := e.conn.b
530 switch {
531 case leaseDead:
532 victims = append(victims, victim{e.conn, "serving lease lapsed"})
533 case !b.Expiry.IsZero() && !b.Expiry.After(now):
534 victims = append(victims, victim{e.conn, "credential expired"})
535 case retired(b.Kid):
536 victims = append(victims, victim{e.conn, "master " + b.Kid + " retired"})
537 case s.adm.OverBudget(e.id, now):
538 victims = append(victims, victim{e.conn, "byte budget"})
539 }
540 }
541 for port, p := range s.pending {
542 if now.Sub(p.at) > pendingLife {
543 delete(s.pending, port)
544 stale = append(stale, p.conn)
545 }
546 }
547 var lapsed []Identity
548 for key, a := range s.admits {
549 if now.Sub(a.at) > pendingLife {
550 delete(s.admits, key)
551 lapsed = append(lapsed, a.id)
552 }
553 }
554 for key, a := range s.lastAuth {
555 if _, live := s.allocs[key]; !live && now.Sub(a.at) > authMemory {
556 delete(s.lastAuth, key)
557 }
558 }
559 // Refusal counters live only for apps the authority still names; the
560 // zero identity is the shared bucket for everything else and stays.
561 for id := range s.refusals {
562 if id == (Identity{}) {
563 continue
564 }
565 if _, ok := s.cfg.Authority().Lookup(id.Account, id.App); !ok {
566 delete(s.refusals, id)
567 }
568 }
569 for k, at := range s.logged {
570 if now.Sub(at) >= refusalLogEvery {
571 delete(s.logged, k)
572 }
573 }
574 s.mu.Unlock()
575
576 for _, id := range lapsed {
577 s.adm.Release(id)
578 }
579 for _, c := range stale {
580 c.Close()
581 }
582 for _, v := range victims {
583 s.log("relay: ending allocation for %s/%s: %s", v.conn.b.ID.Account, v.conn.b.ID.App, v.reason)
584 v.conn.Close()
585 }
586}
587
588// --- turn.RelayAddressGenerator ------------------------------------------
589
590func (s *Server) Validate() error {
591 if s.cfg.RelayIP == nil {
592 return errors.New("relay: RelayIP is required")
593 }
594 if s.cfg.MinPort > s.cfg.MaxPort {
595 return fmt.Errorf("relay: port range %d-%d is empty", s.cfg.MinPort, s.cfg.MaxPort)
596 }
597 return nil
598}
599
600// AllocatePacketConn binds one allocation's relay socket in the configured
601// port range and hands pion our wrapper for it: the wrapper is how the
602// allocation is ended, metered and bounded. The advertised address is the
603// EIP, which is not an address on the box, so the socket itself binds the
604// wildcard.
605func (s *Server) AllocatePacketConn(network string, requestedPort int) (net.PacketConn, net.Addr, error) {
606 if network == "" {
607 network = "udp4"
608 }
609 lo, hi := int(s.cfg.MinPort), int(s.cfg.MaxPort)
610 span := hi - lo + 1
611 tries := span
612 if tries > 64 {
613 tries = 64
614 }
615 ports := make([]int, 0, tries+1)
616 if requestedPort >= lo && requestedPort <= hi {
617 ports = append(ports, requestedPort)
618 }
619 start := rand.IntN(span)
620 for i := 0; i < tries; i++ {
621 ports = append(ports, lo+(start+i)%span)
622 }
623
624 for _, port := range ports {
625 if s.ports.has(port) {
626 continue
627 }
628 pc, err := net.ListenPacket(network, net.JoinHostPort("", strconv.Itoa(port)))
629 if err != nil {
630 continue
631 }
632 bucket := NewBucket(s.cfg.Limits.AllocBitsPerSec, s.now)
633 f := &flow{}
634 var c *relayConn
635 // The socket counts peer ingress under the wrapper, so a peer
636 // packet pion drops for want of a permission is still seen.
637 counted := &readCounter{PacketConn: pc, f: f, onDrop: func(n int64) { s.countRejected(c.b.ID, n) }}
638 c = newRelayConn(counted, &Binding{}, s.cfg.RelayIP, s.ports, bucket, s.box,
639 s.countIn, s.countGated, func(rc *relayConn) { s.release(port, rc) })
640 s.ports.add(port)
641 s.mu.Lock()
642 s.pending[port] = pendingConn{conn: c, flow: f, at: s.now()}
643 s.byPort[port] = c
644 s.mu.Unlock()
645 return c, &net.UDPAddr{IP: s.cfg.RelayIP, Port: port}, nil
646 }
647 return nil, nil, fmt.Errorf("relay: no free port in %d-%d", lo, hi)
648}
649
650// AllocateConn would be a TCP relay allocation (RFC 6062), which we do not
651// offer: a TURN client may reach us over TCP, but every relayed leg is UDP.
652func (s *Server) AllocateConn(string, int) (net.Conn, net.Addr, error) {
653 return nil, nil, errTCPAllocUnsupported
654}
655
656// --- pion handlers -------------------------------------------------------
657
658// auth is pion's AuthHandler: called on every authenticated request, and
659// the whole of spec §4's verification. It returns the key pion checks
660// MESSAGE-INTEGRITY against, so a wrong credential fails inside pion and
661// never reaches here.
662func (s *Server) auth(username, realm string, src net.Addr) ([]byte, bool) {
663 now := s.now()
664 u, err := ParseUsername(username)
665 if err != nil {
666 return s.refused(Identity{}, "bad shape", "relay: auth refused from %s: %v", src, err)
667 }
668 id := Identity{Account: u.Account, App: u.App}
669
670 if !u.Expiry.After(now) || u.Expiry.After(now.Add(MaxCredentialLife)) {
671 return s.refused(id, "expired", "relay: auth refused for %s/%s: expiry %s outside (now, now+%s]",
672 id.Account, id.App, u.Expiry.UTC().Format(time.RFC3339), MaxCredentialLife)
673 }
674
675 s.mu.Lock()
676 bound, isBound := s.bound[typedSrc(src)]
677 s.mu.Unlock()
678 if isBound && (bound.ID != id || bound.Incarnation != u.Incarnation) {
679 return s.refused(id, "bound elsewhere", "relay: auth refused for %s/%s: source %s is bound to %s/%s",
680 id.Account, id.App, typedSrc(src), bound.ID.Account, bound.ID.App)
681 }
682
683 app, ok := s.cfg.Authority().Lookup(u.Account, u.App)
684 switch {
685 case !ok:
686 return s.refused(id, "unknown app", "relay: auth refused for %s/%s: not in the authority record", id.Account, id.App)
687 case app.Incarnation != u.Incarnation:
688 return s.refused(id, "wrong incarnation", "relay: auth refused for %s/%s: incarnation %s, record has %s",
689 id.Account, id.App, u.Incarnation, app.Incarnation)
690 case !contains(app.Regions, s.cfg.Region):
691 return s.refused(id, "wrong region", "relay: auth refused for %s/%s: region %s not in %v",
692 id.Account, id.App, s.cfg.Region, app.Regions)
693 }
694
695 masters := s.cfg.Masters()
696 if masters == nil {
697 return s.refused(id, "unknown kid", "relay: auth refused for %s/%s: no masters loaded", id.Account, id.App)
698 }
699 master, ok := masters.Lookup(u.Kid, now)
700 if !ok {
701 return s.refused(id, "unknown kid", "relay: auth refused for %s/%s: kid %s is unknown or retired",
702 id.Account, id.App, u.Kid)
703 }
704
705 key := DeriveKey(master.Key, u.Account, u.App, u.Incarnation)
706 s.rememberAuth(typedSrc(src), u, now)
707 return turn.GenerateAuthKey(username, realm, Credential(key, username)), true
708}
709
710// rememberAuth stores the username a source last authenticated with, for
711// the allocation event that follows. pion verifies MESSAGE-INTEGRITY only
712// after the handler returns, so this map is written for sources that have
713// not proved anything yet: tick ages entries out, and this caps them at
714// twice the box's live-allocation limit, dropping the oldest quarter when
715// it fills.
716func (s *Server) rememberAuth(key string, u Username, now time.Time) {
717 s.mu.Lock()
718 defer s.mu.Unlock()
719 if _, known := s.lastAuth[key]; !known && len(s.lastAuth) >= s.maxLastAuth {
720 type aged struct {
721 key string
722 at time.Time
723 }
724 all := make([]aged, 0, len(s.lastAuth))
725 for k, r := range s.lastAuth {
726 all = append(all, aged{k, r.at})
727 }
728 sort.Slice(all, func(i, j int) bool { return all[i].at.Before(all[j].at) })
729 drop := len(all) / 4
730 if drop == 0 {
731 drop = 1
732 }
733 for _, a := range all[:drop] {
734 delete(s.lastAuth, a.key)
735 }
736 }
737 s.lastAuth[key] = authRec{u: u, at: now}
738}
739
740// quota is pion's QuotaHandler, called on Allocate only: a refusal answers
741// 486. The lease comes first, because a relay whose authority read is
742// stale must take nothing new on.
743func (s *Server) quota(username, _ string, src net.Addr) bool {
744 now := s.now()
745 if !s.cfg.Lease(now) {
746 s.log("relay: allocation refused: serving lease has lapsed")
747 return false
748 }
749 u, err := ParseUsername(username)
750 if err != nil {
751 return false // auth would already have refused this
752 }
753 id := Identity{Account: u.Account, App: u.App}
754 ok, reason := s.adm.Admit(id)
755 if !ok {
756 s.countRefusal(id, reason)
757 s.log("relay: allocation refused for %s/%s: %s", id.Account, id.App, reason)
758 return false
759 }
760 key := typedSrc(src)
761 s.mu.Lock()
762 old, had := s.admits[key]
763 s.admits[key] = admitRec{id: id, at: now}
764 s.mu.Unlock()
765 if had {
766 // A previous attempt from this source was admitted and never
767 // became an allocation; it owns no socket, so release it here.
768 s.adm.Release(old.id)
769 }
770 return true
771}
772
773// created is pion's OnAllocationCreated, called with the relay address
774// before pion starts reading from the socket. It is where the wrapper we
775// handed out earlier learns whose allocation it is.
776func (s *Server) created(src, _ net.Addr, _, _, _ string, relayAddr net.Addr, _ int) {
777 ua, ok := relayAddr.(*net.UDPAddr)
778 if !ok {
779 s.log("relay: allocation created with a non-UDP relay address %v", relayAddr)
780 return
781 }
782 key := typedSrc(src)
783 now := s.now()
784
785 s.mu.Lock()
786 p, havePending := s.pending[ua.Port]
787 if !havePending {
788 // The sweep already reclaimed this socket, or it was never ours.
789 // Either way pion now owns an allocation no deadline of ours can
790 // end, so end it here by closing the socket it reads from.
791 orphan := s.byPort[ua.Port]
792 s.mu.Unlock()
793 if orphan != nil {
794 s.log("relay: allocation created on port %d after its socket was reclaimed: ending it", ua.Port)
795 orphan.Close()
796 } else {
797 s.log("relay: allocation created on port %d with no socket of ours", ua.Port)
798 }
799 return
800 }
801 delete(s.pending, ua.Port)
802 rec, haveAuth := s.lastAuth[key]
803 if !haveAuth {
804 s.mu.Unlock()
805 s.log("relay: allocation created for %s with no authenticated username", key)
806 p.conn.Close()
807 return
808 }
809 c := p.conn
810 c.b.ID = Identity{Account: rec.u.Account, App: rec.u.App}
811 c.b.Expiry = rec.u.Expiry
812 c.b.Kid = rec.u.Kid
813 c.b.Incarnation = rec.u.Incarnation
814 c.b.Src = key
815 s.seq++
816 e := &allocEntry{conn: c, id: c.b.ID, key: key, seq: s.seq, flow: p.flow}
817 s.allocs[key] = e
818 s.owners[c] = e
819 s.bound[key] = c.b
820 delete(s.admits, key)
821 expiry := rec.u.Expiry
822 s.mu.Unlock()
823
824 // The credential's own deadline. Set outside the lock: a timer whose
825 // deadline has already passed fires on the spot, and closing takes the
826 // lock we would still be holding.
827 t := time.AfterFunc(expiry.Sub(now), func() { c.Close() })
828 s.mu.Lock()
829 if cur, ok := s.allocs[key]; ok && cur == e {
830 e.timer = t
831 } else {
832 t.Stop()
833 }
834 s.mu.Unlock()
835 s.log("relay: allocation for %s/%s on %s from %s until %s",
836 c.b.ID.Account, c.b.ID.App, relayAddr, key, expiry.UTC().Format(time.RFC3339))
837}
838
839// release is the wrapped socket's onClose, whoever closed it: pion on a
840// read error, a timer, tick, EndWhere. Its state is keyed on this socket,
841// not on the typed source, so a replacement allocation bound for the same
842// source while this one was closing (pion removes an allocation from its
843// manager before closing it) keeps its binding and this one still gives
844// its admission back.
845func (s *Server) release(port int, c *relayConn) {
846 s.ports.remove(port)
847 var id Identity
848 var release bool
849 var last int64
850 s.mu.Lock()
851 if p, ok := s.pending[port]; ok && p.conn == c {
852 delete(s.pending, port)
853 last = p.flow.flush()
854 }
855 if cur, ok := s.byPort[port]; ok && cur == c {
856 delete(s.byPort, port)
857 }
858 if e, ok := s.owners[c]; ok {
859 last = e.flow.flush()
860 delete(s.owners, c)
861 if e.timer != nil {
862 e.timer.Stop()
863 }
864 if cur, ok := s.allocs[e.key]; ok && cur == e {
865 delete(s.allocs, e.key)
866 }
867 if b, ok := s.bound[e.key]; ok && b == c.b {
868 delete(s.bound, e.key)
869 }
870 id, release = e.id, true
871 }
872 s.mu.Unlock()
873 if release {
874 s.adm.Release(id)
875 }
876 if last > 0 {
877 // The last peer packet was read and never forwarded.
878 s.countRejected(c.b.ID, last)
879 }
880}
881
882// permit is pion's PermissionHandler: it sees only a peer IP, so the port
883// half of the EIP rule lives in relayConn.WriteTo.
884func (s *Server) permit(client net.Addr, peer net.IP) bool {
885 deny := func(why string) bool {
886 s.mu.Lock()
887 b, ok := s.bound[typedSrc(client)]
888 var id Identity
889 if ok {
890 id = b.ID
891 }
892 s.mu.Unlock()
893 s.countRefusal(id, "peer denied")
894 s.log("relay: permission refused for %s to %s: %s", client, peer, why)
895 return false
896 }
897 if peer == nil {
898 return deny("unspecified address")
899 }
900 // Our own EIP is allowed: two clients that both relay here hold relay
901 // candidates on it and must reach each other.
902 if s.cfg.RelayIP != nil && peer.Equal(s.cfg.RelayIP) {
903 return true
904 }
905 if why := s.peerRefusal(peer); why != "" {
906 return deny(why)
907 }
908 // A NAT64 or 6to4 address is a route to the IPv4 address inside it, so
909 // that address has to pass too.
910 if v4 := embeddedV4(peer); v4 != nil {
911 if why := s.peerRefusal(v4); why != "" {
912 return deny(why + " (embedded in " + peer.String() + ")")
913 }
914 }
915 return true
916}
917
918// peerRefusal is permit's verdict on one address, "" when it may be reached.
919func (s *Server) peerRefusal(peer net.IP) string {
920 switch {
921 case peer.IsUnspecified():
922 return "unspecified address"
923 case peer.IsMulticast():
924 return "multicast address"
925 }
926 // ExtraAllow is the operator's (and the test fixture's) way to permit
927 // a range the built-in list denies. Empty on a real box.
928 for _, n := range s.allow {
929 if n.Contains(peer) {
930 return ""
931 }
932 }
933 for _, n := range s.deny {
934 if n.Contains(peer) {
935 return "denied range " + n.String()
936 }
937 }
938 for _, ip := range s.local {
939 if peer.Equal(ip) {
940 return "the relay's own address"
941 }
942 }
943 return ""
944}
945
946// --- metering ------------------------------------------------------------
947
948// countIn is the client-to-peer leg, counted at the relay socket after
949// pion forwarded it.
950func (s *Server) countIn(id Identity, n int) {
951 if s.cfg.Meter != nil {
952 s.cfg.Meter.Add(id, int64(n), 0, 0)
953 }
954 if s.adm.AddBytes(id, int64(n)) {
955 s.overBudget(id)
956 }
957}
958
959// countOut is the peer-to-client leg, counted at the listener socket and
960// attributed through the owner binding.
961func (s *Server) countOut(id Identity, n int) {
962 if s.cfg.Meter != nil {
963 s.cfg.Meter.Add(id, 0, int64(n), 0)
964 }
965 if s.adm.AddBytes(id, int64(n)) {
966 s.overBudget(id)
967 }
968}
969
970// countRejected is spec §3's `rejected`: peer bytes that reached a relay
971// socket and were dropped for want of a permission. Metered, never charged
972// to a budget.
973func (s *Server) countRejected(id Identity, n int64) {
974 if s.cfg.Meter != nil {
975 s.cfg.Meter.Add(id, 0, 0, n)
976 }
977}
978
979// countGated is the other kind of drop: a client packet aimed at our own
980// EIP on a port that is not a live allocation. It is a policy refusal, not
981// a meter line, so it is counted per app and goes no further.
982func (s *Server) countGated(id Identity, _ int) {
983 s.countRefusal(id, "gated")
984}
985
986// overBudget ends an app's allocations once its rolling-hour budget is
987// crossed, because a budget that only stops new allocations bounds nothing.
988// It runs off the forwarding path, once per identity at a time.
989func (s *Server) overBudget(id Identity) {
990 s.mu.Lock()
991 if s.shedding[id] {
992 s.mu.Unlock()
993 return
994 }
995 s.shedding[id] = true
996 s.mu.Unlock()
997 go func() {
998 s.EndWhere(func(b *Binding) bool { return b.ID == id }, "byte budget")
999 s.mu.Lock()
1000 delete(s.shedding, id)
1001 s.mu.Unlock()
1002 }()
1003}
1004
1005// attrib is which identity a refusal may be counted against. pion hands
1006// the AuthHandler whatever username arrived, before any integrity check,
1007// so an unauthenticated sender picks the account and app strings. Only an
1008// app the authority record actually names gets a row of its own; every
1009// other refusal shares the zero identity.
1010func (s *Server) attrib(id Identity) Identity {
1011 if id.Account == "" || id.App == "" {
1012 return Identity{}
1013 }
1014 if _, ok := s.cfg.Authority().Lookup(id.Account, id.App); !ok {
1015 return Identity{}
1016 }
1017 return id
1018}
1019
1020func (s *Server) countRefusal(id Identity, reason string) {
1021 id = s.attrib(id)
1022 s.mu.Lock()
1023 m := s.refusals[id]
1024 if m == nil && len(s.refusals) >= maxRefusalIdentities {
1025 id = Identity{}
1026 m = s.refusals[id]
1027 }
1028 if m == nil {
1029 m = map[string]int{}
1030 s.refusals[id] = m
1031 }
1032 m[reason]++
1033 s.mu.Unlock()
1034}
1035
1036// mayLog rate-limits one identity's one refusal reason to a line a minute,
1037// so a flood of refusals cannot be turned into a flood of log.
1038func (s *Server) mayLog(id Identity, reason string) bool {
1039 k := logKey{id: s.attrib(id), reason: reason}
1040 now := s.now()
1041 s.mu.Lock()
1042 defer s.mu.Unlock()
1043 if at, ok := s.logged[k]; ok {
1044 if now.Sub(at) < refusalLogEvery {
1045 return false
1046 }
1047 } else if len(s.logged) >= maxLoggedRefusals {
1048 return false
1049 }
1050 s.logged[k] = now
1051 return true
1052}
1053
1054// refused counts a refusal against the app and logs why. Its return values
1055// are the AuthHandler's, so a refusal is one line at the call site.
1056func (s *Server) refused(id Identity, reason, format string, args ...any) ([]byte, bool) {
1057 s.countRefusal(id, reason)
1058 if s.mayLog(id, reason) {
1059 s.log(format, args...)
1060 }
1061 return nil, false
1062}
1063
1064func (s *Server) refusalCount(id Identity, reason string) int {
1065 s.mu.Lock()
1066 defer s.mu.Unlock()
1067 return s.refusals[id][reason]
1068}
1069
1070func (s *Server) refusalIdentities() int {
1071 s.mu.Lock()
1072 defer s.mu.Unlock()
1073 return len(s.refusals)
1074}
1075
1076// entryFor is the owner binding lookup the out leg is attributed through.
1077func (s *Server) entryFor(key string) *allocEntry {
1078 s.mu.Lock()
1079 defer s.mu.Unlock()
1080 return s.allocs[key]
1081}
1082
1083// --- listener wrappers ---------------------------------------------------
1084
1085// listenerConn wraps the UDP listener pion answers clients on. It mirrors
1086// relayConn without the EIP gate: everything written here goes to a client
1087// that asked us for it, so the only questions are whose bytes they are and
1088// whether the buckets still have room.
1089type listenerConn struct {
1090 net.PacketConn
1091 s *Server
1092}
1093
1094func (c *listenerConn) WriteTo(p []byte, addr net.Addr) (int, error) {
1095 // A TURN response is the client asking us something, not relayed
1096 // media: bucketing it would drop Refresh responses at the app's rate
1097 // cap and end calls for a reason spec §3 does not list, and metering
1098 // it would bill the client for its own signalling.
1099 if !relayedPayload(p) {
1100 return c.PacketConn.WriteTo(p, addr)
1101 }
1102 e := c.s.entryFor(typedSrc(addr))
1103 if e != nil {
1104 // Peek both, then spend both: a box refusal must not leave the
1105 // app's bucket debited (see relayConn.WriteTo).
1106 if e.conn.appBucket != nil && !e.conn.appBucket.Peek(len(p)) {
1107 return len(p), nil
1108 }
1109 if c.s.box != nil && !c.s.box.Peek(len(p)) {
1110 return len(p), nil
1111 }
1112 if e.conn.appBucket != nil {
1113 e.conn.appBucket.Spend(len(p))
1114 }
1115 if c.s.box != nil {
1116 c.s.box.Spend(len(p))
1117 }
1118 }
1119 n, err := c.PacketConn.WriteTo(p, addr)
1120 if n > 0 && e != nil {
1121 e.flow.forwarded()
1122 c.s.countOut(e.id, n)
1123 }
1124 return n, err
1125}
1126
1127// relayedPayload reports whether a packet bound for a client carries peer
1128// media rather than TURN signalling: a ChannelData message (first two bits
1129// 0b01, so 0x40 to 0x7f) or a Data indication.
1130func relayedPayload(p []byte) bool {
1131 if len(p) == 0 {
1132 return false
1133 }
1134 if p[0]&0xc0 == 0x40 {
1135 return true
1136 }
1137 if !stun.IsMessage(p) {
1138 return false
1139 }
1140 var t stun.MessageType
1141 t.ReadValue(binary.BigEndian.Uint16(p[0:2]))
1142 return t.Method == stun.MethodData && t.Class == stun.ClassIndication
1143}
1144
1145// countingListener wraps the TCP listener so the out leg is metered for
1146// TCP clients too. A TCP stream cannot have bytes dropped out of it the
1147// way a datagram can, so the buckets are spent but never enforced here.
1148type countingListener struct {
1149 net.Listener
1150 s *Server
1151}
1152
1153func (l *countingListener) Accept() (net.Conn, error) {
1154 c, err := l.Listener.Accept()
1155 if err != nil {
1156 return nil, err
1157 }
1158 return &countingConn{Conn: c, s: l.s, key: typedSrc(c.RemoteAddr())}, nil
1159}
1160
1161type countingConn struct {
1162 net.Conn
1163 s *Server
1164 key string
1165}
1166
1167func (c *countingConn) Write(p []byte) (int, error) {
1168 payload := relayedPayload(p)
1169 n, err := c.Conn.Write(p)
1170 if n > 0 && payload {
1171 if e := c.s.entryFor(c.key); e != nil {
1172 if e.conn.appBucket != nil {
1173 e.conn.appBucket.Spend(n)
1174 }
1175 if c.s.box != nil {
1176 c.s.box.Spend(n)
1177 }
1178 e.flow.forwarded()
1179 c.s.countOut(e.id, n)
1180 }
1181 }
1182 return n, err
1183}
1184
1185func contains(ss []string, s string) bool {
1186 for _, x := range ss {
1187 if x == s {
1188 return true
1189 }
1190 }
1191 return false
1192}
1193