1
0

stream_udp_lifecycle_test.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. package tuic
  2. import (
  3. "bytes"
  4. "context"
  5. "crypto/tls"
  6. "io"
  7. "net"
  8. "testing"
  9. "time"
  10. serverquic "github.com/apernet/quic-go"
  11. "github.com/google/uuid"
  12. clientquic "github.com/quic-go/quic-go"
  13. )
  14. func startLifecycleTestServer(t *testing.T, relayAddr, email string) (*Server, *clientquic.Conn, uuid.UUID, string) {
  15. t.Helper()
  16. cert, key := generateTestCert(t)
  17. clientID := uuid.New()
  18. password := "lifecycle-test-password"
  19. server, err := NewServer(Instance{
  20. Id: 99101, Tag: "lifecycle-test", Listen: "127.0.0.1", Port: 0,
  21. Certificate: string(cert), PrivateKey: string(key), ALPN: []string{"h3"},
  22. AuthenticationTimeout: 2, MaxIdleTime: 30,
  23. Clients: []TuicClientSettings{{UUID: clientID.String(), Password: password, Email: email}},
  24. }, &SocksRelay{Addr: relayAddr, Password: "lifecycle-socks-password"})
  25. if err != nil {
  26. t.Fatalf("create TUIC server: %v", err)
  27. }
  28. if err := server.Start(); err != nil {
  29. t.Fatalf("start TUIC server: %v", err)
  30. }
  31. t.Cleanup(func() { _ = server.Close() })
  32. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  33. defer cancel()
  34. client, err := clientquic.DialAddr(ctx, server.packetConn.LocalAddr().String(),
  35. &tls.Config{InsecureSkipVerify: true, NextProtos: []string{"h3"}},
  36. &clientquic.Config{EnableDatagrams: true})
  37. if err != nil {
  38. t.Fatalf("dial TUIC server: %v", err)
  39. }
  40. t.Cleanup(func() { _ = client.CloseWithError(0, "test complete") })
  41. tlsState := client.ConnectionState().TLS
  42. token, err := tlsState.ExportKeyingMaterial(string(clientID[:]), []byte(password), 32)
  43. if err != nil {
  44. t.Fatalf("derive authentication token: %v", err)
  45. }
  46. stream, err := client.OpenUniStreamSync(ctx)
  47. if err != nil {
  48. t.Fatalf("open authentication stream: %v", err)
  49. }
  50. auth := append([]byte{ProtocolVersion, CmdAuthenticate}, clientID[:]...)
  51. auth = append(auth, token...)
  52. if _, err := stream.Write(auth); err != nil {
  53. t.Fatalf("write authentication: %v", err)
  54. }
  55. if err := stream.Close(); err != nil {
  56. t.Fatalf("close authentication stream: %v", err)
  57. }
  58. return server, client, clientID, password
  59. }
  60. func authenticatedServerConnection(t *testing.T, server *Server, clientID uuid.UUID) (*serverquic.Conn, *User) {
  61. t.Helper()
  62. id := [16]byte(clientID)
  63. deadline := time.Now().Add(2 * time.Second)
  64. for time.Now().Before(deadline) {
  65. server.activeConnsMu.Lock()
  66. for conn, user := range server.activeConns[id] {
  67. server.activeConnsMu.Unlock()
  68. return conn, user
  69. }
  70. server.activeConnsMu.Unlock()
  71. time.Sleep(time.Millisecond)
  72. }
  73. t.Fatal("server did not register authenticated QUIC connection")
  74. return nil, nil
  75. }
  76. func TestMalformedBiStreamDelayedFINReleasesReceiveCredit(t *testing.T) {
  77. _, client, _, _ := startLifecycleTestServer(t, "127.0.0.1:1", "bidi-lifecycle@x")
  78. ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
  79. defer cancel()
  80. for i := 0; i < 110; i++ {
  81. openCtx, openCancel := context.WithTimeout(ctx, 700*time.Millisecond)
  82. stream, err := client.OpenStreamSync(openCtx)
  83. openCancel()
  84. if err != nil {
  85. t.Fatalf("bidirectional stream %d blocked after unsupported commands: %v", i+1, err)
  86. }
  87. if _, err := stream.Write([]byte{ProtocolVersion, 0xff}); err != nil {
  88. t.Fatalf("write unsupported command %d: %v", i+1, err)
  89. }
  90. stream.SetReadDeadline(time.Now().Add(time.Second))
  91. if _, err := io.Copy(io.Discard, stream); err != nil {
  92. t.Fatalf("wait for unsupported stream %d to close: %v", i+1, err)
  93. }
  94. _ = stream.Close()
  95. }
  96. }
  97. func TestDownstreamUDPResponseRefreshesAssociationIdleTime(t *testing.T) {
  98. socksAddr, cleanup := startMockSocks5Server(t, "udp-lifecycle@x", "lifecycle-socks-password")
  99. defer cleanup()
  100. server, client, clientID, _ := startLifecycleTestServer(t, socksAddr, "udp-lifecycle@x")
  101. serverConn, user := authenticatedServerConnection(t, server, clientID)
  102. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  103. defer cancel()
  104. session, err := server.relay.DialUDP(ctx, user.Email)
  105. if err != nil {
  106. t.Fatalf("open SOCKS UDP session: %v", err)
  107. }
  108. t.Cleanup(func() { _ = session.Close() })
  109. const associationID uint16 = 61244
  110. oldActive := time.Now().Add(-udpAssociationIdleTimeout - time.Second)
  111. association := &udpAssociation{
  112. responseTransport: packetTransportDatagram,
  113. relay: &udpRelaySession{relay: session, responseTransport: packetTransportDatagram},
  114. lastActive: oldActive,
  115. }
  116. registry := newUdpAssociationRegistry(maxUdpRelayPacketSize)
  117. registry.associations[associationID] = association
  118. responseDone := make(chan struct{})
  119. go func() {
  120. defer close(responseDone)
  121. server.relayUDPResponses(ctx, serverConn, user, associationID, association, registry, association.relay)
  122. }()
  123. target := &Address{Type: AddrTypeIPv4, IP: net.IPv4(8, 8, 8, 8), Port: 53}
  124. if _, err := session.Send(target, []byte("seed")); err != nil {
  125. t.Fatalf("send SOCKS seed datagram: %v", err)
  126. }
  127. response, err := client.ReceiveDatagram(ctx)
  128. if err != nil {
  129. t.Fatalf("receive echoed UDP response: %v", err)
  130. }
  131. if len(response) < 2 || response[0] != ProtocolVersion || response[1] != CmdPacket {
  132. t.Fatalf("unexpected TUIC UDP response: %x", response)
  133. }
  134. reader := bytes.NewReader(response[2:])
  135. header, err := ReadPacketHeader(reader)
  136. if err != nil {
  137. t.Fatalf("read response header: %v", err)
  138. }
  139. got, err := readPacketPayload(reader, header)
  140. if err != nil || !bytes.Equal(got, []byte("seed")) {
  141. t.Fatalf("echo response payload = %q, error=%v", got, err)
  142. }
  143. if header.AssocID != associationID {
  144. t.Fatalf("response association id = %d, want %d", header.AssocID, associationID)
  145. }
  146. registry.mu.Lock()
  147. refreshedAt := association.lastActive
  148. registry.mu.Unlock()
  149. if !refreshedAt.After(oldActive) {
  150. t.Fatal("successful downstream response did not refresh association activity")
  151. }
  152. registry.reapIdle(oldActive.Add(udpAssociationIdleTimeout + time.Second))
  153. registry.mu.Lock()
  154. remaining := registry.associations[associationID]
  155. registry.mu.Unlock()
  156. if remaining != association {
  157. t.Fatal("association was reaped despite a recently delivered downstream response")
  158. }
  159. _ = session.Close()
  160. select {
  161. case <-responseDone:
  162. case <-time.After(time.Second):
  163. t.Fatal("UDP response relay did not stop after SOCKS session closed")
  164. }
  165. }
  166. func TestUdpAssociationTouchDoesNotRefreshReusedID(t *testing.T) {
  167. registry := newUdpAssociationRegistry(maxUdpRelayPacketSize)
  168. addr := &Address{Type: AddrTypeIPv4, IP: net.IPv4(8, 8, 8, 8), Port: 53}
  169. header := &PacketHeader{AssocID: 17, PktID: 1, FragTotal: 1, FragID: 0, Size: 1, Addr: addr}
  170. old, _, _, complete := registry.feed(packetTransportDatagram, header, []byte("x"))
  171. if !complete {
  172. t.Fatal("failed to create first association generation")
  173. }
  174. oldTime := old.lastActive
  175. if !registry.dissociate(header.AssocID) {
  176. t.Fatal("failed to dissociate first association generation")
  177. }
  178. newGeneration, _, _, complete := registry.feed(packetTransportDatagram, header, []byte("x"))
  179. if !complete || newGeneration == old {
  180. t.Fatal("failed to create replacement association generation")
  181. }
  182. if registry.touch(header.AssocID, old, oldTime.Add(time.Hour)) {
  183. t.Fatal("late response refreshed a replacement association generation")
  184. }
  185. if !newGeneration.lastActive.Before(oldTime.Add(time.Hour)) {
  186. t.Fatal("replacement association timestamp changed after stale touch")
  187. }
  188. }