server_test.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789
  1. package tuic
  2. import (
  3. "bytes"
  4. "context"
  5. "crypto/ecdsa"
  6. "crypto/elliptic"
  7. "crypto/rand"
  8. "crypto/tls"
  9. "crypto/x509"
  10. "crypto/x509/pkix"
  11. "encoding/pem"
  12. "errors"
  13. "io"
  14. "math/big"
  15. "net"
  16. "testing"
  17. "time"
  18. serverquic "github.com/apernet/quic-go"
  19. "github.com/google/uuid"
  20. "github.com/quic-go/quic-go"
  21. )
  22. func generateTestCert(t *testing.T) (certPEM, keyPEM []byte) {
  23. priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  24. if err != nil {
  25. t.Fatalf("failed to generate private key: %v", err)
  26. }
  27. template := x509.Certificate{
  28. SerialNumber: big.NewInt(1),
  29. Subject: pkix.Name{
  30. Organization: []string{"Test TUIC Server"},
  31. },
  32. NotBefore: time.Now().Add(-1 * time.Hour),
  33. NotAfter: time.Now().Add(24 * time.Hour),
  34. KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
  35. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
  36. BasicConstraintsValid: true,
  37. IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
  38. DNSNames: []string{"localhost"},
  39. }
  40. derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
  41. if err != nil {
  42. t.Fatalf("failed to create certificate: %v", err)
  43. }
  44. certPEM = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
  45. privBytes, err := x509.MarshalECPrivateKey(priv)
  46. if err != nil {
  47. t.Fatalf("failed to marshal private key: %v", err)
  48. }
  49. keyPEM = pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: privBytes})
  50. return certPEM, keyPEM
  51. }
  52. func TestServerTCPConnectE2E(t *testing.T) {
  53. for _, controller := range []string{"bbr", "cubic", "new_reno"} {
  54. t.Run(controller, func(t *testing.T) {
  55. testServerTCPConnectE2E(t, controller)
  56. })
  57. }
  58. }
  59. func testServerTCPConnectE2E(t *testing.T, controller string) {
  60. certPEM, keyPEM := generateTestCert(t)
  61. // Start mock SOCKS5 server on loopback
  62. socksAddr, socksCleanup := startMockSocks5Server(t, "[email protected]", "mock-socks-pass")
  63. defer socksCleanup()
  64. testUUID := uuid.New()
  65. testPassword := "secret-client-password"
  66. inst := Instance{
  67. Id: 1,
  68. Tag: "tuic-test",
  69. Listen: "127.0.0.1",
  70. Port: 0,
  71. Certificate: string(certPEM),
  72. PrivateKey: string(keyPEM),
  73. CongestionControl: controller,
  74. ALPN: []string{"h3"},
  75. MaxIdleTime: 5,
  76. AuthenticationTimeout: 2,
  77. Clients: []TuicClientSettings{
  78. {
  79. UUID: testUUID.String(),
  80. Password: testPassword,
  81. Email: "[email protected]",
  82. },
  83. },
  84. }
  85. relay := &SocksRelay{
  86. Addr: socksAddr,
  87. Password: "mock-socks-pass",
  88. }
  89. server, err := NewServer(inst, relay)
  90. if err != nil {
  91. t.Fatalf("NewServer failed: %v", err)
  92. }
  93. if err := server.Start(); err != nil {
  94. t.Fatalf("Server.Start failed: %v", err)
  95. }
  96. defer server.Close()
  97. serverAddr := server.packetConn.LocalAddr().String()
  98. // Connect client to TUIC server via QUIC
  99. clientTLS := &tls.Config{
  100. InsecureSkipVerify: true,
  101. NextProtos: []string{"h3"},
  102. }
  103. quicConfig := &quic.Config{
  104. EnableDatagrams: true,
  105. }
  106. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  107. defer cancel()
  108. conn, err := quic.DialAddr(ctx, serverAddr, clientTLS, quicConfig)
  109. if err != nil {
  110. t.Fatalf("quic.DialAddr failed: %v", err)
  111. }
  112. defer conn.CloseWithError(0, "")
  113. // 1. Authenticate client on a uni stream
  114. tlsState := conn.ConnectionState().TLS
  115. token, err := tlsState.ExportKeyingMaterial(string(testUUID[:]), []byte(testPassword), 32)
  116. if err != nil {
  117. t.Fatalf("ExportKeyingMaterial failed: %v", err)
  118. }
  119. uniStream, err := conn.OpenUniStreamSync(ctx)
  120. if err != nil {
  121. t.Fatalf("OpenUniStreamSync failed: %v", err)
  122. }
  123. // Send: [VER (0x05)][0x00][UUID (16)][TOKEN (32)]
  124. authPayload := make([]byte, 2+16+32)
  125. authPayload[0] = ProtocolVersion
  126. authPayload[1] = CmdAuthenticate
  127. copy(authPayload[2:18], testUUID[:])
  128. copy(authPayload[18:50], token)
  129. if _, err := uniStream.Write(authPayload); err != nil {
  130. t.Fatalf("write auth payload failed: %v", err)
  131. }
  132. _ = uniStream.Close()
  133. // 2. Open bidirectional stream for TCP Connect
  134. biStream, err := conn.OpenStreamSync(ctx)
  135. if err != nil {
  136. t.Fatalf("OpenStreamSync failed: %v", err)
  137. }
  138. defer biStream.Close()
  139. // Send: [VER (0x05)][0x01][ADDR]
  140. target := &Address{
  141. Type: AddrTypeIPv4,
  142. IP: net.ParseIP("1.1.1.1"),
  143. Port: 80,
  144. }
  145. var connectBuf bytes.Buffer
  146. connectBuf.WriteByte(ProtocolVersion)
  147. connectBuf.WriteByte(CmdConnect)
  148. if err := WriteAddress(&connectBuf, target); err != nil {
  149. t.Fatalf("WriteAddress failed: %v", err)
  150. }
  151. if _, err := biStream.Write(connectBuf.Bytes()); err != nil {
  152. t.Fatalf("write connect cmd failed: %v", err)
  153. }
  154. // 3. Send test data and read echo response back through SOCKS5 bridge
  155. testMsg := []byte("ping pong over native go tuic!")
  156. if _, err := biStream.Write(testMsg); err != nil {
  157. t.Fatalf("write test message failed: %v", err)
  158. }
  159. recvBuf := make([]byte, len(testMsg))
  160. if _, err := io.ReadFull(biStream, recvBuf); err != nil {
  161. t.Fatalf("read echo failed: %v", err)
  162. }
  163. if !bytes.Equal(recvBuf, testMsg) {
  164. t.Fatalf("expected %q, got %q", testMsg, recvBuf)
  165. }
  166. // 4. Verify traffic was recorded for [email protected]
  167. activeEmails := server.GetActiveEmails(10 * time.Second)
  168. if len(activeEmails) == 0 || activeEmails[0] != "[email protected]" {
  169. t.Fatalf("expected active email [email protected], got %v", activeEmails)
  170. }
  171. waitForClientTraffic(t, server, "[email protected]", int64(len(testMsg)))
  172. }
  173. // closeUniStream tolerates only the server's STOP_SENDING: it cancels the read side of a
  174. // uni stream once the command is parsed, which can land before the client's FIN.
  175. func closeUniStream(t *testing.T, stream interface {
  176. Close() error
  177. Context() context.Context
  178. },
  179. ) {
  180. t.Helper()
  181. err := stream.Close()
  182. if err == nil {
  183. return
  184. }
  185. cause := context.Cause(stream.Context())
  186. var clientErr *quic.StreamError
  187. var serverErr *serverquic.StreamError
  188. if (errors.As(cause, &clientErr) && clientErr.Remote) || (errors.As(cause, &serverErr) && serverErr.Remote) {
  189. return
  190. }
  191. t.Fatalf("close uni stream: %v (cause %v)", err, cause)
  192. }
  193. // waitForClientTraffic accumulates drained deltas because the up and down counters are
  194. // bumped on different relay goroutines, so the echo can arrive before the upload is counted.
  195. func waitForClientTraffic(t *testing.T, server *Server, email string, minBytes int64) {
  196. t.Helper()
  197. var up, down int64
  198. deadline := time.Now().Add(4 * time.Second)
  199. for {
  200. for _, delta := range server.CollectClientTraffic() {
  201. if delta.Email != email {
  202. t.Fatalf("unexpected traffic delta for %q: %+v", delta.Email, delta)
  203. }
  204. up += delta.Up
  205. down += delta.Down
  206. }
  207. if up >= minBytes && down >= minBytes {
  208. return
  209. }
  210. if time.Now().After(deadline) {
  211. t.Fatalf("traffic for %s = up %d, down %d; want both >= %d", email, up, down, minBytes)
  212. }
  213. time.Sleep(5 * time.Millisecond)
  214. }
  215. }
  216. func TestServerUDPDatagramE2E(t *testing.T) {
  217. for _, controller := range []string{"bbr", "cubic", "new_reno"} {
  218. t.Run(controller, func(t *testing.T) {
  219. testServerUDPDatagramE2E(t, controller)
  220. })
  221. }
  222. }
  223. func testServerUDPDatagramE2E(t *testing.T, controller string) {
  224. certPEM, keyPEM := generateTestCert(t)
  225. socksAddr, socksCleanup := startMockSocks5Server(t, "[email protected]", "mock-socks-pass")
  226. defer socksCleanup()
  227. testUUID := uuid.New()
  228. testPassword := "secret-bob-password"
  229. inst := Instance{
  230. Id: 2,
  231. Tag: "tuic-udp-test",
  232. Listen: "127.0.0.1",
  233. Port: 0,
  234. Certificate: string(certPEM),
  235. PrivateKey: string(keyPEM),
  236. CongestionControl: controller,
  237. ALPN: []string{"h3"},
  238. MaxIdleTime: 5,
  239. AuthenticationTimeout: 2,
  240. Clients: []TuicClientSettings{
  241. {
  242. UUID: testUUID.String(),
  243. Password: testPassword,
  244. Email: "[email protected]",
  245. },
  246. },
  247. }
  248. relay := &SocksRelay{
  249. Addr: socksAddr,
  250. Password: "mock-socks-pass",
  251. }
  252. server, err := NewServer(inst, relay)
  253. if err != nil {
  254. t.Fatalf("NewServer failed: %v", err)
  255. }
  256. if err := server.Start(); err != nil {
  257. t.Fatalf("Server.Start failed: %v", err)
  258. }
  259. defer server.Close()
  260. serverAddr := server.packetConn.LocalAddr().String()
  261. clientTLS := &tls.Config{
  262. InsecureSkipVerify: true,
  263. NextProtos: []string{"h3"},
  264. }
  265. quicConfig := &quic.Config{
  266. EnableDatagrams: true,
  267. }
  268. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  269. defer cancel()
  270. conn, err := quic.DialAddr(ctx, serverAddr, clientTLS, quicConfig)
  271. if err != nil {
  272. t.Fatalf("quic.DialAddr failed: %v", err)
  273. }
  274. defer conn.CloseWithError(0, "")
  275. // 1. Authenticate via uni stream
  276. tlsState := conn.ConnectionState().TLS
  277. token, err := tlsState.ExportKeyingMaterial(string(testUUID[:]), []byte(testPassword), 32)
  278. if err != nil {
  279. t.Fatalf("ExportKeyingMaterial failed: %v", err)
  280. }
  281. uniStream, err := conn.OpenUniStreamSync(ctx)
  282. if err != nil {
  283. t.Fatalf("OpenUniStreamSync failed: %v", err)
  284. }
  285. authPayload := make([]byte, 2+16+32)
  286. authPayload[0] = ProtocolVersion
  287. authPayload[1] = CmdAuthenticate
  288. copy(authPayload[2:18], testUUID[:])
  289. copy(authPayload[18:50], token)
  290. if _, err := uniStream.Write(authPayload); err != nil {
  291. t.Fatalf("write auth payload failed: %v", err)
  292. }
  293. _ = uniStream.Close()
  294. // 2. Send UDP datagram
  295. target := &Address{
  296. Type: AddrTypeIPv4,
  297. IP: net.ParseIP("8.8.8.8"),
  298. Port: 53,
  299. }
  300. udpMsg := bytes.Repeat([]byte("d"), 1300)
  301. // Give a tiny moment for auth to register
  302. time.Sleep(50 * time.Millisecond)
  303. fragmentTotal := (len(udpMsg) + maxDatagramFragmentSize - 1) / maxDatagramFragmentSize
  304. for i := 0; i < fragmentTotal; i++ {
  305. start := i * maxDatagramFragmentSize
  306. end := min(start+maxDatagramFragmentSize, len(udpMsg))
  307. addr := (*Address)(nil)
  308. if i == 0 {
  309. addr = target
  310. }
  311. var frame bytes.Buffer
  312. if err := WritePacket(&frame, 100, 1, uint8(fragmentTotal), uint8(i), addr, udpMsg[start:end]); err != nil {
  313. t.Fatalf("WritePacket failed: %v", err)
  314. }
  315. if err := conn.SendDatagram(frame.Bytes()); err != nil {
  316. t.Fatalf("SendDatagram failed: %v", err)
  317. }
  318. }
  319. // 3. Receive and reassemble the echo reply via datagrams.
  320. replyReassembler := newPacketReassembler(1500)
  321. var replyPayload []byte
  322. for replyPayload == nil {
  323. recvDgram, err := conn.ReceiveDatagram(ctx)
  324. if err != nil {
  325. t.Fatalf("ReceiveDatagram failed: %v", err)
  326. }
  327. if len(recvDgram) < 2 || recvDgram[0] != ProtocolVersion || recvDgram[1] != CmdPacket {
  328. t.Fatalf("unexpected datagram reply: %x", recvDgram)
  329. }
  330. pktReader := bytes.NewReader(recvDgram[2:])
  331. hdr, err := ReadPacketHeader(pktReader)
  332. if err != nil {
  333. t.Fatalf("ReadPacketHeader failed: %v", err)
  334. }
  335. fragment, err := readPacketPayload(pktReader, hdr)
  336. if err != nil || pktReader.Len() != 0 {
  337. t.Fatalf("read reply payload failed: %v", err)
  338. }
  339. _, assembled, complete := replyReassembler.feed(packetTransportDatagram, hdr, fragment)
  340. if complete {
  341. replyPayload = assembled
  342. }
  343. }
  344. if !bytes.Equal(replyPayload, udpMsg) {
  345. t.Fatalf("expected %q, got %q", udpMsg, replyPayload)
  346. }
  347. // 4. Verify traffic
  348. waitForClientTraffic(t, server, "[email protected]", int64(len(udpMsg)))
  349. }
  350. func TestServerUDPStreamE2E(t *testing.T) {
  351. for _, controller := range []string{"bbr", "cubic", "new_reno"} {
  352. t.Run(controller, func(t *testing.T) {
  353. testServerUDPStreamE2E(t, controller)
  354. })
  355. }
  356. }
  357. func testServerUDPStreamE2E(t *testing.T, controller string) {
  358. certPEM, keyPEM := generateTestCert(t)
  359. socksAddr, socksCleanup := startMockSocks5Server(t, "[email protected]", "mock-socks-pass")
  360. defer socksCleanup()
  361. testUUID := uuid.New()
  362. testPassword := "secret-stream-password"
  363. server, err := NewServer(Instance{
  364. Id: 3,
  365. Tag: "tuic-udp-stream-test",
  366. Listen: "127.0.0.1",
  367. Certificate: string(certPEM),
  368. PrivateKey: string(keyPEM),
  369. ALPN: []string{"h3"},
  370. MaxIdleTime: 5,
  371. AuthenticationTimeout: 2,
  372. MaxUdpRelayPacketSize: maxUdpRelayPacketSize,
  373. CongestionControl: controller,
  374. Clients: []TuicClientSettings{{
  375. UUID: testUUID.String(),
  376. Password: testPassword,
  377. Email: "[email protected]",
  378. }},
  379. }, &SocksRelay{Addr: socksAddr, Password: "mock-socks-pass"})
  380. if err != nil {
  381. t.Fatalf("NewServer failed: %v", err)
  382. }
  383. if err := server.Start(); err != nil {
  384. t.Fatalf("Server.Start failed: %v", err)
  385. }
  386. defer server.Close()
  387. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  388. defer cancel()
  389. conn, err := quic.DialAddr(ctx, server.packetConn.LocalAddr().String(), &tls.Config{
  390. InsecureSkipVerify: true,
  391. NextProtos: []string{"h3"},
  392. }, &quic.Config{EnableDatagrams: true})
  393. if err != nil {
  394. t.Fatalf("quic.DialAddr failed: %v", err)
  395. }
  396. defer conn.CloseWithError(0, "")
  397. tlsState := conn.ConnectionState().TLS
  398. token, err := tlsState.ExportKeyingMaterial(string(testUUID[:]), []byte(testPassword), 32)
  399. if err != nil {
  400. t.Fatalf("ExportKeyingMaterial failed: %v", err)
  401. }
  402. authStream, err := conn.OpenUniStreamSync(ctx)
  403. if err != nil {
  404. t.Fatalf("OpenUniStreamSync for authentication failed: %v", err)
  405. }
  406. authPayload := make([]byte, 2+16+32)
  407. authPayload[0] = ProtocolVersion
  408. authPayload[1] = CmdAuthenticate
  409. copy(authPayload[2:18], testUUID[:])
  410. copy(authPayload[18:], token)
  411. if _, err := authStream.Write(authPayload); err != nil {
  412. t.Fatalf("write authentication payload failed: %v", err)
  413. }
  414. closeUniStream(t, authStream)
  415. target := &Address{Type: AddrTypeIPv4, IP: net.ParseIP("8.8.8.8"), Port: 53}
  416. udpMsg := bytes.Repeat([]byte("s"), 8500)
  417. fragmentTotal := (len(udpMsg) + maxStreamFragmentSize - 1) / maxStreamFragmentSize
  418. for i := 0; i < fragmentTotal; i++ {
  419. start := i * maxStreamFragmentSize
  420. end := min(start+maxStreamFragmentSize, len(udpMsg))
  421. addr := (*Address)(nil)
  422. if i == 0 {
  423. addr = target
  424. }
  425. var frame bytes.Buffer
  426. if err := WritePacket(&frame, 300, 1, uint8(fragmentTotal), uint8(i), addr, udpMsg[start:end]); err != nil {
  427. t.Fatalf("WritePacket failed: %v", err)
  428. }
  429. packetStream, err := conn.OpenUniStreamSync(ctx)
  430. if err != nil {
  431. t.Fatalf("OpenUniStreamSync for packet failed: %v", err)
  432. }
  433. if _, err := packetStream.Write(frame.Bytes()); err != nil {
  434. t.Fatalf("write packet frame failed: %v", err)
  435. }
  436. closeUniStream(t, packetStream)
  437. }
  438. replyReassembler := newPacketReassembler(maxUdpRelayPacketSize)
  439. var reply []byte
  440. for reply == nil {
  441. responseStream, err := conn.AcceptUniStream(ctx)
  442. if err != nil {
  443. t.Fatalf("AcceptUniStream for response failed: %v", err)
  444. }
  445. _, command, err := ReadCommand(responseStream)
  446. if err != nil {
  447. t.Fatalf("read response command: %v", err)
  448. }
  449. if command != CmdPacket {
  450. t.Fatalf("response command = %d, want %d", command, CmdPacket)
  451. }
  452. hdr, err := ReadPacketHeader(responseStream)
  453. if err != nil {
  454. t.Fatalf("ReadPacketHeader failed: %v", err)
  455. }
  456. fragment, err := readPacketPayload(responseStream, hdr)
  457. if err != nil {
  458. t.Fatalf("read response payload failed: %v", err)
  459. }
  460. _, assembled, complete := replyReassembler.feed(packetTransportStream, hdr, fragment)
  461. if complete {
  462. reply = assembled
  463. }
  464. }
  465. if !bytes.Equal(reply, udpMsg) {
  466. t.Fatalf("stream response size = %d, want %d", len(reply), len(udpMsg))
  467. }
  468. }
  469. func TestNewServerRejectsOversizedMaxUdpRelayPacketSize(t *testing.T) {
  470. certPEM, keyPEM := generateTestCert(t)
  471. _, err := NewServer(Instance{
  472. Listen: "127.0.0.1",
  473. Port: 8443,
  474. Certificate: string(certPEM),
  475. PrivateKey: string(keyPEM),
  476. MaxUdpRelayPacketSize: maxLegacyUdpRelayPacketSize + 1,
  477. }, &SocksRelay{Addr: "127.0.0.1:1"})
  478. if err == nil {
  479. t.Fatal("expected oversized max UDP relay packet size to be rejected")
  480. }
  481. }
  482. func TestNewServerClampsLegacyUdpPayloadLimit(t *testing.T) {
  483. certPEM, keyPEM := generateTestCert(t)
  484. server, err := NewServer(Instance{
  485. Listen: "127.0.0.1",
  486. Port: 0,
  487. Certificate: string(certPEM),
  488. PrivateKey: string(keyPEM),
  489. MaxUdpRelayPacketSize: maxLegacyUdpRelayPacketSize,
  490. }, &SocksRelay{})
  491. if err != nil {
  492. t.Fatalf("NewServer: %v", err)
  493. }
  494. if server.maxUdpRelayPacketSize != maxSafeUdpRelayPacketSize {
  495. t.Fatalf("legacy UDP limit = %d, want clamped limit %d", server.maxUdpRelayPacketSize, maxSafeUdpRelayPacketSize)
  496. }
  497. }
  498. func TestPacketReassemblerInvalidatesAssemblyWhenFragmentTotalChanges(t *testing.T) {
  499. reassembler := newPacketReassembler(64)
  500. first := &PacketHeader{AssocID: 7, PktID: 9, FragTotal: 2, FragID: 0, Addr: &Address{Type: AddrTypeIPv4, IP: net.ParseIP("127.0.0.1"), Port: 53}, Size: 1}
  501. if _, _, complete := reassembler.feed(packetTransportDatagram, first, []byte("A")); complete {
  502. t.Fatal("first fragment unexpectedly completed")
  503. }
  504. single := &PacketHeader{AssocID: 7, PktID: 9, FragTotal: 1, FragID: 0, Addr: first.Addr, Size: 1}
  505. if _, got, complete := reassembler.feed(packetTransportDatagram, single, []byte("Z")); !complete || string(got) != "Z" {
  506. t.Fatalf("single packet = %q, complete=%v; want Z", got, complete)
  507. }
  508. last := &PacketHeader{AssocID: 7, PktID: 9, FragTotal: 2, FragID: 1, Size: 1}
  509. if _, _, complete := reassembler.feed(packetTransportDatagram, last, []byte("B")); complete {
  510. t.Fatal("stale first fragment was combined with a later packet")
  511. }
  512. }
  513. func TestUdpAssociationPinsFirstPacketModeAndDissociateClearsFragments(t *testing.T) {
  514. registry := newUdpAssociationRegistry(64)
  515. addr := &Address{Type: AddrTypeIPv4, IP: net.ParseIP("127.0.0.1"), Port: 53}
  516. first := &PacketHeader{AssocID: 3, PktID: 1, FragTotal: 2, FragID: 0, Addr: addr, Size: 1}
  517. association, _, _, complete := registry.feed(packetTransportDatagram, first, []byte("A"))
  518. if association == nil || complete {
  519. t.Fatal("expected first native fragment to establish an incomplete association")
  520. }
  521. singleStream := &PacketHeader{AssocID: 3, PktID: 2, FragTotal: 1, FragID: 0, Addr: addr, Size: 1}
  522. association, _, _, complete = registry.feed(packetTransportStream, singleStream, []byte("S"))
  523. if association.responseTransport != packetTransportDatagram || !complete {
  524. t.Fatalf("mixed-mode packet changed response mode: association=%+v complete=%v", association, complete)
  525. }
  526. if !registry.dissociate(3) {
  527. t.Fatal("expected dissociate to remove association")
  528. }
  529. late := &PacketHeader{AssocID: 3, PktID: 1, FragTotal: 2, FragID: 1, Size: 1}
  530. _, _, _, complete = registry.feed(packetTransportDatagram, late, []byte("B"))
  531. if complete {
  532. t.Fatal("late fragment completed an assembly from before dissociate")
  533. }
  534. }
  535. func TestPacketReassembler(t *testing.T) {
  536. pr := newPacketReassembler(1500)
  537. targetAddr := &Address{Type: AddrTypeIPv4, IP: net.ParseIP("1.1.1.1"), Port: 53}
  538. // 1. Unfragmented packet
  539. hdrSingle := &PacketHeader{
  540. AssocID: 1,
  541. PktID: 1,
  542. FragTotal: 1,
  543. FragID: 0,
  544. Size: uint16(len("hello single")),
  545. Addr: targetAddr,
  546. }
  547. addr, payload, complete := pr.feed(packetTransportDatagram, hdrSingle, []byte("hello single"))
  548. if addr == nil || !complete || string(payload) != "hello single" {
  549. t.Fatalf("unexpected single packet result: %v, %s", addr, payload)
  550. }
  551. // 2. In-order fragments (3 parts)
  552. hdr0 := &PacketHeader{AssocID: 2, PktID: 10, FragTotal: 3, FragID: 0, Size: 6, Addr: targetAddr}
  553. hdr1 := &PacketHeader{AssocID: 2, PktID: 10, FragTotal: 3, FragID: 1, Size: 6, Addr: &Address{Type: AddrTypeNone}}
  554. hdr2 := &PacketHeader{AssocID: 2, PktID: 10, FragTotal: 3, FragID: 2, Size: 5, Addr: &Address{Type: AddrTypeNone}}
  555. _, p0, complete := pr.feed(packetTransportDatagram, hdr0, []byte("part0-"))
  556. if p0 != nil || complete {
  557. t.Fatalf("expected nil before all fragments arrive, got %s", p0)
  558. }
  559. _, p1, complete := pr.feed(packetTransportDatagram, hdr1, []byte("part1-"))
  560. if p1 != nil || complete {
  561. t.Fatalf("expected nil before all fragments arrive, got %s", p1)
  562. }
  563. a2, p2, complete := pr.feed(packetTransportDatagram, hdr2, []byte("part2"))
  564. if a2 == nil || !complete || string(p2) != "part0-part1-part2" {
  565. t.Fatalf("expected reassembled payload 'part0-part1-part2', got %v, %s", a2, p2)
  566. }
  567. // 3. Out-of-order fragments (parts 1, 2, 0)
  568. hdrOO0 := &PacketHeader{AssocID: 3, PktID: 20, FragTotal: 3, FragID: 0, Size: 6, Addr: targetAddr}
  569. hdrOO1 := &PacketHeader{AssocID: 3, PktID: 20, FragTotal: 3, FragID: 1, Size: 7, Addr: &Address{Type: AddrTypeNone}}
  570. hdrOO2 := &PacketHeader{AssocID: 3, PktID: 20, FragTotal: 3, FragID: 2, Size: 3, Addr: &Address{Type: AddrTypeNone}}
  571. if _, p, done := pr.feed(packetTransportDatagram, hdrOO1, []byte("MIDDLE-")); p != nil || done {
  572. t.Fatalf("expected nil, got %s", p)
  573. }
  574. if _, p, done := pr.feed(packetTransportDatagram, hdrOO2, []byte("END")); p != nil || done {
  575. t.Fatalf("expected nil, got %s", p)
  576. }
  577. aOO, pOO, done := pr.feed(packetTransportDatagram, hdrOO0, []byte("START-"))
  578. if aOO == nil || !done || string(pOO) != "START-MIDDLE-END" {
  579. t.Fatalf("expected 'START-MIDDLE-END', got %s", pOO)
  580. }
  581. // 4. Invalid FragID >= FragTotal
  582. hdrInv := &PacketHeader{AssocID: 4, PktID: 30, FragTotal: 2, FragID: 2, Size: 7, Addr: targetAddr}
  583. if _, p, done := pr.feed(packetTransportDatagram, hdrInv, []byte("invalid")); p != nil || done {
  584. t.Fatalf("expected nil for invalid FragID, got %s", p)
  585. }
  586. // A changed fragment total for an in-flight packet must discard the packet safely.
  587. hdrMixed0 := &PacketHeader{AssocID: 5, PktID: 40, FragTotal: 2, FragID: 0, Size: 1, Addr: targetAddr}
  588. hdrMixed3 := &PacketHeader{AssocID: 5, PktID: 40, FragTotal: 4, FragID: 3, Size: 1, Addr: &Address{Type: AddrTypeNone}}
  589. if _, _, done := pr.feed(packetTransportDatagram, hdrMixed0, []byte("a")); done {
  590. t.Fatal("expected first mixed-total fragment to remain incomplete")
  591. }
  592. if _, _, done := pr.feed(packetTransportDatagram, hdrMixed3, []byte("b")); done {
  593. t.Fatal("expected inconsistent fragment total to be discarded")
  594. }
  595. if _, ok := pr.packets[packetFragmentKey{assocID: 5, pktID: 40, transport: packetTransportDatagram}]; ok {
  596. t.Fatal("inconsistent packet assembly was not discarded")
  597. }
  598. // Fragments from different transports cannot be combined into one packet.
  599. streamFirst := &PacketHeader{AssocID: 6, PktID: 50, FragTotal: 2, FragID: 0, Size: 1, Addr: targetAddr}
  600. datagramLast := &PacketHeader{AssocID: 6, PktID: 50, FragTotal: 2, FragID: 1, Size: 1, Addr: &Address{Type: AddrTypeNone}}
  601. if _, _, done := pr.feed(packetTransportStream, streamFirst, []byte("a")); done {
  602. t.Fatal("expected first stream fragment to remain incomplete")
  603. }
  604. if _, _, done := pr.feed(packetTransportDatagram, datagramLast, []byte("b")); done {
  605. t.Fatal("fragments from different transports must not combine")
  606. }
  607. if _, _, done := pr.feed(packetTransportStream, datagramLast, []byte("b")); !done {
  608. t.Fatal("expected stream fragments to reassemble")
  609. }
  610. // The configured size limit caps both complete packets and reassembly state.
  611. limited := newPacketReassembler(3)
  612. tooLarge0 := &PacketHeader{AssocID: 7, PktID: 60, FragTotal: 2, FragID: 0, Size: 2, Addr: targetAddr}
  613. tooLarge1 := &PacketHeader{AssocID: 7, PktID: 60, FragTotal: 2, FragID: 1, Size: 2, Addr: &Address{Type: AddrTypeNone}}
  614. if _, _, done := limited.feed(packetTransportDatagram, tooLarge0, []byte("ab")); done {
  615. t.Fatal("expected first oversized packet fragment to remain incomplete")
  616. }
  617. if _, _, done := limited.feed(packetTransportDatagram, tooLarge1, []byte("cd")); done {
  618. t.Fatal("oversized reassembled packet must be rejected")
  619. }
  620. if len(limited.packets) != 0 {
  621. t.Fatal("oversized reassembly state was not discarded")
  622. }
  623. bounded := newPacketReassembler(1500)
  624. for i := 0; i < maxPendingPacketAssemblies; i++ {
  625. hdr := &PacketHeader{
  626. AssocID: 8,
  627. PktID: uint16(i),
  628. FragTotal: 2,
  629. FragID: 0,
  630. Size: 1,
  631. Addr: targetAddr,
  632. }
  633. if _, _, done := bounded.feed(packetTransportDatagram, hdr, []byte("a")); done {
  634. t.Fatal("expected pending fragment to remain incomplete")
  635. }
  636. }
  637. if len(bounded.packets) != maxPendingPacketAssemblies {
  638. t.Fatalf("pending assembly count = %d, want %d", len(bounded.packets), maxPendingPacketAssemblies)
  639. }
  640. extra := &PacketHeader{AssocID: 8, PktID: 100, FragTotal: 2, FragID: 0, Size: 1, Addr: targetAddr}
  641. if _, _, done := bounded.feed(packetTransportDatagram, extra, []byte("a")); done {
  642. t.Fatal("expected new assembly to be rejected when the pending limit is reached")
  643. }
  644. if len(bounded.packets) != maxPendingPacketAssemblies {
  645. t.Fatalf("pending assembly count after overflow = %d, want %d", len(bounded.packets), maxPendingPacketAssemblies)
  646. }
  647. for _, packet := range bounded.packets {
  648. packet.updatedAt = time.Now().Add(-packetAssemblyTimeout - time.Second)
  649. }
  650. if _, _, done := bounded.feed(packetTransportDatagram, extra, []byte("a")); done {
  651. t.Fatal("expected new fragment to remain incomplete after stale entries are evicted")
  652. }
  653. if len(bounded.packets) != 1 {
  654. t.Fatalf("pending assembly count after stale cleanup = %d, want 1", len(bounded.packets))
  655. }
  656. }
  657. func TestAuthenticationTimeoutClosesUnauthenticatedConnections(t *testing.T) {
  658. for _, partial := range []bool{false, true} {
  659. name := "no-authenticate"
  660. if partial {
  661. name = "partial-authenticate"
  662. }
  663. t.Run(name, func(t *testing.T) {
  664. certPEM, keyPEM := generateTestCert(t)
  665. server, err := NewServer(Instance{
  666. Id: 99010,
  667. Tag: "auth-timeout-test",
  668. Listen: "127.0.0.1",
  669. Port: 0,
  670. Certificate: string(certPEM),
  671. PrivateKey: string(keyPEM),
  672. ALPN: []string{"h3"},
  673. MaxIdleTime: 5,
  674. AuthenticationTimeout: 1,
  675. }, &SocksRelay{})
  676. if err != nil {
  677. t.Fatalf("NewServer: %v", err)
  678. }
  679. if err := server.Start(); err != nil {
  680. t.Fatalf("Server.Start: %v", err)
  681. }
  682. t.Cleanup(func() { _ = server.Close() })
  683. ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second)
  684. defer cancel()
  685. conn, err := quic.DialAddr(ctx, server.packetConn.LocalAddr().String(), &tls.Config{
  686. InsecureSkipVerify: true,
  687. NextProtos: []string{"h3"},
  688. }, &quic.Config{EnableDatagrams: true, KeepAlivePeriod: time.Second})
  689. if err != nil {
  690. t.Fatalf("quic.DialAddr: %v", err)
  691. }
  692. defer conn.CloseWithError(0, "")
  693. if partial {
  694. stream, err := conn.OpenUniStreamSync(ctx)
  695. if err != nil {
  696. t.Fatalf("OpenUniStreamSync: %v", err)
  697. }
  698. if _, err := stream.Write([]byte{ProtocolVersion, CmdAuthenticate, 1}); err != nil {
  699. t.Fatalf("write partial Authenticate: %v", err)
  700. }
  701. }
  702. select {
  703. case <-conn.Context().Done():
  704. case <-ctx.Done():
  705. t.Fatalf("server left unauthenticated QUIC connection open: %v", ctx.Err())
  706. }
  707. })
  708. }
  709. }