udp_mux_universal_test.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
  2. // SPDX-License-Identifier: MIT
  3. //go:build !js
  4. // +build !js
  5. package ice
  6. import (
  7. "context"
  8. "net"
  9. "sync"
  10. "testing"
  11. "time"
  12. "github.com/pion/stun"
  13. "github.com/stretchr/testify/require"
  14. )
  15. func TestUniversalUDPMux(t *testing.T) {
  16. conn, err := net.ListenUDP(udp, &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)})
  17. require.NoError(t, err)
  18. udpMux := NewUniversalUDPMuxDefault(UniversalUDPMuxParams{
  19. Logger: nil,
  20. UDPConn: conn,
  21. })
  22. defer func() {
  23. _ = udpMux.Close()
  24. _ = conn.Close()
  25. }()
  26. require.NotNil(t, udpMux.LocalAddr(), "tcpMux.LocalAddr() is nil")
  27. wg := sync.WaitGroup{}
  28. wg.Add(1)
  29. go func() {
  30. defer wg.Done()
  31. testMuxSrflxConnection(t, udpMux, "ufrag4", udp)
  32. }()
  33. wg.Wait()
  34. }
  35. func testMuxSrflxConnection(t *testing.T, udpMux *UniversalUDPMuxDefault, ufrag string, network string) {
  36. pktConn, err := udpMux.GetConn(ufrag, udpMux.LocalAddr())
  37. require.NoError(t, err, "error retrieving muxed connection for ufrag")
  38. defer func() {
  39. _ = pktConn.Close()
  40. }()
  41. remoteConn, err := net.DialUDP(network, nil, &net.UDPAddr{
  42. Port: udpMux.LocalAddr().(*net.UDPAddr).Port,
  43. })
  44. require.NoError(t, err, "error dialing test UDP connection")
  45. defer func() {
  46. _ = remoteConn.Close()
  47. }()
  48. // Use small value for TTL to check expiration of the address
  49. udpMux.params.XORMappedAddrCacheTTL = time.Millisecond * 20
  50. testXORIP := net.ParseIP("213.141.156.236")
  51. testXORPort := 21254
  52. wg := sync.WaitGroup{}
  53. wg.Add(1)
  54. go func() {
  55. defer wg.Done()
  56. address, e := udpMux.GetXORMappedAddr(context.Background(), remoteConn.LocalAddr(), time.Second)
  57. require.NoError(t, e)
  58. require.NotNil(t, address)
  59. require.True(t, address.IP.Equal(testXORIP))
  60. require.Equal(t, address.Port, testXORPort)
  61. }()
  62. // Wait until GetXORMappedAddr calls sendSTUN method
  63. time.Sleep(time.Millisecond)
  64. // Check that mapped address filled correctly after sent STUN
  65. udpMux.mu.Lock()
  66. mappedAddr, ok := udpMux.xorMappedMap[remoteConn.LocalAddr().String()]
  67. require.True(t, ok)
  68. require.NotNil(t, mappedAddr)
  69. require.True(t, mappedAddr.pending())
  70. require.False(t, mappedAddr.expired())
  71. udpMux.mu.Unlock()
  72. // Clean receiver read buffer
  73. buf := make([]byte, receiveMTU)
  74. _, err = remoteConn.Read(buf)
  75. require.NoError(t, err)
  76. // Write back to udpMux XOR message with address
  77. msg := stun.New()
  78. msg.Type = stun.MessageType{Method: stun.MethodBinding, Class: stun.ClassRequest}
  79. msg.Add(stun.AttrUsername, []byte(ufrag+":otherufrag"))
  80. addr := &stun.XORMappedAddress{
  81. IP: testXORIP,
  82. Port: testXORPort,
  83. }
  84. err = addr.AddTo(msg)
  85. require.NoError(t, err)
  86. msg.Encode()
  87. _, err = remoteConn.Write(msg.Raw)
  88. require.NoError(t, err)
  89. // Wait for the packet to be consumed and parsed by udpMux
  90. wg.Wait()
  91. // We should get address immediately from the cached map
  92. address, err := udpMux.GetXORMappedAddr(context.Background(), remoteConn.LocalAddr(), time.Second)
  93. require.NoError(t, err)
  94. require.NotNil(t, address)
  95. udpMux.mu.Lock()
  96. // Check mappedAddr is not pending, we didn't send STUN twice
  97. require.False(t, mappedAddr.pending())
  98. // Check expiration by TTL
  99. time.Sleep(time.Millisecond * 21)
  100. require.True(t, mappedAddr.expired())
  101. udpMux.mu.Unlock()
  102. // After expire, we send STUN request again
  103. // but we not receive response in 5 milliseconds and should get error here
  104. address, err = udpMux.GetXORMappedAddr(context.Background(), remoteConn.LocalAddr(), time.Millisecond*5)
  105. require.NotNil(t, err)
  106. require.Nil(t, address)
  107. }