stream_udp_lifecycle_test.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  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. closeUniStream(t, stream)
  56. return server, client, clientID, password
  57. }
  58. func authenticatedServerConnection(t *testing.T, server *Server, clientID uuid.UUID) (*serverquic.Conn, *User) {
  59. t.Helper()
  60. id := [16]byte(clientID)
  61. deadline := time.Now().Add(2 * time.Second)
  62. for time.Now().Before(deadline) {
  63. server.activeConnsMu.Lock()
  64. for conn, user := range server.activeConns[id] {
  65. server.activeConnsMu.Unlock()
  66. return conn, user
  67. }
  68. server.activeConnsMu.Unlock()
  69. time.Sleep(time.Millisecond)
  70. }
  71. t.Fatal("server did not register authenticated QUIC connection")
  72. return nil, nil
  73. }
  74. func TestMalformedBiStreamDelayedFINReleasesReceiveCredit(t *testing.T) {
  75. _, client, _, _ := startLifecycleTestServer(t, "127.0.0.1:1", "bidi-lifecycle@x")
  76. ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
  77. defer cancel()
  78. for i := 0; i < 110; i++ {
  79. openCtx, openCancel := context.WithTimeout(ctx, 700*time.Millisecond)
  80. stream, err := client.OpenStreamSync(openCtx)
  81. openCancel()
  82. if err != nil {
  83. t.Fatalf("bidirectional stream %d blocked after unsupported commands: %v", i+1, err)
  84. }
  85. if _, err := stream.Write([]byte{ProtocolVersion, 0xff}); err != nil {
  86. t.Fatalf("write unsupported command %d: %v", i+1, err)
  87. }
  88. stream.SetReadDeadline(time.Now().Add(time.Second))
  89. if _, err := io.Copy(io.Discard, stream); err != nil {
  90. t.Fatalf("wait for unsupported stream %d to close: %v", i+1, err)
  91. }
  92. _ = stream.Close()
  93. }
  94. }
  95. func TestDownstreamUDPResponseRefreshesAssociationIdleTime(t *testing.T) {
  96. socksAddr, cleanup := startMockSocks5Server(t, "udp-lifecycle@x", "lifecycle-socks-password")
  97. defer cleanup()
  98. server, client, clientID, _ := startLifecycleTestServer(t, socksAddr, "udp-lifecycle@x")
  99. serverConn, user := authenticatedServerConnection(t, server, clientID)
  100. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  101. defer cancel()
  102. session, err := server.relay.DialUDP(ctx, user.Email)
  103. if err != nil {
  104. t.Fatalf("open SOCKS UDP session: %v", err)
  105. }
  106. t.Cleanup(func() { _ = session.Close() })
  107. const associationID uint16 = 61244
  108. oldActive := time.Now().Add(-udpAssociationIdleTimeout - time.Second)
  109. association := &udpAssociation{
  110. responseTransport: packetTransportDatagram,
  111. relay: &udpRelaySession{relay: session, responseTransport: packetTransportDatagram},
  112. lastActive: oldActive,
  113. }
  114. registry := newUdpAssociationRegistry(maxUdpRelayPacketSize)
  115. registry.associations[associationID] = association
  116. responseDone := make(chan struct{})
  117. go func() {
  118. defer close(responseDone)
  119. server.relayUDPResponses(ctx, serverConn, user, associationID, association, registry, association.relay)
  120. }()
  121. target := &Address{Type: AddrTypeIPv4, IP: net.IPv4(8, 8, 8, 8), Port: 53}
  122. if _, err := session.Send(target, []byte("seed")); err != nil {
  123. t.Fatalf("send SOCKS seed datagram: %v", err)
  124. }
  125. response, err := client.ReceiveDatagram(ctx)
  126. if err != nil {
  127. t.Fatalf("receive echoed UDP response: %v", err)
  128. }
  129. if len(response) < 2 || response[0] != ProtocolVersion || response[1] != CmdPacket {
  130. t.Fatalf("unexpected TUIC UDP response: %x", response)
  131. }
  132. reader := bytes.NewReader(response[2:])
  133. header, err := ReadPacketHeader(reader)
  134. if err != nil {
  135. t.Fatalf("read response header: %v", err)
  136. }
  137. got, err := readPacketPayload(reader, header)
  138. if err != nil || !bytes.Equal(got, []byte("seed")) {
  139. t.Fatalf("echo response payload = %q, error=%v", got, err)
  140. }
  141. if header.AssocID != associationID {
  142. t.Fatalf("response association id = %d, want %d", header.AssocID, associationID)
  143. }
  144. registry.mu.Lock()
  145. refreshedAt := association.lastActive
  146. registry.mu.Unlock()
  147. if !refreshedAt.After(oldActive) {
  148. t.Fatal("successful downstream response did not refresh association activity")
  149. }
  150. registry.reapIdle(oldActive.Add(udpAssociationIdleTimeout + time.Second))
  151. registry.mu.Lock()
  152. remaining := registry.associations[associationID]
  153. registry.mu.Unlock()
  154. if remaining != association {
  155. t.Fatal("association was reaped despite a recently delivered downstream response")
  156. }
  157. _ = session.Close()
  158. select {
  159. case <-responseDone:
  160. case <-time.After(time.Second):
  161. t.Fatal("UDP response relay did not stop after SOCKS session closed")
  162. }
  163. }
  164. func TestUdpAssociationTouchDoesNotRefreshReusedID(t *testing.T) {
  165. registry := newUdpAssociationRegistry(maxUdpRelayPacketSize)
  166. addr := &Address{Type: AddrTypeIPv4, IP: net.IPv4(8, 8, 8, 8), Port: 53}
  167. header := &PacketHeader{AssocID: 17, PktID: 1, FragTotal: 1, FragID: 0, Size: 1, Addr: addr}
  168. old, _, _, complete := registry.feed(packetTransportDatagram, header, []byte("x"))
  169. if !complete {
  170. t.Fatal("failed to create first association generation")
  171. }
  172. oldTime := old.lastActive
  173. if !registry.dissociate(header.AssocID) {
  174. t.Fatal("failed to dissociate first association generation")
  175. }
  176. newGeneration, _, _, complete := registry.feed(packetTransportDatagram, header, []byte("x"))
  177. if !complete || newGeneration == old {
  178. t.Fatal("failed to create replacement association generation")
  179. }
  180. if registry.touch(header.AssocID, old, oldTime.Add(time.Hour)) {
  181. t.Fatal("late response refreshed a replacement association generation")
  182. }
  183. if !newGeneration.lastActive.Before(oldTime.Add(time.Hour)) {
  184. t.Fatal("replacement association timestamp changed after stale touch")
  185. }
  186. }