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 "crypto/sha256"
5 "fmt"
6 "net"
7 "strings"
8 "sync"
9 "testing"
10 "time"
11
12 "github.com/pion/logging"
13 "github.com/pion/turn/v4"
14)
15
16// testMaster is the fixture region master every server test derives from.
17func testMaster() []byte {
18 m := sha256.Sum256([]byte("carlos-relay-test-master"))
19 return m[:]
20}
21
22// newTestServer starts a relay on loopback with one enabled app,
23// abc/meet, incarnation 0123456789abcdef, in region "test". The returned
24// setNow moves the server's clock; the returned Masters and Authority are
25// the live ones, so a test can rewrite them in place.
26// relayIP is the address the fixture server advertises. Loopback is what
27// the brief's tests use; a test that carries real traffic between two
28// loopback sockets must advertise something else, because the EIP port
29// gate (spec §3) drops anything aimed at the advertised address on a port
30// that is not a live allocation — and on loopback every peer is on it.
31// tweak, when given, is applied to the config before the server starts.
32func newTestServer(t *testing.T, relayIP string, meter *fakeMeter, tweak func(*ServerConfig)) (s *Server, setNow func(time.Time), now func() time.Time, masters *Masters, auth *Authority) {
33 t.Helper()
34 master := testMaster()
35 masters, err := ParseMasters(strings.NewReader("k1 " + b64(master) + " -\n"))
36 if err != nil {
37 t.Fatal(err)
38 }
39 auth = &Authority{
40 Apps: []AuthorityApp{{Account: "abc", App: "meet", Incarnation: "0123456789abcdef", Regions: []string{"test"}}},
41 WrittenAt: time.Now(),
42 }
43 var mu sync.Mutex
44 at := time.Now()
45 clock := func() time.Time { mu.Lock(); defer mu.Unlock(); return at }
46 setNow = func(t time.Time) { mu.Lock(); at = t; mu.Unlock() }
47
48 // A client's retransmit can reach the server's read loop after the test
49 // body returns, and t.Logf from a finished test is a data race, so the
50 // log goes quiet once the server is shut down.
51 var logMu sync.Mutex
52 quiet := false
53 logf := func(format string, args ...any) {
54 logMu.Lock()
55 defer logMu.Unlock()
56 if !quiet {
57 t.Logf(format, args...)
58 }
59 }
60
61 cfg := ServerConfig{
62 Region: "test", RelayIP: net.ParseIP(relayIP), UDP: "127.0.0.1:0",
63 MinPort: 40000, MaxPort: 40100,
64 Masters: func() *Masters { return masters }, Authority: func() *Authority { return auth },
65 Lease: func(time.Time) bool { return true }, Limits: DefaultLimits(), Now: clock, Log: logf,
66 }
67 if meter != nil {
68 cfg.Meter = meter
69 }
70 if tweak != nil {
71 tweak(&cfg)
72 }
73 s, err = NewServer(cfg)
74 if err != nil {
75 t.Fatal(err)
76 }
77 t.Cleanup(func() {
78 s.Close()
79 logMu.Lock()
80 quiet = true
81 logMu.Unlock()
82 })
83 return s, setNow, clock, masters, auth
84}
85
86// creds mints a username and credential for account/app/incarnation under
87// kid k1, expiring an hour after now.
88func creds(now time.Time, account, app, incarnation string) (string, string) {
89 username := fmt.Sprintf("%d:%s", now.Add(time.Hour).Unix(), Suffix(account, app, incarnation, "k1"))
90 key := DeriveKey(testMaster(), account, app, incarnation)
91 return username, Credential(key, username)
92}
93
94func turnClient(t *testing.T, addr net.Addr, username, password string) *turn.Client {
95 t.Helper()
96 conn, err := net.ListenPacket("udp4", "127.0.0.1:0")
97 if err != nil {
98 t.Fatal(err)
99 }
100 c, err := turn.NewClient(&turn.ClientConfig{
101 STUNServerAddr: addr.String(),
102 TURNServerAddr: addr.String(),
103 Conn: conn,
104 Username: username,
105 Password: password,
106 Realm: "carlos",
107 LoggerFactory: logging.NewDefaultLoggerFactory(),
108 })
109 if err != nil {
110 conn.Close()
111 t.Fatal(err)
112 }
113 if err := c.Listen(); err != nil {
114 c.Close()
115 conn.Close()
116 t.Fatal(err)
117 }
118 t.Cleanup(func() { c.Close(); conn.Close() })
119 return c
120}
121
122func waitFor(t *testing.T, cond func() bool) {
123 t.Helper()
124 deadline := time.Now().Add(2 * time.Second)
125 for time.Now().Before(deadline) {
126 if cond() {
127 return
128 }
129 time.Sleep(10 * time.Millisecond)
130 }
131 t.Fatal("condition not met within two seconds")
132}
133
134func TestServerEndToEnd(t *testing.T) {
135 s, setNow, now, _, _ := newTestServer(t, "127.0.0.1", nil, nil)
136
137 username, password := creds(now(), "abc", "meet", "0123456789abcdef")
138 client := turnClient(t, s.UDPAddr(), username, password)
139 relayConn, err := client.Allocate()
140 if err != nil {
141 t.Fatalf("allocate: %v", err)
142 }
143 defer relayConn.Close()
144 if s.Live() != 1 {
145 t.Fatalf("live = %d", s.Live())
146 }
147 if got := s.Listeners(); len(got) != 1 || !strings.HasPrefix(got[0], "udp:") {
148 t.Fatalf("listeners = %v", got)
149 }
150
151 // A relay candidate on the advertised address, in our port range.
152 ra, ok := relayConn.LocalAddr().(*net.UDPAddr)
153 if !ok || !ra.IP.Equal(net.ParseIP("127.0.0.1")) || ra.Port < 40000 || ra.Port > 40100 {
154 t.Fatalf("relay address = %v, want 127.0.0.1 in 40000-40100", relayConn.LocalAddr())
155 }
156
157 // Wrong app's credential is refused: abc/docs is not in the record.
158 otherUser, otherPass := creds(now(), "abc", "docs", "0123456789abcdef")
159 if _, err := turnClient(t, s.UDPAddr(), otherUser, otherPass).Allocate(); err == nil {
160 t.Fatal("unknown app allocated")
161 }
162 // It is counted against the shared unknown bucket, not against an
163 // identity the caller made up.
164 if s.refusalCount(Identity{}, "unknown app") != 1 {
165 t.Fatalf("refusals = %v", s.refusals)
166 }
167
168 // Expiry ends the allocation with no client action.
169 setNow(now().Add(2 * time.Hour))
170 s.tick()
171 waitFor(t, func() bool { return s.Live() == 0 })
172}
173
174// TestServerRelaysBothLegs carries real traffic client to peer and back,
175// and checks both meter legs. It advertises an EIP that is not the peers'
176// address, as a real box does.
177func TestServerRelaysBothLegs(t *testing.T) {
178 meter := &fakeMeter{}
179 s, _, now, _, _ := newTestServer(t, "203.0.113.5", meter, allowLoopbackPeers)
180 username, password := creds(now(), "abc", "meet", "0123456789abcdef")
181 client := turnClient(t, s.UDPAddr(), username, password)
182 relayConn, err := client.Allocate()
183 if err != nil {
184 t.Fatalf("allocate: %v", err)
185 }
186 defer relayConn.Close()
187
188 peer, err := net.ListenPacket("udp4", "127.0.0.1:0")
189 if err != nil {
190 t.Fatal(err)
191 }
192 defer peer.Close()
193 if err := client.CreatePermission(peer.LocalAddr()); err != nil {
194 t.Fatalf("create permission: %v", err)
195 }
196 if _, err := relayConn.WriteTo([]byte("ping"), peer.LocalAddr()); err != nil {
197 t.Fatalf("write to peer: %v", err)
198 }
199 buf := make([]byte, 64)
200 peer.SetReadDeadline(time.Now().Add(2 * time.Second))
201 n, from, err := peer.ReadFrom(buf)
202 if err != nil {
203 t.Fatalf("peer read: %v", err)
204 }
205 if string(buf[:n]) != "ping" {
206 t.Fatalf("peer got %q", buf[:n])
207 }
208 if _, err := peer.WriteTo([]byte("pong"), from); err != nil {
209 t.Fatalf("peer write: %v", err)
210 }
211 relayConn.SetReadDeadline(time.Now().Add(2 * time.Second))
212 n, _, err = relayConn.ReadFrom(buf)
213 if err != nil {
214 t.Fatalf("client read: %v", err)
215 }
216 if string(buf[:n]) != "pong" {
217 t.Fatalf("client got %q", buf[:n])
218 }
219
220 id := Identity{"abc", "meet"}
221 // Socket delivery can finish before the forwarding goroutine records bytes.
222 waitFor(t, func() bool {
223 in, out, _ := meter.totals(id)
224 return in >= 4 && out >= 4
225 })
226 in, out, _ := meter.totals(id)
227 if in < 4 {
228 t.Fatalf("in = %d, want at least the 4 bytes forwarded to the peer", in)
229 }
230 if out < 4 {
231 t.Fatalf("out = %d, want at least the 4 bytes forwarded to the client", out)
232 }
233}
234
235// allowLoopbackPeers is the ExtraAllow seam: a relay on a real box never
236// permits a loopback peer, and a test that carries traffic between two
237// sockets on this machine has nowhere else to put them.
238func allowLoopbackPeers(cfg *ServerConfig) {
239 cfg.ExtraAllow = mustCIDRs("127.0.0.0/8")
240}
241
242// fakeMeter is the Task 7 meter's shape, which ServerConfig.Meter names as
243// an interface so this file does not wait on it.
244type fakeMeter struct {
245 mu sync.Mutex
246 rows map[Identity][3]int64
247}
248
249func (m *fakeMeter) Add(id Identity, in, out, rejected int64) {
250 m.mu.Lock()
251 defer m.mu.Unlock()
252 if m.rows == nil {
253 m.rows = map[Identity][3]int64{}
254 }
255 r := m.rows[id]
256 m.rows[id] = [3]int64{r[0] + in, r[1] + out, r[2] + rejected}
257}
258
259func (m *fakeMeter) totals(id Identity) (in, out, rejected int64) {
260 m.mu.Lock()
261 defer m.mu.Unlock()
262 r := m.rows[id]
263 return r[0], r[1], r[2]
264}
265
266func TestServerRefusesPrivatePeer(t *testing.T) {
267 s, _, now, _, _ := newTestServer(t, "127.0.0.1", nil, nil)
268 username, password := creds(now(), "abc", "meet", "0123456789abcdef")
269 client := turnClient(t, s.UDPAddr(), username, password)
270 if _, err := client.Allocate(); err != nil {
271 t.Fatalf("allocate: %v", err)
272 }
273 if err := client.CreatePermission(&net.UDPAddr{IP: net.ParseIP("10.0.0.1"), Port: 1}); err == nil {
274 t.Fatal("permission to a private peer was granted")
275 }
276}
277
278func TestServerEndWhere(t *testing.T) {
279 s, _, now, _, _ := newTestServer(t, "127.0.0.1", nil, nil)
280 username, password := creds(now(), "abc", "meet", "0123456789abcdef")
281 client := turnClient(t, s.UDPAddr(), username, password)
282 if _, err := client.Allocate(); err != nil {
283 t.Fatalf("allocate: %v", err)
284 }
285 if s.Live() != 1 {
286 t.Fatalf("live = %d", s.Live())
287 }
288 s.EndWhere(func(b *Binding) bool { return b.ID.App == "meet" }, "disable")
289 waitFor(t, func() bool { return s.Live() == 0 })
290}
291
292func TestServerShedOldest(t *testing.T) {
293 s, _, now, _, _ := newTestServer(t, "127.0.0.1", nil, nil)
294 username, password := creds(now(), "abc", "meet", "0123456789abcdef")
295 first := turnClient(t, s.UDPAddr(), username, password)
296 if _, err := first.Allocate(); err != nil {
297 t.Fatalf("allocate: %v", err)
298 }
299 second := turnClient(t, s.UDPAddr(), username, password)
300 if _, err := second.Allocate(); err != nil {
301 t.Fatalf("allocate: %v", err)
302 }
303 if s.Live() != 2 {
304 t.Fatalf("live = %d", s.Live())
305 }
306 s.ShedOldest(1)
307 waitFor(t, func() bool { return s.Live() == 1 })
308}
309
310// TestServerBudgetEndsAllocations is the spec's rule that a budget which
311// only refuses new allocations bounds nothing: seeded past its hourly
312// budget, the app's live allocation goes too.
313func TestServerBudgetEndsAllocations(t *testing.T) {
314 s, _, now, _, _ := newTestServer(t, "127.0.0.1", nil, nil)
315 username, password := creds(now(), "abc", "meet", "0123456789abcdef")
316 client := turnClient(t, s.UDPAddr(), username, password)
317 if _, err := client.Allocate(); err != nil {
318 t.Fatalf("allocate: %v", err)
319 }
320 s.Seed(Identity{"abc", "meet"}, now(), DefaultLimits().AppBytesPerHour+1)
321 s.tick()
322 waitFor(t, func() bool { return s.Live() == 0 })
323}
324
325func TestServerAuthRefusals(t *testing.T) {
326 s, _, now, masters, auth := newTestServer(t, "127.0.0.1", nil, nil)
327 src := &net.UDPAddr{IP: net.ParseIP("192.0.2.7"), Port: 5000}
328
329 ok := func(username string) bool {
330 key, ok := s.auth(username, "carlos", src)
331 return ok && len(key) > 0
332 }
333
334 good, _ := creds(now(), "abc", "meet", "0123456789abcdef")
335 if !ok(good) {
336 t.Fatal("a good username was refused")
337 }
338
339 cases := []struct{ name, username, reason string }{
340 {"bad shape", "not-a-username", "bad shape"},
341 {"expired", fmt.Sprintf("%d:%s", now().Add(-time.Minute).Unix(), Suffix("abc", "meet", "0123456789abcdef", "k1")), "expired"},
342 {"too far ahead", fmt.Sprintf("%d:%s", now().Add(5*time.Hour).Unix(), Suffix("abc", "meet", "0123456789abcdef", "k1")), "expired"},
343 {"unknown app", fmt.Sprintf("%d:%s", now().Add(time.Hour).Unix(), Suffix("abc", "docs", "0123456789abcdef", "k1")), "unknown app"},
344 {"wrong incarnation", fmt.Sprintf("%d:%s", now().Add(time.Hour).Unix(), Suffix("abc", "meet", "fedcba9876543210", "k1")), "wrong incarnation"},
345 {"unknown kid", fmt.Sprintf("%d:%s", now().Add(time.Hour).Unix(), Suffix("abc", "meet", "0123456789abcdef", "k9")), "unknown kid"},
346 }
347 for _, c := range cases {
348 if ok(c.username) {
349 t.Fatalf("%s: accepted", c.name)
350 }
351 if s.refusalCount(s.attrib(refusalIdentity(c.username)), c.reason) == 0 {
352 t.Fatalf("%s: reason %q not counted (%v)", c.name, c.reason, s.refusals)
353 }
354 }
355
356 // Wrong region.
357 auth.Apps[0].Regions = []string{"elsewhere"}
358 if ok(good) {
359 t.Fatal("wrong region accepted")
360 }
361 if s.refusalCount(Identity{"abc", "meet"}, "wrong region") == 0 {
362 t.Fatalf("wrong region not counted (%v)", s.refusals)
363 }
364 auth.Apps[0].Regions = []string{"test"}
365
366 // A retired master ends the app's use of that kid.
367 masters.byKid["k1"] = Master{Kid: "k1", Key: testMaster(), RetireAt: now().Add(-time.Second)}
368 if ok(good) {
369 t.Fatal("retired kid accepted")
370 }
371 masters.byKid["k1"] = Master{Kid: "k1", Key: testMaster()}
372
373 // Owner binding: a live allocation's typed source refuses another app's
374 // valid credential.
375 s.mu.Lock()
376 s.bound[typedSrc(src)] = &Binding{ID: Identity{"abc", "meet"}, Incarnation: "0123456789abcdef"}
377 s.mu.Unlock()
378 auth.Apps = append(auth.Apps, AuthorityApp{Account: "abc", App: "docs", Incarnation: "0123456789abcdef", Regions: []string{"test"}})
379 docs := fmt.Sprintf("%d:%s", now().Add(time.Hour).Unix(), Suffix("abc", "docs", "0123456789abcdef", "k1"))
380 if ok(docs) {
381 t.Fatal("a second app allocated from a bound source")
382 }
383 if s.refusalCount(Identity{"abc", "docs"}, "bound elsewhere") == 0 {
384 t.Fatalf("bound elsewhere not counted (%v)", s.refusals)
385 }
386 // The bound app itself is still served, and a TCP source with the same
387 // address string is a separate binding.
388 if !ok(good) {
389 t.Fatal("the bound app was refused on its own source")
390 }
391 tcpSrc := &net.TCPAddr{IP: src.IP, Port: src.Port}
392 if key, okAuth := s.auth(docs, "carlos", tcpSrc); !okAuth || len(key) == 0 {
393 t.Fatal("a tcp source shares the udp source's binding")
394 }
395}
396
397// TestServerRefusalStateIsBounded is the memory-exhaustion case: pion
398// hands the AuthHandler the raw username before it checks
399// MESSAGE-INTEGRITY, and the nonce is not source-bound, so an unauthenticated
400// sender chooses the account and app strings. None of that may become a
401// map entry of its own.
402func TestServerRefusalStateIsBounded(t *testing.T) {
403 s, _, now, _, _ := newTestServer(t, "127.0.0.1", nil, nil)
404 src := &net.UDPAddr{IP: net.ParseIP("192.0.2.10"), Port: 6000}
405 exp := now().Add(time.Hour).Unix()
406 const flood = 10000
407 for i := 0; i < flood; i++ {
408 username := fmt.Sprintf("%d:%s", exp, Suffix(fmt.Sprintf("a%d", i), "meet", "0123456789abcdef", "k1"))
409 if _, ok := s.auth(username, "carlos", src); ok {
410 t.Fatalf("made-up account %d authenticated", i)
411 }
412 }
413 if n := s.refusalIdentities(); n > 2 {
414 t.Fatalf("refusal identities = %d, want at most 2", n)
415 }
416 if got := s.refusalCount(Identity{}, "unknown app"); got != flood {
417 t.Fatalf("unknown-app refusals = %d, want %d", got, flood)
418 }
419 s.mu.Lock()
420 auths, logs := len(s.lastAuth), len(s.logged)
421 s.mu.Unlock()
422 if auths != 0 {
423 t.Fatalf("lastAuth = %d, want 0: nothing authenticated", auths)
424 }
425 if logs > 2 {
426 t.Fatalf("log records = %d, want the refusal log rate-limited", logs)
427 }
428}
429
430// TestServerAuthMemoryIsCapped covers the other half: usernames that do
431// verify are remembered for the allocation event, but only so many.
432func TestServerAuthMemoryIsCapped(t *testing.T) {
433 s, _, now, _, _ := newTestServer(t, "127.0.0.1", nil, nil)
434 username, _ := creds(now(), "abc", "meet", "0123456789abcdef")
435 for i := 0; i < s.maxLastAuth+600; i++ {
436 src := &net.UDPAddr{IP: net.ParseIP("192.0.2.11"), Port: 1024 + i}
437 if _, ok := s.auth(username, "carlos", src); !ok {
438 t.Fatalf("source %d refused", i)
439 }
440 }
441 s.mu.Lock()
442 n := len(s.lastAuth)
443 s.mu.Unlock()
444 if n > s.maxLastAuth {
445 t.Fatalf("lastAuth = %d, want at most %d", n, s.maxLastAuth)
446 }
447}
448
449// TestServerCountsRejectedPeerBytes is spec §3's `rejected`: peer bytes
450// that reached a relay socket and were dropped for want of a permission.
451func TestServerCountsRejectedPeerBytes(t *testing.T) {
452 meter := &fakeMeter{}
453 s, _, now, _, _ := newTestServer(t, "203.0.113.5", meter, nil)
454 username, password := creds(now(), "abc", "meet", "0123456789abcdef")
455 client := turnClient(t, s.UDPAddr(), username, password)
456 relayed, err := client.Allocate()
457 if err != nil {
458 t.Fatalf("allocate: %v", err)
459 }
460 defer relayed.Close()
461 port := relayed.LocalAddr().(*net.UDPAddr).Port
462
463 peer, err := net.ListenPacket("udp4", "127.0.0.1:0")
464 if err != nil {
465 t.Fatal(err)
466 }
467 defer peer.Close()
468 // No permission was ever created for this peer, so pion drops both.
469 to := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: port}
470 for i := 0; i < 2; i++ {
471 if _, err := peer.WriteTo(make([]byte, 100), to); err != nil {
472 t.Fatalf("peer write: %v", err)
473 }
474 }
475 id := Identity{"abc", "meet"}
476 waitFor(t, func() bool { _, _, rejected := meter.totals(id); return rejected >= 100 })
477
478 s.EndAll("test")
479 waitFor(t, func() bool { return s.Live() == 0 })
480 in, out, rejected := meter.totals(id)
481 if rejected != 200 {
482 t.Fatalf("rejected = %d, want 200", rejected)
483 }
484 if out != 0 {
485 t.Fatalf("out = %d, want 0: nothing was forwarded to the client", out)
486 }
487 if in != 0 {
488 t.Fatalf("in = %d, want 0", in)
489 }
490}
491
492// TestServerControlIsNotBucketed: at a saturated per-allocation bucket a
493// client must still be able to allocate and refresh. Only relayed payload
494// is bucketed and metered.
495func TestServerControlIsNotBucketed(t *testing.T) {
496 meter := &fakeMeter{}
497 s, _, now, _, _ := newTestServer(t, "203.0.113.5", meter, func(cfg *ServerConfig) {
498 cfg.Limits.AllocBitsPerSec = 8 // one byte a second: every control response would be dropped
499 cfg.Limits.BoxBitsPerSec = 8
500 })
501 username, password := creds(now(), "abc", "meet", "0123456789abcdef")
502 client := turnClient(t, s.UDPAddr(), username, password)
503 relayed, err := client.Allocate()
504 if err != nil {
505 t.Fatalf("allocate at a saturated bucket: %v", err)
506 }
507 defer relayed.Close()
508 // A second round trip on the same listener, after the allocation
509 // exists: its response must get through too.
510 if _, err := client.SendBindingRequest(); err != nil {
511 t.Fatalf("binding request at a saturated bucket: %v", err)
512 }
513 if in, out, rejected := meter.totals(Identity{"abc", "meet"}); in|out|rejected != 0 {
514 t.Fatalf("signalling was metered: in=%d out=%d rejected=%d", in, out, rejected)
515 }
516}
517
518// TestServerClosesOrphanedAllocation: if the pending socket is gone by the
519// time pion reports the allocation, pion owns an allocation no deadline of
520// ours can end. It must be ended on the spot.
521func TestServerClosesOrphanedAllocation(t *testing.T) {
522 s, setNow, now, _, _ := newTestServer(t, "127.0.0.1", nil, nil)
523 src := &net.UDPAddr{IP: net.ParseIP("192.0.2.12"), Port: 7000}
524
525 // The socket is still ours, but its pending record has gone.
526 _, addr, err := s.AllocatePacketConn("udp4", 0)
527 if err != nil {
528 t.Fatal(err)
529 }
530 port := addr.(*net.UDPAddr).Port
531 username, _ := creds(now(), "abc", "meet", "0123456789abcdef")
532 if _, ok := s.auth(username, "carlos", src); !ok {
533 t.Fatal("fixture credential refused")
534 }
535 s.mu.Lock()
536 delete(s.pending, port)
537 s.mu.Unlock()
538 s.created(src, nil, "udp", username, "carlos", addr, 0)
539 if s.Live() != 0 {
540 t.Fatalf("live = %d, want the orphan ended", s.Live())
541 }
542 if s.ports.has(port) {
543 t.Fatal("the orphaned socket was left open")
544 }
545
546 // And the real race: the sweep reclaimed the socket before pion
547 // reported the allocation.
548 _, addr2, err := s.AllocatePacketConn("udp4", 0)
549 if err != nil {
550 t.Fatal(err)
551 }
552 port2 := addr2.(*net.UDPAddr).Port
553 setNow(now().Add(pendingLife + time.Second))
554 s.tick()
555 if s.ports.has(port2) {
556 t.Fatal("the sweep left the socket open")
557 }
558 username2, _ := creds(now(), "abc", "meet", "0123456789abcdef")
559 if _, ok := s.auth(username2, "carlos", src); !ok {
560 t.Fatal("fixture credential refused after the clock moved")
561 }
562 s.created(src, nil, "udp", username2, "carlos", addr2, 0)
563 if s.Live() != 0 {
564 t.Fatalf("live = %d, want no allocation for a reclaimed socket", s.Live())
565 }
566}
567
568// refusalIdentity is the identity a refusal is attributed to: the parsed
569// username's, or the zero identity when the username would not parse.
570func refusalIdentity(username string) Identity {
571 u, err := ParseUsername(username)
572 if err != nil {
573 return Identity{}
574 }
575 return Identity{u.Account, u.App}
576}
577
578func TestServerPermit(t *testing.T) {
579 _, deny, err := net.ParseCIDR("172.31.0.0/16")
580 if err != nil {
581 t.Fatal(err)
582 }
583 s, err := NewServer(ServerConfig{
584 Region: "test", RelayIP: net.ParseIP("203.0.113.5"), UDP: "127.0.0.1:0",
585 MinPort: 40200, MaxPort: 40300, DenyCIDRs: []*net.IPNet{deny},
586 Masters: func() *Masters { return nil },
587 Authority: func() *Authority { return nil },
588 Lease: func(time.Time) bool { return true },
589 Limits: DefaultLimits(), Log: t.Logf,
590 })
591 if err != nil {
592 t.Fatal(err)
593 }
594 defer s.Close()
595
596 client := &net.UDPAddr{IP: net.ParseIP("198.51.100.1"), Port: 1}
597 refused := []string{
598 "127.0.0.1", "::1", "169.254.169.254", "fe80::1", "10.0.0.1",
599 "172.16.5.5", "192.168.1.1", "fd00::1", "100.64.0.1", "172.31.9.9",
600 "0.0.0.0", "224.0.0.1",
601 // Discussion #220: "this network" beyond the unspecified address,
602 // IETF protocol assignments, benchmarking.
603 "0.1.2.3", "0.255.255.255", "192.0.0.8", "198.18.0.1", "198.19.255.254",
604 // NAT64 and 6to4 judged by the IPv4 address they carry.
605 "64:ff9b::a00:1", "64:ff9b::7f00:1", "64:ff9b::a9fe:a9fe", "64:ff9b::ac1f:909",
606 "2002:a00:1::1", "2002:a9fe:a9fe::", "2002:c0a8:101::1", "2002:0:0::1",
607 // IPv4-mapped forms are matched by the IPv4 ranges already.
608 "::ffff:10.0.0.1", "::ffff:198.18.0.1",
609 }
610 for _, ip := range refused {
611 if s.permit(client, net.ParseIP(ip)) {
612 t.Fatalf("peer %s was permitted", ip)
613 }
614 }
615 for _, ip := range []string{
616 "203.0.113.5", "198.51.100.9", "2001:db8::1",
617 // Neighbours of the new ranges stay reachable, and a public address
618 // behind NAT64 or 6to4 is an ordinary peer.
619 "1.0.0.1", "192.0.1.1", "198.17.255.255", "198.20.0.1",
620 "64:ff9b::808:808", "2002:808:808::1",
621 } {
622 if !s.permit(client, net.ParseIP(ip)) {
623 t.Fatalf("peer %s was refused", ip)
624 }
625 }
626}
627
628// TestServerCloseKeepsReplacementBinding is the race the spec calls out:
629// pion removes an allocation from its manager before closing its socket, so
630// a replacement allocation on the same typed source can exist while the old
631// wrapper is still closing. The old wrapper's close must not erase it.
632func TestServerCloseKeepsReplacementBinding(t *testing.T) {
633 s, _, now, _, _ := newTestServer(t, "127.0.0.1", nil, nil)
634 username, password := creds(now(), "abc", "meet", "0123456789abcdef")
635 client := turnClient(t, s.UDPAddr(), username, password)
636 if _, err := client.Allocate(); err != nil {
637 t.Fatalf("allocate: %v", err)
638 }
639 key := ""
640 var old *relayConn
641 s.mu.Lock()
642 for k, e := range s.allocs {
643 key, old = k, e.conn
644 }
645 s.mu.Unlock()
646
647 // A replacement allocation lands for the same typed source while the
648 // old socket is still closing.
649 replacement := &Binding{ID: Identity{"abc", "meet"}, Src: key}
650 replConn := newRelayConn(&fakePacketConn{}, replacement, nil, s.ports, nil, nil, nil, nil, func(*relayConn) {})
651 replEntry := &allocEntry{conn: replConn, id: replacement.ID, key: key, seq: 99}
652 s.mu.Lock()
653 s.bound[key] = replacement
654 s.allocs[key] = replEntry
655 s.owners[replConn] = replEntry
656 s.mu.Unlock()
657
658 old.Close()
659
660 s.mu.Lock()
661 gotBound, gotAlloc := s.bound[key], s.allocs[key]
662 _, oldOwned := s.owners[old]
663 s.mu.Unlock()
664 if gotBound != replacement {
665 t.Fatalf("close erased the replacement binding: %v", gotBound)
666 }
667 if gotAlloc != replEntry {
668 t.Fatalf("close erased the replacement allocation: %v", gotAlloc)
669 }
670 if oldOwned {
671 t.Fatal("the closed socket is still owned, so its admission never came back")
672 }
673}
674
675// TestServerRefusesPermissionIntoConfiguredDenyCIDR covers the operator's
676// half of the peer policy, which the built-in private ranges do not reach:
677// CARLOS_RELAY_DENY_CIDRS carries the box's own VPC CIDR, and that CIDR is
678// ordinary public-looking space as far as the built-in list is concerned.
679// It is the one thing standing between a TURN allocation and the deployment's
680// private network, and until now only the built-in ranges had a test
681// (discussion #120).
682func TestServerRefusesPermissionIntoConfiguredDenyCIDR(t *testing.T) {
683 if raceDetector {
684 t.Skip("pion/turn races on Permission.lifetimeTimer between AddPermission and Close; see race_on_test.go")
685 }
686 // 198.51.100.0/24 stands in for the VPC: globally routable as far as
687 // the built-in private list is concerned, denied only because the
688 // operator said so.
689 _, vpc, err := net.ParseCIDR("198.51.100.0/24")
690 if err != nil {
691 t.Fatal(err)
692 }
693 s, _, now, _, _ := newTestServer(t, "127.0.0.1", nil, func(cfg *ServerConfig) {
694 cfg.DenyCIDRs = append(cfg.DenyCIDRs, vpc)
695 })
696 username, password := creds(now(), "abc", "meet", "0123456789abcdef")
697 client := turnClient(t, s.UDPAddr(), username, password)
698 if _, err := client.Allocate(); err != nil {
699 t.Fatalf("allocate: %v", err)
700 }
701 if err := client.CreatePermission(&net.UDPAddr{IP: net.ParseIP("198.51.100.9"), Port: 1}); err == nil {
702 t.Error("permission into the configured deny CIDR was granted — a TURN client could reach the VPC")
703 }
704 // The control: an address outside both the built-in list and the
705 // configured range is still permitted, so the test is not passing
706 // because permissions are broken outright.
707 if err := client.CreatePermission(&net.UDPAddr{IP: net.ParseIP("203.0.113.9"), Port: 1}); err != nil {
708 t.Errorf("permission to an ordinary public peer was refused: %v", err)
709 }
710}
711