server_test.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
  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. waitForClientTraffic(t, server, "[email protected]", int64(len(testMsg)))
  170. }
  171. // waitForClientTraffic accumulates drained deltas because the up and down counters are
  172. // bumped on different relay goroutines, so the echo can arrive before the upload is counted.
  173. func waitForClientTraffic(t *testing.T, server *Server, email string, minBytes int64) {
  174. t.Helper()
  175. var up, down int64
  176. deadline := time.Now().Add(4 * time.Second)
  177. for {
  178. for _, delta := range server.CollectClientTraffic() {
  179. if delta.Email != email {
  180. t.Fatalf("unexpected traffic delta for %q: %+v", delta.Email, delta)
  181. }
  182. up += delta.Up
  183. down += delta.Down
  184. }
  185. if up >= minBytes && down >= minBytes {
  186. return
  187. }
  188. if time.Now().After(deadline) {
  189. t.Fatalf("traffic for %s = up %d, down %d; want both >= %d", email, up, down, minBytes)
  190. }
  191. time.Sleep(5 * time.Millisecond)
  192. }
  193. }
  194. func TestServerUDPDatagramE2E(t *testing.T) {
  195. for _, controller := range []string{"bbr", "cubic", "new_reno"} {
  196. t.Run(controller, func(t *testing.T) {
  197. testServerUDPDatagramE2E(t, controller)
  198. })
  199. }
  200. }
  201. func testServerUDPDatagramE2E(t *testing.T, controller string) {
  202. certPEM, keyPEM := generateTestCert(t)
  203. socksAddr, socksCleanup := startMockSocks5Server(t, "[email protected]", "mock-socks-pass")
  204. defer socksCleanup()
  205. testUUID := uuid.New()
  206. testPassword := "secret-bob-password"
  207. inst := Instance{
  208. Id: 2,
  209. Tag: "tuic-udp-test",
  210. Listen: "127.0.0.1",
  211. Port: 0,
  212. Certificate: string(certPEM),
  213. PrivateKey: string(keyPEM),
  214. CongestionControl: controller,
  215. ALPN: []string{"h3"},
  216. MaxIdleTime: 5,
  217. AuthenticationTimeout: 2,
  218. Clients: []TuicClientSettings{
  219. {
  220. UUID: testUUID.String(),
  221. Password: testPassword,
  222. Email: "[email protected]",
  223. },
  224. },
  225. }
  226. relay := &SocksRelay{
  227. Addr: socksAddr,
  228. Password: "mock-socks-pass",
  229. }
  230. server, err := NewServer(inst, relay)
  231. if err != nil {
  232. t.Fatalf("NewServer failed: %v", err)
  233. }
  234. if err := server.Start(); err != nil {
  235. t.Fatalf("Server.Start failed: %v", err)
  236. }
  237. defer server.Close()
  238. serverAddr := server.packetConn.LocalAddr().String()
  239. clientTLS := &tls.Config{
  240. InsecureSkipVerify: true,
  241. NextProtos: []string{"h3"},
  242. }
  243. quicConfig := &quic.Config{
  244. EnableDatagrams: true,
  245. }
  246. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  247. defer cancel()
  248. conn, err := quic.DialAddr(ctx, serverAddr, clientTLS, quicConfig)
  249. if err != nil {
  250. t.Fatalf("quic.DialAddr failed: %v", err)
  251. }
  252. defer conn.CloseWithError(0, "")
  253. // 1. Authenticate via uni stream
  254. tlsState := conn.ConnectionState().TLS
  255. token, err := tlsState.ExportKeyingMaterial(string(testUUID[:]), []byte(testPassword), 32)
  256. if err != nil {
  257. t.Fatalf("ExportKeyingMaterial failed: %v", err)
  258. }
  259. uniStream, err := conn.OpenUniStreamSync(ctx)
  260. if err != nil {
  261. t.Fatalf("OpenUniStreamSync failed: %v", err)
  262. }
  263. authPayload := make([]byte, 2+16+32)
  264. authPayload[0] = ProtocolVersion
  265. authPayload[1] = CmdAuthenticate
  266. copy(authPayload[2:18], testUUID[:])
  267. copy(authPayload[18:50], token)
  268. if _, err := uniStream.Write(authPayload); err != nil {
  269. t.Fatalf("write auth payload failed: %v", err)
  270. }
  271. _ = uniStream.Close()
  272. // 2. Send UDP datagram
  273. target := &Address{
  274. Type: AddrTypeIPv4,
  275. IP: net.ParseIP("8.8.8.8"),
  276. Port: 53,
  277. }
  278. udpMsg := bytes.Repeat([]byte("d"), 1300)
  279. // Give a tiny moment for auth to register
  280. time.Sleep(50 * time.Millisecond)
  281. fragmentTotal := (len(udpMsg) + maxDatagramFragmentSize - 1) / maxDatagramFragmentSize
  282. for i := 0; i < fragmentTotal; i++ {
  283. start := i * maxDatagramFragmentSize
  284. end := min(start+maxDatagramFragmentSize, len(udpMsg))
  285. addr := (*Address)(nil)
  286. if i == 0 {
  287. addr = target
  288. }
  289. var frame bytes.Buffer
  290. if err := WritePacket(&frame, 100, 1, uint8(fragmentTotal), uint8(i), addr, udpMsg[start:end]); err != nil {
  291. t.Fatalf("WritePacket failed: %v", err)
  292. }
  293. if err := conn.SendDatagram(frame.Bytes()); err != nil {
  294. t.Fatalf("SendDatagram failed: %v", err)
  295. }
  296. }
  297. // 3. Receive and reassemble the echo reply via datagrams.
  298. replyReassembler := newPacketReassembler(1500)
  299. var replyPayload []byte
  300. for replyPayload == nil {
  301. recvDgram, err := conn.ReceiveDatagram(ctx)
  302. if err != nil {
  303. t.Fatalf("ReceiveDatagram failed: %v", err)
  304. }
  305. if len(recvDgram) < 2 || recvDgram[0] != ProtocolVersion || recvDgram[1] != CmdPacket {
  306. t.Fatalf("unexpected datagram reply: %x", recvDgram)
  307. }
  308. pktReader := bytes.NewReader(recvDgram[2:])
  309. hdr, err := ReadPacketHeader(pktReader)
  310. if err != nil {
  311. t.Fatalf("ReadPacketHeader failed: %v", err)
  312. }
  313. fragment, err := readPacketPayload(pktReader, hdr)
  314. if err != nil || pktReader.Len() != 0 {
  315. t.Fatalf("read reply payload failed: %v", err)
  316. }
  317. _, assembled, complete := replyReassembler.feed(packetTransportDatagram, hdr, fragment)
  318. if complete {
  319. replyPayload = assembled
  320. }
  321. }
  322. if !bytes.Equal(replyPayload, udpMsg) {
  323. t.Fatalf("expected %q, got %q", udpMsg, replyPayload)
  324. }
  325. // 4. Verify traffic
  326. waitForClientTraffic(t, server, "[email protected]", int64(len(udpMsg)))
  327. }
  328. func TestServerUDPStreamE2E(t *testing.T) {
  329. for _, controller := range []string{"bbr", "cubic", "new_reno"} {
  330. t.Run(controller, func(t *testing.T) {
  331. testServerUDPStreamE2E(t, controller)
  332. })
  333. }
  334. }
  335. func testServerUDPStreamE2E(t *testing.T, controller string) {
  336. certPEM, keyPEM := generateTestCert(t)
  337. socksAddr, socksCleanup := startMockSocks5Server(t, "[email protected]", "mock-socks-pass")
  338. defer socksCleanup()
  339. testUUID := uuid.New()
  340. testPassword := "secret-stream-password"
  341. server, err := NewServer(Instance{
  342. Id: 3,
  343. Tag: "tuic-udp-stream-test",
  344. Listen: "127.0.0.1",
  345. Certificate: string(certPEM),
  346. PrivateKey: string(keyPEM),
  347. ALPN: []string{"h3"},
  348. MaxIdleTime: 5,
  349. AuthenticationTimeout: 2,
  350. MaxUdpRelayPacketSize: maxUdpRelayPacketSize,
  351. CongestionControl: controller,
  352. Clients: []TuicClientSettings{{
  353. UUID: testUUID.String(),
  354. Password: testPassword,
  355. Email: "[email protected]",
  356. }},
  357. }, &SocksRelay{Addr: socksAddr, Password: "mock-socks-pass"})
  358. if err != nil {
  359. t.Fatalf("NewServer failed: %v", err)
  360. }
  361. if err := server.Start(); err != nil {
  362. t.Fatalf("Server.Start failed: %v", err)
  363. }
  364. defer server.Close()
  365. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  366. defer cancel()
  367. conn, err := quic.DialAddr(ctx, server.packetConn.LocalAddr().String(), &tls.Config{
  368. InsecureSkipVerify: true,
  369. NextProtos: []string{"h3"},
  370. }, &quic.Config{EnableDatagrams: true})
  371. if err != nil {
  372. t.Fatalf("quic.DialAddr failed: %v", err)
  373. }
  374. defer conn.CloseWithError(0, "")
  375. tlsState := conn.ConnectionState().TLS
  376. token, err := tlsState.ExportKeyingMaterial(string(testUUID[:]), []byte(testPassword), 32)
  377. if err != nil {
  378. t.Fatalf("ExportKeyingMaterial failed: %v", err)
  379. }
  380. authStream, err := conn.OpenUniStreamSync(ctx)
  381. if err != nil {
  382. t.Fatalf("OpenUniStreamSync for authentication failed: %v", err)
  383. }
  384. authPayload := make([]byte, 2+16+32)
  385. authPayload[0] = ProtocolVersion
  386. authPayload[1] = CmdAuthenticate
  387. copy(authPayload[2:18], testUUID[:])
  388. copy(authPayload[18:], token)
  389. if _, err := authStream.Write(authPayload); err != nil {
  390. t.Fatalf("write authentication payload failed: %v", err)
  391. }
  392. if err := authStream.Close(); err != nil {
  393. t.Fatalf("close authentication stream failed: %v", err)
  394. }
  395. target := &Address{Type: AddrTypeIPv4, IP: net.ParseIP("8.8.8.8"), Port: 53}
  396. udpMsg := bytes.Repeat([]byte("s"), 8500)
  397. fragmentTotal := (len(udpMsg) + maxStreamFragmentSize - 1) / maxStreamFragmentSize
  398. for i := 0; i < fragmentTotal; i++ {
  399. start := i * maxStreamFragmentSize
  400. end := min(start+maxStreamFragmentSize, len(udpMsg))
  401. addr := (*Address)(nil)
  402. if i == 0 {
  403. addr = target
  404. }
  405. var frame bytes.Buffer
  406. if err := WritePacket(&frame, 300, 1, uint8(fragmentTotal), uint8(i), addr, udpMsg[start:end]); err != nil {
  407. t.Fatalf("WritePacket failed: %v", err)
  408. }
  409. packetStream, err := conn.OpenUniStreamSync(ctx)
  410. if err != nil {
  411. t.Fatalf("OpenUniStreamSync for packet failed: %v", err)
  412. }
  413. if _, err := packetStream.Write(frame.Bytes()); err != nil {
  414. t.Fatalf("write packet frame failed: %v", err)
  415. }
  416. if err := packetStream.Close(); err != nil {
  417. t.Fatalf("close packet stream failed: %v", err)
  418. }
  419. }
  420. replyReassembler := newPacketReassembler(maxUdpRelayPacketSize)
  421. var reply []byte
  422. for reply == nil {
  423. responseStream, err := conn.AcceptUniStream(ctx)
  424. if err != nil {
  425. t.Fatalf("AcceptUniStream for response failed: %v", err)
  426. }
  427. _, command, err := ReadCommand(responseStream)
  428. if err != nil {
  429. t.Fatalf("read response command: %v", err)
  430. }
  431. if command != CmdPacket {
  432. t.Fatalf("response command = %d, want %d", command, CmdPacket)
  433. }
  434. hdr, err := ReadPacketHeader(responseStream)
  435. if err != nil {
  436. t.Fatalf("ReadPacketHeader failed: %v", err)
  437. }
  438. fragment, err := readPacketPayload(responseStream, hdr)
  439. if err != nil {
  440. t.Fatalf("read response payload failed: %v", err)
  441. }
  442. _, assembled, complete := replyReassembler.feed(packetTransportStream, hdr, fragment)
  443. if complete {
  444. reply = assembled
  445. }
  446. }
  447. if !bytes.Equal(reply, udpMsg) {
  448. t.Fatalf("stream response size = %d, want %d", len(reply), len(udpMsg))
  449. }
  450. }
  451. func TestNewServerRejectsOversizedMaxUdpRelayPacketSize(t *testing.T) {
  452. certPEM, keyPEM := generateTestCert(t)
  453. _, err := NewServer(Instance{
  454. Listen: "127.0.0.1",
  455. Port: 8443,
  456. Certificate: string(certPEM),
  457. PrivateKey: string(keyPEM),
  458. MaxUdpRelayPacketSize: maxLegacyUdpRelayPacketSize + 1,
  459. }, &SocksRelay{Addr: "127.0.0.1:1"})
  460. if err == nil {
  461. t.Fatal("expected oversized max UDP relay packet size to be rejected")
  462. }
  463. }
  464. func TestNewServerClampsLegacyUdpPayloadLimit(t *testing.T) {
  465. certPEM, keyPEM := generateTestCert(t)
  466. server, err := NewServer(Instance{
  467. Listen: "127.0.0.1",
  468. Port: 0,
  469. Certificate: string(certPEM),
  470. PrivateKey: string(keyPEM),
  471. MaxUdpRelayPacketSize: maxLegacyUdpRelayPacketSize,
  472. }, &SocksRelay{})
  473. if err != nil {
  474. t.Fatalf("NewServer: %v", err)
  475. }
  476. if server.maxUdpRelayPacketSize != maxSafeUdpRelayPacketSize {
  477. t.Fatalf("legacy UDP limit = %d, want clamped limit %d", server.maxUdpRelayPacketSize, maxSafeUdpRelayPacketSize)
  478. }
  479. }
  480. func TestPacketReassemblerInvalidatesAssemblyWhenFragmentTotalChanges(t *testing.T) {
  481. reassembler := newPacketReassembler(64)
  482. 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}
  483. if _, _, complete := reassembler.feed(packetTransportDatagram, first, []byte("A")); complete {
  484. t.Fatal("first fragment unexpectedly completed")
  485. }
  486. single := &PacketHeader{AssocID: 7, PktID: 9, FragTotal: 1, FragID: 0, Addr: first.Addr, Size: 1}
  487. if _, got, complete := reassembler.feed(packetTransportDatagram, single, []byte("Z")); !complete || string(got) != "Z" {
  488. t.Fatalf("single packet = %q, complete=%v; want Z", got, complete)
  489. }
  490. last := &PacketHeader{AssocID: 7, PktID: 9, FragTotal: 2, FragID: 1, Size: 1}
  491. if _, _, complete := reassembler.feed(packetTransportDatagram, last, []byte("B")); complete {
  492. t.Fatal("stale first fragment was combined with a later packet")
  493. }
  494. }
  495. func TestUdpAssociationPinsFirstPacketModeAndDissociateClearsFragments(t *testing.T) {
  496. registry := newUdpAssociationRegistry(64)
  497. addr := &Address{Type: AddrTypeIPv4, IP: net.ParseIP("127.0.0.1"), Port: 53}
  498. first := &PacketHeader{AssocID: 3, PktID: 1, FragTotal: 2, FragID: 0, Addr: addr, Size: 1}
  499. association, _, _, complete := registry.feed(packetTransportDatagram, first, []byte("A"))
  500. if association == nil || complete {
  501. t.Fatal("expected first native fragment to establish an incomplete association")
  502. }
  503. singleStream := &PacketHeader{AssocID: 3, PktID: 2, FragTotal: 1, FragID: 0, Addr: addr, Size: 1}
  504. association, _, _, complete = registry.feed(packetTransportStream, singleStream, []byte("S"))
  505. if association.responseTransport != packetTransportDatagram || !complete {
  506. t.Fatalf("mixed-mode packet changed response mode: association=%+v complete=%v", association, complete)
  507. }
  508. if !registry.dissociate(3) {
  509. t.Fatal("expected dissociate to remove association")
  510. }
  511. late := &PacketHeader{AssocID: 3, PktID: 1, FragTotal: 2, FragID: 1, Size: 1}
  512. _, _, _, complete = registry.feed(packetTransportDatagram, late, []byte("B"))
  513. if complete {
  514. t.Fatal("late fragment completed an assembly from before dissociate")
  515. }
  516. }
  517. func TestPacketReassembler(t *testing.T) {
  518. pr := newPacketReassembler(1500)
  519. targetAddr := &Address{Type: AddrTypeIPv4, IP: net.ParseIP("1.1.1.1"), Port: 53}
  520. // 1. Unfragmented packet
  521. hdrSingle := &PacketHeader{
  522. AssocID: 1,
  523. PktID: 1,
  524. FragTotal: 1,
  525. FragID: 0,
  526. Size: uint16(len("hello single")),
  527. Addr: targetAddr,
  528. }
  529. addr, payload, complete := pr.feed(packetTransportDatagram, hdrSingle, []byte("hello single"))
  530. if addr == nil || !complete || string(payload) != "hello single" {
  531. t.Fatalf("unexpected single packet result: %v, %s", addr, payload)
  532. }
  533. // 2. In-order fragments (3 parts)
  534. hdr0 := &PacketHeader{AssocID: 2, PktID: 10, FragTotal: 3, FragID: 0, Size: 6, Addr: targetAddr}
  535. hdr1 := &PacketHeader{AssocID: 2, PktID: 10, FragTotal: 3, FragID: 1, Size: 6, Addr: &Address{Type: AddrTypeNone}}
  536. hdr2 := &PacketHeader{AssocID: 2, PktID: 10, FragTotal: 3, FragID: 2, Size: 5, Addr: &Address{Type: AddrTypeNone}}
  537. _, p0, complete := pr.feed(packetTransportDatagram, hdr0, []byte("part0-"))
  538. if p0 != nil || complete {
  539. t.Fatalf("expected nil before all fragments arrive, got %s", p0)
  540. }
  541. _, p1, complete := pr.feed(packetTransportDatagram, hdr1, []byte("part1-"))
  542. if p1 != nil || complete {
  543. t.Fatalf("expected nil before all fragments arrive, got %s", p1)
  544. }
  545. a2, p2, complete := pr.feed(packetTransportDatagram, hdr2, []byte("part2"))
  546. if a2 == nil || !complete || string(p2) != "part0-part1-part2" {
  547. t.Fatalf("expected reassembled payload 'part0-part1-part2', got %v, %s", a2, p2)
  548. }
  549. // 3. Out-of-order fragments (parts 1, 2, 0)
  550. hdrOO0 := &PacketHeader{AssocID: 3, PktID: 20, FragTotal: 3, FragID: 0, Size: 6, Addr: targetAddr}
  551. hdrOO1 := &PacketHeader{AssocID: 3, PktID: 20, FragTotal: 3, FragID: 1, Size: 7, Addr: &Address{Type: AddrTypeNone}}
  552. hdrOO2 := &PacketHeader{AssocID: 3, PktID: 20, FragTotal: 3, FragID: 2, Size: 3, Addr: &Address{Type: AddrTypeNone}}
  553. if _, p, done := pr.feed(packetTransportDatagram, hdrOO1, []byte("MIDDLE-")); p != nil || done {
  554. t.Fatalf("expected nil, got %s", p)
  555. }
  556. if _, p, done := pr.feed(packetTransportDatagram, hdrOO2, []byte("END")); p != nil || done {
  557. t.Fatalf("expected nil, got %s", p)
  558. }
  559. aOO, pOO, done := pr.feed(packetTransportDatagram, hdrOO0, []byte("START-"))
  560. if aOO == nil || !done || string(pOO) != "START-MIDDLE-END" {
  561. t.Fatalf("expected 'START-MIDDLE-END', got %s", pOO)
  562. }
  563. // 4. Invalid FragID >= FragTotal
  564. hdrInv := &PacketHeader{AssocID: 4, PktID: 30, FragTotal: 2, FragID: 2, Size: 7, Addr: targetAddr}
  565. if _, p, done := pr.feed(packetTransportDatagram, hdrInv, []byte("invalid")); p != nil || done {
  566. t.Fatalf("expected nil for invalid FragID, got %s", p)
  567. }
  568. // A changed fragment total for an in-flight packet must discard the packet safely.
  569. hdrMixed0 := &PacketHeader{AssocID: 5, PktID: 40, FragTotal: 2, FragID: 0, Size: 1, Addr: targetAddr}
  570. hdrMixed3 := &PacketHeader{AssocID: 5, PktID: 40, FragTotal: 4, FragID: 3, Size: 1, Addr: &Address{Type: AddrTypeNone}}
  571. if _, _, done := pr.feed(packetTransportDatagram, hdrMixed0, []byte("a")); done {
  572. t.Fatal("expected first mixed-total fragment to remain incomplete")
  573. }
  574. if _, _, done := pr.feed(packetTransportDatagram, hdrMixed3, []byte("b")); done {
  575. t.Fatal("expected inconsistent fragment total to be discarded")
  576. }
  577. if _, ok := pr.packets[packetFragmentKey{assocID: 5, pktID: 40, transport: packetTransportDatagram}]; ok {
  578. t.Fatal("inconsistent packet assembly was not discarded")
  579. }
  580. // Fragments from different transports cannot be combined into one packet.
  581. streamFirst := &PacketHeader{AssocID: 6, PktID: 50, FragTotal: 2, FragID: 0, Size: 1, Addr: targetAddr}
  582. datagramLast := &PacketHeader{AssocID: 6, PktID: 50, FragTotal: 2, FragID: 1, Size: 1, Addr: &Address{Type: AddrTypeNone}}
  583. if _, _, done := pr.feed(packetTransportStream, streamFirst, []byte("a")); done {
  584. t.Fatal("expected first stream fragment to remain incomplete")
  585. }
  586. if _, _, done := pr.feed(packetTransportDatagram, datagramLast, []byte("b")); done {
  587. t.Fatal("fragments from different transports must not combine")
  588. }
  589. if _, _, done := pr.feed(packetTransportStream, datagramLast, []byte("b")); !done {
  590. t.Fatal("expected stream fragments to reassemble")
  591. }
  592. // The configured size limit caps both complete packets and reassembly state.
  593. limited := newPacketReassembler(3)
  594. tooLarge0 := &PacketHeader{AssocID: 7, PktID: 60, FragTotal: 2, FragID: 0, Size: 2, Addr: targetAddr}
  595. tooLarge1 := &PacketHeader{AssocID: 7, PktID: 60, FragTotal: 2, FragID: 1, Size: 2, Addr: &Address{Type: AddrTypeNone}}
  596. if _, _, done := limited.feed(packetTransportDatagram, tooLarge0, []byte("ab")); done {
  597. t.Fatal("expected first oversized packet fragment to remain incomplete")
  598. }
  599. if _, _, done := limited.feed(packetTransportDatagram, tooLarge1, []byte("cd")); done {
  600. t.Fatal("oversized reassembled packet must be rejected")
  601. }
  602. if len(limited.packets) != 0 {
  603. t.Fatal("oversized reassembly state was not discarded")
  604. }
  605. bounded := newPacketReassembler(1500)
  606. for i := 0; i < maxPendingPacketAssemblies; i++ {
  607. hdr := &PacketHeader{
  608. AssocID: 8,
  609. PktID: uint16(i),
  610. FragTotal: 2,
  611. FragID: 0,
  612. Size: 1,
  613. Addr: targetAddr,
  614. }
  615. if _, _, done := bounded.feed(packetTransportDatagram, hdr, []byte("a")); done {
  616. t.Fatal("expected pending fragment to remain incomplete")
  617. }
  618. }
  619. if len(bounded.packets) != maxPendingPacketAssemblies {
  620. t.Fatalf("pending assembly count = %d, want %d", len(bounded.packets), maxPendingPacketAssemblies)
  621. }
  622. extra := &PacketHeader{AssocID: 8, PktID: 100, FragTotal: 2, FragID: 0, Size: 1, Addr: targetAddr}
  623. if _, _, done := bounded.feed(packetTransportDatagram, extra, []byte("a")); done {
  624. t.Fatal("expected new assembly to be rejected when the pending limit is reached")
  625. }
  626. if len(bounded.packets) != maxPendingPacketAssemblies {
  627. t.Fatalf("pending assembly count after overflow = %d, want %d", len(bounded.packets), maxPendingPacketAssemblies)
  628. }
  629. for _, packet := range bounded.packets {
  630. packet.updatedAt = time.Now().Add(-packetAssemblyTimeout - time.Second)
  631. }
  632. if _, _, done := bounded.feed(packetTransportDatagram, extra, []byte("a")); done {
  633. t.Fatal("expected new fragment to remain incomplete after stale entries are evicted")
  634. }
  635. if len(bounded.packets) != 1 {
  636. t.Fatalf("pending assembly count after stale cleanup = %d, want 1", len(bounded.packets))
  637. }
  638. }
  639. func TestAuthenticationTimeoutClosesUnauthenticatedConnections(t *testing.T) {
  640. for _, partial := range []bool{false, true} {
  641. name := "no-authenticate"
  642. if partial {
  643. name = "partial-authenticate"
  644. }
  645. t.Run(name, func(t *testing.T) {
  646. certPEM, keyPEM := generateTestCert(t)
  647. server, err := NewServer(Instance{
  648. Id: 99010,
  649. Tag: "auth-timeout-test",
  650. Listen: "127.0.0.1",
  651. Port: 0,
  652. Certificate: string(certPEM),
  653. PrivateKey: string(keyPEM),
  654. ALPN: []string{"h3"},
  655. MaxIdleTime: 5,
  656. AuthenticationTimeout: 1,
  657. }, &SocksRelay{})
  658. if err != nil {
  659. t.Fatalf("NewServer: %v", err)
  660. }
  661. if err := server.Start(); err != nil {
  662. t.Fatalf("Server.Start: %v", err)
  663. }
  664. t.Cleanup(func() { _ = server.Close() })
  665. ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second)
  666. defer cancel()
  667. conn, err := quic.DialAddr(ctx, server.packetConn.LocalAddr().String(), &tls.Config{
  668. InsecureSkipVerify: true,
  669. NextProtos: []string{"h3"},
  670. }, &quic.Config{EnableDatagrams: true, KeepAlivePeriod: time.Second})
  671. if err != nil {
  672. t.Fatalf("quic.DialAddr: %v", err)
  673. }
  674. defer conn.CloseWithError(0, "")
  675. if partial {
  676. stream, err := conn.OpenUniStreamSync(ctx)
  677. if err != nil {
  678. t.Fatalf("OpenUniStreamSync: %v", err)
  679. }
  680. if _, err := stream.Write([]byte{ProtocolVersion, CmdAuthenticate, 1}); err != nil {
  681. t.Fatalf("write partial Authenticate: %v", err)
  682. }
  683. }
  684. select {
  685. case <-conn.Context().Done():
  686. case <-ctx.Done():
  687. t.Fatalf("server left unauthenticated QUIC connection open: %v", ctx.Err())
  688. }
  689. })
  690. }
  691. }