diff --git a/wgengine/magicsock/magicsock_test.go b/wgengine/magicsock/magicsock_test.go index 3d3589666..b1f87c7db 100644 --- a/wgengine/magicsock/magicsock_test.go +++ b/wgengine/magicsock/magicsock_test.go @@ -6,10 +6,14 @@ package magicsock import ( "fmt" + "log" "net" "strings" + "sync" "testing" "time" + + "tailscale.com/stun" ) func TestListen(t *testing.T) { @@ -20,14 +24,13 @@ func TestListen(t *testing.T) { } } - // TODO(crawshaw): break test dependency on the network - // using "gortc.io/stun" (like stunner_test.go). - stunServers := DefaultSTUN + stunAddr, stunCleanupFn := serveSTUN(t) + defer stunCleanupFn() port := pickPort(t) conn, err := Listen(Options{ Port: port, - STUN: stunServers, + STUN: []string{stunAddr.String()}, EndpointsFunc: epFunc, }) if err != nil { @@ -119,3 +122,60 @@ func TestPickDERPFallback(t *testing.T) { t.Errorf("not sticky: got %v; want %v", got, someNode) } } + +type stunStats struct { + mu sync.Mutex + readIPv4 int + readIPv6 int +} + +func serveSTUN(t *testing.T) (addr net.Addr, cleanupFn func()) { + t.Helper() + + // TODO(crawshaw): use stats to test re-STUN logic + var stats stunStats + + pc, err := net.ListenPacket("udp4", ":3478") + if err != nil { + t.Fatalf("failed to open STUN listener: %v", err) + } + + go runSTUN(pc, &stats) + return pc.LocalAddr(), func() { pc.Close() } +} + +func runSTUN(pc net.PacketConn, stats *stunStats) { + var buf [64 << 10]byte + for { + n, addr, err := pc.ReadFrom(buf[:]) + if err != nil { + if strings.Contains(err.Error(), "closed network connection") { + log.Printf("STUN server shutdown") + return + } + continue + } + ua := addr.(*net.UDPAddr) + pkt := buf[:n] + if !stun.Is(pkt) { + continue + } + txid, err := stun.ParseBindingRequest(pkt) + if err != nil { + continue + } + + stats.mu.Lock() + if ua.IP.To4() != nil { + stats.readIPv4++ + } else { + stats.readIPv6++ + } + stats.mu.Unlock() + + res := stun.Response(txid, ua.IP, uint16(ua.Port)) + if _, err := pc.WriteTo(res, addr); err != nil { + log.Printf("STUN server write failed: %v", err) + } + } +}