server_test.go 25 KB

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