// SPDX-FileCopyrightText: 2023 The Pion community // SPDX-License-Identifier: MIT //go:build !js // +build !js package ice import ( "crypto/rand" "crypto/sha256" "encoding/binary" "errors" "io" "net" "sync" "testing" "time" "github.com/pion/ice/v4/internal/fakenet" "github.com/pion/logging" "github.com/pion/stun/v3" "github.com/pion/transport/v3/test" "github.com/stretchr/testify/require" ) func TestUDPMux(t *testing.T) { //nolint:cyclop defer test.CheckRoutines(t)() defer test.TimeOut(time.Second * 30).Stop() conn4, err := net.ListenUDP(udp, &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)}) require.NoError(t, err) conn6, err := net.ListenUDP(udp, &net.UDPAddr{IP: net.IPv6loopback}) if err != nil { t.Log("IPv6 is not supported on this machine") } connUnspecified, err := net.ListenUDP(udp, nil) require.NoError(t, err) conn4Unspecified, err := net.ListenUDP(udp, &net.UDPAddr{IP: net.IPv4zero}) require.NoError(t, err) conn6Unspecified, err := net.ListenUDP(udp, &net.UDPAddr{IP: net.IPv6unspecified}) if err != nil { t.Log("IPv6 is not supported on this machine") } type testCase struct { name string conn net.PacketConn network string } testCases := []testCase{ {name: "IPv4loopback", conn: conn4, network: udp4}, {name: "IPv6loopback", conn: conn6, network: udp6}, {name: "Unspecified", conn: connUnspecified, network: udp}, {name: "IPv4Unspecified", conn: conn4Unspecified, network: udp4}, {name: "IPv6Unspecified", conn: conn6Unspecified, network: udp6}, } if ipv6Available(t) { addr6 := getLocalIPAddress(t, NetworkTypeUDP6) conn6Unspecified, listenEerr := net.ListenUDP(udp, &net.UDPAddr{ IP: addr6.AsSlice(), Zone: addr6.Zone(), }) if listenEerr != nil { t.Log("IPv6 is not supported on this machine") } testCases = append(testCases, testCase{name: "IPv6Specified", conn: conn6Unspecified, network: udp6}, ) } for _, subTest := range testCases { network, conn := subTest.network, subTest.conn if udpConn, ok := conn.(*net.UDPConn); !ok || udpConn == nil { continue } t.Run(subTest.name, func(t *testing.T) { udpMux := NewUDPMuxDefault(UDPMuxParams{ Logger: nil, UDPConn: conn, }) defer func() { _ = udpMux.Close() _ = conn.Close() }() require.NotNil(t, udpMux.LocalAddr(), "udpMux.LocalAddr() is nil") wg := sync.WaitGroup{} wg.Add(1) go func() { defer wg.Done() testMuxConnection(t, udpMux, "ufrag1", udp) }() const ptrSize = 32 << (^uintptr(0) >> 63) if network == udp { wg.Add(1) go func() { defer wg.Done() testMuxConnection(t, udpMux, "ufrag2", udp4) }() testMuxConnection(t, udpMux, "ufrag3", udp6) } else if ptrSize != 32 || network != udp6 { testMuxConnection(t, udpMux, "ufrag2", network) } wg.Wait() require.NoError(t, udpMux.Close()) // Can't create more connections _, err = udpMux.GetConn("failufrag", udpMux.LocalAddr()) require.Error(t, err) }) } } func testMuxConnection(t *testing.T, udpMux *UDPMuxDefault, ufrag string, network string) { t.Helper() pktConn, err := udpMux.GetConn(ufrag, udpMux.LocalAddr()) require.NoError(t, err, "error retrieving muxed connection for ufrag") defer func() { _ = pktConn.Close() }() addr, ok := pktConn.LocalAddr().(*net.UDPAddr) require.True(t, ok, "pktConn.LocalAddr() is not a net.UDPAddr") if addr.IP.IsUnspecified() { addr = &net.UDPAddr{Port: addr.Port} } remoteConn, err := net.DialUDP(network, nil, addr) require.NoError(t, err, "error dialing test UDP connection") testMuxConnectionPair(t, pktConn, remoteConn, ufrag) } func testMuxConnectionPair(t *testing.T, pktConn net.PacketConn, remoteConn *net.UDPConn, ufrag string) { t.Helper() // Initial messages are dropped _, err := remoteConn.Write([]byte("dropped bytes")) require.NoError(t, err) // Wait for packet to be consumed time.Sleep(time.Millisecond) // Write out to establish connection msg := stun.New() msg.Type = stun.MessageType{Method: stun.MethodBinding, Class: stun.ClassRequest} msg.Add(stun.AttrUsername, []byte(ufrag+":otherufrag")) msg.Encode() _, err = pktConn.WriteTo(msg.Raw, remoteConn.LocalAddr()) require.NoError(t, err) // Ensure received buf := make([]byte, receiveMTU) n, err := remoteConn.Read(buf) require.NoError(t, err) require.Equal(t, msg.Raw, buf[:n]) // Start writing packets through mux targetSize := 1 * 1024 * 1024 readDone := make(chan struct{}, 1) remoteReadDone := make(chan struct{}, 1) // Read packets from the muxed side go func() { defer func() { t.Logf("closing read chan for: %s", ufrag) close(readDone) }() readBuf := make([]byte, receiveMTU) nextSeq := uint32(0) for read := 0; read < targetSize; { n, _, err := pktConn.ReadFrom(readBuf) require.NoError(t, err) require.Equal(t, receiveMTU, n) verifyPacket(t, readBuf[:n], nextSeq) // Write it back to sender _, err = pktConn.WriteTo(readBuf[:n], remoteConn.LocalAddr()) require.NoError(t, err) read += n nextSeq++ } }() go func() { defer func() { close(remoteReadDone) }() readBuf := make([]byte, receiveMTU) nextSeq := uint32(0) for read := 0; read < targetSize; { n, _, err := remoteConn.ReadFrom(readBuf) require.NoError(t, err) require.Equal(t, receiveMTU, n) verifyPacket(t, readBuf[:n], nextSeq) read += n nextSeq++ } }() sequence := 0 for written := 0; written < targetSize; { buf := make([]byte, receiveMTU) // Byte 0-4: sequence // Bytes 4-36: sha256 checksum // Bytes2 36-mtu: random data _, err := rand.Read(buf[36:]) require.NoError(t, err) h := sha256.Sum256(buf[36:]) copy(buf[4:36], h[:]) binary.LittleEndian.PutUint32(buf[0:4], uint32(sequence)) //nolint:gosec // G115 _, err = remoteConn.Write(buf) require.NoError(t, err) written += len(buf) sequence++ time.Sleep(time.Millisecond) } <-readDone <-remoteReadDone } func verifyPacket(t *testing.T, b []byte, nextSeq uint32) { t.Helper() readSeq := binary.LittleEndian.Uint32(b[0:4]) require.Equal(t, nextSeq, readSeq) h := sha256.Sum256(b[36:]) require.Equal(t, h[:], b[4:36]) } func TestUDPMux_Agent_Restart(t *testing.T) { oneSecond := time.Second connA, connB := pipe(t, &AgentConfig{ DisconnectedTimeout: &oneSecond, FailedTimeout: &oneSecond, }) defer closePipe(t, connA, connB) aNotifier, aConnected := onConnected() require.NoError(t, connA.agent.OnConnectionStateChange(aNotifier)) bNotifier, bConnected := onConnected() require.NoError(t, connB.agent.OnConnectionStateChange(bNotifier)) // Maintain Credentials across restarts ufragA, pwdA, err := connA.agent.GetLocalUserCredentials() require.NoError(t, err) ufragB, pwdB, err := connB.agent.GetLocalUserCredentials() require.NoError(t, err) require.NoError(t, err) // Restart and Re-Signal require.NoError(t, connA.agent.Restart(ufragA, pwdA)) require.NoError(t, connB.agent.Restart(ufragB, pwdB)) require.NoError(t, connA.agent.SetRemoteCredentials(ufragB, pwdB)) require.NoError(t, connB.agent.SetRemoteCredentials(ufragA, pwdA)) gatherAndExchangeCandidates(t, connA.agent, connB.agent) // Wait until both have gone back to connected <-aConnected <-bConnected } func secondTestMuxedConn(t *testing.T, capBytes int) *udpMuxedConn { t.Helper() pool := &sync.Pool{ New: func() any { return &bufferHolder{buf: make([]byte, capBytes)} }, } params := &udpMuxedConnParams{ AddrPool: pool, LocalAddr: &net.UDPAddr{IP: net.IPv4zero, Port: 0}, } return newUDPMuxedConn(params) } func TestUDPMuxedConn_ReadFrom(t *testing.T) { conn := secondTestMuxedConn(t, 1500) remote := &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 5678} payload := []byte("this is a payload of length 29!") require.NoError(t, conn.writePacket(payload, remote)) // read with too small of a buffer -> expect io.ErrShortBuffer, n=0, rAddr=nil small := make([]byte, 8) n, raddr, err := conn.ReadFrom(small) require.ErrorIs(t, err, io.ErrShortBuffer) require.Equal(t, 0, n) require.Nil(t, raddr) // try again with sufficient buffer require.NoError(t, conn.writePacket(payload, remote)) dst := make([]byte, len(payload)) n, raddr, err = conn.ReadFrom(dst) require.NoError(t, err) require.Equal(t, len(payload), n) require.Equal(t, payload, dst[:n]) // rAddr should be what was set on the packet. require.NotNil(t, raddr) require.Equal(t, remote.String(), raddr.String()) } func TestUDPMuxedConn_ReadFrom_EOFAfterClose(t *testing.T) { conn := secondTestMuxedConn(t, 64) // close with empty queue -> immediate EOF branch inside ReadFrom. require.NoError(t, conn.Close()) buf := make([]byte, 16) n, raddr, err := conn.ReadFrom(buf) require.Equal(t, 0, n) require.Nil(t, raddr) require.ErrorIs(t, err, io.EOF) } func TestUDPMuxedConn_ReadFrom_WaitingThenClosedEOF(t *testing.T) { conn := secondTestMuxedConn(t, 64) errCh := make(chan error, 1) go func() { // empty queue sets state to waiting and block on notify/closedChan. _, _, err := conn.ReadFrom(make([]byte, 16)) errCh <- err }() // let goroutine enter Waiting state. time.Sleep(10 * time.Millisecond) require.NoError(t, conn.Close()) select { case err := <-errCh: require.ErrorIs(t, err, io.EOF) case <-time.After(1 * time.Second): require.Fail(t, "timeout waiting for ReadFrom to return after Close") } } func TestUDPMuxedConn_WriteTo_ClosedPipe(t *testing.T) { conn := secondTestMuxedConn(t, 64) require.NoError(t, conn.Close()) n, err := conn.WriteTo([]byte("x"), &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 1234}) require.Equal(t, 0, n) require.ErrorIs(t, err, io.ErrClosedPipe) } // non-*net.UDPAddr that still satisfies net.Addr. type notUDPAddr struct{} func (notUDPAddr) Network() string { return "udp" } func (notUDPAddr) String() string { return "not-a-udp-addr" } func TestUDPMuxedConn_WriteTo_BadAddrType(t *testing.T) { conn := secondTestMuxedConn(t, 64) n, err := conn.WriteTo([]byte("x"), notUDPAddr{}) require.Equal(t, 0, n) require.ErrorIs(t, err, errFailedToCastUDPAddr) } // uses invalid IP length so newIPPort returns error. func TestUDPMuxedConn_WriteTo_newIPPortError(t *testing.T) { conn := secondTestMuxedConn(t, 64) invalidIP := net.IP{1} // len=1 -> invalid raddr := &net.UDPAddr{IP: invalidIP, Port: 1234} n, err := conn.WriteTo([]byte("x"), raddr) require.Equal(t, 0, n) require.Error(t, err) } func TestUDPMuxedConn_WriteTo_InvalidPort(t *testing.T) { conn := secondTestMuxedConn(t, 64) raddr := &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: -1} n, err := conn.WriteTo([]byte("x"), raddr) require.Equal(t, 0, n) require.ErrorIs(t, err, ErrPort) raddr = &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 0x10000} n, err = conn.WriteTo([]byte("x"), raddr) require.Equal(t, 0, n) require.ErrorIs(t, err, ErrPort) } func TestUDPMuxedConn_SetDeadlines(t *testing.T) { conn := secondTestMuxedConn(t, 64) // While open require.NoError(t, conn.SetReadDeadline(time.Now().Add(250*time.Millisecond))) require.NoError(t, conn.SetWriteDeadline(time.Now().Add(250*time.Millisecond))) // After close require.NoError(t, conn.Close()) require.NoError(t, conn.SetReadDeadline(time.Now().Add(250*time.Millisecond))) require.NoError(t, conn.SetWriteDeadline(time.Now().Add(250*time.Millisecond))) } func TestUDPMuxedConn_removeAddress(t *testing.T) { conn := secondTestMuxedConn(t, 64) mk := func(ip string, port uint16) ipPort { p, err := newIPPort(net.ParseIP(ip), "", port) require.NoError(t, err) return p } a1 := mk("1.1.1.1", 1000) a2 := mk("2.2.2.2", 2000) a3 := mk("3.3.3.3", 3000) a4 := mk("9.9.9.9", 9000) // non-existent in lists below t.Run("remove-existing-middle", func(t *testing.T) { // true for a1/a3, false for a2 conn.addresses = []ipPort{a1, a2, a3} conn.removeAddress(a2) got := conn.getAddresses() require.Equal(t, []ipPort{a1, a3}, got) }) t.Run("remove-non-existing", func(t *testing.T) { // only true (no matches) conn.addresses = []ipPort{a1, a3} conn.removeAddress(a4) got := conn.getAddresses() require.Equal(t, []ipPort{a1, a3}, got) }) t.Run("remove-duplicates-all", func(t *testing.T) { // all occurrences are removed (false twice) conn.addresses = []ipPort{a1, a1, a2} conn.removeAddress(a1) got := conn.getAddresses() require.Equal(t, []ipPort{a2}, got) }) t.Run("remove-from-empty", func(t *testing.T) { // no iters loop, no panic + remain empty conn.addresses = nil conn.removeAddress(a1) got := conn.getAddresses() require.Empty(t, got) }) } func TestUDPMuxedConn_writePacket_ShortBuffer(t *testing.T) { conn := secondTestMuxedConn(t, 8) // pool buf cap=8 addr := &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 9999} err := conn.writePacket(make([]byte, 16), addr) // len=16 > cap=8 require.ErrorIs(t, err, io.ErrShortBuffer) require.Nil(t, conn.bufHead) require.Nil(t, conn.bufTail) } func TestUDPMuxedConn_writePacket_ClosedState(t *testing.T) { conn := secondTestMuxedConn(t, 64) addr := &net.UDPAddr{IP: net.IPv4(5, 6, 7, 8), Port: 1234} // closed state before write conn.mu.Lock() conn.state = udpMuxedConnClosed conn.mu.Unlock() err := conn.writePacket([]byte{1, 2, 3}, addr) // fits in cap require.ErrorIs(t, err, io.ErrClosedPipe) // queue unchanged require.Nil(t, conn.bufHead) require.Nil(t, conn.bufTail) } func TestUDPMuxedConn_writePacket_NotifyDefaultBranch(t *testing.T) { conn := secondTestMuxedConn(t, 64) addr := &net.UDPAddr{IP: net.IPv4(9, 9, 9, 9), Port: 4242} // fill notify channel so send would block conn.notify <- struct{}{} // set pre-state to waiting so the post-unlock select triggers conn.mu.Lock() conn.state = udpMuxedConnWaiting conn.mu.Unlock() // write should take default branch err := conn.writePacket([]byte("hello"), addr) require.NoError(t, err) // packet enqueued require.NotNil(t, conn.bufHead) require.NotNil(t, conn.bufTail) // channel still full => no send happened (default path executed) require.Equal(t, 1, len(conn.notify)) } func TestNewUDPMuxDefault_LocalAddrNotUDPAddr(t *testing.T) { defer test.CheckRoutines(t)() c1, c2 := net.Pipe() defer func() { _ = c2.Close() }() pc := &fakenet.PacketConn{Conn: c1} mux := NewUDPMuxDefault(UDPMuxParams{ Logger: logging.NewDefaultLoggerFactory().NewLogger("ice"), UDPConn: pc, }) require.NotNil(t, mux) defer func() { _ = mux.Close() }() addrs := mux.GetListenAddresses() require.Len(t, addrs, 1) require.Equal(t, pc.LocalAddr().String(), addrs[0].String()) } func TestUDPMuxDefault_GetConn_InvalidAddress(t *testing.T) { defer test.CheckRoutines(t)() connA, err := net.ListenUDP(udp, &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)}) require.NoError(t, err) udpMux := NewUDPMuxDefault(UDPMuxParams{ Logger: nil, UDPConn: connA, }) defer func() { _ = udpMux.Close() _ = connA.Close() }() connB, err := net.ListenUDP(udp, &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)}) require.NoError(t, err) defer func() { _ = connB.Close() }() pc, gerr := udpMux.GetConn("some-ufrag", connB.LocalAddr()) require.Nil(t, pc) require.ErrorIs(t, gerr, errInvalidAddress) } func TestUDPMuxDefault_registerConnForAddress_ClosedMuxEarlyReturn(t *testing.T) { defer test.CheckRoutines(t)() udp, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)}) require.NoError(t, err) udpMux := NewUDPMuxDefault(UDPMuxParams{UDPConn: udp}) require.NoError(t, udpMux.Close()) _ = udp.Close() conn := secondTestMuxedConn(t, 64) addr, err := newIPPort(net.ParseIP("1.2.3.4"), "", 9999) require.NoError(t, err) before := len(udpMux.addressMap) udpMux.registerConnForAddress(conn, addr) after := len(udpMux.addressMap) require.Equal(t, before, after) _, exists := udpMux.addressMap[addr] require.False(t, exists) } func TestUDPMuxDefault_registerConnForAddress_ReplacesExisting(t *testing.T) { defer test.CheckRoutines(t)() udp, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)}) require.NoError(t, err) udpMux := NewUDPMuxDefault(UDPMuxParams{UDPConn: udp}) defer func() { _ = udpMux.Close() _ = udp.Close() }() ipAddr, err := newIPPort(net.ParseIP("5.6.7.8"), "", 12345) require.NoError(t, err) existing := secondTestMuxedConn(t, 64) existing.addresses = []ipPort{ipAddr} udpMux.addressMapMu.Lock() udpMux.addressMap[ipAddr] = existing udpMux.addressMapMu.Unlock() // new conn should replace existing mapping and cause removeAddress on the old one. newConn := secondTestMuxedConn(t, 64) udpMux.registerConnForAddress(newConn, ipAddr) // map should now point to newConn. udpMux.addressMapMu.RLock() mapped := udpMux.addressMap[ipAddr] udpMux.addressMapMu.RUnlock() require.Equal(t, newConn, mapped) // old conn should have ipAddr removed from its addresses. require.False(t, existing.containsAddress(ipAddr), "old conn should have removed the address backref") } func stunWithLen(l uint16) []byte { m := stun.New() m.Type = stun.MessageType{Method: stun.MethodBinding, Class: stun.ClassRequest} m.Encode() out := append([]byte{}, m.Raw...) out[2] = byte(l >> 8) out[3] = byte(l & 0xff) return out } type scriptedUDPPC struct { local *net.UDPAddr seq []struct { data []byte addr net.Addr err error } i int } func (s *scriptedUDPPC) ReadFrom(p []byte) (int, net.Addr, error) { if s.i >= len(s.seq) { return 0, s.local, errIoEOF } step := s.seq[s.i] s.i++ if step.err != nil { return 0, step.addr, step.err } n := copy(p, step.data) return n, step.addr, nil } func (s *scriptedUDPPC) WriteTo([]byte, net.Addr) (int, error) { return 0, nil } func (s *scriptedUDPPC) Close() error { return nil } func (s *scriptedUDPPC) LocalAddr() net.Addr { return s.local } func (s *scriptedUDPPC) SetDeadline(time.Time) error { return nil } func (s *scriptedUDPPC) SetReadDeadline(time.Time) error { return nil } func (s *scriptedUDPPC) SetWriteDeadline(time.Time) error { return nil } var errIoEOF = errors.New("EOF") func TestUDPMux_connWorker_AddrNotUDP(t *testing.T) { defer test.CheckRoutines(t)() c1, c2 := net.Pipe() defer func() { _ = c2.Close() }() pc := &fakenet.PacketConn{Conn: c1} mux := NewUDPMuxDefault(UDPMuxParams{UDPConn: pc}) require.NotNil(t, mux) defer func() { _ = mux.Close() }() _, _ = c2.Write([]byte("frame")) _ = c2.Close() } func TestUDPMux_connWorker_ReadError_Timeout(t *testing.T) { defer test.CheckRoutines(t)() c1, c2 := net.Pipe() pc := &fakenet.PacketConn{Conn: c1} mux := NewUDPMuxDefault(UDPMuxParams{UDPConn: pc}) require.NotNil(t, mux) _ = pc.SetReadDeadline(time.Unix(0, 0)) _ = c2.Close() _ = mux.Close() } func TestUDPMux_connWorker_NewIPPortError(t *testing.T) { defer test.CheckRoutines(t)() badIP := net.IP{1} remote := &net.UDPAddr{IP: badIP, Port: 9999} pc := &scriptedUDPPC{ local: &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 7000}, seq: []struct { data []byte addr net.Addr err error }{ {data: []byte{1}, addr: remote, err: nil}, // triggers newIPPort error }, } mux := NewUDPMuxDefault(UDPMuxParams{UDPConn: pc}) require.NotNil(t, mux) _ = mux.Close() } func TestUDPMux_connWorker_STUNDecodeError(t *testing.T) { defer test.CheckRoutines(t)() remote := &net.UDPAddr{IP: net.IPv4(10, 0, 0, 2), Port: 5678} pc := &scriptedUDPPC{ local: &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 7001}, seq: []struct { data []byte addr net.Addr err error }{ // bad STUN length -> Decode() error -> Warnf + continue {data: stunWithLen(4), addr: remote, err: nil}, {data: nil, addr: remote, err: errIoEOF}, // exit loop }, } mux := NewUDPMuxDefault(UDPMuxParams{UDPConn: pc}) require.NotNil(t, mux) _ = mux.Close() } func TestUDPMux_connWorker_STUNNoUsername(t *testing.T) { defer test.CheckRoutines(t)() msg := stun.New() msg.Type = stun.MessageType{Method: stun.MethodBinding, Class: stun.ClassRequest} msg.Encode() // valid STUN + no USERNAME remote := &net.UDPAddr{IP: net.IPv4(10, 0, 0, 3), Port: 5679} pc := &scriptedUDPPC{ local: &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 7002}, seq: []struct { data []byte addr net.Addr err error }{ {data: append([]byte{}, msg.Raw...), addr: remote, err: nil}, // Get(USERNAME) fails {data: nil, addr: remote, err: errIoEOF}, // exit loop }, } mux := NewUDPMuxDefault(UDPMuxParams{UDPConn: pc}) require.NotNil(t, mux) _ = mux.Close() } func TestUDPMux_connWorker_WritePacketError(t *testing.T) { defer test.CheckRoutines(t)() local := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 7003} remote := &net.UDPAddr{IP: net.IPv4(203, 0, 113, 7), Port: 5555} payload := []byte("0123456789ABCDEF") pc := &scriptedUDPPC{ local: local, seq: []struct { data []byte addr net.Addr err error }{ {data: payload, addr: remote, err: nil}, {data: nil, addr: remote, err: errIoEOF}, // exit loop }, } mux := NewUDPMuxDefault(UDPMuxParams{UDPConn: pc}) require.NotNil(t, mux) defer func() { _ = mux.Close() }() // shrink pool to force io.ErrShortBuffer in writePacket mux.pool = &sync.Pool{New: func() any { return newBufferHolder(8) }} // make connWorker route to new conn. c, err := mux.GetConn("ufragX", mux.LocalAddr()) require.NoError(t, err) defer func() { _ = c.Close() }() // remote port is controlled. we use 5555 here to skip int overflow check as we would // otherwise have to cast remote.Port (int) to uint16. ipport, err := newIPPort(remote.IP, remote.Zone, 5555) require.NoError(t, err) cInner, ok := c.(*udpMuxedConn) require.True(t, ok, "expected *udpMuxedConn from UDPMuxDefault.GetConn") mux.registerConnForAddress(cInner, ipport) } func TestNewUDPMuxDefault_UnspecifiedAddr_AutoInitNet(t *testing.T) { defer test.CheckRoutines(t)() conn, err := net.ListenUDP(udp, &net.UDPAddr{IP: net.IPv4zero}) require.NoError(t, err) defer func() { _ = conn.Close() }() mux := NewUDPMuxDefault(UDPMuxParams{ Logger: nil, UDPConn: conn, Net: nil, }) require.NotNil(t, mux) defer func() { _ = mux.Close() }() addrs := mux.GetListenAddresses() require.GreaterOrEqual(t, len(addrs), 1, "should list at least one local listen address") udpAddr, ok := conn.LocalAddr().(*net.UDPAddr) require.True(t, ok, "LocalAddr is not *net.UDPAddr") wantPort := udpAddr.Port for _, a := range addrs { ua, ok := a.(*net.UDPAddr) require.True(t, ok, "returned listen address must be *net.UDPAddr") require.Equal(t, wantPort, ua.Port, "listen addresses should reuse the same UDP port") } }