Просмотр исходного кода

Merge pull request #627 from rod-hynes/master

QUIC/UDP updates
Rod Hynes 3 лет назад
Родитель
Сommit
138d40efd9
100 измененных файлов с 2101 добавлено и 1237 удалено
  1. 4 4
      go.mod
  2. 9 0
      go.sum
  3. 63 14
      psiphon/UDPConn.go
  4. 0 65
      psiphon/UDPConn_bind.go
  5. 0 44
      psiphon/UDPConn_nobind.go
  6. 83 42
      psiphon/common/quic/obfuscator.go
  7. 75 51
      psiphon/common/quic/quic.go
  8. 2 1
      vendor/github.com/Psiphon-Labs/quic-go/README.md
  9. 4 8
      vendor/github.com/Psiphon-Labs/quic-go/client.go
  10. 6 6
      vendor/github.com/Psiphon-Labs/quic-go/conn_id_generator.go
  11. 1 1
      vendor/github.com/Psiphon-Labs/quic-go/conn_id_manager.go
  12. 249 117
      vendor/github.com/Psiphon-Labs/quic-go/connection.go
  13. 16 4
      vendor/github.com/Psiphon-Labs/quic-go/datagram_queue.go
  14. 3 0
      vendor/github.com/Psiphon-Labs/quic-go/http3/body.go
  15. 55 0
      vendor/github.com/Psiphon-Labs/quic-go/http3/capsule.go
  16. 5 5
      vendor/github.com/Psiphon-Labs/quic-go/http3/client.go
  17. 14 13
      vendor/github.com/Psiphon-Labs/quic-go/http3/frames.go
  18. 10 5
      vendor/github.com/Psiphon-Labs/quic-go/http3/http_stream.go
  19. 3 4
      vendor/github.com/Psiphon-Labs/quic-go/http3/request_writer.go
  20. 8 6
      vendor/github.com/Psiphon-Labs/quic-go/http3/response_writer.go
  21. 40 19
      vendor/github.com/Psiphon-Labs/quic-go/http3/server.go
  22. 20 6
      vendor/github.com/Psiphon-Labs/quic-go/interface.go
  23. 0 20
      vendor/github.com/Psiphon-Labs/quic-go/internal/ackhandler/interfaces.go
  24. 49 0
      vendor/github.com/Psiphon-Labs/quic-go/internal/ackhandler/packet.go
  25. 7 5
      vendor/github.com/Psiphon-Labs/quic-go/internal/ackhandler/received_packet_handler.go
  26. 6 11
      vendor/github.com/Psiphon-Labs/quic-go/internal/ackhandler/received_packet_history.go
  27. 15 13
      vendor/github.com/Psiphon-Labs/quic-go/internal/ackhandler/received_packet_tracker.go
  28. 29 9
      vendor/github.com/Psiphon-Labs/quic-go/internal/ackhandler/sent_packet_handler.go
  29. 38 32
      vendor/github.com/Psiphon-Labs/quic-go/internal/ackhandler/sent_packet_history.go
  30. 1 1
      vendor/github.com/Psiphon-Labs/quic-go/internal/flowcontrol/base_flow_controller.go
  31. 5 6
      vendor/github.com/Psiphon-Labs/quic-go/internal/handshake/crypto_setup.go
  32. 1 1
      vendor/github.com/Psiphon-Labs/quic-go/internal/handshake/initial_aead.go
  33. 4 5
      vendor/github.com/Psiphon-Labs/quic-go/internal/handshake/session_ticket.go
  34. 4 4
      vendor/github.com/Psiphon-Labs/quic-go/internal/handshake/token_generator.go
  35. 17 0
      vendor/github.com/Psiphon-Labs/quic-go/internal/logutils/frame.go
  36. 60 25
      vendor/github.com/Psiphon-Labs/quic-go/internal/protocol/connection_id.go
  37. 1 1
      vendor/github.com/Psiphon-Labs/quic-go/internal/protocol/params.go
  38. 1 1
      vendor/github.com/Psiphon-Labs/quic-go/internal/qerr/error_codes.go
  39. 12 5
      vendor/github.com/Psiphon-Labs/quic-go/internal/qerr/errors.go
  40. 1 1
      vendor/github.com/Psiphon-Labs/quic-go/internal/qtls/go119.go
  41. 106 1
      vendor/github.com/Psiphon-Labs/quic-go/internal/qtls/go120.go
  42. 4 0
      vendor/github.com/Psiphon-Labs/quic-go/internal/utils/byteorder.go
  43. 14 0
      vendor/github.com/Psiphon-Labs/quic-go/internal/utils/byteorder_big_endian.go
  44. 16 16
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/ack_frame.go
  45. 24 0
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/ack_frame_pool.go
  46. 8 8
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/connection_close_frame.go
  47. 6 6
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/crypto_frame.go
  48. 4 5
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/data_blocked_frame.go
  49. 6 6
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/datagram_frame.go
  50. 14 2
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/frame_parser.go
  51. 2 3
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/handshake_done_frame.go
  52. 56 6
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/header.go
  53. 2 4
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/interface.go
  54. 5 5
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/max_data_frame.go
  55. 5 5
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/max_stream_data_frame.go
  56. 5 5
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/max_streams_frame.go
  57. 9 12
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/new_connection_id_frame.go
  58. 5 5
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/new_token_frame.go
  59. 4 4
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/path_challenge_frame.go
  60. 4 4
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/path_response_frame.go
  61. 3 4
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/ping_frame.go
  62. 6 6
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/reset_stream_frame.go
  63. 4 4
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/retire_connection_id_frame.go
  64. 55 0
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/short_header.go
  65. 5 5
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/stop_sending_frame.go
  66. 5 5
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/stream_data_blocked_frame.go
  67. 14 14
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/stream_frame.go
  68. 5 5
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/streams_blocked_frame.go
  69. 87 79
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/transport_parameters.go
  70. 17 18
      vendor/github.com/Psiphon-Labs/quic-go/internal/wire/version_negotiation.go
  71. 16 4
      vendor/github.com/Psiphon-Labs/quic-go/logging/interface.go
  72. 2 2
      vendor/github.com/Psiphon-Labs/quic-go/logging/mockgen.go
  73. 18 6
      vendor/github.com/Psiphon-Labs/quic-go/logging/multiplex.go
  74. 20 11
      vendor/github.com/Psiphon-Labs/quic-go/logging/null_tracer.go
  75. 2 2
      vendor/github.com/Psiphon-Labs/quic-go/mockgen.go
  76. 1 1
      vendor/github.com/Psiphon-Labs/quic-go/mockgen_private.sh
  77. 5 6
      vendor/github.com/Psiphon-Labs/quic-go/multiplexer.go
  78. 20 18
      vendor/github.com/Psiphon-Labs/quic-go/packet_handler_map.go
  79. 112 179
      vendor/github.com/Psiphon-Labs/quic-go/packet_packer.go
  80. 69 37
      vendor/github.com/Psiphon-Labs/quic-go/packet_unpacker.go
  81. 19 0
      vendor/github.com/Psiphon-Labs/quic-go/quicvarint/varint.go
  82. 7 0
      vendor/github.com/Psiphon-Labs/quic-go/send_queue.go
  83. 1 1
      vendor/github.com/Psiphon-Labs/quic-go/send_stream.go
  84. 51 43
      vendor/github.com/Psiphon-Labs/quic-go/server.go
  85. 29 21
      vendor/github.com/Psiphon-Labs/quic-go/sys_conn_oob.go
  86. 2 1
      vendor/github.com/Psiphon-Labs/quic-go/tools.go
  87. 1 2
      vendor/github.com/marten-seemann/qpack/README.md
  88. 2 2
      vendor/github.com/marten-seemann/qpack/encoder.go
  89. 1 0
      vendor/github.com/marten-seemann/qpack/static_table.go
  90. 14 0
      vendor/golang.org/x/sys/unix/sockcmsg_unix.go
  91. 1 0
      vendor/golang.org/x/sys/unix/syscall_linux.go
  92. 0 16
      vendor/gopkg.in/yaml.v3/.travis.yml
  93. 1 0
      vendor/gopkg.in/yaml.v3/apic.go
  94. 106 37
      vendor/gopkg.in/yaml.v3/decode.go
  95. 43 15
      vendor/gopkg.in/yaml.v3/emitterc.go
  96. 23 7
      vendor/gopkg.in/yaml.v3/encode.go
  97. 43 14
      vendor/gopkg.in/yaml.v3/parserc.go
  98. 31 18
      vendor/gopkg.in/yaml.v3/scannerc.go
  99. 38 2
      vendor/gopkg.in/yaml.v3/yaml.go
  100. 2 0
      vendor/gopkg.in/yaml.v3/yamlh.go

+ 4 - 4
go.mod

@@ -18,7 +18,7 @@ require (
 	github.com/Psiphon-Inc/rotate-safe-writer v0.0.0-20210303140923-464a7a37606e
 	github.com/Psiphon-Labs/bolt v0.0.0-20200624191537-23cedaef7ad7
 	github.com/Psiphon-Labs/goptlib v0.0.0-20200406165125-c0e32a7a3464
-	github.com/Psiphon-Labs/quic-go v0.0.0-20221014165902-1b7c3975fcf3
+	github.com/Psiphon-Labs/quic-go v0.0.0-20230124165616-fe8e9a215a66
 	github.com/Psiphon-Labs/tls-tris v0.0.0-20210713133851-676a693d51ad
 	github.com/armon/go-proxyproto v0.0.0-20180202201750-5b7edb60ff5f
 	github.com/bifurcation/mint v0.0.0-20180306135233-198357931e61
@@ -51,7 +51,7 @@ require (
 	golang.org/x/crypto v0.0.0-20221012134737-56aed061732a
 	golang.org/x/net v0.0.0-20221014081412-f15817d10f9b
 	golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
-	golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43
+	golang.org/x/sys v0.2.0
 	golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
 )
 
@@ -73,7 +73,7 @@ require (
 	github.com/josharian/native v1.0.0 // indirect
 	github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 // indirect
 	github.com/klauspost/compress v1.15.10-0.20220729101446-5a3a4a965cc6 // indirect
-	github.com/marten-seemann/qpack v0.2.1 // indirect
+	github.com/marten-seemann/qpack v0.3.0 // indirect
 	github.com/mdlayher/netlink v1.4.2-0.20210930205308-a81a8c23d40a // indirect
 	github.com/mdlayher/socket v0.0.0-20210624160740-9dbe287ded84 // indirect
 	github.com/mroth/weightedrand v0.4.1 // indirect
@@ -87,6 +87,6 @@ require (
 	golang.org/x/text v0.3.7 // indirect
 	golang.org/x/tools v0.1.12 // indirect
 	google.golang.org/protobuf v1.28.0 // indirect
-	gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
 	honnef.co/go/tools v0.2.1 // indirect
 )

+ 9 - 0
go.sum

@@ -22,6 +22,8 @@ github.com/Psiphon-Labs/quic-go v0.0.0-20221012142746-f3b75b440582 h1:T8I7L8tHDI
 github.com/Psiphon-Labs/quic-go v0.0.0-20221012142746-f3b75b440582/go.mod h1:Dcp6EsbioehAvUSMTMbqFV6Z+q+IApml2Q3r8eXQS5M=
 github.com/Psiphon-Labs/quic-go v0.0.0-20221014165902-1b7c3975fcf3 h1:BKSZdSkhOGV23vBl4cYPabzkRlWk5Zm7Snpo4i0lSXQ=
 github.com/Psiphon-Labs/quic-go v0.0.0-20221014165902-1b7c3975fcf3/go.mod h1:llhtSl7dUXTssUN4m4MjUDJrULGNxgZBMKYjExuk6EM=
+github.com/Psiphon-Labs/quic-go v0.0.0-20230124165616-fe8e9a215a66 h1:xTGnmXqEEUphnohYLGwGrlVzWPNpTNAFViJYxQUMHRU=
+github.com/Psiphon-Labs/quic-go v0.0.0-20230124165616-fe8e9a215a66/go.mod h1:cu4yhfHkyt+uQ9FFFjTpjCjcQYf52ntEAyoV4Zg0+fg=
 github.com/Psiphon-Labs/tls-tris v0.0.0-20210713133851-676a693d51ad h1:m6HS84+b5xDPLj7D/ya1CeixyaHOCZoMbBilJ48y+Ts=
 github.com/Psiphon-Labs/tls-tris v0.0.0-20210713133851-676a693d51ad/go.mod h1:v3y9GXFo9Sf2mO6auD2ExGG7oDgrK8TI7eb49ZnUxrE=
 github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI=
@@ -131,6 +133,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
 github.com/marten-seemann/qpack v0.2.1 h1:jvTsT/HpCn2UZJdP+UUB53FfUUgeOyG5K1ns0OJOGVs=
 github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
+github.com/marten-seemann/qpack v0.3.0 h1:UiWstOgT8+znlkDPOg2+3rIuYXJ2CnGDkGUXN6ki6hE=
+github.com/marten-seemann/qpack v0.3.0/go.mod h1:cGfKPBiP4a9EQdxCwEwI/GEeWAsjSekBvx/X8mh58+g=
 github.com/marusama/semaphore v0.0.0-20171214154724-565ffd8e868a h1:6SRny9FLB1eWasPyDUqBQnMi9NhXU01XIlB0ao89YoI=
 github.com/marusama/semaphore v0.0.0-20171214154724-565ffd8e868a/go.mod h1:TmeOqAKoDinfPfSohs14CO3VcEf7o+Bem6JiNe05yrQ=
 github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43 h1:WgyLFv10Ov49JAQI/ZLUkCZ7VJS3r74hwFIGXJsgZlY=
@@ -170,6 +174,7 @@ github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9k
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
 github.com/oschwald/maxminddb-golang v1.2.1-0.20170901134056-26fe5ace1c70 h1:2skBNJsk5emoC/p8tOS9DQL5Ia7ND1UNEdmgfvP+mPI=
 github.com/oschwald/maxminddb-golang v1.2.1-0.20170901134056-26fe5ace1c70/go.mod h1:3jhIUymTJ5VREKyIhWm66LJiQt04F0UCDdodShpjWsY=
 github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
@@ -296,6 +301,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9w
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 h1:OK7RB6t2WQX54srQQYSXMW8dF5C6/8+oA/s5QBmmto4=
 golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
+golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -338,5 +345,7 @@ gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 honnef.co/go/tools v0.2.1 h1:/EPr//+UMMXwMTkXvCCoaJDq8cpjMO80Ou+L4PDo2mY=
 honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=

+ 63 - 14
psiphon/UDPConn.go

@@ -29,15 +29,16 @@ import (
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
 )
 
-// NewUDPConn resolves addr and configures a new UDP conn. The UDP socket is
-// created using options in DialConfig, including DeviceBinder. The returned
-// UDPAddr uses DialConfig options IPv6Synthesizer and ResolvedIPCallback.
+// NewUDPConn resolves addr and configures a new *net.UDPConn. The UDP socket
+// is created using options in DialConfig, including DeviceBinder. The
+// returned UDPAddr uses DialConfig options IPv6Synthesizer and
+// ResolvedIPCallback.
 //
 // The UDP conn is not dialed; it is intended for use with WriteTo using the
 // returned UDPAddr, not Write.
 //
-// The returned conn is not a Closer; the caller is expected to wrap this conn
-// with another higher-level conn that provides that interface.
+// The returned conn is not a common.Closer; the caller is expected to wrap
+// this conn with another higher-level conn that provides that interface.
 func NewUDPConn(
 	ctx context.Context, addr string, config *DialConfig) (net.PacketConn, *net.UDPAddr, error) {
 
@@ -81,23 +82,71 @@ func NewUDPConn(
 		}
 	}
 
-	var domain int
-	if ipAddr != nil && ipAddr.To4() != nil {
-		domain = syscall.AF_INET
-	} else if ipAddr != nil && ipAddr.To16() != nil {
-		domain = syscall.AF_INET6
-	} else {
-		return nil, nil, errors.Tracef("invalid IP address: %s", ipAddr.String())
+	listen := &net.ListenConfig{
+		Control: func(_, _ string, c syscall.RawConn) error {
+			var controlErr error
+			err := c.Control(func(fd uintptr) {
+
+				socketFD := int(fd)
+
+				setAdditionalSocketOptions(socketFD)
+
+				if config.BPFProgramInstructions != nil {
+					err := setSocketBPF(config.BPFProgramInstructions, socketFD)
+					if err != nil {
+						controlErr = errors.Tracef("setSocketBPF failed: %s", err)
+						return
+					}
+				}
+
+				if config.DeviceBinder != nil {
+					_, err := config.DeviceBinder.BindToDevice(socketFD)
+					if err != nil {
+						controlErr = errors.Tracef("BindToDevice failed: %s", err)
+						return
+					}
+				}
+			})
+			if controlErr != nil {
+				return errors.Trace(controlErr)
+			}
+			return errors.Trace(err)
+		},
+	}
+
+	network := "udp4"
+	if ipAddr.To4() == nil {
+		network = "udp6"
 	}
 
-	conn, err := newUDPConn(domain, config)
+	// It's necessary to create an "unconnected" UDP socket, for use with
+	// WriteTo, as required by quic-go. As documented in net.ListenUDP: with
+	// an unspecified IP address, the resulting conn "listens on all
+	// available IP addresses of the local system except multicast IP
+	// addresses".
+	//
+	// Limitation: these UDP sockets are not necessarily closed when a device
+	// changes active network (e.g., WiFi to mobile). It's possible that a
+	// QUIC connection does not immediately close on a network change, and
+	// instead outbound packets are sent from a different active interface.
+	// As quic-go does not yet support connection migration, these packets
+	// will be dropped by the server. This situation is mitigated by use of
+	// DeviceBinder; by network change event detection, which initiates new
+	// tunnel connections; and by timeouts/keep-alives.
+
+	conn, err := listen.ListenPacket(ctx, network, "")
 	if err != nil {
 		return nil, nil, errors.Trace(err)
 	}
 
+	udpConn, ok := conn.(*net.UDPConn)
+	if !ok {
+		return nil, nil, errors.Tracef("unexpected conn type: %T", conn)
+	}
+
 	if config.ResolvedIPCallback != nil {
 		config.ResolvedIPCallback(ipAddr.String())
 	}
 
-	return conn, &net.UDPAddr{IP: ipAddr, Port: port}, nil
+	return udpConn, &net.UDPAddr{IP: ipAddr, Port: port}, nil
 }

+ 0 - 65
psiphon/UDPConn_bind.go

@@ -1,65 +0,0 @@
-// +build !windows
-
-/*
- * Copyright (c) 2018, Psiphon Inc.
- * All rights reserved.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package psiphon
-
-import (
-	"net"
-	"os"
-	"syscall"
-
-	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
-)
-
-func newUDPConn(domain int, config *DialConfig) (net.PacketConn, error) {
-
-	// TODO: use https://golang.org/pkg/net/#Dialer.Control, introduced in Go 1.11?
-
-	socketFD, err := syscall.Socket(domain, syscall.SOCK_DGRAM, 0)
-	if err != nil {
-		return nil, errors.Trace(err)
-	}
-
-	syscall.CloseOnExec(socketFD)
-
-	setAdditionalSocketOptions(socketFD)
-
-	if config.DeviceBinder != nil {
-		_, err = config.DeviceBinder.BindToDevice(socketFD)
-		if err != nil {
-			syscall.Close(socketFD)
-			return nil, errors.Tracef("BindToDevice failed: %s", err)
-		}
-	}
-
-	// Convert the socket fd to a net.PacketConn
-	// This code block is from:
-	// https://github.com/golang/go/issues/6966
-
-	file := os.NewFile(uintptr(socketFD), "")
-	conn, err := net.FilePacketConn(file) // net.FilePackateConn() dups socketFD
-	file.Close()                          // file.Close() closes socketFD
-	if err != nil {
-		return nil, errors.Trace(err)
-	}
-
-	return conn, nil
-}

+ 0 - 44
psiphon/UDPConn_nobind.go

@@ -1,44 +0,0 @@
-// +build windows
-
-/*
- * Copyright (c) 2018, Psiphon Inc.
- * All rights reserved.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package psiphon
-
-import (
-	"net"
-	"syscall"
-
-	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
-)
-
-func newUDPConn(domain int, config *DialConfig) (net.PacketConn, error) {
-
-	if config.DeviceBinder != nil {
-		return nil, errors.TraceNew("newUDPConn with DeviceBinder not supported on this platform")
-	}
-
-	network := "udp4"
-
-	if domain == syscall.AF_INET6 {
-		network = "udp6"
-	}
-
-	return net.ListenUDP(network, nil)
-}

+ 83 - 42
psiphon/common/quic/obfuscator.go

@@ -102,7 +102,7 @@ const (
 // payload will increase UDP packets beyond the QUIC max of 1280 bytes,
 // introducing some risk of fragmentation and/or dropped packets.
 type ObfuscatedPacketConn struct {
-	ietf_quic.OOBCapablePacketConn
+	net.PacketConn
 	isServer         bool
 	isIETFClient     bool
 	isDecoyClient    bool
@@ -130,79 +130,74 @@ func (p *peerMode) isStale() bool {
 
 // NewObfuscatedPacketConn creates a new ObfuscatedPacketConn.
 func NewObfuscatedPacketConn(
-	conn net.PacketConn,
+	packetConn net.PacketConn,
 	isServer bool,
 	isIETFClient bool,
 	isDecoyClient bool,
 	obfuscationKey string,
 	paddingSeed *prng.Seed) (*ObfuscatedPacketConn, error) {
 
-	oobPacketConn, ok := conn.(ietf_quic.OOBCapablePacketConn)
-	if !ok {
-		return nil, errors.TraceNew("conn must support OOBCapablePacketConn")
-	}
-
 	// There is no replay of obfuscation "encryption", just padding.
 	nonceSeed, err := prng.NewSeed()
 	if err != nil {
 		return nil, errors.Trace(err)
 	}
 
-	packetConn := &ObfuscatedPacketConn{
-		OOBCapablePacketConn: oobPacketConn,
-		isServer:             isServer,
-		isIETFClient:         isIETFClient,
-		isDecoyClient:        isDecoyClient,
-		peerModes:            make(map[string]*peerMode),
-		noncePRNG:            prng.NewPRNGWithSeed(nonceSeed),
-		paddingPRNG:          prng.NewPRNGWithSeed(paddingSeed),
+	conn := &ObfuscatedPacketConn{
+		PacketConn:    packetConn,
+		isServer:      isServer,
+		isIETFClient:  isIETFClient,
+		isDecoyClient: isDecoyClient,
+		peerModes:     make(map[string]*peerMode),
+		noncePRNG:     prng.NewPRNGWithSeed(nonceSeed),
+		paddingPRNG:   prng.NewPRNGWithSeed(paddingSeed),
 	}
 
 	secret := []byte(obfuscationKey)
 	salt := []byte("quic-obfuscation-key")
 	_, err = io.ReadFull(
-		hkdf.New(sha256.New, secret, salt, nil), packetConn.obfuscationKey[:])
+		hkdf.New(sha256.New, secret, salt, nil), conn.obfuscationKey[:])
 	if err != nil {
 		return nil, errors.Trace(err)
 	}
 
 	if isDecoyClient {
-		packetConn.decoyPacketCount = int32(packetConn.paddingPRNG.Range(
+		conn.decoyPacketCount = int32(conn.paddingPRNG.Range(
 			MIN_DECOY_PACKETS, MAX_DECOY_PACKETS))
-		packetConn.decoyBuffer = make([]byte, MAX_PACKET_SIZE)
+		conn.decoyBuffer = make([]byte, MAX_PACKET_SIZE)
 	}
 
 	if isServer {
 
-		packetConn.runWaitGroup = new(sync.WaitGroup)
-		packetConn.stopBroadcast = make(chan struct{})
+		conn.runWaitGroup = new(sync.WaitGroup)
+		conn.stopBroadcast = make(chan struct{})
 
 		// Reap stale peer mode information to reclaim memory.
 
-		packetConn.runWaitGroup.Add(1)
+		conn.runWaitGroup.Add(1)
 		go func() {
-			defer packetConn.runWaitGroup.Done()
+			defer conn.runWaitGroup.Done()
 
 			ticker := time.NewTicker(SERVER_IDLE_TIMEOUT / 2)
 			defer ticker.Stop()
 			for {
 				select {
 				case <-ticker.C:
-					packetConn.peerModesMutex.Lock()
-					for address, mode := range packetConn.peerModes {
+					conn.peerModesMutex.Lock()
+					for address, mode := range conn.peerModes {
 						if mode.isStale() {
-							delete(packetConn.peerModes, address)
+							delete(conn.peerModes, address)
 						}
 					}
-					packetConn.peerModesMutex.Unlock()
-				case <-packetConn.stopBroadcast:
+					conn.peerModesMutex.Unlock()
+				case <-conn.stopBroadcast:
 					return
 				}
 			}
 		}()
 	}
 
-	return packetConn, nil
+	return conn, nil
 }
 
 func (conn *ObfuscatedPacketConn) Close() error {
@@ -217,7 +212,7 @@ func (conn *ObfuscatedPacketConn) Close() error {
 		conn.runWaitGroup.Wait()
 	}
 
-	return conn.OOBCapablePacketConn.Close()
+	return conn.PacketConn.Close()
 }
 
 type temporaryNetError struct {
@@ -242,7 +237,7 @@ func (e *temporaryNetError) Error() string {
 
 func (conn *ObfuscatedPacketConn) ReadFrom(p []byte) (int, net.Addr, error) {
 	n, _, _, addr, _, err := conn.readPacketWithType(p, nil)
-	// Do not wrap any I/O err returned by conn.OOBCapablePacketConn
+	// Do not wrap any I/O err returned by conn.PacketConn
 	return n, addr, err
 }
 
@@ -252,7 +247,7 @@ func (conn *ObfuscatedPacketConn) WriteTo(p []byte, addr net.Addr) (int, error)
 		return 0, errors.TraceNew("unexpected addr type")
 	}
 	n, _, err := conn.writePacket(p, nil, udpAddr)
-	// Do not wrap any I/O err returned by conn.OOBCapablePacketConn
+	// Do not wrap any I/O err returned by conn.PacketConn
 	return n, err
 }
 
@@ -273,13 +268,13 @@ func (conn *ObfuscatedPacketConn) WriteTo(p []byte, addr net.Addr) (int, error)
 
 func (conn *ObfuscatedPacketConn) ReadMsgUDP(p, oob []byte) (int, int, int, *net.UDPAddr, error) {
 	n, oobn, flags, addr, _, err := conn.readPacketWithType(p, nil)
-	// Do not wrap any I/O err returned by conn.OOBCapablePacketConn
+	// Do not wrap any I/O err returned by conn.PacketConn
 	return n, oobn, flags, addr, err
 }
 
 func (conn *ObfuscatedPacketConn) WriteMsgUDP(p, oob []byte, addr *net.UDPAddr) (int, int, error) {
 	n, oobn, err := conn.writePacket(p, oob, addr)
-	// Do not wrap any I/O err returned by conn.OOBCapablePacketConn
+	// Do not wrap any I/O err returned by conn.PacketConn
 	return n, oobn, err
 }
 
@@ -298,7 +293,7 @@ func (conn *ObfuscatedPacketConn) ReadBatch(ms []ipv4.Message, _ int) (int, erro
 	ms[0].N, ms[0].NN, ms[0].Flags, ms[0].Addr, _, err =
 		conn.readPacketWithType(ms[0].Buffers[0], ms[0].OOB)
 	if err != nil {
-		// Do not wrap any I/O err returned by conn.OOBCapablePacketConn
+		// Do not wrap any I/O err returned by conn.PacketConn
 		return 0, err
 	}
 	return 1, nil
@@ -363,7 +358,7 @@ func (conn *ObfuscatedPacketConn) readPacketWithType(
 			continue
 		}
 
-		// Do not wrap any I/O err returned by conn.OOBCapablePacketConn
+		// Do not wrap any I/O err returned by conn.PacketConn
 		return n, oobn, flags, addr, isIETF, err
 	}
 }
@@ -371,7 +366,26 @@ func (conn *ObfuscatedPacketConn) readPacketWithType(
 func (conn *ObfuscatedPacketConn) readPacket(
 	p, oob []byte) (int, int, int, *net.UDPAddr, bool, error) {
 
-	n, oobn, flags, addr, err := conn.OOBCapablePacketConn.ReadMsgUDP(p, oob)
+	var n, oobn, flags int
+	var addr *net.UDPAddr
+	var err error
+
+	oobCapablePacketConn, ok := conn.PacketConn.(ietf_quic.OOBCapablePacketConn)
+	if ok {
+		// Read OOB ECN bits when supported by the packet conn.
+		n, oobn, flags, addr, err = oobCapablePacketConn.ReadMsgUDP(p, oob)
+	} else {
+		// Fall back to a generic ReadFrom, supported by any packet conn.
+		var netAddr net.Addr
+		n, netAddr, err = conn.PacketConn.ReadFrom(p)
+		if netAddr != nil {
+			// Directly convert from net.Addr to *net.UDPAddr, if possible.
+			addr, ok = netAddr.(*net.UDPAddr)
+			if !ok {
+				addr, err = net.ResolveUDPAddr("udp", netAddr.String())
+			}
+		}
+	}
 
 	// Data is processed even when err != nil, as ReadFrom may return both
 	// a packet and an error, such as io.EOF.
@@ -556,7 +570,7 @@ func (conn *ObfuscatedPacketConn) readPacket(
 		}
 	}
 
-	// Do not wrap any I/O err returned by conn.OOBCapablePacketConn
+	// Do not wrap any I/O err returned by conn.PacketConn
 	return n, oobn, flags, addr, isIETF, err
 }
 
@@ -648,15 +662,42 @@ func (conn *ObfuscatedPacketConn) writePacket(
 		}
 	}
 
-	_, oobn, err := conn.OOBCapablePacketConn.WriteMsgUDP(p, oob, addr)
+	var oobn int
+	var err error
+
+	oobCapablePacketConn, ok := conn.PacketConn.(ietf_quic.OOBCapablePacketConn)
+	if ok {
+
+		// Write OOB bits if supported by the packet conn.
+		//
+		// At this time, quic-go reads but does not write ECN OOB bits. On the
+		// client-side, the Dial function arranges for conn.PacketConn to not
+		// implement OOBCapablePacketConn when using obfuscated QUIC, and so
+		// quic-go is not expected to write ECN bits -- a potential
+		// obfuscation fingerprint -- in the future, on the client-side.
+		//
+		// Limitation: on the server-side, the single UDP server socket is
+		// wrapped with ObfuscatedPacketConn and supports both obfuscated and
+		// regular QUIC; as it stands, this logic will support writing ECN
+		// bits for both obfuscated and regular QUIC.
+
+		_, oobn, err = oobCapablePacketConn.WriteMsgUDP(p, oob, addr)
+
+	} else {
+
+		// Fall back to WriteTo, supported by any packet conn. If there are
+		// OOB bits to be written, fail.
+
+		if oob != nil {
+			return 0, 0, errors.TraceNew("unexpected OOB payload for non-OOBCapablePacketConn")
+		}
+		_, err = conn.PacketConn.WriteTo(p, addr)
+	}
 
-	// quic-go uses OOB to manipulate ECN bits in the IP header; these are not
-	// obfuscated.
-	//
 	// Return n = len(input p) bytes written even when p is an obfuscated
 	// buffer and longer than the input p.
 
-	// Do not wrap any I/O err returned by conn.OOBCapablePacketConn
+	// Do not wrap any I/O err returned by conn.PacketConn
 	return n, oobn, err
 }
 

+ 75 - 51
psiphon/common/quic/quic.go

@@ -21,7 +21,6 @@
  */
 
 /*
-
 Package quic wraps github.com/lucas-clemente/quic-go with net.Listener and
 net.Conn types that provide a drop-in replacement for net.TCPConn.
 
@@ -39,7 +38,6 @@ Conns mask or translate qerr.PeerGoingAway to io.EOF as appropriate.
 QUIC idle timeouts and keep alives are tuned to mitigate aggressive UDP NAT
 timeouts on mobile data networks while accounting for the fact that mobile
 devices in standby/sleep may not be able to initiate the keep alive.
-
 */
 package quic
 
@@ -50,7 +48,7 @@ import (
 	"io"
 	"net"
 	"net/http"
-	"os"
+	"strings"
 	"sync"
 	"sync/atomic"
 	"syscall"
@@ -347,8 +345,8 @@ func (listener *Listener) Close() error {
 // may be cancelled by ctx; packetConn will be closed if the dial is
 // cancelled or fails.
 //
-// Keep alive and idle timeout functionality in QUIC is disabled as these
-// aspects are expected to be handled at a higher level.
+// When packetConn is a *net.UDPConn, QUIC ECN bit operations are supported,
+// unless the specified QUIC version is obfuscated.
 func Dial(
 	ctx context.Context,
 	packetConn net.PacketConn,
@@ -386,14 +384,38 @@ func Dial(
 		return nil, errors.Tracef("invalid destination port: %d", remoteAddr.Port)
 	}
 
-	udpConn, ok := packetConn.(udpConn)
-	if !ok {
-		return nil, errors.TraceNew("packetConn must implement net.UDPConn functions")
-	}
+	udpConn, ok := packetConn.(*net.UDPConn)
 
-	// Ensure blocked packet writes eventually timeout.
-	packetConn = &writeTimeoutUDPConn{
-		udpConn: udpConn,
+	if !ok || isObfuscated(quicVersion) {
+
+		// quic-go uses OOB operations to manipulate ECN bits in IP packet
+		// headers. These operations are available only when the packet conn
+		// is a *net.UDPConn. At this time, quic-go reads but does not write
+		// ECN OOB bits; see quic-go PR 2789.
+		//
+		// To guard against future writes to ECN bits, a potential fingerprint
+		// when using obfuscated QUIC, this non-OOB code path is taken for
+		// isObfuscated QUIC versions. This mitigates upstream fingerprints;
+		// see ObfuscatedPacketConn.writePacket for the server-side
+		// downstream limitation.
+
+		// Ensure blocked packet writes eventually timeout.
+		packetConn = &writeTimeoutPacketConn{
+			PacketConn: packetConn,
+		}
+
+		// Double check that OOB support won't be detected by quic-go.
+		_, ok := packetConn.(ietf_quic.OOBCapablePacketConn)
+		if ok {
+			return nil, errors.TraceNew("unexpected OOBCapablePacketConn")
+		}
+
+	} else {
+
+		// Ensure blocked packet writes eventually timeout.
+		packetConn = &writeTimeoutUDPConn{
+			UDPConn: udpConn,
+		}
 	}
 
 	maxPacketSizeAdjustment := 0
@@ -443,6 +465,7 @@ func Dial(
 	connection, err := dialQUIC(
 		ctx,
 		packetConn,
+		false,
 		remoteAddr,
 		quicSNIAddress,
 		versionNumber,
@@ -501,35 +524,6 @@ func Dial(
 	return conn, nil
 }
 
-// udpConn matches net.UDPConn, which implements both net.Conn and
-// net.PacketConn. udpConn enables handling of Dial packetConn inputs that
-// are not concrete *net.UDPConn types but which still implement all the
-// required functions. A udpConn instance can be passed to quic-go; various
-// quic-go code paths check that the input conn implements net.Conn and/or
-// net.PacketConn.
-//
-// TODO: add *AddrPort functions introduced in Go 1.18
-type udpConn interface {
-	Close() error
-	File() (f *os.File, err error)
-	LocalAddr() net.Addr
-	Read(b []byte) (int, error)
-	ReadFrom(b []byte) (int, net.Addr, error)
-	ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error)
-	ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error)
-	RemoteAddr() net.Addr
-	SetDeadline(t time.Time) error
-	SetReadBuffer(bytes int) error
-	SetReadDeadline(t time.Time) error
-	SetWriteBuffer(bytes int) error
-	SetWriteDeadline(t time.Time) error
-	SyscallConn() (syscall.RawConn, error)
-	Write(b []byte) (int, error)
-	WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error)
-	WriteTo(b []byte, addr net.Addr) (int, error)
-	WriteToUDP(b []byte, addr *net.UDPAddr) (int, error)
-}
-
 // writeTimeoutUDPConn sets write deadlines before each UDP packet write.
 //
 // Generally, a UDP packet write doesn't block. However, Go's
@@ -543,7 +537,7 @@ type udpConn interface {
 // Note that quic-go manages read deadlines; we set only the write deadline
 // here.
 type writeTimeoutUDPConn struct {
-	udpConn
+	*net.UDPConn
 }
 
 func (conn *writeTimeoutUDPConn) Write(b []byte) (int, error) {
@@ -554,7 +548,7 @@ func (conn *writeTimeoutUDPConn) Write(b []byte) (int, error) {
 	}
 
 	// Do not wrap any I/O err returned by udpConn
-	return conn.udpConn.Write(b)
+	return conn.UDPConn.Write(b)
 }
 
 func (conn *writeTimeoutUDPConn) WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (int, int, error) {
@@ -565,7 +559,7 @@ func (conn *writeTimeoutUDPConn) WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (
 	}
 
 	// Do not wrap any I/O err returned by udpConn
-	return conn.udpConn.WriteMsgUDP(b, oob, addr)
+	return conn.UDPConn.WriteMsgUDP(b, oob, addr)
 }
 
 func (conn *writeTimeoutUDPConn) WriteTo(b []byte, addr net.Addr) (int, error) {
@@ -576,7 +570,7 @@ func (conn *writeTimeoutUDPConn) WriteTo(b []byte, addr net.Addr) (int, error) {
 	}
 
 	// Do not wrap any I/O err returned by udpConn
-	return conn.udpConn.WriteTo(b, addr)
+	return conn.UDPConn.WriteTo(b, addr)
 }
 
 func (conn *writeTimeoutUDPConn) WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) {
@@ -587,7 +581,24 @@ func (conn *writeTimeoutUDPConn) WriteToUDP(b []byte, addr *net.UDPAddr) (int, e
 	}
 
 	// Do not wrap any I/O err returned by udpConn
-	return conn.udpConn.WriteToUDP(b, addr)
+	return conn.UDPConn.WriteToUDP(b, addr)
+}
+
+// writeTimeoutPacketConn is the equivilent of writeTimeoutUDPConn for
+// non-*net.UDPConns.
+type writeTimeoutPacketConn struct {
+	net.PacketConn
+}
+
+func (conn *writeTimeoutPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
+
+	err := conn.SetWriteDeadline(time.Now().Add(UDP_PACKET_WRITE_TIMEOUT))
+	if err != nil {
+		return 0, errors.Trace(err)
+	}
+
+	// Do not wrap any I/O err returned by udpConn
+	return conn.PacketConn.WriteTo(b, addr)
 }
 
 // Conn is a net.Conn and psiphon/common.Closer.
@@ -864,9 +875,21 @@ func (t *QUICTransporter) dialQUIC() (retConnection quicConnection, retErr error
 		return nil, errors.Trace(err)
 	}
 
+	// Check for a *net.UDPConn, as expected, to support OOB operations.
+	udpConn, ok := packetConn.(*net.UDPConn)
+	if !ok {
+		return nil, errors.Tracef("unexpected packetConn type: %T", packetConn)
+	}
+
+	// Ensure blocked packet writes eventually timeout.
+	packetConn = &writeTimeoutUDPConn{
+		UDPConn: udpConn,
+	}
+
 	connection, err := dialQUIC(
 		ctx,
 		packetConn,
+		true,
 		remoteAddr,
 		t.quicSNIAddress,
 		versionNumber,
@@ -985,15 +1008,16 @@ func (c *ietfQUICConnection) isErrorIndicatingClosed(err error) bool {
 		return false
 	}
 	errStr := err.Error()
-	// The target errors are of type qerr.ApplicationError and
-	// qerr.IdleTimeoutError, but these are not exported by quic-go.
-	return errStr == "Application error 0x0" ||
+	// The target errors are of type qerr.ApplicationError[Code] and
+	// qerr.IdleTimeoutError, but these are not both exported by quic-go.
+	return strings.HasPrefix(errStr, "Application error 0x0") ||
 		errStr == "timeout: no recent network activity"
 }
 
 func dialQUIC(
 	ctx context.Context,
 	packetConn net.PacketConn,
+	expectNetUDPConn bool,
 	remoteAddr *net.UDPAddr,
 	quicSNIAddress string,
 	versionNumber uint32,
@@ -1153,7 +1177,7 @@ func (conn *muxPacketConn) SetWriteDeadline(t time.Time) error {
 // https://godoc.org/github.com/lucas-clemente/quic-go#ECNCapablePacketConn.
 
 func (conn *muxPacketConn) SetReadBuffer(bytes int) error {
-	c, ok := conn.listener.conn.OOBCapablePacketConn.(interface {
+	c, ok := conn.listener.conn.PacketConn.(interface {
 		SetReadBuffer(int) error
 	})
 	if !ok {
@@ -1163,7 +1187,7 @@ func (conn *muxPacketConn) SetReadBuffer(bytes int) error {
 }
 
 func (conn *muxPacketConn) SyscallConn() (syscall.RawConn, error) {
-	c, ok := conn.listener.conn.OOBCapablePacketConn.(interface {
+	c, ok := conn.listener.conn.PacketConn.(interface {
 		SyscallConn() (syscall.RawConn, error)
 	})
 	if !ok {

+ 2 - 1
vendor/github.com/Psiphon-Labs/quic-go/README.md

@@ -5,7 +5,8 @@
 [![PkgGoDev](https://pkg.go.dev/badge/github.com/lucas-clemente/quic-go)](https://pkg.go.dev/github.com/lucas-clemente/quic-go)
 [![Code Coverage](https://img.shields.io/codecov/c/github/lucas-clemente/quic-go/master.svg?style=flat-square)](https://codecov.io/gh/lucas-clemente/quic-go/)
 
-quic-go is an implementation of the QUIC protocol ([RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000), [RFC 9001](https://datatracker.ietf.org/doc/html/rfc9001), [RFC 9002](https://datatracker.ietf.org/doc/html/rfc9002)) in Go, including the Unreliable Datagram Extension ([RFC 9221](https://datatracker.ietf.org/doc/html/rfc9221)). It has support for HTTP/3 ([RFC 9114](https://datatracker.ietf.org/doc/html/rfc9114)), including QPACK ([RFC 9204](https://datatracker.ietf.org/doc/html/rfc9204)).
+quic-go is an implementation of the QUIC protocol ([RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000), [RFC 9001](https://datatracker.ietf.org/doc/html/rfc9001), [RFC 9002](https://datatracker.ietf.org/doc/html/rfc9002)) in Go, including the Unreliable Datagram Extension ([RFC 9221](https://datatracker.ietf.org/doc/html/rfc9221)) and Datagram Packetization Layer Path MTU
+   Discovery (DPLPMTUD, [RFC 8899](https://datatracker.ietf.org/doc/html/rfc8899)). It has support for HTTP/3 ([RFC 9114](https://datatracker.ietf.org/doc/html/rfc9114)), including QPACK ([RFC 9204](https://datatracker.ietf.org/doc/html/rfc9204)).
 
 In addition to the RFCs listed above, it currently implements the [IETF QUIC draft-29](https://tools.ietf.org/html/draft-ietf-quic-transport-29). Support for draft-29 will eventually be dropped, as it is phased out of the ecosystem.
 

+ 4 - 8
vendor/github.com/Psiphon-Labs/quic-go/client.go

@@ -6,7 +6,6 @@ import (
 	"errors"
 	"fmt"
 	"net"
-	"strings"
 
 	"github.com/Psiphon-Labs/quic-go/internal/protocol"
 	"github.com/Psiphon-Labs/quic-go/internal/utils"
@@ -232,13 +231,10 @@ func newClient(
 		tlsConf = tlsConf.Clone()
 	}
 	if tlsConf.ServerName == "" {
-		sni := host
-		if strings.IndexByte(sni, ':') != -1 {
-			var err error
-			sni, _, err = net.SplitHostPort(sni)
-			if err != nil {
-				return nil, err
-			}
+		sni, _, err := net.SplitHostPort(host)
+		if err != nil {
+			// It's ok if net.SplitHostPort returns an error - it could be a hostname/IP address without a port.
+			sni = host
 		}
 
 		tlsConf.ServerName = sni

+ 6 - 6
vendor/github.com/Psiphon-Labs/quic-go/conn_id_generator.go

@@ -14,7 +14,7 @@ type connIDGenerator struct {
 	highestSeq uint64
 
 	activeSrcConnIDs        map[uint64]protocol.ConnectionID
-	initialClientDestConnID protocol.ConnectionID
+	initialClientDestConnID *protocol.ConnectionID // nil for the client
 
 	addConnectionID        func(protocol.ConnectionID)
 	getStatelessResetToken func(protocol.ConnectionID) protocol.StatelessResetToken
@@ -28,7 +28,7 @@ type connIDGenerator struct {
 
 func newConnIDGenerator(
 	initialConnectionID protocol.ConnectionID,
-	initialClientDestConnID protocol.ConnectionID, // nil for the client
+	initialClientDestConnID *protocol.ConnectionID, // nil for the client
 	addConnectionID func(protocol.ConnectionID),
 	getStatelessResetToken func(protocol.ConnectionID) protocol.StatelessResetToken,
 	removeConnectionID func(protocol.ConnectionID),
@@ -84,7 +84,7 @@ func (m *connIDGenerator) Retire(seq uint64, sentWithDestConnID protocol.Connect
 	if !ok {
 		return nil
 	}
-	if connID.Equal(sentWithDestConnID) {
+	if connID == sentWithDestConnID {
 		return &qerr.TransportError{
 			ErrorCode:    qerr.ProtocolViolation,
 			ErrorMessage: fmt.Sprintf("retired connection ID %d (%s), which was used as the Destination Connection ID on this packet", seq, connID),
@@ -117,14 +117,14 @@ func (m *connIDGenerator) issueNewConnID() error {
 
 func (m *connIDGenerator) SetHandshakeComplete() {
 	if m.initialClientDestConnID != nil {
-		m.retireConnectionID(m.initialClientDestConnID)
+		m.retireConnectionID(*m.initialClientDestConnID)
 		m.initialClientDestConnID = nil
 	}
 }
 
 func (m *connIDGenerator) RemoveAll() {
 	if m.initialClientDestConnID != nil {
-		m.removeConnectionID(m.initialClientDestConnID)
+		m.removeConnectionID(*m.initialClientDestConnID)
 	}
 	for _, connID := range m.activeSrcConnIDs {
 		m.removeConnectionID(connID)
@@ -134,7 +134,7 @@ func (m *connIDGenerator) RemoveAll() {
 func (m *connIDGenerator) ReplaceWithClosed(pers protocol.Perspective, connClose []byte) {
 	connIDs := make([]protocol.ConnectionID, 0, len(m.activeSrcConnIDs)+1)
 	if m.initialClientDestConnID != nil {
-		connIDs = append(connIDs, m.initialClientDestConnID)
+		connIDs = append(connIDs, *m.initialClientDestConnID)
 	}
 	for _, connID := range m.activeSrcConnIDs {
 		connIDs = append(connIDs, connID)

+ 1 - 1
vendor/github.com/Psiphon-Labs/quic-go/conn_id_manager.go

@@ -121,7 +121,7 @@ func (h *connIDManager) addConnectionID(seq uint64, connID protocol.ConnectionID
 	// insert a new element somewhere in the middle
 	for el := h.queue.Front(); el != nil; el = el.Next() {
 		if el.Value.SequenceNumber == seq {
-			if !el.Value.ConnectionID.Equal(connID) {
+			if el.Value.ConnectionID != connID {
 				return fmt.Errorf("received conflicting connection IDs for sequence number %d", seq)
 			}
 			if el.Value.StatelessResetToken != resetToken {

+ 249 - 117
vendor/github.com/Psiphon-Labs/quic-go/connection.go

@@ -25,7 +25,8 @@ import (
 )
 
 type unpacker interface {
-	Unpack(hdr *wire.Header, rcvTime time.Time, data []byte) (*unpackedPacket, error)
+	UnpackLongHeader(hdr *wire.Header, rcvTime time.Time, data []byte) (*unpackedPacket, error)
+	UnpackShortHeader(rcvTime time.Time, data []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error)
 }
 
 type streamGetter interface {
@@ -260,7 +261,7 @@ var newConnection = func(
 		logger:                logger,
 		version:               v,
 	}
-	if origDestConnID != nil {
+	if origDestConnID.Len() > 0 {
 		s.logID = origDestConnID.String()
 	} else {
 		s.logID = destConnID.String()
@@ -273,7 +274,7 @@ var newConnection = func(
 	)
 	s.connIDGenerator = newConnIDGenerator(
 		srcConnID,
-		clientDestConnID,
+		&clientDestConnID,
 		func(connID protocol.ConnectionID) { runner.Add(connID, s) },
 		runner.GetStatelessResetToken,
 		runner.Remove,
@@ -372,7 +373,7 @@ var newConnection = func(
 		s.perspective,
 		s.version,
 	)
-	s.unpacker = newPacketUnpacker(cs, s.version)
+	s.unpacker = newPacketUnpacker(cs, s.srcConnIDLen, s.version)
 	s.cryptoStreamManager = newCryptoStreamManager(cs, initialStream, handshakeStream, s.oneRTTStream)
 	return s
 }
@@ -492,7 +493,7 @@ var newClientConnection = func(
 	s.clientHelloWritten = clientHelloWritten
 	s.cryptoStreamHandler = cs
 	s.cryptoStreamManager = newCryptoStreamManager(cs, initialStream, handshakeStream, newCryptoStream())
-	s.unpacker = newPacketUnpacker(cs, s.version)
+	s.unpacker = newPacketUnpacker(cs, s.srcConnIDLen, s.version)
 	s.packer = newPacketPacker(
 		srcConnID,
 		s.connIDManager.Get,
@@ -760,6 +761,7 @@ func (s *connection) ConnectionState() ConnectionState {
 	return ConnectionState{
 		TLS:               s.cryptoStreamHandler.ConnectionState(),
 		SupportsDatagrams: s.supportsDatagrams(),
+		Version:           s.version,
 	}
 }
 
@@ -890,61 +892,133 @@ func (s *connection) handlePacketImpl(rp *receivedPacket) bool {
 	data := rp.data
 	p := rp
 	for len(data) > 0 {
+		var destConnID protocol.ConnectionID
 		if counter > 0 {
 			p = p.Clone()
 			p.data = data
-		}
 
-		hdr, packetData, rest, err := wire.ParsePacket(p.data, s.srcConnIDLen)
-		if err != nil {
-			if s.tracer != nil {
-				dropReason := logging.PacketDropHeaderParseError
-				if err == wire.ErrUnsupportedVersion {
-					dropReason = logging.PacketDropUnsupportedVersion
+			var err error
+			destConnID, err = wire.ParseConnectionID(p.data, s.srcConnIDLen)
+			if err != nil {
+				if s.tracer != nil {
+					s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, protocol.ByteCount(len(data)), logging.PacketDropHeaderParseError)
 				}
-				s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, protocol.ByteCount(len(data)), dropReason)
+				s.logger.Debugf("error parsing packet, couldn't parse connection ID: %s", err)
+				break
+			}
+			if destConnID != lastConnID {
+				if s.tracer != nil {
+					s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, protocol.ByteCount(len(data)), logging.PacketDropUnknownConnectionID)
+				}
+				s.logger.Debugf("coalesced packet has different destination connection ID: %s, expected %s", destConnID, lastConnID)
+				break
 			}
-			s.logger.Debugf("error parsing packet: %s", err)
-			break
 		}
 
-		if hdr.IsLongHeader && hdr.Version != s.version {
-			if s.tracer != nil {
-				s.tracer.DroppedPacket(logging.PacketTypeFromHeader(hdr), protocol.ByteCount(len(data)), logging.PacketDropUnexpectedVersion)
+		if wire.IsLongHeaderPacket(p.data[0]) {
+			hdr, packetData, rest, err := wire.ParsePacket(p.data, s.srcConnIDLen)
+			if err != nil {
+				if s.tracer != nil {
+					dropReason := logging.PacketDropHeaderParseError
+					if err == wire.ErrUnsupportedVersion {
+						dropReason = logging.PacketDropUnsupportedVersion
+					}
+					s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, protocol.ByteCount(len(data)), dropReason)
+				}
+				s.logger.Debugf("error parsing packet: %s", err)
+				break
 			}
-			s.logger.Debugf("Dropping packet with version %x. Expected %x.", hdr.Version, s.version)
-			break
-		}
+			lastConnID = hdr.DestConnectionID
 
-		if counter > 0 && !hdr.DestConnectionID.Equal(lastConnID) {
-			if s.tracer != nil {
-				s.tracer.DroppedPacket(logging.PacketTypeFromHeader(hdr), protocol.ByteCount(len(data)), logging.PacketDropUnknownConnectionID)
+			if hdr.Version != s.version {
+				if s.tracer != nil {
+					s.tracer.DroppedPacket(logging.PacketTypeFromHeader(hdr), protocol.ByteCount(len(data)), logging.PacketDropUnexpectedVersion)
+				}
+				s.logger.Debugf("Dropping packet with version %x. Expected %x.", hdr.Version, s.version)
+				break
 			}
-			s.logger.Debugf("coalesced packet has different destination connection ID: %s, expected %s", hdr.DestConnectionID, lastConnID)
+
+			if counter > 0 {
+				p.buffer.Split()
+			}
+			counter++
+
+			// only log if this actually a coalesced packet
+			if s.logger.Debug() && (counter > 1 || len(rest) > 0) {
+				s.logger.Debugf("Parsed a coalesced packet. Part %d: %d bytes. Remaining: %d bytes.", counter, len(packetData), len(rest))
+			}
+
+			p.data = packetData
+
+			if wasProcessed := s.handleLongHeaderPacket(p, hdr); wasProcessed {
+				processed = true
+			}
+			data = rest
+		} else {
+			if counter > 0 {
+				p.buffer.Split()
+			}
+			processed = s.handleShortHeaderPacket(p, destConnID)
 			break
 		}
-		lastConnID = hdr.DestConnectionID
+	}
 
-		if counter > 0 {
-			p.buffer.Split()
-		}
-		counter++
+	p.buffer.MaybeRelease()
+	return processed
+}
+
+func (s *connection) handleShortHeaderPacket(p *receivedPacket, destConnID protocol.ConnectionID) bool {
+	var wasQueued bool
 
-		// only log if this actually a coalesced packet
-		if s.logger.Debug() && (counter > 1 || len(rest) > 0) {
-			s.logger.Debugf("Parsed a coalesced packet. Part %d: %d bytes. Remaining: %d bytes.", counter, len(packetData), len(rest))
+	defer func() {
+		// Put back the packet buffer if the packet wasn't queued for later decryption.
+		if !wasQueued {
+			p.buffer.Decrement()
 		}
-		p.data = packetData
-		if wasProcessed := s.handleSinglePacket(p, hdr); wasProcessed {
-			processed = true
+	}()
+
+	pn, pnLen, keyPhase, data, err := s.unpacker.UnpackShortHeader(p.rcvTime, p.data)
+	if err != nil {
+		wasQueued = s.handleUnpackError(err, p, logging.PacketType1RTT)
+		return false
+	}
+
+	if s.logger.Debug() {
+		s.logger.Debugf("<- Reading packet %d (%d bytes) for connection %s, 1-RTT", pn, p.Size(), destConnID)
+		wire.LogShortHeader(s.logger, destConnID, pn, pnLen, keyPhase)
+	}
+
+	if s.receivedPacketHandler.IsPotentiallyDuplicate(pn, protocol.Encryption1RTT) {
+		s.logger.Debugf("Dropping (potentially) duplicate packet.")
+		if s.tracer != nil {
+			s.tracer.DroppedPacket(logging.PacketType1RTT, p.Size(), logging.PacketDropDuplicate)
 		}
-		data = rest
+		return false
 	}
-	p.buffer.MaybeRelease()
-	return processed
+
+	var log func([]logging.Frame)
+	if s.tracer != nil {
+		log = func(frames []logging.Frame) {
+			s.tracer.ReceivedShortHeaderPacket(
+				&logging.ShortHeader{
+					DestConnectionID: destConnID,
+					PacketNumber:     pn,
+					PacketNumberLen:  pnLen,
+					KeyPhase:         keyPhase,
+				},
+				p.Size(),
+				frames,
+			)
+		}
+	}
+	if err := s.handleUnpackedShortHeaderPacket(destConnID, pn, data, p.ecn, p.rcvTime, log); err != nil {
+		s.closeLocal(err)
+		return false
+	}
+	return true
 }
 
-func (s *connection) handleSinglePacket(p *receivedPacket, hdr *wire.Header) bool /* was the packet successfully processed */ {
+func (s *connection) handleLongHeaderPacket(p *receivedPacket, hdr *wire.Header) bool /* was the packet successfully processed */ {
 	var wasQueued bool
 
 	defer func() {
@@ -960,7 +1034,7 @@ func (s *connection) handleSinglePacket(p *receivedPacket, hdr *wire.Header) boo
 
 	// The server can change the source connection ID with the first Handshake packet.
 	// After this, all packets with a different source connection have to be ignored.
-	if s.receivedFirstPacket && hdr.IsLongHeader && hdr.Type == protocol.PacketTypeInitial && !hdr.SrcConnectionID.Equal(s.handshakeDestConnID) {
+	if s.receivedFirstPacket && hdr.Type == protocol.PacketTypeInitial && hdr.SrcConnectionID != s.handshakeDestConnID {
 		if s.tracer != nil {
 			s.tracer.DroppedPacket(logging.PacketTypeInitial, p.Size(), logging.PacketDropUnknownConnectionID)
 		}
@@ -975,53 +1049,18 @@ func (s *connection) handleSinglePacket(p *receivedPacket, hdr *wire.Header) boo
 		return false
 	}
 
-	packet, err := s.unpacker.Unpack(hdr, p.rcvTime, p.data)
+	packet, err := s.unpacker.UnpackLongHeader(hdr, p.rcvTime, p.data)
 	if err != nil {
-		switch err {
-		case handshake.ErrKeysDropped:
-			if s.tracer != nil {
-				s.tracer.DroppedPacket(logging.PacketTypeFromHeader(hdr), p.Size(), logging.PacketDropKeyUnavailable)
-			}
-			s.logger.Debugf("Dropping %s packet (%d bytes) because we already dropped the keys.", hdr.PacketType(), p.Size())
-		case handshake.ErrKeysNotYetAvailable:
-			// Sealer for this encryption level not yet available.
-			// Try again later.
-			wasQueued = true
-			s.tryQueueingUndecryptablePacket(p, hdr)
-		case wire.ErrInvalidReservedBits:
-			s.closeLocal(&qerr.TransportError{
-				ErrorCode:    qerr.ProtocolViolation,
-				ErrorMessage: err.Error(),
-			})
-		case handshake.ErrDecryptionFailed:
-			// This might be a packet injected by an attacker. Drop it.
-			if s.tracer != nil {
-				s.tracer.DroppedPacket(logging.PacketTypeFromHeader(hdr), p.Size(), logging.PacketDropPayloadDecryptError)
-			}
-			s.logger.Debugf("Dropping %s packet (%d bytes) that could not be unpacked. Error: %s", hdr.PacketType(), p.Size(), err)
-		default:
-			var headerErr *headerParseError
-			if errors.As(err, &headerErr) {
-				// This might be a packet injected by an attacker. Drop it.
-				if s.tracer != nil {
-					s.tracer.DroppedPacket(logging.PacketTypeFromHeader(hdr), p.Size(), logging.PacketDropHeaderParseError)
-				}
-				s.logger.Debugf("Dropping %s packet (%d bytes) for which we couldn't unpack the header. Error: %s", hdr.PacketType(), p.Size(), err)
-			} else {
-				// This is an error returned by the AEAD (other than ErrDecryptionFailed).
-				// For example, a PROTOCOL_VIOLATION due to key updates.
-				s.closeLocal(err)
-			}
-		}
+		wasQueued = s.handleUnpackError(err, p, logging.PacketTypeFromHeader(hdr))
 		return false
 	}
 
 	if s.logger.Debug() {
-		s.logger.Debugf("<- Reading packet %d (%d bytes) for connection %s, %s", packet.packetNumber, p.Size(), hdr.DestConnectionID, packet.encryptionLevel)
+		s.logger.Debugf("<- Reading packet %d (%d bytes) for connection %s, %s", packet.hdr.PacketNumber, p.Size(), hdr.DestConnectionID, packet.encryptionLevel)
 		packet.hdr.Log(s.logger)
 	}
 
-	if s.receivedPacketHandler.IsPotentiallyDuplicate(packet.packetNumber, packet.encryptionLevel) {
+	if s.receivedPacketHandler.IsPotentiallyDuplicate(packet.hdr.PacketNumber, packet.encryptionLevel) {
 		s.logger.Debugf("Dropping (potentially) duplicate packet.")
 		if s.tracer != nil {
 			s.tracer.DroppedPacket(logging.PacketTypeFromHeader(hdr), p.Size(), logging.PacketDropDuplicate)
@@ -1036,6 +1075,46 @@ func (s *connection) handleSinglePacket(p *receivedPacket, hdr *wire.Header) boo
 	return true
 }
 
+func (s *connection) handleUnpackError(err error, p *receivedPacket, pt logging.PacketType) (wasQueued bool) {
+	switch err {
+	case handshake.ErrKeysDropped:
+		if s.tracer != nil {
+			s.tracer.DroppedPacket(pt, p.Size(), logging.PacketDropKeyUnavailable)
+		}
+		s.logger.Debugf("Dropping %s packet (%d bytes) because we already dropped the keys.", pt, p.Size())
+	case handshake.ErrKeysNotYetAvailable:
+		// Sealer for this encryption level not yet available.
+		// Try again later.
+		s.tryQueueingUndecryptablePacket(p, pt)
+		return true
+	case wire.ErrInvalidReservedBits:
+		s.closeLocal(&qerr.TransportError{
+			ErrorCode:    qerr.ProtocolViolation,
+			ErrorMessage: err.Error(),
+		})
+	case handshake.ErrDecryptionFailed:
+		// This might be a packet injected by an attacker. Drop it.
+		if s.tracer != nil {
+			s.tracer.DroppedPacket(pt, p.Size(), logging.PacketDropPayloadDecryptError)
+		}
+		s.logger.Debugf("Dropping %s packet (%d bytes) that could not be unpacked. Error: %s", pt, p.Size(), err)
+	default:
+		var headerErr *headerParseError
+		if errors.As(err, &headerErr) {
+			// This might be a packet injected by an attacker. Drop it.
+			if s.tracer != nil {
+				s.tracer.DroppedPacket(pt, p.Size(), logging.PacketDropHeaderParseError)
+			}
+			s.logger.Debugf("Dropping %s packet (%d bytes) for which we couldn't unpack the header. Error: %s", pt, p.Size(), err)
+		} else {
+			// This is an error returned by the AEAD (other than ErrDecryptionFailed).
+			// For example, a PROTOCOL_VIOLATION due to key updates.
+			s.closeLocal(err)
+		}
+	}
+	return false
+}
+
 func (s *connection) handleRetryPacket(hdr *wire.Header, data []byte) bool /* was this a valid Retry */ {
 	if s.perspective == protocol.PerspectiveServer {
 		if s.tracer != nil {
@@ -1052,7 +1131,7 @@ func (s *connection) handleRetryPacket(hdr *wire.Header, data []byte) bool /* wa
 		return false
 	}
 	destConnID := s.connIDManager.Get()
-	if hdr.SrcConnectionID.Equal(destConnID) {
+	if hdr.SrcConnectionID == destConnID {
 		if s.tracer != nil {
 			s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedPacket)
 		}
@@ -1107,7 +1186,7 @@ func (s *connection) handleVersionNegotiationPacket(p *receivedPacket) {
 		return
 	}
 
-	hdr, supportedVersions, err := wire.ParseVersionNegotiationPacket(bytes.NewReader(p.data))
+	src, dest, supportedVersions, err := wire.ParseVersionNegotiationPacket(p.data)
 	if err != nil {
 		if s.tracer != nil {
 			s.tracer.DroppedPacket(logging.PacketTypeVersionNegotiation, p.Size(), logging.PacketDropHeaderParseError)
@@ -1129,7 +1208,7 @@ func (s *connection) handleVersionNegotiationPacket(p *receivedPacket) {
 
 	s.logger.Infof("Received a Version Negotiation packet. Supported Versions: %s", supportedVersions)
 	if s.tracer != nil {
-		s.tracer.ReceivedVersionNegotiationPacket(hdr, supportedVersions)
+		s.tracer.ReceivedVersionNegotiationPacket(dest, src, supportedVersions)
 	}
 	newVersion, ok := protocol.ChooseSupportedVersion(s.config.Versions, supportedVersions)
 	if !ok {
@@ -1158,13 +1237,6 @@ func (s *connection) handleUnpackedPacket(
 	rcvTime time.Time,
 	packetSize protocol.ByteCount, // only for logging
 ) error {
-	if len(packet.data) == 0 {
-		return &qerr.TransportError{
-			ErrorCode:    qerr.ProtocolViolation,
-			ErrorMessage: "empty packet",
-		}
-	}
-
 	if !s.receivedFirstPacket {
 		s.receivedFirstPacket = true
 		if !s.versionNegotiated && s.tracer != nil {
@@ -1178,7 +1250,7 @@ func (s *connection) handleUnpackedPacket(
 			s.tracer.NegotiatedVersion(s.version, clientVersions, serverVersions)
 		}
 		// The server can change the source connection ID with the first Handshake packet.
-		if s.perspective == protocol.PerspectiveClient && packet.hdr.IsLongHeader && !packet.hdr.SrcConnectionID.Equal(s.handshakeDestConnID) {
+		if s.perspective == protocol.PerspectiveClient && packet.hdr.IsLongHeader && packet.hdr.SrcConnectionID != s.handshakeDestConnID {
 			cid := packet.hdr.SrcConnectionID
 			s.logger.Debugf("Received first packet. Switching destination connection ID to: %s", cid)
 			s.handshakeDestConnID = cid
@@ -1187,10 +1259,10 @@ func (s *connection) handleUnpackedPacket(
 		// We create the connection as soon as we receive the first packet from the client.
 		// We do that before authenticating the packet.
 		// That means that if the source connection ID was corrupted,
-		// we might have create a connection with an incorrect source connection ID.
+		// we might have created a connection with an incorrect source connection ID.
 		// Once we authenticate the first packet, we need to update it.
 		if s.perspective == protocol.PerspectiveServer {
-			if !packet.hdr.SrcConnectionID.Equal(s.handshakeDestConnID) {
+			if packet.hdr.SrcConnectionID != s.handshakeDestConnID {
 				s.handshakeDestConnID = packet.hdr.SrcConnectionID
 				s.connIDManager.ChangeInitialConnID(packet.hdr.SrcConnectionID)
 			}
@@ -1209,16 +1281,53 @@ func (s *connection) handleUnpackedPacket(
 	s.firstAckElicitingPacketAfterIdleSentTime = time.Time{}
 	s.keepAlivePingSent = false
 
+	var log func([]logging.Frame)
+	if s.tracer != nil {
+		log = func(frames []logging.Frame) {
+			s.tracer.ReceivedLongHeaderPacket(packet.hdr, packetSize, frames)
+		}
+	}
+	isAckEliciting, err := s.handleFrames(packet.data, packet.hdr.DestConnectionID, packet.encryptionLevel, log)
+	if err != nil {
+		return err
+	}
+	return s.receivedPacketHandler.ReceivedPacket(packet.hdr.PacketNumber, ecn, packet.encryptionLevel, rcvTime, isAckEliciting)
+}
+
+func (s *connection) handleUnpackedShortHeaderPacket(
+	destConnID protocol.ConnectionID,
+	pn protocol.PacketNumber,
+	data []byte,
+	ecn protocol.ECN,
+	rcvTime time.Time,
+	log func([]logging.Frame),
+) error {
+	s.lastPacketReceivedTime = rcvTime
+	s.firstAckElicitingPacketAfterIdleSentTime = time.Time{}
+	s.keepAlivePingSent = false
+
+	isAckEliciting, err := s.handleFrames(data, destConnID, protocol.Encryption1RTT, log)
+	if err != nil {
+		return err
+	}
+	return s.receivedPacketHandler.ReceivedPacket(pn, ecn, protocol.Encryption1RTT, rcvTime, isAckEliciting)
+}
+
+func (s *connection) handleFrames(
+	data []byte,
+	destConnID protocol.ConnectionID,
+	encLevel protocol.EncryptionLevel,
+	log func([]logging.Frame),
+) (isAckEliciting bool, _ error) {
 	// Only used for tracing.
 	// If we're not tracing, this slice will always remain empty.
 	var frames []wire.Frame
-	r := bytes.NewReader(packet.data)
-	var isAckEliciting bool
-	for {
-		frame, err := s.frameParser.ParseNext(r, packet.encryptionLevel)
+	for len(data) > 0 {
+		l, frame, err := s.frameParser.ParseNext(data, encLevel)
 		if err != nil {
-			return err
+			return false, err
 		}
+		data = data[l:]
 		if frame == nil {
 			break
 		}
@@ -1227,29 +1336,28 @@ func (s *connection) handleUnpackedPacket(
 		}
 		// Only process frames now if we're not logging.
 		// If we're logging, we need to make sure that the packet_received event is logged first.
-		if s.tracer == nil {
-			if err := s.handleFrame(frame, packet.encryptionLevel, packet.hdr.DestConnectionID); err != nil {
-				return err
+		if log == nil {
+			if err := s.handleFrame(frame, encLevel, destConnID); err != nil {
+				return false, err
 			}
 		} else {
 			frames = append(frames, frame)
 		}
 	}
 
-	if s.tracer != nil {
+	if log != nil {
 		fs := make([]logging.Frame, len(frames))
 		for i, frame := range frames {
 			fs[i] = logutils.ConvertFrame(frame)
 		}
-		s.tracer.ReceivedPacket(packet.hdr, packetSize, fs)
+		log(fs)
 		for _, frame := range frames {
-			if err := s.handleFrame(frame, packet.encryptionLevel, packet.hdr.DestConnectionID); err != nil {
-				return err
+			if err := s.handleFrame(frame, encLevel, destConnID); err != nil {
+				return false, err
 			}
 		}
 	}
-
-	return s.receivedPacketHandler.ReceivedPacket(packet.packetNumber, ecn, packet.encryptionLevel, rcvTime, isAckEliciting)
+	return
 }
 
 func (s *connection) handleFrame(f wire.Frame, encLevel protocol.EncryptionLevel, destConnID protocol.ConnectionID) error {
@@ -1262,6 +1370,7 @@ func (s *connection) handleFrame(f wire.Frame, encLevel protocol.EncryptionLevel
 		err = s.handleStreamFrame(frame)
 	case *wire.AckFrame:
 		err = s.handleAckFrame(frame, encLevel)
+		wire.PutAckFrame(frame)
 	case *wire.ConnectionCloseFrame:
 		s.handleConnectionCloseFrame(frame)
 	case *wire.ResetStreamFrame:
@@ -1636,7 +1745,7 @@ func (s *connection) checkTransportParameters(params *wire.TransportParameters)
 	}
 
 	// check the initial_source_connection_id
-	if !params.InitialSourceConnectionID.Equal(s.handshakeDestConnID) {
+	if params.InitialSourceConnectionID != s.handshakeDestConnID {
 		return fmt.Errorf("expected initial_source_connection_id to equal %s, is %s", s.handshakeDestConnID, params.InitialSourceConnectionID)
 	}
 
@@ -1644,14 +1753,14 @@ func (s *connection) checkTransportParameters(params *wire.TransportParameters)
 		return nil
 	}
 	// check the original_destination_connection_id
-	if !params.OriginalDestinationConnectionID.Equal(s.origDestConnID) {
+	if params.OriginalDestinationConnectionID != s.origDestConnID {
 		return fmt.Errorf("expected original_destination_connection_id to equal %s, is %s", s.origDestConnID, params.OriginalDestinationConnectionID)
 	}
 	if s.retrySrcConnID != nil { // a Retry was performed
 		if params.RetrySourceConnectionID == nil {
 			return errors.New("missing retry_source_connection_id")
 		}
-		if !(*params.RetrySourceConnectionID).Equal(*s.retrySrcConnID) {
+		if *params.RetrySourceConnectionID != *s.retrySrcConnID {
 			return fmt.Errorf("expected retry_source_connection_id to equal %s, is %s", s.retrySrcConnID, *params.RetrySourceConnectionID)
 		}
 	} else if params.RetrySourceConnectionID != nil {
@@ -1748,7 +1857,24 @@ func (s *connection) sendPackets() error {
 }
 
 func (s *connection) maybeSendAckOnlyPacket() error {
-	packet, err := s.packer.MaybePackAckPacket(s.handshakeConfirmed)
+	if !s.handshakeConfirmed {
+		packet, err := s.packer.PackCoalescedPacket(true)
+		if err != nil {
+			return err
+		}
+		if packet == nil {
+			return nil
+		}
+		s.logCoalescedPacket(packet)
+		for _, p := range packet.packets {
+			s.sentPacketHandler.SentPacket(p.ToAckHandlerPacket(time.Now(), s.retransmissionQueue))
+		}
+		s.connIDManager.SentPacket()
+		s.sendQueue.Send(packet.buffer)
+		return nil
+	}
+
+	packet, err := s.packer.PackPacket(true)
 	if err != nil {
 		return err
 	}
@@ -1809,7 +1935,7 @@ func (s *connection) sendPacket() (bool, error) {
 
 	now := time.Now()
 	if !s.handshakeConfirmed {
-		packet, err := s.packer.PackCoalescedPacket()
+		packet, err := s.packer.PackCoalescedPacket(false)
 		if err != nil || packet == nil {
 			return false, err
 		}
@@ -1833,7 +1959,7 @@ func (s *connection) sendPacket() (bool, error) {
 		s.sendPackedPacket(packet, now)
 		return true, nil
 	}
-	packet, err := s.packer.PackPacket()
+	packet, err := s.packer.PackPacket(false)
 	if err != nil || packet == nil {
 		return false, err
 	}
@@ -1880,7 +2006,11 @@ func (s *connection) logPacketContents(p *packetContents) {
 		for _, f := range p.frames {
 			frames = append(frames, logutils.ConvertFrame(f.Frame))
 		}
-		s.tracer.SentPacket(p.header, p.length, p.ack, frames)
+		var ack *logging.AckFrame
+		if p.ack != nil {
+			ack = logutils.ConvertAckFrame(p.ack)
+		}
+		s.tracer.SentPacket(p.header, p.length, ack, frames)
 	}
 
 	// quic-go logging
@@ -1971,20 +2101,22 @@ func (s *connection) scheduleSending() {
 	}
 }
 
-func (s *connection) tryQueueingUndecryptablePacket(p *receivedPacket, hdr *wire.Header) {
+// tryQueueingUndecryptablePacket queues a packet for which we're missing the decryption keys.
+// The logging.PacketType is only used for logging purposes.
+func (s *connection) tryQueueingUndecryptablePacket(p *receivedPacket, pt logging.PacketType) {
 	if s.handshakeComplete {
 		panic("shouldn't queue undecryptable packets after handshake completion")
 	}
 	if len(s.undecryptablePackets)+1 > protocol.MaxUndecryptablePackets {
 		if s.tracer != nil {
-			s.tracer.DroppedPacket(logging.PacketTypeFromHeader(hdr), p.Size(), logging.PacketDropDOSPrevention)
+			s.tracer.DroppedPacket(pt, p.Size(), logging.PacketDropDOSPrevention)
 		}
 		s.logger.Infof("Dropping undecryptable packet (%d bytes). Undecryptable packet queue full.", p.Size())
 		return
 	}
 	s.logger.Infof("Queueing packet (%d bytes) for later decryption", p.Size())
 	if s.tracer != nil {
-		s.tracer.BufferedPacket(logging.PacketTypeFromHeader(hdr))
+		s.tracer.BufferedPacket(pt, p.Size())
 	}
 	s.undecryptablePackets = append(s.undecryptablePackets, p)
 }

+ 16 - 4
vendor/github.com/Psiphon-Labs/quic-go/datagram_queue.go

@@ -8,6 +8,7 @@ import (
 
 type datagramQueue struct {
 	sendQueue chan *wire.DatagramFrame
+	nextFrame *wire.DatagramFrame
 	rcvQueue  chan []byte
 
 	closeErr error
@@ -49,15 +50,26 @@ func (h *datagramQueue) AddAndWait(f *wire.DatagramFrame) error {
 	}
 }
 
-// Get dequeues a DATAGRAM frame for sending.
-func (h *datagramQueue) Get() *wire.DatagramFrame {
+// Peek gets the next DATAGRAM frame for sending.
+// If actually sent out, Pop needs to be called before the next call to Peek.
+func (h *datagramQueue) Peek() *wire.DatagramFrame {
+	if h.nextFrame != nil {
+		return h.nextFrame
+	}
 	select {
-	case f := <-h.sendQueue:
+	case h.nextFrame = <-h.sendQueue:
 		h.dequeued <- struct{}{}
-		return f
 	default:
 		return nil
 	}
+	return h.nextFrame
+}
+
+func (h *datagramQueue) Pop() {
+	if h.nextFrame == nil {
+		panic("datagramQueue BUG: Pop called for nil frame")
+	}
+	h.nextFrame = nil
 }
 
 // HandleDatagramFrame handles a received DATAGRAM frame.

+ 3 - 0
vendor/github.com/Psiphon-Labs/quic-go/http3/body.go

@@ -18,12 +18,15 @@ type HTTPStreamer interface {
 }
 
 type StreamCreator interface {
+	// Context returns a context that is cancelled when the underlying connection is closed.
+	Context() context.Context
 	OpenStream() (quic.Stream, error)
 	OpenStreamSync(context.Context) (quic.Stream, error)
 	OpenUniStream() (quic.SendStream, error)
 	OpenUniStreamSync(context.Context) (quic.SendStream, error)
 	LocalAddr() net.Addr
 	RemoteAddr() net.Addr
+	ConnectionState() quic.ConnectionState
 }
 
 var _ StreamCreator = quic.Connection(nil)

+ 55 - 0
vendor/github.com/Psiphon-Labs/quic-go/http3/capsule.go

@@ -0,0 +1,55 @@
+package http3
+
+import (
+	"io"
+
+	"github.com/Psiphon-Labs/quic-go/quicvarint"
+)
+
+// CapsuleType is the type of the capsule.
+type CapsuleType uint64
+
+type exactReader struct {
+	R *io.LimitedReader
+}
+
+func (r *exactReader) Read(b []byte) (int, error) {
+	n, err := r.R.Read(b)
+	if r.R.N > 0 {
+		return n, io.ErrUnexpectedEOF
+	}
+	return n, err
+}
+
+// ParseCapsule parses the header of a Capsule.
+// It returns an io.LimitedReader that can be used to read the Capsule value.
+// The Capsule value must be read entirely (i.e. until the io.EOF) before using r again.
+func ParseCapsule(r quicvarint.Reader) (CapsuleType, io.Reader, error) {
+	ct, err := quicvarint.Read(r)
+	if err != nil {
+		if err == io.EOF {
+			return 0, nil, io.ErrUnexpectedEOF
+		}
+		return 0, nil, err
+	}
+	l, err := quicvarint.Read(r)
+	if err != nil {
+		if err == io.EOF {
+			return 0, nil, io.ErrUnexpectedEOF
+		}
+		return 0, nil, err
+	}
+	return CapsuleType(ct), &exactReader{R: io.LimitReader(r, int64(l)).(*io.LimitedReader)}, nil
+}
+
+// WriteCapsule writes a capsule
+func WriteCapsule(w quicvarint.Writer, ct CapsuleType, value []byte) error {
+	b := make([]byte, 0, 16)
+	b = quicvarint.Append(b, uint64(ct))
+	b = quicvarint.Append(b, uint64(len(value)))
+	if _, err := w.Write(b); err != nil {
+		return err
+	}
+	_, err := w.Write(value)
+	return err
+}

+ 5 - 5
vendor/github.com/Psiphon-Labs/quic-go/http3/client.go

@@ -1,7 +1,6 @@
 package http3
 
 import (
-	"bytes"
 	"context"
 	"crypto/tls"
 	"errors"
@@ -152,11 +151,11 @@ func (c *client) setupConn() error {
 	if err != nil {
 		return err
 	}
-	buf := &bytes.Buffer{}
-	quicvarint.Write(buf, streamTypeControlStream)
+	b := make([]byte, 0, 64)
+	b = quicvarint.Append(b, streamTypeControlStream)
 	// send the SETTINGS frame
-	(&settingsFrame{Datagram: c.opts.EnableDatagram, Other: c.opts.AdditionalSettings}).Write(buf)
-	_, err = str.Write(buf.Bytes())
+	b = (&settingsFrame{Datagram: c.opts.EnableDatagram, Other: c.opts.AdditionalSettings}).Append(b)
+	_, err = str.Write(b)
 	return err
 }
 
@@ -414,6 +413,7 @@ func (c *client) doRequest(req *http.Request, str quic.Stream, opt RoundTripOpt,
 		ProtoMajor: 3,
 		Header:     http.Header{},
 		TLS:        &connState,
+		Request:    req,
 	}
 	for _, hf := range hfs {
 		switch hf.Name {

+ 14 - 13
vendor/github.com/Psiphon-Labs/quic-go/http3/frames.go

@@ -74,18 +74,18 @@ type dataFrame struct {
 	Length uint64
 }
 
-func (f *dataFrame) Write(b *bytes.Buffer) {
-	quicvarint.Write(b, 0x0)
-	quicvarint.Write(b, f.Length)
+func (f *dataFrame) Append(b []byte) []byte {
+	b = quicvarint.Append(b, 0x0)
+	return quicvarint.Append(b, f.Length)
 }
 
 type headersFrame struct {
 	Length uint64
 }
 
-func (f *headersFrame) Write(b *bytes.Buffer) {
-	quicvarint.Write(b, 0x1)
-	quicvarint.Write(b, f.Length)
+func (f *headersFrame) Append(b []byte) []byte {
+	b = quicvarint.Append(b, 0x1)
+	return quicvarint.Append(b, f.Length)
 }
 
 const settingDatagram = 0xffd277
@@ -142,8 +142,8 @@ func parseSettingsFrame(r io.Reader, l uint64) (*settingsFrame, error) {
 	return frame, nil
 }
 
-func (f *settingsFrame) Write(b *bytes.Buffer) {
-	quicvarint.Write(b, 0x4)
+func (f *settingsFrame) Append(b []byte) []byte {
+	b = quicvarint.Append(b, 0x4)
 	var l protocol.ByteCount
 	for id, val := range f.Other {
 		l += quicvarint.Len(id) + quicvarint.Len(val)
@@ -151,13 +151,14 @@ func (f *settingsFrame) Write(b *bytes.Buffer) {
 	if f.Datagram {
 		l += quicvarint.Len(settingDatagram) + quicvarint.Len(1)
 	}
-	quicvarint.Write(b, uint64(l))
+	b = quicvarint.Append(b, uint64(l))
 	if f.Datagram {
-		quicvarint.Write(b, settingDatagram)
-		quicvarint.Write(b, 1)
+		b = quicvarint.Append(b, settingDatagram)
+		b = quicvarint.Append(b, 1)
 	}
 	for id, val := range f.Other {
-		quicvarint.Write(b, id)
-		quicvarint.Write(b, val)
+		b = quicvarint.Append(b, id)
+		b = quicvarint.Append(b, val)
 	}
+	return b
 }

+ 10 - 5
vendor/github.com/Psiphon-Labs/quic-go/http3/http_stream.go

@@ -1,7 +1,6 @@
 package http3
 
 import (
-	"bytes"
 	"fmt"
 
 	"github.com/Psiphon-Labs/quic-go"
@@ -16,6 +15,8 @@ type Stream quic.Stream
 type stream struct {
 	quic.Stream
 
+	buf []byte
+
 	onFrameError          func()
 	bytesRemainingInFrame uint64
 }
@@ -23,7 +24,11 @@ type stream struct {
 var _ Stream = &stream{}
 
 func newStream(str quic.Stream, onFrameError func()) *stream {
-	return &stream{Stream: str, onFrameError: onFrameError}
+	return &stream{
+		Stream:       str,
+		onFrameError: onFrameError,
+		buf:          make([]byte, 0, 16),
+	}
 }
 
 func (s *stream) Read(b []byte) (int, error) {
@@ -62,9 +67,9 @@ func (s *stream) Read(b []byte) (int, error) {
 }
 
 func (s *stream) Write(b []byte) (int, error) {
-	buf := &bytes.Buffer{}
-	(&dataFrame{Length: uint64(len(b))}).Write(buf)
-	if _, err := s.Stream.Write(buf.Bytes()); err != nil {
+	s.buf = s.buf[:0]
+	s.buf = (&dataFrame{Length: uint64(len(b))}).Append(s.buf)
+	if _, err := s.Stream.Write(s.buf); err != nil {
 		return 0, err
 	}
 	return s.Stream.Write(b)

+ 3 - 4
vendor/github.com/Psiphon-Labs/quic-go/http3/request_writer.go

@@ -58,10 +58,9 @@ func (w *requestWriter) writeHeaders(wr io.Writer, req *http.Request, gzip bool)
 		return err
 	}
 
-	buf := &bytes.Buffer{}
-	hf := headersFrame{Length: uint64(w.headerBuf.Len())}
-	hf.Write(buf)
-	if _, err := wr.Write(buf.Bytes()); err != nil {
+	b := make([]byte, 0, 128)
+	b = (&headersFrame{Length: uint64(w.headerBuf.Len())}).Append(b)
+	if _, err := wr.Write(b); err != nil {
 		return err
 	}
 	_, err := wr.Write(w.headerBuf.Bytes())

+ 8 - 6
vendor/github.com/Psiphon-Labs/quic-go/http3/response_writer.go

@@ -15,6 +15,7 @@ import (
 type responseWriter struct {
 	conn        quic.Connection
 	bufferedStr *bufio.Writer
+	buf         []byte
 
 	header        http.Header
 	status        int // status code passed to WriteHeader
@@ -32,6 +33,7 @@ var (
 func newResponseWriter(str quic.Stream, conn quic.Connection, logger utils.Logger) *responseWriter {
 	return &responseWriter{
 		header:      http.Header{},
+		buf:         make([]byte, 16),
 		conn:        conn,
 		bufferedStr: bufio.NewWriter(str),
 		logger:      logger,
@@ -62,10 +64,10 @@ func (w *responseWriter) WriteHeader(status int) {
 		}
 	}
 
-	buf := &bytes.Buffer{}
-	(&headersFrame{Length: uint64(headers.Len())}).Write(buf)
+	w.buf = w.buf[:0]
+	w.buf = (&headersFrame{Length: uint64(headers.Len())}).Append(w.buf)
 	w.logger.Infof("Responding with %d", status)
-	if _, err := w.bufferedStr.Write(buf.Bytes()); err != nil {
+	if _, err := w.bufferedStr.Write(w.buf); err != nil {
 		w.logger.Errorf("could not write headers frame: %s", err.Error())
 	}
 	if _, err := w.bufferedStr.Write(headers.Bytes()); err != nil {
@@ -84,9 +86,9 @@ func (w *responseWriter) Write(p []byte) (int, error) {
 		return 0, http.ErrBodyNotAllowed
 	}
 	df := &dataFrame{Length: uint64(len(p))}
-	buf := &bytes.Buffer{}
-	df.Write(buf)
-	if _, err := w.bufferedStr.Write(buf.Bytes()); err != nil {
+	w.buf = w.buf[:0]
+	w.buf = df.Append(w.buf)
+	if _, err := w.bufferedStr.Write(w.buf); err != nil {
 		return 0, err
 	}
 	return w.bufferedStr.Write(p)

+ 40 - 19
vendor/github.com/Psiphon-Labs/quic-go/http3/server.go

@@ -1,7 +1,6 @@
 package http3
 
 import (
-	"bytes"
 	"context"
 	"crypto/tls"
 	"errors"
@@ -29,8 +28,10 @@ var (
 )
 
 const (
-	nextProtoH3Draft29 = "h3-29"
-	nextProtoH3        = "h3"
+	// NextProtoH3Draft29 is the ALPN protocol negotiated during the TLS handshake, for QUIC draft 29.
+	NextProtoH3Draft29 = "h3-29"
+	// NextProtoH3 is the ALPN protocol negotiated during the TLS handshake, for QUIC v1 and v2.
+	NextProtoH3 = "h3"
 )
 
 // StreamType is the stream type of a unidirectional stream.
@@ -45,10 +46,10 @@ const (
 
 func versionToALPN(v protocol.VersionNumber) string {
 	if v == protocol.Version1 || v == protocol.Version2 {
-		return nextProtoH3
+		return NextProtoH3
 	}
 	if v == protocol.VersionTLS || v == protocol.VersionDraft29 {
-		return nextProtoH3Draft29
+		return NextProtoH3Draft29
 	}
 	return ""
 }
@@ -63,7 +64,7 @@ func ConfigureTLSConfig(tlsConf *tls.Config) *tls.Config {
 	return &tls.Config{
 		GetConfigForClient: func(ch *tls.ClientHelloInfo) (*tls.Config, error) {
 			// determine the ALPN from the QUIC version used
-			proto := nextProtoH3
+			proto := NextProtoH3
 			if qconn, ok := ch.Conn.(handshake.ConnWithVersion); ok {
 				proto = versionToALPN(qconn.GetQUICVersion())
 			}
@@ -225,11 +226,22 @@ func (s *Server) ListenAndServeTLS(certFile, keyFile string) error {
 
 // Serve an existing UDP connection.
 // It is possible to reuse the same connection for outgoing connections.
-// Closing the server does not close the packet conn.
+// Closing the server does not close the connection.
 func (s *Server) Serve(conn net.PacketConn) error {
 	return s.serveConn(s.TLSConfig, conn)
 }
 
+// ServeQUICConn serves a single QUIC connection.
+func (s *Server) ServeQUICConn(conn quic.Connection) error {
+	s.mutex.Lock()
+	if s.logger == nil {
+		s.logger = utils.DefaultLogger.WithPrefix("server")
+	}
+	s.mutex.Unlock()
+
+	return s.handleConn(conn)
+}
+
 // ServeListener serves an existing QUIC listener.
 // Make sure you use http3.ConfigureTLSConfig to configure a tls.Config
 // and use it to construct a http3-friendly QUIC listener.
@@ -296,7 +308,11 @@ func (s *Server) serveListener(ln quic.EarlyListener) error {
 		if err != nil {
 			return err
 		}
-		go s.handleConn(conn)
+		go func() {
+			if err := s.handleConn(conn); err != nil {
+				s.logger.Debugf(err.Error())
+			}
+		}()
 	}
 }
 
@@ -404,19 +420,18 @@ func (s *Server) removeListener(l *quic.EarlyListener) {
 	s.mutex.Unlock()
 }
 
-func (s *Server) handleConn(conn quic.EarlyConnection) {
+func (s *Server) handleConn(conn quic.Connection) error {
 	decoder := qpack.NewDecoder(nil)
 
 	// send a SETTINGS frame
 	str, err := conn.OpenUniStream()
 	if err != nil {
-		s.logger.Debugf("Opening the control stream failed.")
-		return
+		return fmt.Errorf("opening the control stream failed: %w", err)
 	}
-	buf := &bytes.Buffer{}
-	quicvarint.Write(buf, streamTypeControlStream) // stream type
-	(&settingsFrame{Datagram: s.EnableDatagrams, Other: s.AdditionalSettings}).Write(buf)
-	str.Write(buf.Bytes())
+	b := make([]byte, 0, 64)
+	b = quicvarint.Append(b, streamTypeControlStream) // stream type
+	b = (&settingsFrame{Datagram: s.EnableDatagrams, Other: s.AdditionalSettings}).Append(b)
+	str.Write(b)
 
 	go s.handleUnidirectionalStreams(conn)
 
@@ -425,8 +440,11 @@ func (s *Server) handleConn(conn quic.EarlyConnection) {
 	for {
 		str, err := conn.AcceptStream(context.Background())
 		if err != nil {
-			s.logger.Debugf("Accepting stream failed: %s", err)
-			return
+			var appErr *quic.ApplicationError
+			if errors.As(err, &appErr) && appErr.ErrorCode == quic.ApplicationErrorCode(errorNoError) {
+				return nil
+			}
+			return fmt.Errorf("accepting stream failed: %w", err)
 		}
 		go func() {
 			rerr := s.handleRequest(conn, str, decoder, func() {
@@ -454,7 +472,7 @@ func (s *Server) handleConn(conn quic.EarlyConnection) {
 	}
 }
 
-func (s *Server) handleUnidirectionalStreams(conn quic.EarlyConnection) {
+func (s *Server) handleUnidirectionalStreams(conn quic.Connection) {
 	for {
 		str, err := conn.AcceptUniStream(context.Background())
 		if err != nil {
@@ -577,12 +595,15 @@ func (s *Server) handleRequest(conn quic.Connection, str quic.Stream, decoder *q
 	func() {
 		defer func() {
 			if p := recover(); p != nil {
+				panicked = true
+				if p == http.ErrAbortHandler {
+					return
+				}
 				// Copied from net/http/server.go
 				const size = 64 << 10
 				buf := make([]byte, size)
 				buf = buf[:runtime.Stack(buf, false)]
 				s.logger.Errorf("http: panic serving: %v\n%s", p, buf)
-				panicked = true
 			}
 		}()
 		handler.ServeHTTP(r, req)

+ 20 - 6
vendor/github.com/Psiphon-Labs/quic-go/interface.go

@@ -174,7 +174,7 @@ type Connection interface {
 	// CloseWithError closes the connection with an error.
 	// The error string will be sent to the peer.
 	CloseWithError(ApplicationErrorCode, string) error
-	// The context is cancelled when the connection is closed.
+	// Context returns a context that is cancelled when the connection is closed.
 	Context() context.Context
 	// ConnectionState returns basic details about the QUIC connection.
 	// It blocks until the handshake completes.
@@ -202,6 +202,20 @@ type EarlyConnection interface {
 	NextConnection() Connection
 }
 
+// StatelessResetKey is a key used to derive stateless reset tokens.
+type StatelessResetKey [32]byte
+
+// A ConnectionID is a QUIC Connection ID, as defined in RFC 9000.
+// It is not able to handle QUIC Connection IDs longer than 20 bytes,
+// as they are allowed by RFC 8999.
+type ConnectionID = protocol.ConnectionID
+
+// ConnectionIDFromBytes interprets b as a Connection ID. It panics if b is
+// longer than 20 bytes.
+func ConnectionIDFromBytes(b []byte) ConnectionID {
+	return protocol.ParseConnectionID(b)
+}
+
 // A ConnectionIDGenerator is an interface that allows clients to implement their own format
 // for the Connection IDs that servers/clients use as SrcConnectionID in QUIC packets.
 //
@@ -209,7 +223,7 @@ type EarlyConnection interface {
 type ConnectionIDGenerator interface {
 	// GenerateConnectionID generates a new ConnectionID.
 	// Generated ConnectionIDs should be unique and observers should not be able to correlate two ConnectionIDs.
-	GenerateConnectionID() ([]byte, error)
+	GenerateConnectionID() (ConnectionID, error)
 
 	// ConnectionIDLen tells what is the length of the ConnectionIDs generated by the implementation of
 	// this interface.
@@ -286,7 +300,7 @@ type Config struct {
 	// limit the memory usage.
 	// To avoid deadlocks, it is not valid to call other functions on the connection or on streams
 	// in this callback.
-	AllowConnectionWindowIncrease func(sess Connection, delta uint64) bool
+	AllowConnectionWindowIncrease func(conn Connection, delta uint64) bool
 	// MaxIncomingStreams is the maximum number of concurrent bidirectional streams that a peer is allowed to open.
 	// Values above 2^60 are invalid.
 	// If not set, it will default to 100.
@@ -299,7 +313,7 @@ type Config struct {
 	MaxIncomingUniStreams int64
 	// The StatelessResetKey is used to generate stateless reset tokens.
 	// If no key is configured, sending of stateless resets is disabled.
-	StatelessResetKey []byte
+	StatelessResetKey *StatelessResetKey
 	// KeepAlivePeriod defines whether this peer will periodically send a packet to keep the connection alive.
 	// If set to 0, then no keep alive is sent. Otherwise, the keep alive is sent on that period (or at most
 	// every half of MaxIdleTimeout, whichever is smaller).
@@ -312,8 +326,7 @@ type Config struct {
 	// This can be useful if version information is exchanged out-of-band.
 	// It has no effect for a client.
 	DisableVersionNegotiationPackets bool
-	// See https://datatracker.ietf.org/doc/draft-ietf-quic-datagram/.
-	// Datagrams will only be available when both peers enable datagram support.
+	// Enable QUIC datagram support (RFC 9221).
 	EnableDatagrams bool
 	Tracer          logging.Tracer
 
@@ -356,6 +369,7 @@ type Config struct {
 type ConnectionState struct {
 	TLS               handshake.ConnectionState
 	SupportsDatagrams bool
+	Version           VersionNumber
 }
 
 // A Listener for incoming QUIC connections

+ 0 - 20
vendor/github.com/Psiphon-Labs/quic-go/internal/ackhandler/interfaces.go

@@ -7,26 +7,6 @@ import (
 	"github.com/Psiphon-Labs/quic-go/internal/wire"
 )
 
-// A Packet is a packet
-type Packet struct {
-	PacketNumber    protocol.PacketNumber
-	Frames          []Frame
-	LargestAcked    protocol.PacketNumber // InvalidPacketNumber if the packet doesn't contain an ACK
-	Length          protocol.ByteCount
-	EncryptionLevel protocol.EncryptionLevel
-	SendTime        time.Time
-
-	IsPathMTUProbePacket bool // We don't report the loss of Path MTU probe packets to the congestion controller.
-
-	includedInBytesInFlight bool
-	declaredLost            bool
-	skippedPacket           bool
-}
-
-func (p *Packet) outstanding() bool {
-	return !p.declaredLost && !p.skippedPacket && !p.IsPathMTUProbePacket
-}
-
 // SentPacketHandler handles ACKs received for outgoing packets
 type SentPacketHandler interface {
 	// SentPacket may modify the packet

+ 49 - 0
vendor/github.com/Psiphon-Labs/quic-go/internal/ackhandler/packet.go

@@ -0,0 +1,49 @@
+package ackhandler
+
+import (
+	"sync"
+	"time"
+
+	"github.com/Psiphon-Labs/quic-go/internal/protocol"
+)
+
+// A Packet is a packet
+type Packet struct {
+	PacketNumber    protocol.PacketNumber
+	Frames          []Frame
+	LargestAcked    protocol.PacketNumber // InvalidPacketNumber if the packet doesn't contain an ACK
+	Length          protocol.ByteCount
+	EncryptionLevel protocol.EncryptionLevel
+	SendTime        time.Time
+
+	IsPathMTUProbePacket bool // We don't report the loss of Path MTU probe packets to the congestion controller.
+
+	includedInBytesInFlight bool
+	declaredLost            bool
+	skippedPacket           bool
+}
+
+func (p *Packet) outstanding() bool {
+	return !p.declaredLost && !p.skippedPacket && !p.IsPathMTUProbePacket
+}
+
+var packetPool = sync.Pool{New: func() any { return &Packet{} }}
+
+func GetPacket() *Packet {
+	p := packetPool.Get().(*Packet)
+	p.PacketNumber = 0
+	p.Frames = nil
+	p.LargestAcked = 0
+	p.Length = 0
+	p.EncryptionLevel = protocol.EncryptionLevel(0)
+	p.SendTime = time.Time{}
+	p.IsPathMTUProbePacket = false
+	p.includedInBytesInFlight = false
+	p.declaredLost = false
+	p.skippedPacket = false
+	return p
+}
+
+// We currently only return Packets back into the pool when they're acknowledged (not when they're lost).
+// This simplifies the code, and gives the vast majority of the performance benefit we can gain from using the pool.
+func putPacket(p *Packet) { packetPool.Put(p) }

+ 7 - 5
vendor/github.com/Psiphon-Labs/quic-go/internal/ackhandler/received_packet_handler.go

@@ -46,24 +46,26 @@ func (h *receivedPacketHandler) ReceivedPacket(
 	h.sentPackets.ReceivedPacket(encLevel)
 	switch encLevel {
 	case protocol.EncryptionInitial:
-		h.initialPackets.ReceivedPacket(pn, ecn, rcvTime, shouldInstigateAck)
+		return h.initialPackets.ReceivedPacket(pn, ecn, rcvTime, shouldInstigateAck)
 	case protocol.EncryptionHandshake:
-		h.handshakePackets.ReceivedPacket(pn, ecn, rcvTime, shouldInstigateAck)
+		return h.handshakePackets.ReceivedPacket(pn, ecn, rcvTime, shouldInstigateAck)
 	case protocol.Encryption0RTT:
 		if h.lowest1RTTPacket != protocol.InvalidPacketNumber && pn > h.lowest1RTTPacket {
 			return fmt.Errorf("received packet number %d on a 0-RTT packet after receiving %d on a 1-RTT packet", pn, h.lowest1RTTPacket)
 		}
-		h.appDataPackets.ReceivedPacket(pn, ecn, rcvTime, shouldInstigateAck)
+		return h.appDataPackets.ReceivedPacket(pn, ecn, rcvTime, shouldInstigateAck)
 	case protocol.Encryption1RTT:
 		if h.lowest1RTTPacket == protocol.InvalidPacketNumber || pn < h.lowest1RTTPacket {
 			h.lowest1RTTPacket = pn
 		}
+		if err := h.appDataPackets.ReceivedPacket(pn, ecn, rcvTime, shouldInstigateAck); err != nil {
+			return err
+		}
 		h.appDataPackets.IgnoreBelow(h.sentPackets.GetLowestPacketNotConfirmedAcked())
-		h.appDataPackets.ReceivedPacket(pn, ecn, rcvTime, shouldInstigateAck)
+		return nil
 	default:
 		panic(fmt.Sprintf("received packet with unknown encryption level: %s", encLevel))
 	}
-	return nil
 }
 
 func (h *receivedPacketHandler) DropPackets(encLevel protocol.EncryptionLevel) {

+ 6 - 11
vendor/github.com/Psiphon-Labs/quic-go/internal/ackhandler/received_packet_history.go

@@ -107,17 +107,12 @@ func (h *receivedPacketHistory) DeleteBelow(p protocol.PacketNumber) {
 	}
 }
 
-// GetAckRanges gets a slice of all AckRanges that can be used in an AckFrame
-func (h *receivedPacketHistory) GetAckRanges() []wire.AckRange {
-	if h.ranges.Len() == 0 {
-		return nil
-	}
-
-	ackRanges := make([]wire.AckRange, h.ranges.Len())
-	i := 0
-	for el := h.ranges.Back(); el != nil; el = el.Prev() {
-		ackRanges[i] = wire.AckRange{Smallest: el.Value.Start, Largest: el.Value.End}
-		i++
+// AppendAckRanges appends to a slice of all AckRanges that can be used in an AckFrame
+func (h *receivedPacketHistory) AppendAckRanges(ackRanges []wire.AckRange) []wire.AckRange {
+	if h.ranges.Len() > 0 {
+		for el := h.ranges.Back(); el != nil; el = el.Prev() {
+			ackRanges = append(ackRanges, wire.AckRange{Smallest: el.Value.Start, Largest: el.Value.End})
+		}
 	}
 	return ackRanges
 }

+ 15 - 13
vendor/github.com/Psiphon-Labs/quic-go/internal/ackhandler/received_packet_tracker.go

@@ -1,6 +1,7 @@
 package ackhandler
 
 import (
+	"fmt"
 	"time"
 
 	"github.com/Psiphon-Labs/quic-go/internal/protocol"
@@ -48,9 +49,9 @@ func newReceivedPacketTracker(
 	}
 }
 
-func (h *receivedPacketTracker) ReceivedPacket(packetNumber protocol.PacketNumber, ecn protocol.ECN, rcvTime time.Time, shouldInstigateAck bool) {
-	if packetNumber < h.ignoreBelow {
-		return
+func (h *receivedPacketTracker) ReceivedPacket(packetNumber protocol.PacketNumber, ecn protocol.ECN, rcvTime time.Time, shouldInstigateAck bool) error {
+	if isNew := h.packetHistory.ReceivedPacket(packetNumber); !isNew {
+		return fmt.Errorf("recevedPacketTracker BUG: ReceivedPacket called for old / duplicate packet %d", packetNumber)
 	}
 
 	isMissing := h.isMissing(packetNumber)
@@ -59,7 +60,7 @@ func (h *receivedPacketTracker) ReceivedPacket(packetNumber protocol.PacketNumbe
 		h.largestObservedReceivedTime = rcvTime
 	}
 
-	if isNew := h.packetHistory.ReceivedPacket(packetNumber); isNew && shouldInstigateAck {
+	if shouldInstigateAck {
 		h.hasNewAck = true
 	}
 	if shouldInstigateAck {
@@ -74,6 +75,7 @@ func (h *receivedPacketTracker) ReceivedPacket(packetNumber protocol.PacketNumbe
 	case protocol.ECNCE:
 		h.ecnce++
 	}
+	return nil
 }
 
 // IgnoreBelow sets a lower limit for acknowledging packets.
@@ -171,16 +173,16 @@ func (h *receivedPacketTracker) GetAckFrame(onlyIfQueued bool) *wire.AckFrame {
 		}
 	}
 
-	ack := &wire.AckFrame{
-		AckRanges: h.packetHistory.GetAckRanges(),
-		// Make sure that the DelayTime is always positive.
-		// This is not guaranteed on systems that don't have a monotonic clock.
-		DelayTime: utils.Max(0, now.Sub(h.largestObservedReceivedTime)),
-		ECT0:      h.ect0,
-		ECT1:      h.ect1,
-		ECNCE:     h.ecnce,
-	}
+	ack := wire.GetAckFrame()
+	ack.DelayTime = utils.Max(0, now.Sub(h.largestObservedReceivedTime))
+	ack.ECT0 = h.ect0
+	ack.ECT1 = h.ect1
+	ack.ECNCE = h.ecnce
+	ack.AckRanges = h.packetHistory.AppendAckRanges(ack.AckRanges)
 
+	if h.lastAck != nil {
+		wire.PutAckFrame(h.lastAck)
+	}
 	h.lastAck = ack
 	h.ackAlarm = time.Time{}
 	h.ackQueued = false

+ 29 - 9
vendor/github.com/Psiphon-Labs/quic-go/internal/ackhandler/sent_packet_handler.go

@@ -23,6 +23,8 @@ const (
 	amplificationFactor = 3
 	// We use Retry packets to derive an RTT estimate. Make sure we don't set the RTT to a super low value yet.
 	minRTTAfterRetry = 5 * time.Millisecond
+	// The PTO duration uses exponential backoff, but is truncated to a maximum value, as allowed by RFC 8961, section 4.4.
+	maxPTODuration = 60 * time.Second
 )
 
 type packetNumberSpace struct {
@@ -226,14 +228,20 @@ func (h *sentPacketHandler) packetsInFlight() int {
 	return packetsInFlight
 }
 
-func (h *sentPacketHandler) SentPacket(packet *Packet) {
-	h.bytesSent += packet.Length
+func (h *sentPacketHandler) SentPacket(p *Packet) {
+	h.bytesSent += p.Length
 	// For the client, drop the Initial packet number space when the first Handshake packet is sent.
-	if h.perspective == protocol.PerspectiveClient && packet.EncryptionLevel == protocol.EncryptionHandshake && h.initialPackets != nil {
+	if h.perspective == protocol.PerspectiveClient && p.EncryptionLevel == protocol.EncryptionHandshake && h.initialPackets != nil {
 		h.dropPackets(protocol.EncryptionInitial)
 	}
-	isAckEliciting := h.sentPacketImpl(packet)
-	h.getPacketNumberSpace(packet.EncryptionLevel).history.SentPacket(packet, isAckEliciting)
+	isAckEliciting := h.sentPacketImpl(p)
+	if isAckEliciting {
+		h.getPacketNumberSpace(p.EncryptionLevel).history.SentAckElicitingPacket(p)
+	} else {
+		h.getPacketNumberSpace(p.EncryptionLevel).history.SentNonAckElicitingPacket(p.PacketNumber, p.EncryptionLevel, p.SendTime)
+		putPacket(p)
+		p = nil //nolint:ineffassign // This is just to be on the safe side.
+	}
 	if h.tracer != nil && isAckEliciting {
 		h.tracer.UpdatedMetrics(h.rttStats, h.congestion.GetCongestionWindow(), h.bytesInFlight, h.packetsInFlight())
 	}
@@ -334,7 +342,11 @@ func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.En
 			acked1RTTPacket = true
 		}
 		h.removeFromBytesInFlight(p)
+		putPacket(p)
 	}
+	// After this point, we must not use ackedPackets any longer!
+	// We've already returned the buffers.
+	ackedPackets = nil //nolint:ineffassign // This is just to be on the safe side.
 
 	// Reset the pto_count unless the client is unsure if the server has validated the client's address.
 	if h.peerCompletedAddressValidation {
@@ -447,6 +459,14 @@ func (h *sentPacketHandler) getLossTimeAndSpace() (time.Time, protocol.Encryptio
 	return lossTime, encLevel
 }
 
+func (h *sentPacketHandler) getScaledPTO(includeMaxAckDelay bool) time.Duration {
+	pto := h.rttStats.PTO(includeMaxAckDelay) << h.ptoCount
+	if pto > maxPTODuration || pto <= 0 {
+		return maxPTODuration
+	}
+	return pto
+}
+
 // same logic as getLossTimeAndSpace, but for lastAckElicitingPacketTime instead of lossTime
 func (h *sentPacketHandler) getPTOTimeAndSpace() (pto time.Time, encLevel protocol.EncryptionLevel, ok bool) {
 	// We only send application data probe packets once the handshake is confirmed,
@@ -455,7 +475,7 @@ func (h *sentPacketHandler) getPTOTimeAndSpace() (pto time.Time, encLevel protoc
 		if h.peerCompletedAddressValidation {
 			return
 		}
-		t := time.Now().Add(h.rttStats.PTO(false) << h.ptoCount)
+		t := time.Now().Add(h.getScaledPTO(false))
 		if h.initialPackets != nil {
 			return t, protocol.EncryptionInitial, true
 		}
@@ -465,18 +485,18 @@ func (h *sentPacketHandler) getPTOTimeAndSpace() (pto time.Time, encLevel protoc
 	if h.initialPackets != nil {
 		encLevel = protocol.EncryptionInitial
 		if t := h.initialPackets.lastAckElicitingPacketTime; !t.IsZero() {
-			pto = t.Add(h.rttStats.PTO(false) << h.ptoCount)
+			pto = t.Add(h.getScaledPTO(false))
 		}
 	}
 	if h.handshakePackets != nil && !h.handshakePackets.lastAckElicitingPacketTime.IsZero() {
-		t := h.handshakePackets.lastAckElicitingPacketTime.Add(h.rttStats.PTO(false) << h.ptoCount)
+		t := h.handshakePackets.lastAckElicitingPacketTime.Add(h.getScaledPTO(false))
 		if pto.IsZero() || (!t.IsZero() && t.Before(pto)) {
 			pto = t
 			encLevel = protocol.EncryptionHandshake
 		}
 	}
 	if h.handshakeConfirmed && !h.appDataPackets.lastAckElicitingPacketTime.IsZero() {
-		t := h.appDataPackets.lastAckElicitingPacketTime.Add(h.rttStats.PTO(true) << h.ptoCount)
+		t := h.appDataPackets.lastAckElicitingPacketTime.Add(h.getScaledPTO(true))
 		if pto.IsZero() || (!t.IsZero() && t.Before(pto)) {
 			pto = t
 			encLevel = protocol.Encryption1RTT

+ 38 - 32
vendor/github.com/Psiphon-Labs/quic-go/internal/ackhandler/sent_packet_history.go

@@ -11,47 +11,53 @@ import (
 
 type sentPacketHistory struct {
 	rttStats              *utils.RTTStats
-	outstandingPacketList *list.List[Packet]
-	etcPacketList         *list.List[Packet]
-	packetMap             map[protocol.PacketNumber]*list.Element[Packet]
+	outstandingPacketList *list.List[*Packet]
+	etcPacketList         *list.List[*Packet]
+	packetMap             map[protocol.PacketNumber]*list.Element[*Packet]
 	highestSent           protocol.PacketNumber
 }
 
 func newSentPacketHistory(rttStats *utils.RTTStats) *sentPacketHistory {
 	return &sentPacketHistory{
 		rttStats:              rttStats,
-		outstandingPacketList: list.New[Packet](),
-		etcPacketList:         list.New[Packet](),
-		packetMap:             make(map[protocol.PacketNumber]*list.Element[Packet]),
+		outstandingPacketList: list.New[*Packet](),
+		etcPacketList:         list.New[*Packet](),
+		packetMap:             make(map[protocol.PacketNumber]*list.Element[*Packet]),
 		highestSent:           protocol.InvalidPacketNumber,
 	}
 }
 
-func (h *sentPacketHistory) SentPacket(p *Packet, isAckEliciting bool) {
-	if p.PacketNumber <= h.highestSent {
+func (h *sentPacketHistory) SentNonAckElicitingPacket(pn protocol.PacketNumber, encLevel protocol.EncryptionLevel, t time.Time) {
+	h.registerSentPacket(pn, encLevel, t)
+}
+
+func (h *sentPacketHistory) SentAckElicitingPacket(p *Packet) {
+	h.registerSentPacket(p.PacketNumber, p.EncryptionLevel, p.SendTime)
+
+	var el *list.Element[*Packet]
+	if p.outstanding() {
+		el = h.outstandingPacketList.PushBack(p)
+	} else {
+		el = h.etcPacketList.PushBack(p)
+	}
+	h.packetMap[p.PacketNumber] = el
+}
+
+func (h *sentPacketHistory) registerSentPacket(pn protocol.PacketNumber, encLevel protocol.EncryptionLevel, t time.Time) {
+	if pn <= h.highestSent {
 		panic("non-sequential packet number use")
 	}
 	// Skipped packet numbers.
-	for pn := h.highestSent + 1; pn < p.PacketNumber; pn++ {
-		el := h.etcPacketList.PushBack(Packet{
-			PacketNumber:    pn,
-			EncryptionLevel: p.EncryptionLevel,
-			SendTime:        p.SendTime,
+	for p := h.highestSent + 1; p < pn; p++ {
+		el := h.etcPacketList.PushBack(&Packet{
+			PacketNumber:    p,
+			EncryptionLevel: encLevel,
+			SendTime:        t,
 			skippedPacket:   true,
 		})
-		h.packetMap[pn] = el
-	}
-	h.highestSent = p.PacketNumber
-
-	if isAckEliciting {
-		var el *list.Element[Packet]
-		if p.outstanding() {
-			el = h.outstandingPacketList.PushBack(*p)
-		} else {
-			el = h.etcPacketList.PushBack(*p)
-		}
-		h.packetMap[p.PacketNumber] = el
+		h.packetMap[p] = el
 	}
+	h.highestSent = pn
 }
 
 // Iterate iterates through all packets.
@@ -59,7 +65,7 @@ func (h *sentPacketHistory) Iterate(cb func(*Packet) (cont bool, err error)) err
 	cont := true
 	outstandingEl := h.outstandingPacketList.Front()
 	etcEl := h.etcPacketList.Front()
-	var el *list.Element[Packet]
+	var el *list.Element[*Packet]
 	// whichever has the next packet number is returned first
 	for cont {
 		if outstandingEl == nil || (etcEl != nil && etcEl.Value.PacketNumber < outstandingEl.Value.PacketNumber) {
@@ -76,7 +82,7 @@ func (h *sentPacketHistory) Iterate(cb func(*Packet) (cont bool, err error)) err
 			etcEl = etcEl.Next()
 		}
 		var err error
-		cont, err = cb(&el.Value)
+		cont, err = cb(el.Value)
 		if err != nil {
 			return err
 		}
@@ -90,7 +96,7 @@ func (h *sentPacketHistory) FirstOutstanding() *Packet {
 	if el == nil {
 		return nil
 	}
-	return &el.Value
+	return el.Value
 }
 
 func (h *sentPacketHistory) Len() int {
@@ -114,7 +120,7 @@ func (h *sentPacketHistory) HasOutstandingPackets() bool {
 
 func (h *sentPacketHistory) DeleteOldPackets(now time.Time) {
 	maxAge := 3 * h.rttStats.PTO(false)
-	var nextEl *list.Element[Packet]
+	var nextEl *list.Element[*Packet]
 	// we don't iterate outstandingPacketList, as we should not delete outstanding packets.
 	// being outstanding for more than 3*PTO should only happen in the case of drastic RTT changes.
 	for el := h.etcPacketList.Front(); el != nil; el = nextEl {
@@ -145,10 +151,10 @@ func (h *sentPacketHistory) DeclareLost(p *Packet) *Packet {
 		}
 	}
 	if el == nil {
-		el = h.etcPacketList.PushFront(*p)
+		el = h.etcPacketList.PushFront(p)
 	} else {
-		el = h.etcPacketList.InsertAfter(*p, el)
+		el = h.etcPacketList.InsertAfter(p, el)
 	}
 	h.packetMap[p.PacketNumber] = el
-	return &el.Value
+	return el.Value
 }

+ 1 - 1
vendor/github.com/Psiphon-Labs/quic-go/internal/flowcontrol/base_flow_controller.go

@@ -47,7 +47,7 @@ func (c *baseFlowController) AddBytesSent(n protocol.ByteCount) {
 	c.bytesSent += n
 }
 
-// UpdateSendWindow is be called after receiving a MAX_{STREAM_}DATA frame.
+// UpdateSendWindow is called after receiving a MAX_{STREAM_}DATA frame.
 func (c *baseFlowController) UpdateSendWindow(offset protocol.ByteCount) {
 	if offset > c.sendWindow {
 		c.sendWindow = offset

+ 5 - 6
vendor/github.com/Psiphon-Labs/quic-go/internal/handshake/crypto_setup.go

@@ -365,7 +365,7 @@ func (h *cryptoSetup) onError(alert uint8, message string) {
 	if alert == 0 {
 		err = &qerr.TransportError{ErrorCode: qerr.InternalError, ErrorMessage: message}
 	} else {
-		err = qerr.NewCryptoError(alert, message)
+		err = qerr.NewLocalCryptoError(alert, message)
 	}
 	h.runner.OnError(err)
 }
@@ -457,11 +457,10 @@ func (h *cryptoSetup) handleTransportParameters(data []byte) {
 
 // must be called after receiving the transport parameters
 func (h *cryptoSetup) marshalDataForSessionState() []byte {
-	buf := &bytes.Buffer{}
-	quicvarint.Write(buf, clientSessionStateRevision)
-	quicvarint.Write(buf, uint64(h.rttStats.SmoothedRTT().Microseconds()))
-	h.peerParams.MarshalForSessionTicket(buf)
-	return buf.Bytes()
+	b := make([]byte, 0, 256)
+	b = quicvarint.Append(b, clientSessionStateRevision)
+	b = quicvarint.Append(b, uint64(h.rttStats.SmoothedRTT().Microseconds()))
+	return h.peerParams.MarshalForSessionTicket(b)
 }
 
 func (h *cryptoSetup) handleDataFromSessionState(data []byte) {

+ 1 - 1
vendor/github.com/Psiphon-Labs/quic-go/internal/handshake/initial_aead.go

@@ -62,7 +62,7 @@ func NewInitialAEAD(connID protocol.ConnectionID, pers protocol.Perspective, v p
 }
 
 func computeSecrets(connID protocol.ConnectionID, v protocol.VersionNumber) (clientSecret, serverSecret []byte) {
-	initialSecret := hkdf.Extract(crypto.SHA256.New, connID, getSalt(v))
+	initialSecret := hkdf.Extract(crypto.SHA256.New, connID.Bytes(), getSalt(v))
 	clientSecret = hkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "client in", crypto.SHA256.Size())
 	serverSecret = hkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "server in", crypto.SHA256.Size())
 	return

+ 4 - 5
vendor/github.com/Psiphon-Labs/quic-go/internal/handshake/session_ticket.go

@@ -18,11 +18,10 @@ type sessionTicket struct {
 }
 
 func (t *sessionTicket) Marshal() []byte {
-	b := &bytes.Buffer{}
-	quicvarint.Write(b, sessionTicketRevision)
-	quicvarint.Write(b, uint64(t.RTT.Microseconds()))
-	t.Parameters.MarshalForSessionTicket(b)
-	return b.Bytes()
+	b := make([]byte, 0, 256)
+	b = quicvarint.Append(b, sessionTicketRevision)
+	b = quicvarint.Append(b, uint64(t.RTT.Microseconds()))
+	return t.Parameters.MarshalForSessionTicket(b)
 }
 
 func (t *sessionTicket) Unmarshal(b []byte) error {

+ 4 - 4
vendor/github.com/Psiphon-Labs/quic-go/internal/handshake/token_generator.go

@@ -65,8 +65,8 @@ func (g *TokenGenerator) NewRetryToken(
 	data, err := asn1.Marshal(token{
 		IsRetryToken:             true,
 		RemoteAddr:               encodeRemoteAddr(raddr),
-		OriginalDestConnectionID: origDestConnID,
-		RetrySrcConnectionID:     retrySrcConnID,
+		OriginalDestConnectionID: origDestConnID.Bytes(),
+		RetrySrcConnectionID:     retrySrcConnID.Bytes(),
 		Timestamp:                time.Now().UnixNano(),
 	})
 	if err != nil {
@@ -112,8 +112,8 @@ func (g *TokenGenerator) DecodeToken(encrypted []byte) (*Token, error) {
 		encodedRemoteAddr: t.RemoteAddr,
 	}
 	if t.IsRetryToken {
-		token.OriginalDestConnectionID = protocol.ConnectionID(t.OriginalDestConnectionID)
-		token.RetrySrcConnectionID = protocol.ConnectionID(t.RetrySrcConnectionID)
+		token.OriginalDestConnectionID = protocol.ParseConnectionID(t.OriginalDestConnectionID)
+		token.RetrySrcConnectionID = protocol.ParseConnectionID(t.RetrySrcConnectionID)
 	}
 	return token, nil
 }

+ 17 - 0
vendor/github.com/Psiphon-Labs/quic-go/internal/logutils/frame.go

@@ -11,6 +11,10 @@ import (
 // Furthermore, it removes the data slices from CRYPTO and STREAM frames.
 func ConvertFrame(frame wire.Frame) logging.Frame {
 	switch f := frame.(type) {
+	case *wire.AckFrame:
+		// We use a pool for ACK frames.
+		// Implementations of the tracer interface may hold on to frames, so we need to make a copy here.
+		return ConvertAckFrame(f)
 	case *wire.CryptoFrame:
 		return &logging.CryptoFrame{
 			Offset: f.Offset,
@@ -31,3 +35,16 @@ func ConvertFrame(frame wire.Frame) logging.Frame {
 		return logging.Frame(frame)
 	}
 }
+
+func ConvertAckFrame(f *wire.AckFrame) *logging.AckFrame {
+	ranges := make([]wire.AckRange, 0, len(f.AckRanges))
+	ranges = append(ranges, f.AckRanges...)
+	ack := &logging.AckFrame{
+		AckRanges: ranges,
+		DelayTime: f.DelayTime,
+		ECNCE:     f.ECNCE,
+		ECT0:      f.ECT0,
+		ECT1:      f.ECT1,
+	}
+	return ack
+}

+ 60 - 25
vendor/github.com/Psiphon-Labs/quic-go/internal/protocol/connection_id.go

@@ -1,24 +1,60 @@
 package protocol
 
 import (
-	"bytes"
 	"crypto/rand"
+	"errors"
 	"fmt"
 	"io"
 )
 
-// A ConnectionID in QUIC
-type ConnectionID []byte
+var ErrInvalidConnectionIDLen = errors.New("invalid Connection ID length")
+
+// An ArbitraryLenConnectionID is a QUIC Connection ID able to represent Connection IDs according to RFC 8999.
+// Future QUIC versions might allow connection ID lengths up to 255 bytes, while QUIC v1
+// restricts the length to 20 bytes.
+type ArbitraryLenConnectionID []byte
+
+func (c ArbitraryLenConnectionID) Len() int {
+	return len(c)
+}
+
+func (c ArbitraryLenConnectionID) Bytes() []byte {
+	return c
+}
+
+func (c ArbitraryLenConnectionID) String() string {
+	if c.Len() == 0 {
+		return "(empty)"
+	}
+	return fmt.Sprintf("%x", c.Bytes())
+}
 
 const maxConnectionIDLen = 20
 
+// A ConnectionID in QUIC
+type ConnectionID struct {
+	b [20]byte
+	l uint8
+}
+
 // GenerateConnectionID generates a connection ID using cryptographic random
-func GenerateConnectionID(len int) (ConnectionID, error) {
-	b := make([]byte, len)
-	if _, err := rand.Read(b); err != nil {
-		return nil, err
+func GenerateConnectionID(l int) (ConnectionID, error) {
+	var c ConnectionID
+	c.l = uint8(l)
+	_, err := rand.Read(c.b[:l])
+	return c, err
+}
+
+// ParseConnectionID interprets b as a Connection ID.
+// It panics if b is longer than 20 bytes.
+func ParseConnectionID(b []byte) ConnectionID {
+	if len(b) > maxConnectionIDLen {
+		panic("invalid conn id length")
 	}
-	return ConnectionID(b), nil
+	var c ConnectionID
+	c.l = uint8(len(b))
+	copy(c.b[:c.l], b)
+	return c
 }
 
 // GenerateConnectionIDForInitial generates a connection ID for the Initial packet.
@@ -26,39 +62,38 @@ func GenerateConnectionID(len int) (ConnectionID, error) {
 func GenerateConnectionIDForInitial() (ConnectionID, error) {
 	r := make([]byte, 1)
 	if _, err := rand.Read(r); err != nil {
-		return nil, err
+		return ConnectionID{}, err
 	}
-	len := MinConnectionIDLenInitial + int(r[0])%(maxConnectionIDLen-MinConnectionIDLenInitial+1)
-	return GenerateConnectionID(len)
+	l := MinConnectionIDLenInitial + int(r[0])%(maxConnectionIDLen-MinConnectionIDLenInitial+1)
+	return GenerateConnectionID(l)
 }
 
 // ReadConnectionID reads a connection ID of length len from the given io.Reader.
 // It returns io.EOF if there are not enough bytes to read.
-func ReadConnectionID(r io.Reader, len int) (ConnectionID, error) {
-	if len == 0 {
-		return nil, nil
+func ReadConnectionID(r io.Reader, l int) (ConnectionID, error) {
+	var c ConnectionID
+	if l == 0 {
+		return c, nil
+	}
+	if l > maxConnectionIDLen {
+		return c, ErrInvalidConnectionIDLen
 	}
-	c := make(ConnectionID, len)
-	_, err := io.ReadFull(r, c)
+	c.l = uint8(l)
+	_, err := io.ReadFull(r, c.b[:l])
 	if err == io.ErrUnexpectedEOF {
-		return nil, io.EOF
+		return c, io.EOF
 	}
 	return c, err
 }
 
-// Equal says if two connection IDs are equal
-func (c ConnectionID) Equal(other ConnectionID) bool {
-	return bytes.Equal(c, other)
-}
-
 // Len returns the length of the connection ID in bytes
 func (c ConnectionID) Len() int {
-	return len(c)
+	return int(c.l)
 }
 
 // Bytes returns the byte representation
 func (c ConnectionID) Bytes() []byte {
-	return []byte(c)
+	return c.b[:c.l]
 }
 
 func (c ConnectionID) String() string {
@@ -72,7 +107,7 @@ type DefaultConnectionIDGenerator struct {
 	ConnLen int
 }
 
-func (d *DefaultConnectionIDGenerator) GenerateConnectionID() ([]byte, error) {
+func (d *DefaultConnectionIDGenerator) GenerateConnectionID() (ConnectionID, error) {
 	return GenerateConnectionID(d.ConnLen)
 }
 

+ 1 - 1
vendor/github.com/Psiphon-Labs/quic-go/internal/protocol/params.go

@@ -134,7 +134,7 @@ const MaxAckFrameSize ByteCount = 1000
 
 // MaxDatagramFrameSize is the maximum size of a DATAGRAM frame (RFC 9221).
 // The size is chosen such that a DATAGRAM frame fits into a QUIC packet.
-const MaxDatagramFrameSize ByteCount = 1220
+const MaxDatagramFrameSize ByteCount = 1200
 
 // DatagramRcvQueueLen is the length of the receive queue for DATAGRAM frames (RFC 9221)
 const DatagramRcvQueueLen = 128

+ 1 - 1
vendor/github.com/Psiphon-Labs/quic-go/internal/qerr/error_codes.go

@@ -81,7 +81,7 @@ func (e TransportErrorCode) String() string {
 		return "NO_VIABLE_PATH"
 	default:
 		if e.IsCryptoError() {
-			return fmt.Sprintf("CRYPTO_ERROR (%#x)", uint16(e))
+			return fmt.Sprintf("CRYPTO_ERROR %#x", uint16(e))
 		}
 		return fmt.Sprintf("unknown error code: %#x", uint16(e))
 	}

+ 12 - 5
vendor/github.com/Psiphon-Labs/quic-go/internal/qerr/errors.go

@@ -21,8 +21,8 @@ type TransportError struct {
 
 var _ error = &TransportError{}
 
-// NewCryptoError create a new TransportError instance for a crypto error
-func NewCryptoError(tlsAlert uint8, errorMessage string) *TransportError {
+// NewLocalCryptoError create a new TransportError instance for a crypto error
+func NewLocalCryptoError(tlsAlert uint8, errorMessage string) *TransportError {
 	return &TransportError{
 		ErrorCode:    0x100 + TransportErrorCode(tlsAlert),
 		ErrorMessage: errorMessage,
@@ -30,7 +30,7 @@ func NewCryptoError(tlsAlert uint8, errorMessage string) *TransportError {
 }
 
 func (e *TransportError) Error() string {
-	str := e.ErrorCode.String()
+	str := fmt.Sprintf("%s (%s)", e.ErrorCode.String(), getRole(e.Remote))
 	if e.FrameType != 0 {
 		str += fmt.Sprintf(" (frame type: %#x)", e.FrameType)
 	}
@@ -68,9 +68,9 @@ var _ error = &ApplicationError{}
 
 func (e *ApplicationError) Error() string {
 	if len(e.ErrorMessage) == 0 {
-		return fmt.Sprintf("Application error %#x", e.ErrorCode)
+		return fmt.Sprintf("Application error %#x (%s)", e.ErrorCode, getRole(e.Remote))
 	}
-	return fmt.Sprintf("Application error %#x: %s", e.ErrorCode, e.ErrorMessage)
+	return fmt.Sprintf("Application error %#x (%s): %s", e.ErrorCode, getRole(e.Remote), e.ErrorMessage)
 }
 
 type IdleTimeoutError struct{}
@@ -122,3 +122,10 @@ func (e *StatelessResetError) Is(target error) bool {
 
 func (e *StatelessResetError) Timeout() bool   { return false }
 func (e *StatelessResetError) Temporary() bool { return true }
+
+func getRole(remote bool) string {
+	if remote {
+		return "remote"
+	}
+	return "local"
+}

+ 1 - 1
vendor/github.com/Psiphon-Labs/quic-go/internal/qtls/go119.go

@@ -1,4 +1,4 @@
-//go:build go1.19
+//go:build go1.19 && !go1.20
 
 package qtls
 

+ 106 - 1
vendor/github.com/Psiphon-Labs/quic-go/internal/qtls/go120.go

@@ -2,4 +2,109 @@
 
 package qtls
 
-var _ int = "The version of quic-go you're using can't be built on Go 1.20 yet. For more details, please see https://github.com/lucas-clemente/quic-go/wiki/quic-go-and-Go-versions."
+// [Psiphon]
+// For now, use qtls-go1-19 with Go 1.20. Psiphon does not yet require the
+// updates in Go 1.20 crypto/tls, and the crypto/tls.Config in Go 1.20 is
+// compatible with qtls-go1-19.
+
+import (
+	"crypto"
+	"crypto/cipher"
+	"crypto/tls"
+	"net"
+	"unsafe"
+
+	"github.com/Psiphon-Labs/qtls-go1-19"
+)
+
+type (
+	// Alert is a TLS alert
+	Alert = qtls.Alert
+	// A Certificate is qtls.Certificate.
+	Certificate = qtls.Certificate
+	// CertificateRequestInfo contains information about a certificate request.
+	CertificateRequestInfo = qtls.CertificateRequestInfo
+	// A CipherSuiteTLS13 is a cipher suite for TLS 1.3
+	CipherSuiteTLS13 = qtls.CipherSuiteTLS13
+	// ClientHelloInfo contains information about a ClientHello.
+	ClientHelloInfo = qtls.ClientHelloInfo
+	// ClientSessionCache is a cache used for session resumption.
+	ClientSessionCache = qtls.ClientSessionCache
+	// ClientSessionState is a state needed for session resumption.
+	ClientSessionState = qtls.ClientSessionState
+	// A Config is a qtls.Config.
+	Config = qtls.Config
+	// A Conn is a qtls.Conn.
+	Conn = qtls.Conn
+	// ConnectionState contains information about the state of the connection.
+	ConnectionState = qtls.ConnectionStateWith0RTT
+	// EncryptionLevel is the encryption level of a message.
+	EncryptionLevel = qtls.EncryptionLevel
+	// Extension is a TLS extension
+	Extension = qtls.Extension
+	// ExtraConfig is the qtls.ExtraConfig
+	ExtraConfig = qtls.ExtraConfig
+	// RecordLayer is a qtls RecordLayer.
+	RecordLayer = qtls.RecordLayer
+)
+
+const (
+	// EncryptionHandshake is the Handshake encryption level
+	EncryptionHandshake = qtls.EncryptionHandshake
+	// Encryption0RTT is the 0-RTT encryption level
+	Encryption0RTT = qtls.Encryption0RTT
+	// EncryptionApplication is the application data encryption level
+	EncryptionApplication = qtls.EncryptionApplication
+)
+
+// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3
+func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD {
+	return qtls.AEADAESGCMTLS13(key, fixedNonce)
+}
+
+// Client returns a new TLS client side connection.
+func Client(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn {
+	return qtls.Client(conn, config, extraConfig)
+}
+
+// Server returns a new TLS server side connection.
+func Server(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn {
+	return qtls.Server(conn, config, extraConfig)
+}
+
+func GetConnectionState(conn *Conn) ConnectionState {
+	return conn.ConnectionStateWith0RTT()
+}
+
+// ToTLSConnectionState extracts the tls.ConnectionState
+func ToTLSConnectionState(cs ConnectionState) tls.ConnectionState {
+	return cs.ConnectionState
+}
+
+type cipherSuiteTLS13 struct {
+	ID     uint16
+	KeyLen int
+	AEAD   func(key, fixedNonce []byte) cipher.AEAD
+	Hash   crypto.Hash
+}
+
+//go:linkname cipherSuiteTLS13ByID github.com/Psiphon-Labs/qtls-go1-19.cipherSuiteTLS13ByID
+func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13
+
+// CipherSuiteTLS13ByID gets a TLS 1.3 cipher suite.
+func CipherSuiteTLS13ByID(id uint16) *CipherSuiteTLS13 {
+	val := cipherSuiteTLS13ByID(id)
+	cs := (*cipherSuiteTLS13)(unsafe.Pointer(val))
+	return &qtls.CipherSuiteTLS13{
+		ID:     cs.ID,
+		KeyLen: cs.KeyLen,
+		AEAD:   cs.AEAD,
+		Hash:   cs.Hash,
+	}
+}
+
+// [Psiphon]
+
+func ReadClientHelloRandom(data []byte) ([]byte, error) {
+	return qtls.ReadClientHelloRandom(data)
+}

+ 4 - 0
vendor/github.com/Psiphon-Labs/quic-go/internal/utils/byteorder.go

@@ -7,6 +7,10 @@ import (
 
 // A ByteOrder specifies how to convert byte sequences into 16-, 32-, or 64-bit unsigned integers.
 type ByteOrder interface {
+	Uint32([]byte) uint32
+	Uint24([]byte) uint32
+	Uint16([]byte) uint16
+
 	ReadUint32(io.ByteReader) (uint32, error)
 	ReadUint24(io.ByteReader) (uint32, error)
 	ReadUint16(io.ByteReader) (uint16, error)

+ 14 - 0
vendor/github.com/Psiphon-Labs/quic-go/internal/utils/byteorder_big_endian.go

@@ -2,6 +2,7 @@ package utils
 
 import (
 	"bytes"
+	"encoding/binary"
 	"io"
 )
 
@@ -73,6 +74,19 @@ func (bigEndian) ReadUint16(b io.ByteReader) (uint16, error) {
 	return uint16(b1) + uint16(b2)<<8, nil
 }
 
+func (bigEndian) Uint32(b []byte) uint32 {
+	return binary.BigEndian.Uint32(b)
+}
+
+func (bigEndian) Uint24(b []byte) uint32 {
+	_ = b[2] // bounds check hint to compiler; see golang.org/issue/14808
+	return uint32(b[2]) | uint32(b[1])<<8 | uint32(b[0])<<16
+}
+
+func (bigEndian) Uint16(b []byte) uint16 {
+	return binary.BigEndian.Uint16(b)
+}
+
 // WriteUint32 writes a uint32
 func (bigEndian) WriteUint32(b *bytes.Buffer, i uint32) {
 	b.Write([]byte{uint8(i >> 24), uint8(i >> 16), uint8(i >> 8), uint8(i)})

+ 16 - 16
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/ack_frame.go

@@ -29,7 +29,7 @@ func parseAckFrame(r *bytes.Reader, ackDelayExponent uint8, _ protocol.VersionNu
 	}
 	ecn := typeByte&0x1 > 0
 
-	frame := &AckFrame{}
+	frame := GetAckFrame()
 
 	la, err := quicvarint.Read(r)
 	if err != nil {
@@ -106,41 +106,41 @@ func parseAckFrame(r *bytes.Reader, ackDelayExponent uint8, _ protocol.VersionNu
 	return frame, nil
 }
 
-// Write writes an ACK frame.
-func (f *AckFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error {
+// Append appends an ACK frame.
+func (f *AckFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
 	hasECN := f.ECT0 > 0 || f.ECT1 > 0 || f.ECNCE > 0
 	if hasECN {
-		b.WriteByte(0x3)
+		b = append(b, 0b11)
 	} else {
-		b.WriteByte(0x2)
+		b = append(b, 0b10)
 	}
-	quicvarint.Write(b, uint64(f.LargestAcked()))
-	quicvarint.Write(b, encodeAckDelay(f.DelayTime))
+	b = quicvarint.Append(b, uint64(f.LargestAcked()))
+	b = quicvarint.Append(b, encodeAckDelay(f.DelayTime))
 
 	numRanges := f.numEncodableAckRanges()
-	quicvarint.Write(b, uint64(numRanges-1))
+	b = quicvarint.Append(b, uint64(numRanges-1))
 
 	// write the first range
 	_, firstRange := f.encodeAckRange(0)
-	quicvarint.Write(b, firstRange)
+	b = quicvarint.Append(b, firstRange)
 
 	// write all the other range
 	for i := 1; i < numRanges; i++ {
 		gap, len := f.encodeAckRange(i)
-		quicvarint.Write(b, gap)
-		quicvarint.Write(b, len)
+		b = quicvarint.Append(b, gap)
+		b = quicvarint.Append(b, len)
 	}
 
 	if hasECN {
-		quicvarint.Write(b, f.ECT0)
-		quicvarint.Write(b, f.ECT1)
-		quicvarint.Write(b, f.ECNCE)
+		b = quicvarint.Append(b, f.ECT0)
+		b = quicvarint.Append(b, f.ECT1)
+		b = quicvarint.Append(b, f.ECNCE)
 	}
-	return nil
+	return b, nil
 }
 
 // Length of a written frame
-func (f *AckFrame) Length(version protocol.VersionNumber) protocol.ByteCount {
+func (f *AckFrame) Length(_ protocol.VersionNumber) protocol.ByteCount {
 	largestAcked := f.AckRanges[0].Largest
 	numRanges := f.numEncodableAckRanges()
 

+ 24 - 0
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/ack_frame_pool.go

@@ -0,0 +1,24 @@
+package wire
+
+import "sync"
+
+var ackFramePool = sync.Pool{New: func() any {
+	return &AckFrame{}
+}}
+
+func GetAckFrame() *AckFrame {
+	f := ackFramePool.Get().(*AckFrame)
+	f.AckRanges = f.AckRanges[:0]
+	f.ECNCE = 0
+	f.ECT0 = 0
+	f.ECT1 = 0
+	f.DelayTime = 0
+	return f
+}
+
+func PutAckFrame(f *AckFrame) {
+	if cap(f.AckRanges) > 4 {
+		return
+	}
+	ackFramePool.Put(f)
+}

+ 8 - 8
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/connection_close_frame.go

@@ -66,18 +66,18 @@ func (f *ConnectionCloseFrame) Length(protocol.VersionNumber) protocol.ByteCount
 	return length
 }
 
-func (f *ConnectionCloseFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
+func (f *ConnectionCloseFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
 	if f.IsApplicationError {
-		b.WriteByte(0x1d)
+		b = append(b, 0x1d)
 	} else {
-		b.WriteByte(0x1c)
+		b = append(b, 0x1c)
 	}
 
-	quicvarint.Write(b, f.ErrorCode)
+	b = quicvarint.Append(b, f.ErrorCode)
 	if !f.IsApplicationError {
-		quicvarint.Write(b, f.FrameType)
+		b = quicvarint.Append(b, f.FrameType)
 	}
-	quicvarint.Write(b, uint64(len(f.ReasonPhrase)))
-	b.WriteString(f.ReasonPhrase)
-	return nil
+	b = quicvarint.Append(b, uint64(len(f.ReasonPhrase)))
+	b = append(b, []byte(f.ReasonPhrase)...)
+	return b, nil
 }

+ 6 - 6
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/crypto_frame.go

@@ -42,12 +42,12 @@ func parseCryptoFrame(r *bytes.Reader, _ protocol.VersionNumber) (*CryptoFrame,
 	return frame, nil
 }
 
-func (f *CryptoFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error {
-	b.WriteByte(0x6)
-	quicvarint.Write(b, uint64(f.Offset))
-	quicvarint.Write(b, uint64(len(f.Data)))
-	b.Write(f.Data)
-	return nil
+func (f *CryptoFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+	b = append(b, 0x6)
+	b = quicvarint.Append(b, uint64(f.Offset))
+	b = quicvarint.Append(b, uint64(len(f.Data)))
+	b = append(b, f.Data...)
+	return b, nil
 }
 
 // Length of a written frame

+ 4 - 5
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/data_blocked_frame.go

@@ -25,11 +25,10 @@ func parseDataBlockedFrame(r *bytes.Reader, _ protocol.VersionNumber) (*DataBloc
 	}, nil
 }
 
-func (f *DataBlockedFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
-	typeByte := uint8(0x14)
-	b.WriteByte(typeByte)
-	quicvarint.Write(b, uint64(f.MaximumData))
-	return nil
+func (f *DataBlockedFrame) Append(b []byte, version protocol.VersionNumber) ([]byte, error) {
+	b = append(b, 0x14)
+	b = quicvarint.Append(b, uint64(f.MaximumData))
+	return b, nil
 }
 
 // Length of a written frame

+ 6 - 6
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/datagram_frame.go

@@ -44,17 +44,17 @@ func parseDatagramFrame(r *bytes.Reader, _ protocol.VersionNumber) (*DatagramFra
 	return f, nil
 }
 
-func (f *DatagramFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error {
+func (f *DatagramFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
 	typeByte := uint8(0x30)
 	if f.DataLenPresent {
-		typeByte ^= 0x1
+		typeByte ^= 0b1
 	}
-	b.WriteByte(typeByte)
+	b = append(b, typeByte)
 	if f.DataLenPresent {
-		quicvarint.Write(b, uint64(len(f.Data)))
+		b = quicvarint.Append(b, uint64(len(f.Data)))
 	}
-	b.Write(f.Data)
-	return nil
+	b = append(b, f.Data...)
+	return b, nil
 }
 
 // MaxDataLen returns the maximum data length

+ 14 - 2
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/frame_parser.go

@@ -11,6 +11,8 @@ import (
 )
 
 type frameParser struct {
+	r bytes.Reader // cached bytes.Reader, so we don't have to repeatedly allocate them
+
 	ackDelayExponent uint8
 
 	supportsDatagrams bool
@@ -21,6 +23,7 @@ type frameParser struct {
 // NewFrameParser creates a new frame parser.
 func NewFrameParser(supportsDatagrams bool, v protocol.VersionNumber) FrameParser {
 	return &frameParser{
+		r:                 *bytes.NewReader(nil),
 		supportsDatagrams: supportsDatagrams,
 		version:           v,
 	}
@@ -28,9 +31,18 @@ func NewFrameParser(supportsDatagrams bool, v protocol.VersionNumber) FrameParse
 
 // ParseNext parses the next frame.
 // It skips PADDING frames.
-func (p *frameParser) ParseNext(r *bytes.Reader, encLevel protocol.EncryptionLevel) (Frame, error) {
+func (p *frameParser) ParseNext(data []byte, encLevel protocol.EncryptionLevel) (int, Frame, error) {
+	startLen := len(data)
+	p.r.Reset(data)
+	frame, err := p.parseNext(&p.r, encLevel)
+	n := startLen - p.r.Len()
+	p.r.Reset(nil)
+	return n, frame, err
+}
+
+func (p *frameParser) parseNext(r *bytes.Reader, encLevel protocol.EncryptionLevel) (Frame, error) {
 	for r.Len() != 0 {
-		typeByte, _ := r.ReadByte()
+		typeByte, _ := p.r.ReadByte()
 		if typeByte == 0x0 { // PADDING frame
 			continue
 		}

+ 2 - 3
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/handshake_done_frame.go

@@ -17,9 +17,8 @@ func parseHandshakeDoneFrame(r *bytes.Reader, _ protocol.VersionNumber) (*Handsh
 	return &HandshakeDoneFrame{}, nil
 }
 
-func (f *HandshakeDoneFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error {
-	b.WriteByte(0x1e)
-	return nil
+func (f *HandshakeDoneFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+	return append(b, 0x1e), nil
 }
 
 // Length of a written frame

+ 56 - 6
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/header.go

@@ -17,22 +17,63 @@ import (
 // That means that the connection ID must not be used after the packet buffer is released.
 func ParseConnectionID(data []byte, shortHeaderConnIDLen int) (protocol.ConnectionID, error) {
 	if len(data) == 0 {
-		return nil, io.EOF
+		return protocol.ConnectionID{}, io.EOF
 	}
 	if !IsLongHeaderPacket(data[0]) {
 		if len(data) < shortHeaderConnIDLen+1 {
-			return nil, io.EOF
+			return protocol.ConnectionID{}, io.EOF
 		}
-		return protocol.ConnectionID(data[1 : 1+shortHeaderConnIDLen]), nil
+		return protocol.ParseConnectionID(data[1 : 1+shortHeaderConnIDLen]), nil
 	}
 	if len(data) < 6 {
-		return nil, io.EOF
+		return protocol.ConnectionID{}, io.EOF
 	}
 	destConnIDLen := int(data[5])
+	if destConnIDLen > protocol.MaxConnIDLen {
+		return protocol.ConnectionID{}, protocol.ErrInvalidConnectionIDLen
+	}
 	if len(data) < 6+destConnIDLen {
-		return nil, io.EOF
+		return protocol.ConnectionID{}, io.EOF
+	}
+	return protocol.ParseConnectionID(data[6 : 6+destConnIDLen]), nil
+}
+
+// ParseArbitraryLenConnectionIDs parses the most general form of a Long Header packet,
+// using only the version-independent packet format as described in Section 5.1 of RFC 8999:
+// https://datatracker.ietf.org/doc/html/rfc8999#section-5.1.
+// This function should only be called on Long Header packets for which we don't support the version.
+func ParseArbitraryLenConnectionIDs(data []byte) (bytesParsed int, dest, src protocol.ArbitraryLenConnectionID, _ error) {
+	r := bytes.NewReader(data)
+	remaining := r.Len()
+	src, dest, err := parseArbitraryLenConnectionIDs(r)
+	return remaining - r.Len(), src, dest, err
+}
+
+func parseArbitraryLenConnectionIDs(r *bytes.Reader) (dest, src protocol.ArbitraryLenConnectionID, _ error) {
+	r.Seek(5, io.SeekStart) // skip first byte and version field
+	destConnIDLen, err := r.ReadByte()
+	if err != nil {
+		return nil, nil, err
+	}
+	destConnID := make(protocol.ArbitraryLenConnectionID, destConnIDLen)
+	if _, err := io.ReadFull(r, destConnID); err != nil {
+		if err == io.ErrUnexpectedEOF {
+			err = io.EOF
+		}
+		return nil, nil, err
+	}
+	srcConnIDLen, err := r.ReadByte()
+	if err != nil {
+		return nil, nil, err
+	}
+	srcConnID := make(protocol.ArbitraryLenConnectionID, srcConnIDLen)
+	if _, err := io.ReadFull(r, srcConnID); err != nil {
+		if err == io.ErrUnexpectedEOF {
+			err = io.EOF
+		}
+		return nil, nil, err
 	}
-	return protocol.ConnectionID(data[6 : 6+destConnIDLen]), nil
+	return destConnID, srcConnID, nil
 }
 
 // IsLongHeaderPacket says if this is a Long Header packet
@@ -40,6 +81,15 @@ func IsLongHeaderPacket(firstByte byte) bool {
 	return firstByte&0x80 > 0
 }
 
+// ParseVersion parses the QUIC version.
+// It should only be called for Long Header packets (Short Header packets don't contain a version number).
+func ParseVersion(data []byte) (protocol.VersionNumber, error) {
+	if len(data) < 5 {
+		return 0, io.EOF
+	}
+	return protocol.VersionNumber(binary.BigEndian.Uint32(data[1:5])), nil
+}
+
 // IsVersionNegotiationPacket says if this is a version negotiation packet
 func IsVersionNegotiationPacket(b []byte) bool {
 	if len(b) < 5 {

+ 2 - 4
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/interface.go

@@ -1,19 +1,17 @@
 package wire
 
 import (
-	"bytes"
-
 	"github.com/Psiphon-Labs/quic-go/internal/protocol"
 )
 
 // A Frame in QUIC
 type Frame interface {
-	Write(b *bytes.Buffer, version protocol.VersionNumber) error
+	Append(b []byte, version protocol.VersionNumber) ([]byte, error)
 	Length(version protocol.VersionNumber) protocol.ByteCount
 }
 
 // A FrameParser parses QUIC frames, one by one.
 type FrameParser interface {
-	ParseNext(*bytes.Reader, protocol.EncryptionLevel) (Frame, error)
+	ParseNext([]byte, protocol.EncryptionLevel) (int, Frame, error)
 	SetAckDelayExponent(uint8)
 }

+ 5 - 5
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/max_data_frame.go

@@ -28,13 +28,13 @@ func parseMaxDataFrame(r *bytes.Reader, _ protocol.VersionNumber) (*MaxDataFrame
 }
 
 // Write writes a MAX_STREAM_DATA frame
-func (f *MaxDataFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
-	b.WriteByte(0x10)
-	quicvarint.Write(b, uint64(f.MaximumData))
-	return nil
+func (f *MaxDataFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+	b = append(b, 0x10)
+	b = quicvarint.Append(b, uint64(f.MaximumData))
+	return b, nil
 }
 
 // Length of a written frame
-func (f *MaxDataFrame) Length(version protocol.VersionNumber) protocol.ByteCount {
+func (f *MaxDataFrame) Length(_ protocol.VersionNumber) protocol.ByteCount {
 	return 1 + quicvarint.Len(uint64(f.MaximumData))
 }

+ 5 - 5
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/max_stream_data_frame.go

@@ -33,11 +33,11 @@ func parseMaxStreamDataFrame(r *bytes.Reader, _ protocol.VersionNumber) (*MaxStr
 	}, nil
 }
 
-func (f *MaxStreamDataFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
-	b.WriteByte(0x11)
-	quicvarint.Write(b, uint64(f.StreamID))
-	quicvarint.Write(b, uint64(f.MaximumStreamData))
-	return nil
+func (f *MaxStreamDataFrame) Append(b []byte, version protocol.VersionNumber) ([]byte, error) {
+	b = append(b, 0x11)
+	b = quicvarint.Append(b, uint64(f.StreamID))
+	b = quicvarint.Append(b, uint64(f.MaximumStreamData))
+	return b, nil
 }
 
 // Length of a written frame

+ 5 - 5
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/max_streams_frame.go

@@ -38,15 +38,15 @@ func parseMaxStreamsFrame(r *bytes.Reader, _ protocol.VersionNumber) (*MaxStream
 	return f, nil
 }
 
-func (f *MaxStreamsFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error {
+func (f *MaxStreamsFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
 	switch f.Type {
 	case protocol.StreamTypeBidi:
-		b.WriteByte(0x12)
+		b = append(b, 0x12)
 	case protocol.StreamTypeUni:
-		b.WriteByte(0x13)
+		b = append(b, 0x13)
 	}
-	quicvarint.Write(b, uint64(f.MaxStreamNum))
-	return nil
+	b = quicvarint.Append(b, uint64(f.MaxStreamNum))
+	return b, nil
 }
 
 // Length of a written frame

+ 9 - 12
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/new_connection_id_frame.go

@@ -38,9 +38,6 @@ func parseNewConnectionIDFrame(r *bytes.Reader, _ protocol.VersionNumber) (*NewC
 	if err != nil {
 		return nil, err
 	}
-	if connIDLen > protocol.MaxConnIDLen {
-		return nil, fmt.Errorf("invalid connection ID length: %d", connIDLen)
-	}
 	connID, err := protocol.ReadConnectionID(r, int(connIDLen))
 	if err != nil {
 		return nil, err
@@ -60,18 +57,18 @@ func parseNewConnectionIDFrame(r *bytes.Reader, _ protocol.VersionNumber) (*NewC
 	return frame, nil
 }
 
-func (f *NewConnectionIDFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error {
-	b.WriteByte(0x18)
-	quicvarint.Write(b, f.SequenceNumber)
-	quicvarint.Write(b, f.RetirePriorTo)
+func (f *NewConnectionIDFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+	b = append(b, 0x18)
+	b = quicvarint.Append(b, f.SequenceNumber)
+	b = quicvarint.Append(b, f.RetirePriorTo)
 	connIDLen := f.ConnectionID.Len()
 	if connIDLen > protocol.MaxConnIDLen {
-		return fmt.Errorf("invalid connection ID length: %d", connIDLen)
+		return nil, fmt.Errorf("invalid connection ID length: %d", connIDLen)
 	}
-	b.WriteByte(uint8(connIDLen))
-	b.Write(f.ConnectionID.Bytes())
-	b.Write(f.StatelessResetToken[:])
-	return nil
+	b = append(b, uint8(connIDLen))
+	b = append(b, f.ConnectionID.Bytes()...)
+	b = append(b, f.StatelessResetToken[:]...)
+	return b, nil
 }
 
 // Length of a written frame

+ 5 - 5
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/new_token_frame.go

@@ -35,11 +35,11 @@ func parseNewTokenFrame(r *bytes.Reader, _ protocol.VersionNumber) (*NewTokenFra
 	return &NewTokenFrame{Token: token}, nil
 }
 
-func (f *NewTokenFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error {
-	b.WriteByte(0x7)
-	quicvarint.Write(b, uint64(len(f.Token)))
-	b.Write(f.Token)
-	return nil
+func (f *NewTokenFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+	b = append(b, 0x7)
+	b = quicvarint.Append(b, uint64(len(f.Token)))
+	b = append(b, f.Token...)
+	return b, nil
 }
 
 // Length of a written frame

+ 4 - 4
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/path_challenge_frame.go

@@ -26,10 +26,10 @@ func parsePathChallengeFrame(r *bytes.Reader, _ protocol.VersionNumber) (*PathCh
 	return frame, nil
 }
 
-func (f *PathChallengeFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error {
-	b.WriteByte(0x1a)
-	b.Write(f.Data[:])
-	return nil
+func (f *PathChallengeFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+	b = append(b, 0x1a)
+	b = append(b, f.Data[:]...)
+	return b, nil
 }
 
 // Length of a written frame

+ 4 - 4
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/path_response_frame.go

@@ -26,10 +26,10 @@ func parsePathResponseFrame(r *bytes.Reader, _ protocol.VersionNumber) (*PathRes
 	return frame, nil
 }
 
-func (f *PathResponseFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error {
-	b.WriteByte(0x1b)
-	b.Write(f.Data[:])
-	return nil
+func (f *PathResponseFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+	b = append(b, 0x1b)
+	b = append(b, f.Data[:]...)
+	return b, nil
 }
 
 // Length of a written frame

+ 3 - 4
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/ping_frame.go

@@ -16,12 +16,11 @@ func parsePingFrame(r *bytes.Reader, _ protocol.VersionNumber) (*PingFrame, erro
 	return &PingFrame{}, nil
 }
 
-func (f *PingFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
-	b.WriteByte(0x1)
-	return nil
+func (f *PingFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+	return append(b, 0x1), nil
 }
 
 // Length of a written frame
-func (f *PingFrame) Length(version protocol.VersionNumber) protocol.ByteCount {
+func (f *PingFrame) Length(_ protocol.VersionNumber) protocol.ByteCount {
 	return 1
 }

+ 6 - 6
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/reset_stream_frame.go

@@ -44,12 +44,12 @@ func parseResetStreamFrame(r *bytes.Reader, _ protocol.VersionNumber) (*ResetStr
 	}, nil
 }
 
-func (f *ResetStreamFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error {
-	b.WriteByte(0x4)
-	quicvarint.Write(b, uint64(f.StreamID))
-	quicvarint.Write(b, uint64(f.ErrorCode))
-	quicvarint.Write(b, uint64(f.FinalSize))
-	return nil
+func (f *ResetStreamFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+	b = append(b, 0x4)
+	b = quicvarint.Append(b, uint64(f.StreamID))
+	b = quicvarint.Append(b, uint64(f.ErrorCode))
+	b = quicvarint.Append(b, uint64(f.FinalSize))
+	return b, nil
 }
 
 // Length of a written frame

+ 4 - 4
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/retire_connection_id_frame.go

@@ -24,10 +24,10 @@ func parseRetireConnectionIDFrame(r *bytes.Reader, _ protocol.VersionNumber) (*R
 	return &RetireConnectionIDFrame{SequenceNumber: seq}, nil
 }
 
-func (f *RetireConnectionIDFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error {
-	b.WriteByte(0x19)
-	quicvarint.Write(b, f.SequenceNumber)
-	return nil
+func (f *RetireConnectionIDFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+	b = append(b, 0x19)
+	b = quicvarint.Append(b, f.SequenceNumber)
+	return b, nil
 }
 
 // Length of a written frame

+ 55 - 0
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/short_header.go

@@ -0,0 +1,55 @@
+package wire
+
+import (
+	"errors"
+	"fmt"
+	"io"
+
+	"github.com/Psiphon-Labs/quic-go/internal/protocol"
+	"github.com/Psiphon-Labs/quic-go/internal/utils"
+)
+
+func ParseShortHeader(data []byte, connIDLen int) (length int, _ protocol.PacketNumber, _ protocol.PacketNumberLen, _ protocol.KeyPhaseBit, _ error) {
+	if len(data) == 0 {
+		return 0, 0, 0, 0, io.EOF
+	}
+	if data[0]&0x80 > 0 {
+		return 0, 0, 0, 0, errors.New("not a short header packet")
+	}
+	if data[0]&0x40 == 0 {
+		return 0, 0, 0, 0, errors.New("not a QUIC packet")
+	}
+	pnLen := protocol.PacketNumberLen(data[0]&0b11) + 1
+	if len(data) < 1+int(pnLen)+connIDLen {
+		return 0, 0, 0, 0, io.EOF
+	}
+
+	pos := 1 + connIDLen
+	var pn protocol.PacketNumber
+	switch pnLen {
+	case protocol.PacketNumberLen1:
+		pn = protocol.PacketNumber(data[pos])
+	case protocol.PacketNumberLen2:
+		pn = protocol.PacketNumber(utils.BigEndian.Uint16(data[pos : pos+2]))
+	case protocol.PacketNumberLen3:
+		pn = protocol.PacketNumber(utils.BigEndian.Uint24(data[pos : pos+3]))
+	case protocol.PacketNumberLen4:
+		pn = protocol.PacketNumber(utils.BigEndian.Uint32(data[pos : pos+4]))
+	default:
+		return 0, 0, 0, 0, fmt.Errorf("invalid packet number length: %d", pnLen)
+	}
+	kp := protocol.KeyPhaseZero
+	if data[0]&0b100 > 0 {
+		kp = protocol.KeyPhaseOne
+	}
+
+	var err error
+	if data[0]&0x18 != 0 {
+		err = ErrInvalidReservedBits
+	}
+	return 1 + connIDLen + int(pnLen), pn, pnLen, kp, err
+}
+
+func LogShortHeader(logger utils.Logger, dest protocol.ConnectionID, pn protocol.PacketNumber, pnLen protocol.PacketNumberLen, kp protocol.KeyPhaseBit) {
+	logger.Debugf("\tShort Header{DestConnectionID: %s, PacketNumber: %d, PacketNumberLen: %d, KeyPhase: %s}", dest, pn, pnLen, kp)
+}

+ 5 - 5
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/stop_sending_frame.go

@@ -40,9 +40,9 @@ func (f *StopSendingFrame) Length(_ protocol.VersionNumber) protocol.ByteCount {
 	return 1 + quicvarint.Len(uint64(f.StreamID)) + quicvarint.Len(uint64(f.ErrorCode))
 }
 
-func (f *StopSendingFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error {
-	b.WriteByte(0x5)
-	quicvarint.Write(b, uint64(f.StreamID))
-	quicvarint.Write(b, uint64(f.ErrorCode))
-	return nil
+func (f *StopSendingFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+	b = append(b, 0x5)
+	b = quicvarint.Append(b, uint64(f.StreamID))
+	b = quicvarint.Append(b, uint64(f.ErrorCode))
+	return b, nil
 }

+ 5 - 5
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/stream_data_blocked_frame.go

@@ -33,11 +33,11 @@ func parseStreamDataBlockedFrame(r *bytes.Reader, _ protocol.VersionNumber) (*St
 	}, nil
 }
 
-func (f *StreamDataBlockedFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
-	b.WriteByte(0x15)
-	quicvarint.Write(b, uint64(f.StreamID))
-	quicvarint.Write(b, uint64(f.MaximumStreamData))
-	return nil
+func (f *StreamDataBlockedFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+	b = append(b, 0x15)
+	b = quicvarint.Append(b, uint64(f.StreamID))
+	b = quicvarint.Append(b, uint64(f.MaximumStreamData))
+	return b, nil
 }
 
 // Length of a written frame

+ 14 - 14
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/stream_frame.go

@@ -26,9 +26,9 @@ func parseStreamFrame(r *bytes.Reader, _ protocol.VersionNumber) (*StreamFrame,
 		return nil, err
 	}
 
-	hasOffset := typeByte&0x4 > 0
-	fin := typeByte&0x1 > 0
-	hasDataLen := typeByte&0x2 > 0
+	hasOffset := typeByte&0b100 > 0
+	fin := typeByte&0b1 > 0
+	hasDataLen := typeByte&0b10 > 0
 
 	streamID, err := quicvarint.Read(r)
 	if err != nil {
@@ -84,32 +84,32 @@ func parseStreamFrame(r *bytes.Reader, _ protocol.VersionNumber) (*StreamFrame,
 }
 
 // Write writes a STREAM frame
-func (f *StreamFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
+func (f *StreamFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
 	if len(f.Data) == 0 && !f.Fin {
-		return errors.New("StreamFrame: attempting to write empty frame without FIN")
+		return nil, errors.New("StreamFrame: attempting to write empty frame without FIN")
 	}
 
 	typeByte := byte(0x8)
 	if f.Fin {
-		typeByte ^= 0x1
+		typeByte ^= 0b1
 	}
 	hasOffset := f.Offset != 0
 	if f.DataLenPresent {
-		typeByte ^= 0x2
+		typeByte ^= 0b10
 	}
 	if hasOffset {
-		typeByte ^= 0x4
+		typeByte ^= 0b100
 	}
-	b.WriteByte(typeByte)
-	quicvarint.Write(b, uint64(f.StreamID))
+	b = append(b, typeByte)
+	b = quicvarint.Append(b, uint64(f.StreamID))
 	if hasOffset {
-		quicvarint.Write(b, uint64(f.Offset))
+		b = quicvarint.Append(b, uint64(f.Offset))
 	}
 	if f.DataLenPresent {
-		quicvarint.Write(b, uint64(f.DataLen()))
+		b = quicvarint.Append(b, uint64(f.DataLen()))
 	}
-	b.Write(f.Data)
-	return nil
+	b = append(b, f.Data...)
+	return b, nil
 }
 
 // Length returns the total length of the STREAM frame

+ 5 - 5
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/streams_blocked_frame.go

@@ -38,15 +38,15 @@ func parseStreamsBlockedFrame(r *bytes.Reader, _ protocol.VersionNumber) (*Strea
 	return f, nil
 }
 
-func (f *StreamsBlockedFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error {
+func (f *StreamsBlockedFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
 	switch f.Type {
 	case protocol.StreamTypeBidi:
-		b.WriteByte(0x16)
+		b = append(b, 0x16)
 	case protocol.StreamTypeUni:
-		b.WriteByte(0x17)
+		b = append(b, 0x17)
 	}
-	quicvarint.Write(b, uint64(f.StreamLimit))
-	return nil
+	b = quicvarint.Append(b, uint64(f.StreamLimit))
+	return b, nil
 }
 
 // Length of a written frame

+ 87 - 79
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/transport_parameters.go

@@ -2,6 +2,7 @@ package wire
 
 import (
 	"bytes"
+	"encoding/binary"
 	"errors"
 	"fmt"
 	"io"
@@ -321,88 +322,92 @@ func (p *TransportParameters) Marshal(pers protocol.Perspective, clientHelloPRNG
 		return p.marshalClientRandomized(clientHelloPRNG)
 	}
 
-	b := &bytes.Buffer{}
+	// Typical Transport Parameters consume around 110 bytes, depending on the exact values,
+	// especially the lengths of the Connection IDs.
+	// Allocate 256 bytes, so we won't have to grow the slice in any case.
+	b := make([]byte, 0, 256)
 
 	// add a greased value
-	quicvarint.Write(b, uint64(27+31*rand.Intn(100)))
+	b = quicvarint.Append(b, uint64(27+31*rand.Intn(100)))
 	length := rand.Intn(16)
-	randomData := make([]byte, length)
-	rand.Read(randomData)
-	quicvarint.Write(b, uint64(length))
-	b.Write(randomData)
+	b = quicvarint.Append(b, uint64(length))
+	b = b[:len(b)+length]
+	rand.Read(b[len(b)-length:])
 
 	// initial_max_stream_data_bidi_local
-	p.marshalVarintParam(b, initialMaxStreamDataBidiLocalParameterID, uint64(p.InitialMaxStreamDataBidiLocal))
+	b = p.marshalVarintParam(b, initialMaxStreamDataBidiLocalParameterID, uint64(p.InitialMaxStreamDataBidiLocal))
 	// initial_max_stream_data_bidi_remote
-	p.marshalVarintParam(b, initialMaxStreamDataBidiRemoteParameterID, uint64(p.InitialMaxStreamDataBidiRemote))
+	b = p.marshalVarintParam(b, initialMaxStreamDataBidiRemoteParameterID, uint64(p.InitialMaxStreamDataBidiRemote))
 	// initial_max_stream_data_uni
-	p.marshalVarintParam(b, initialMaxStreamDataUniParameterID, uint64(p.InitialMaxStreamDataUni))
+	b = p.marshalVarintParam(b, initialMaxStreamDataUniParameterID, uint64(p.InitialMaxStreamDataUni))
 	// initial_max_data
-	p.marshalVarintParam(b, initialMaxDataParameterID, uint64(p.InitialMaxData))
+	b = p.marshalVarintParam(b, initialMaxDataParameterID, uint64(p.InitialMaxData))
 	// initial_max_bidi_streams
-	p.marshalVarintParam(b, initialMaxStreamsBidiParameterID, uint64(p.MaxBidiStreamNum))
+	b = p.marshalVarintParam(b, initialMaxStreamsBidiParameterID, uint64(p.MaxBidiStreamNum))
 	// initial_max_uni_streams
-	p.marshalVarintParam(b, initialMaxStreamsUniParameterID, uint64(p.MaxUniStreamNum))
+	b = p.marshalVarintParam(b, initialMaxStreamsUniParameterID, uint64(p.MaxUniStreamNum))
 	// idle_timeout
-	p.marshalVarintParam(b, maxIdleTimeoutParameterID, uint64(p.MaxIdleTimeout/time.Millisecond))
+	b = p.marshalVarintParam(b, maxIdleTimeoutParameterID, uint64(p.MaxIdleTimeout/time.Millisecond))
 	// max_packet_size
-	p.marshalVarintParam(b, maxUDPPayloadSizeParameterID, uint64(protocol.MaxPacketBufferSize))
+	b = p.marshalVarintParam(b, maxUDPPayloadSizeParameterID, uint64(protocol.MaxPacketBufferSize))
 	// max_ack_delay
 	// Only send it if is different from the default value.
 	if p.MaxAckDelay != protocol.DefaultMaxAckDelay {
-		p.marshalVarintParam(b, maxAckDelayParameterID, uint64(p.MaxAckDelay/time.Millisecond))
+		b = p.marshalVarintParam(b, maxAckDelayParameterID, uint64(p.MaxAckDelay/time.Millisecond))
 	}
 	// ack_delay_exponent
 	// Only send it if is different from the default value.
 	if p.AckDelayExponent != protocol.DefaultAckDelayExponent {
-		p.marshalVarintParam(b, ackDelayExponentParameterID, uint64(p.AckDelayExponent))
+		b = p.marshalVarintParam(b, ackDelayExponentParameterID, uint64(p.AckDelayExponent))
 	}
 	// disable_active_migration
 	if p.DisableActiveMigration {
-		quicvarint.Write(b, uint64(disableActiveMigrationParameterID))
-		quicvarint.Write(b, 0)
+		b = quicvarint.Append(b, uint64(disableActiveMigrationParameterID))
+		b = quicvarint.Append(b, 0)
 	}
 	if pers == protocol.PerspectiveServer {
 		// stateless_reset_token
 		if p.StatelessResetToken != nil {
-			quicvarint.Write(b, uint64(statelessResetTokenParameterID))
-			quicvarint.Write(b, 16)
-			b.Write(p.StatelessResetToken[:])
+			b = quicvarint.Append(b, uint64(statelessResetTokenParameterID))
+			b = quicvarint.Append(b, 16)
+			b = append(b, p.StatelessResetToken[:]...)
 		}
 		// original_destination_connection_id
-		quicvarint.Write(b, uint64(originalDestinationConnectionIDParameterID))
-		quicvarint.Write(b, uint64(p.OriginalDestinationConnectionID.Len()))
-		b.Write(p.OriginalDestinationConnectionID.Bytes())
+		b = quicvarint.Append(b, uint64(originalDestinationConnectionIDParameterID))
+		b = quicvarint.Append(b, uint64(p.OriginalDestinationConnectionID.Len()))
+		b = append(b, p.OriginalDestinationConnectionID.Bytes()...)
 		// preferred_address
 		if p.PreferredAddress != nil {
-			quicvarint.Write(b, uint64(preferredAddressParameterID))
-			quicvarint.Write(b, 4+2+16+2+1+uint64(p.PreferredAddress.ConnectionID.Len())+16)
+			b = quicvarint.Append(b, uint64(preferredAddressParameterID))
+			b = quicvarint.Append(b, 4+2+16+2+1+uint64(p.PreferredAddress.ConnectionID.Len())+16)
 			ipv4 := p.PreferredAddress.IPv4
-			b.Write(ipv4[len(ipv4)-4:])
-			utils.BigEndian.WriteUint16(b, p.PreferredAddress.IPv4Port)
-			b.Write(p.PreferredAddress.IPv6)
-			utils.BigEndian.WriteUint16(b, p.PreferredAddress.IPv6Port)
-			b.WriteByte(uint8(p.PreferredAddress.ConnectionID.Len()))
-			b.Write(p.PreferredAddress.ConnectionID.Bytes())
-			b.Write(p.PreferredAddress.StatelessResetToken[:])
+			b = append(b, ipv4[len(ipv4)-4:]...)
+			b = append(b, []byte{0, 0}...)
+			binary.BigEndian.PutUint16(b[len(b)-2:], p.PreferredAddress.IPv4Port)
+			b = append(b, p.PreferredAddress.IPv6...)
+			b = append(b, []byte{0, 0}...)
+			binary.BigEndian.PutUint16(b[len(b)-2:], p.PreferredAddress.IPv6Port)
+			b = append(b, uint8(p.PreferredAddress.ConnectionID.Len()))
+			b = append(b, p.PreferredAddress.ConnectionID.Bytes()...)
+			b = append(b, p.PreferredAddress.StatelessResetToken[:]...)
 		}
 	}
 	// active_connection_id_limit
-	p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit)
+	b = p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit)
 	// initial_source_connection_id
-	quicvarint.Write(b, uint64(initialSourceConnectionIDParameterID))
-	quicvarint.Write(b, uint64(p.InitialSourceConnectionID.Len()))
-	b.Write(p.InitialSourceConnectionID.Bytes())
+	b = quicvarint.Append(b, uint64(initialSourceConnectionIDParameterID))
+	b = quicvarint.Append(b, uint64(p.InitialSourceConnectionID.Len()))
+	b = append(b, p.InitialSourceConnectionID.Bytes()...)
 	// retry_source_connection_id
 	if pers == protocol.PerspectiveServer && p.RetrySourceConnectionID != nil {
-		quicvarint.Write(b, uint64(retrySourceConnectionIDParameterID))
-		quicvarint.Write(b, uint64(p.RetrySourceConnectionID.Len()))
-		b.Write(p.RetrySourceConnectionID.Bytes())
+		b = quicvarint.Append(b, uint64(retrySourceConnectionIDParameterID))
+		b = quicvarint.Append(b, uint64(p.RetrySourceConnectionID.Len()))
+		b = append(b, p.RetrySourceConnectionID.Bytes()...)
 	}
 	if p.MaxDatagramFrameSize != protocol.InvalidByteCount {
-		p.marshalVarintParam(b, maxDatagramFrameSizeParameterID, uint64(p.MaxDatagramFrameSize))
+		b = p.marshalVarintParam(b, maxDatagramFrameSizeParameterID, uint64(p.MaxDatagramFrameSize))
 	}
-	return b.Bytes()
+	return b
 }
 
 // [Psiphon]
@@ -410,65 +415,67 @@ func (p *TransportParameters) Marshal(pers protocol.Perspective, clientHelloPRNG
 // Marshal is retained as-is to ease future merging.
 func (p *TransportParameters) marshalClientRandomized(clientHelloPRNG *prng.PRNG) []byte {
 
-	b := &bytes.Buffer{}
+	// Typical Transport Parameters consume around 110 bytes, depending on the exact values,
+	// especially the lengths of the Connection IDs.
+	// Allocate 256 bytes, so we won't have to grow the slice in any case.
+	b := make([]byte, 0, 256)
 
 	marshallers := []func(){
 
 		func() {
 			// add a greased value
-			quicvarint.Write(b, uint64(27+31*rand.Intn(100)))
+			b = quicvarint.Append(b, uint64(27+31*rand.Intn(100)))
 			length := rand.Intn(16)
-			randomData := make([]byte, length)
-			rand.Read(randomData)
-			quicvarint.Write(b, uint64(length))
-			b.Write(randomData)
+			b = quicvarint.Append(b, uint64(length))
+			b = b[:len(b)+length]
+			rand.Read(b[len(b)-length:])
 		},
 
 		func() {
 			// initial_max_stream_data_bidi_local
-			p.marshalVarintParam(b, initialMaxStreamDataBidiLocalParameterID, uint64(p.InitialMaxStreamDataBidiLocal))
+			b = p.marshalVarintParam(b, initialMaxStreamDataBidiLocalParameterID, uint64(p.InitialMaxStreamDataBidiLocal))
 		},
 
 		func() {
 			// initial_max_stream_data_bidi_remote
-			p.marshalVarintParam(b, initialMaxStreamDataBidiRemoteParameterID, uint64(p.InitialMaxStreamDataBidiRemote))
+			b = p.marshalVarintParam(b, initialMaxStreamDataBidiRemoteParameterID, uint64(p.InitialMaxStreamDataBidiRemote))
 		},
 
 		func() {
 			// initial_max_stream_data_uni
-			p.marshalVarintParam(b, initialMaxStreamDataUniParameterID, uint64(p.InitialMaxStreamDataUni))
+			b = p.marshalVarintParam(b, initialMaxStreamDataUniParameterID, uint64(p.InitialMaxStreamDataUni))
 		},
 
 		func() {
 			// initial_max_data
-			p.marshalVarintParam(b, initialMaxDataParameterID, uint64(p.InitialMaxData))
+			b = p.marshalVarintParam(b, initialMaxDataParameterID, uint64(p.InitialMaxData))
 		},
 
 		func() {
 			// initial_max_bidi_streams
-			p.marshalVarintParam(b, initialMaxStreamsBidiParameterID, uint64(p.MaxBidiStreamNum))
+			b = p.marshalVarintParam(b, initialMaxStreamsBidiParameterID, uint64(p.MaxBidiStreamNum))
 		},
 
 		func() {
 			// initial_max_uni_streams
-			p.marshalVarintParam(b, initialMaxStreamsUniParameterID, uint64(p.MaxUniStreamNum))
+			b = p.marshalVarintParam(b, initialMaxStreamsUniParameterID, uint64(p.MaxUniStreamNum))
 		},
 
 		func() {
 			// idle_timeout
-			p.marshalVarintParam(b, maxIdleTimeoutParameterID, uint64(p.MaxIdleTimeout/time.Millisecond))
+			b = p.marshalVarintParam(b, maxIdleTimeoutParameterID, uint64(p.MaxIdleTimeout/time.Millisecond))
 		},
 
 		func() {
 			// max_packet_size
-			p.marshalVarintParam(b, maxUDPPayloadSizeParameterID, uint64(protocol.MaxPacketBufferSize))
+			b = p.marshalVarintParam(b, maxUDPPayloadSizeParameterID, uint64(protocol.MaxPacketBufferSize))
 		},
 
 		func() {
 			// max_ack_delay
 			// Only send it if is different from the default value.
 			if p.MaxAckDelay != protocol.DefaultMaxAckDelay {
-				p.marshalVarintParam(b, maxAckDelayParameterID, uint64(p.MaxAckDelay/time.Millisecond))
+				b = p.marshalVarintParam(b, maxAckDelayParameterID, uint64(p.MaxAckDelay/time.Millisecond))
 			}
 		},
 
@@ -476,33 +483,33 @@ func (p *TransportParameters) marshalClientRandomized(clientHelloPRNG *prng.PRNG
 			// ack_delay_exponent
 			// Only send it if is different from the default value.
 			if p.AckDelayExponent != protocol.DefaultAckDelayExponent {
-				p.marshalVarintParam(b, ackDelayExponentParameterID, uint64(p.AckDelayExponent))
+				b = p.marshalVarintParam(b, ackDelayExponentParameterID, uint64(p.AckDelayExponent))
 			}
 		},
 
 		func() {
 			// disable_active_migration
 			if p.DisableActiveMigration {
-				quicvarint.Write(b, uint64(disableActiveMigrationParameterID))
-				quicvarint.Write(b, 0)
+				b = quicvarint.Append(b, uint64(disableActiveMigrationParameterID))
+				b = quicvarint.Append(b, 0)
 			}
 		},
 
 		func() {
 			// active_connection_id_limit
-			p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit)
+			b = p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit)
 		},
 
 		func() {
 			// initial_source_connection_id
-			quicvarint.Write(b, uint64(initialSourceConnectionIDParameterID))
-			quicvarint.Write(b, uint64(p.InitialSourceConnectionID.Len()))
-			b.Write(p.InitialSourceConnectionID.Bytes())
+			b = quicvarint.Append(b, uint64(initialSourceConnectionIDParameterID))
+			b = quicvarint.Append(b, uint64(p.InitialSourceConnectionID.Len()))
+			b = append(b, p.InitialSourceConnectionID.Bytes()...)
 		},
 
 		func() {
 			if p.MaxDatagramFrameSize != protocol.InvalidByteCount {
-				p.marshalVarintParam(b, maxDatagramFrameSizeParameterID, uint64(p.MaxDatagramFrameSize))
+				b = p.marshalVarintParam(b, maxDatagramFrameSizeParameterID, uint64(p.MaxDatagramFrameSize))
 			}
 		},
 	}
@@ -512,12 +519,13 @@ func (p *TransportParameters) marshalClientRandomized(clientHelloPRNG *prng.PRNG
 		marshallers[j]()
 	}
 
-	return b.Bytes()
+	return b
 }
-func (p *TransportParameters) marshalVarintParam(b *bytes.Buffer, id transportParameterID, val uint64) {
-	quicvarint.Write(b, uint64(id))
-	quicvarint.Write(b, uint64(quicvarint.Len(val)))
-	quicvarint.Write(b, val)
+
+func (p *TransportParameters) marshalVarintParam(b []byte, id transportParameterID, val uint64) []byte {
+	b = quicvarint.Append(b, uint64(id))
+	b = quicvarint.Append(b, uint64(quicvarint.Len(val)))
+	return quicvarint.Append(b, val)
 }
 
 // MarshalForSessionTicket marshals the transport parameters we save in the session ticket.
@@ -528,23 +536,23 @@ func (p *TransportParameters) marshalVarintParam(b *bytes.Buffer, id transportPa
 // if the transport parameters changed.
 // Since the session ticket is encrypted, the serialization format is defined by the server.
 // For convenience, we use the same format that we also use for sending the transport parameters.
-func (p *TransportParameters) MarshalForSessionTicket(b *bytes.Buffer) {
-	quicvarint.Write(b, transportParameterMarshalingVersion)
+func (p *TransportParameters) MarshalForSessionTicket(b []byte) []byte {
+	b = quicvarint.Append(b, transportParameterMarshalingVersion)
 
 	// initial_max_stream_data_bidi_local
-	p.marshalVarintParam(b, initialMaxStreamDataBidiLocalParameterID, uint64(p.InitialMaxStreamDataBidiLocal))
+	b = p.marshalVarintParam(b, initialMaxStreamDataBidiLocalParameterID, uint64(p.InitialMaxStreamDataBidiLocal))
 	// initial_max_stream_data_bidi_remote
-	p.marshalVarintParam(b, initialMaxStreamDataBidiRemoteParameterID, uint64(p.InitialMaxStreamDataBidiRemote))
+	b = p.marshalVarintParam(b, initialMaxStreamDataBidiRemoteParameterID, uint64(p.InitialMaxStreamDataBidiRemote))
 	// initial_max_stream_data_uni
-	p.marshalVarintParam(b, initialMaxStreamDataUniParameterID, uint64(p.InitialMaxStreamDataUni))
+	b = p.marshalVarintParam(b, initialMaxStreamDataUniParameterID, uint64(p.InitialMaxStreamDataUni))
 	// initial_max_data
-	p.marshalVarintParam(b, initialMaxDataParameterID, uint64(p.InitialMaxData))
+	b = p.marshalVarintParam(b, initialMaxDataParameterID, uint64(p.InitialMaxData))
 	// initial_max_bidi_streams
-	p.marshalVarintParam(b, initialMaxStreamsBidiParameterID, uint64(p.MaxBidiStreamNum))
+	b = p.marshalVarintParam(b, initialMaxStreamsBidiParameterID, uint64(p.MaxBidiStreamNum))
 	// initial_max_uni_streams
-	p.marshalVarintParam(b, initialMaxStreamsUniParameterID, uint64(p.MaxUniStreamNum))
+	b = p.marshalVarintParam(b, initialMaxStreamsUniParameterID, uint64(p.MaxUniStreamNum))
 	// active_connection_id_limit
-	p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit)
+	return p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit)
 }
 
 // UnmarshalFromSessionTicket unmarshals transport parameters from a session ticket.

+ 17 - 18
vendor/github.com/Psiphon-Labs/quic-go/internal/wire/version_negotiation.go

@@ -3,6 +3,7 @@ package wire
 import (
 	"bytes"
 	"crypto/rand"
+	"encoding/binary"
 	"errors"
 
 	"github.com/Psiphon-Labs/quic-go/internal/protocol"
@@ -10,32 +11,30 @@ import (
 )
 
 // ParseVersionNegotiationPacket parses a Version Negotiation packet.
-func ParseVersionNegotiationPacket(b *bytes.Reader) (*Header, []protocol.VersionNumber, error) {
-	hdr, err := parseHeader(b, 0)
+func ParseVersionNegotiationPacket(b []byte) (dest, src protocol.ArbitraryLenConnectionID, _ []protocol.VersionNumber, _ error) {
+	n, dest, src, err := ParseArbitraryLenConnectionIDs(b)
 	if err != nil {
-		return nil, nil, err
+		return nil, nil, nil, err
 	}
-	if b.Len() == 0 {
+	b = b[n:]
+	if len(b) == 0 {
 		//nolint:stylecheck
-		return nil, nil, errors.New("Version Negotiation packet has empty version list")
+		return nil, nil, nil, errors.New("Version Negotiation packet has empty version list")
 	}
-	if b.Len()%4 != 0 {
+	if len(b)%4 != 0 {
 		//nolint:stylecheck
-		return nil, nil, errors.New("Version Negotiation packet has a version list with an invalid length")
+		return nil, nil, nil, errors.New("Version Negotiation packet has a version list with an invalid length")
 	}
-	versions := make([]protocol.VersionNumber, b.Len()/4)
-	for i := 0; b.Len() > 0; i++ {
-		v, err := utils.BigEndian.ReadUint32(b)
-		if err != nil {
-			return nil, nil, err
-		}
-		versions[i] = protocol.VersionNumber(v)
+	versions := make([]protocol.VersionNumber, len(b)/4)
+	for i := 0; len(b) > 0; i++ {
+		versions[i] = protocol.VersionNumber(binary.BigEndian.Uint32(b[:4]))
+		b = b[4:]
 	}
-	return hdr, versions, nil
+	return dest, src, versions, nil
 }
 
 // ComposeVersionNegotiation composes a Version Negotiation
-func ComposeVersionNegotiation(destConnID, srcConnID protocol.ConnectionID, versions []protocol.VersionNumber) []byte {
+func ComposeVersionNegotiation(destConnID, srcConnID protocol.ArbitraryLenConnectionID, versions []protocol.VersionNumber) []byte {
 	greasedVersions := protocol.GetGreasedVersions(versions)
 	expectedLen := 1 /* type byte */ + 4 /* version field */ + 1 /* dest connection ID length field */ + destConnID.Len() + 1 /* src connection ID length field */ + srcConnID.Len() + len(greasedVersions)*4
 	buf := bytes.NewBuffer(make([]byte, 0, expectedLen))
@@ -44,9 +43,9 @@ func ComposeVersionNegotiation(destConnID, srcConnID protocol.ConnectionID, vers
 	buf.WriteByte(r[0] | 0x80)
 	utils.BigEndian.WriteUint32(buf, 0) // version 0
 	buf.WriteByte(uint8(destConnID.Len()))
-	buf.Write(destConnID)
+	buf.Write(destConnID.Bytes())
 	buf.WriteByte(uint8(srcConnID.Len()))
-	buf.Write(srcConnID)
+	buf.Write(srcConnID.Bytes())
 	for _, v := range greasedVersions {
 		utils.BigEndian.WriteUint32(buf, uint32(v))
 	}

+ 16 - 4
vendor/github.com/Psiphon-Labs/quic-go/logging/interface.go

@@ -19,6 +19,8 @@ type (
 	ByteCount = protocol.ByteCount
 	// A ConnectionID is a QUIC Connection ID.
 	ConnectionID = protocol.ConnectionID
+	// An ArbitraryLenConnectionID is a QUIC Connection ID that can be up to 255 bytes long.
+	ArbitraryLenConnectionID = protocol.ArbitraryLenConnectionID
 	// The EncryptionLevel is the encryption level of a packet.
 	EncryptionLevel = protocol.EncryptionLevel
 	// The KeyPhase is the key phase of the 1-RTT keys.
@@ -42,7 +44,7 @@ type (
 
 	// The Header is the QUIC packet header, before removing header protection.
 	Header = wire.Header
-	// The ExtendedHeader is the QUIC packet header, after removing header protection.
+	// The ExtendedHeader is the QUIC Long Header packet header, after removing header protection.
 	ExtendedHeader = wire.ExtendedHeader
 	// The TransportParameters are QUIC transport parameters.
 	TransportParameters = wire.TransportParameters
@@ -90,6 +92,14 @@ const (
 	StreamTypeBidi = protocol.StreamTypeBidi
 )
 
+// The ShortHeader is the QUIC Short Header packet header, after removing header protection.
+type ShortHeader struct {
+	DestConnectionID ConnectionID
+	PacketNumber     PacketNumber
+	PacketNumberLen  protocol.PacketNumberLen
+	KeyPhase         KeyPhaseBit
+}
+
 // A Tracer traces events.
 type Tracer interface {
 	// TracerForConnection requests a new tracer for a connection.
@@ -99,6 +109,7 @@ type Tracer interface {
 	TracerForConnection(ctx context.Context, p Perspective, odcid ConnectionID) ConnectionTracer
 
 	SentPacket(net.Addr, *Header, ByteCount, []Frame)
+	SentVersionNegotiationPacket(_ net.Addr, dest, src ArbitraryLenConnectionID, _ []VersionNumber)
 	DroppedPacket(net.Addr, PacketType, ByteCount, PacketDropReason)
 }
 
@@ -111,10 +122,11 @@ type ConnectionTracer interface {
 	ReceivedTransportParameters(*TransportParameters)
 	RestoredTransportParameters(parameters *TransportParameters) // for 0-RTT
 	SentPacket(hdr *ExtendedHeader, size ByteCount, ack *AckFrame, frames []Frame)
-	ReceivedVersionNegotiationPacket(*Header, []VersionNumber)
+	ReceivedVersionNegotiationPacket(dest, src ArbitraryLenConnectionID, _ []VersionNumber)
 	ReceivedRetry(*Header)
-	ReceivedPacket(hdr *ExtendedHeader, size ByteCount, frames []Frame)
-	BufferedPacket(PacketType)
+	ReceivedLongHeaderPacket(hdr *ExtendedHeader, size ByteCount, frames []Frame)
+	ReceivedShortHeaderPacket(hdr *ShortHeader, size ByteCount, frames []Frame)
+	BufferedPacket(PacketType, ByteCount)
 	DroppedPacket(PacketType, ByteCount, PacketDropReason)
 	UpdatedMetrics(rttStats *RTTStats, cwnd, bytesInFlight ByteCount, packetsInFlight int)
 	AcknowledgedPacket(EncryptionLevel, PacketNumber)

+ 2 - 2
vendor/github.com/Psiphon-Labs/quic-go/logging/mockgen.go

@@ -1,4 +1,4 @@
 package logging
 
-//go:generate sh -c "mockgen -package logging -self_package github.com/Psiphon-Labs/quic-go/logging -destination mock_connection_tracer_test.go github.com/Psiphon-Labs/quic-go/logging ConnectionTracer"
-//go:generate sh -c "mockgen -package logging -self_package github.com/Psiphon-Labs/quic-go/logging -destination mock_tracer_test.go github.com/Psiphon-Labs/quic-go/logging Tracer"
+//go:generate sh -c "go run github.com/golang/mock/mockgen -package logging -self_package github.com/Psiphon-Labs/quic-go/logging -destination mock_connection_tracer_test.go github.com/Psiphon-Labs/quic-go/logging ConnectionTracer"
+//go:generate sh -c "go run github.com/golang/mock/mockgen -package logging -self_package github.com/Psiphon-Labs/quic-go/logging -destination mock_tracer_test.go github.com/Psiphon-Labs/quic-go/logging Tracer"

+ 18 - 6
vendor/github.com/Psiphon-Labs/quic-go/logging/multiplex.go

@@ -39,6 +39,12 @@ func (m *tracerMultiplexer) SentPacket(remote net.Addr, hdr *Header, size ByteCo
 	}
 }
 
+func (m *tracerMultiplexer) SentVersionNegotiationPacket(remote net.Addr, dest, src ArbitraryLenConnectionID, versions []VersionNumber) {
+	for _, t := range m.tracers {
+		t.SentVersionNegotiationPacket(remote, dest, src, versions)
+	}
+}
+
 func (m *tracerMultiplexer) DroppedPacket(remote net.Addr, typ PacketType, size ByteCount, reason PacketDropReason) {
 	for _, t := range m.tracers {
 		t.DroppedPacket(remote, typ, size, reason)
@@ -104,9 +110,9 @@ func (m *connTracerMultiplexer) SentPacket(hdr *ExtendedHeader, size ByteCount,
 	}
 }
 
-func (m *connTracerMultiplexer) ReceivedVersionNegotiationPacket(hdr *Header, versions []VersionNumber) {
+func (m *connTracerMultiplexer) ReceivedVersionNegotiationPacket(dest, src ArbitraryLenConnectionID, versions []VersionNumber) {
 	for _, t := range m.tracers {
-		t.ReceivedVersionNegotiationPacket(hdr, versions)
+		t.ReceivedVersionNegotiationPacket(dest, src, versions)
 	}
 }
 
@@ -116,15 +122,21 @@ func (m *connTracerMultiplexer) ReceivedRetry(hdr *Header) {
 	}
 }
 
-func (m *connTracerMultiplexer) ReceivedPacket(hdr *ExtendedHeader, size ByteCount, frames []Frame) {
+func (m *connTracerMultiplexer) ReceivedLongHeaderPacket(hdr *ExtendedHeader, size ByteCount, frames []Frame) {
+	for _, t := range m.tracers {
+		t.ReceivedLongHeaderPacket(hdr, size, frames)
+	}
+}
+
+func (m *connTracerMultiplexer) ReceivedShortHeaderPacket(hdr *ShortHeader, size ByteCount, frames []Frame) {
 	for _, t := range m.tracers {
-		t.ReceivedPacket(hdr, size, frames)
+		t.ReceivedShortHeaderPacket(hdr, size, frames)
 	}
 }
 
-func (m *connTracerMultiplexer) BufferedPacket(typ PacketType) {
+func (m *connTracerMultiplexer) BufferedPacket(typ PacketType, size ByteCount) {
 	for _, t := range m.tracers {
-		t.BufferedPacket(typ)
+		t.BufferedPacket(typ, size)
 	}
 }
 

+ 20 - 11
vendor/github.com/Psiphon-Labs/quic-go/logging/null_tracer.go

@@ -10,31 +10,40 @@ import (
 // It is useful for embedding.
 type NullTracer struct{}
 
+var _ Tracer = &NullTracer{}
+
 func (n NullTracer) TracerForConnection(context.Context, Perspective, ConnectionID) ConnectionTracer {
 	return NullConnectionTracer{}
 }
-func (n NullTracer) SentPacket(net.Addr, *Header, ByteCount, []Frame)                {}
+func (n NullTracer) SentPacket(net.Addr, *Header, ByteCount, []Frame) {}
+func (n NullTracer) SentVersionNegotiationPacket(_ net.Addr, dest, src ArbitraryLenConnectionID, _ []VersionNumber) {
+}
 func (n NullTracer) DroppedPacket(net.Addr, PacketType, ByteCount, PacketDropReason) {}
 
 // The NullConnectionTracer is a ConnectionTracer that does nothing.
 // It is useful for embedding.
 type NullConnectionTracer struct{}
 
+var _ ConnectionTracer = &NullConnectionTracer{}
+
 func (n NullConnectionTracer) StartedConnection(local, remote net.Addr, srcConnID, destConnID ConnectionID) {
 }
 
 func (n NullConnectionTracer) NegotiatedVersion(chosen VersionNumber, clientVersions, serverVersions []VersionNumber) {
 }
-func (n NullConnectionTracer) ClosedConnection(err error)                                         {}
-func (n NullConnectionTracer) SentTransportParameters(*TransportParameters)                       {}
-func (n NullConnectionTracer) ReceivedTransportParameters(*TransportParameters)                   {}
-func (n NullConnectionTracer) RestoredTransportParameters(*TransportParameters)                   {}
-func (n NullConnectionTracer) SentPacket(*ExtendedHeader, ByteCount, *AckFrame, []Frame)          {}
-func (n NullConnectionTracer) ReceivedVersionNegotiationPacket(*Header, []VersionNumber)          {}
-func (n NullConnectionTracer) ReceivedRetry(*Header)                                              {}
-func (n NullConnectionTracer) ReceivedPacket(hdr *ExtendedHeader, size ByteCount, frames []Frame) {}
-func (n NullConnectionTracer) BufferedPacket(PacketType)                                          {}
-func (n NullConnectionTracer) DroppedPacket(PacketType, ByteCount, PacketDropReason)              {}
+func (n NullConnectionTracer) ClosedConnection(err error)                                {}
+func (n NullConnectionTracer) SentTransportParameters(*TransportParameters)              {}
+func (n NullConnectionTracer) ReceivedTransportParameters(*TransportParameters)          {}
+func (n NullConnectionTracer) RestoredTransportParameters(*TransportParameters)          {}
+func (n NullConnectionTracer) SentPacket(*ExtendedHeader, ByteCount, *AckFrame, []Frame) {}
+func (n NullConnectionTracer) ReceivedVersionNegotiationPacket(dest, src ArbitraryLenConnectionID, _ []VersionNumber) {
+}
+func (n NullConnectionTracer) ReceivedRetry(*Header)                                        {}
+func (n NullConnectionTracer) ReceivedLongHeaderPacket(*ExtendedHeader, ByteCount, []Frame) {}
+func (n NullConnectionTracer) ReceivedShortHeaderPacket(*ShortHeader, ByteCount, []Frame)   {}
+func (n NullConnectionTracer) BufferedPacket(PacketType, ByteCount)                         {}
+func (n NullConnectionTracer) DroppedPacket(PacketType, ByteCount, PacketDropReason)        {}
+
 func (n NullConnectionTracer) UpdatedMetrics(rttStats *RTTStats, cwnd, bytesInFlight ByteCount, packetsInFlight int) {
 }
 func (n NullConnectionTracer) AcknowledgedPacket(EncryptionLevel, PacketNumber)            {}

+ 2 - 2
vendor/github.com/Psiphon-Labs/quic-go/mockgen.go

@@ -23,5 +23,5 @@ package quic
 //go:generate sh -c "./mockgen_private.sh quic mock_packet_handler_manager_test.go github.com/Psiphon-Labs/quic-go packetHandlerManager"
 //go:generate sh -c "./mockgen_private.sh quic mock_multiplexer_test.go github.com/Psiphon-Labs/quic-go multiplexer"
 //go:generate sh -c "./mockgen_private.sh quic mock_batch_conn_test.go github.com/Psiphon-Labs/quic-go batchConn"
-//go:generate sh -c "mockgen -package quic -self_package github.com/Psiphon-Labs/quic-go -destination mock_token_store_test.go github.com/Psiphon-Labs/quic-go TokenStore"
-//go:generate sh -c "mockgen -package quic -self_package github.com/Psiphon-Labs/quic-go -destination mock_packetconn_test.go net PacketConn"
+//go:generate sh -c "go run github.com/golang/mock/mockgen -package quic -self_package github.com/Psiphon-Labs/quic-go -destination mock_token_store_test.go github.com/Psiphon-Labs/quic-go TokenStore"
+//go:generate sh -c "go run github.com/golang/mock/mockgen -package quic -self_package github.com/Psiphon-Labs/quic-go -destination mock_packetconn_test.go net PacketConn"

+ 1 - 1
vendor/github.com/Psiphon-Labs/quic-go/mockgen_private.sh

@@ -44,6 +44,6 @@ AUX_FILES=$(IFS=, ; echo "${AUX[*]}")
 ## create a public alias for the interface, so that mockgen can process it
 echo -e "package $1\n" > $TMPFILE
 echo "$INTERFACE" | sed "s/$ORIG_INTERFACE_NAME/$INTERFACE_NAME/" >> $TMPFILE
-mockgen -package $1 -self_package $3 -destination $DEST -source=$TMPFILE -aux_files $AUX_FILES
+go run github.com/golang/mock/mockgen -package $1 -self_package $3 -destination $DEST -source=$TMPFILE -aux_files $AUX_FILES
 sed "s/$TMPFILE/$SRC/" "$DEST" > "$DEST.new" && mv "$DEST.new" "$DEST"
 rm "$TMPFILE"

+ 5 - 6
vendor/github.com/Psiphon-Labs/quic-go/multiplexer.go

@@ -1,7 +1,6 @@
 package quic
 
 import (
-	"bytes"
 	"fmt"
 	"net"
 	"sync"
@@ -20,13 +19,13 @@ type indexableConn interface {
 }
 
 type multiplexer interface {
-	AddConn(c net.PacketConn, connIDLen int, statelessResetKey []byte, tracer logging.Tracer) (packetHandlerManager, error)
+	AddConn(c net.PacketConn, connIDLen int, statelessResetKey *StatelessResetKey, tracer logging.Tracer) (packetHandlerManager, error)
 	RemoveConn(indexableConn) error
 }
 
 type connManager struct {
 	connIDLen         int
-	statelessResetKey []byte
+	statelessResetKey *StatelessResetKey
 	tracer            logging.Tracer
 	manager           packetHandlerManager
 }
@@ -37,7 +36,7 @@ type connMultiplexer struct {
 	mutex sync.Mutex
 
 	conns                   map[string] /* LocalAddr().String() */ connManager
-	newPacketHandlerManager func(net.PacketConn, int, []byte, logging.Tracer, utils.Logger) (packetHandlerManager, error) // so it can be replaced in the tests
+	newPacketHandlerManager func(net.PacketConn, int, *StatelessResetKey, logging.Tracer, utils.Logger) (packetHandlerManager, error) // so it can be replaced in the tests
 
 	logger utils.Logger
 }
@@ -58,7 +57,7 @@ func getMultiplexer() multiplexer {
 func (m *connMultiplexer) AddConn(
 	c net.PacketConn,
 	connIDLen int,
-	statelessResetKey []byte,
+	statelessResetKey *StatelessResetKey,
 	tracer logging.Tracer,
 ) (packetHandlerManager, error) {
 	m.mutex.Lock()
@@ -83,7 +82,7 @@ func (m *connMultiplexer) AddConn(
 		if p.connIDLen != connIDLen {
 			return nil, fmt.Errorf("cannot use %d byte connection IDs on a connection that is already using %d byte connction IDs", connIDLen, p.connIDLen)
 		}
-		if statelessResetKey != nil && !bytes.Equal(p.statelessResetKey, statelessResetKey) {
+		if statelessResetKey != nil && p.statelessResetKey != statelessResetKey {
 			return nil, fmt.Errorf("cannot use different stateless reset keys on the same packet conn")
 		}
 		if tracer != p.tracer {

+ 20 - 18
vendor/github.com/Psiphon-Labs/quic-go/packet_handler_map.go

@@ -47,7 +47,7 @@ type packetHandlerMap struct {
 
 	closeQueue chan closePacket
 
-	handlers          map[string] /* string(ConnectionID)*/ packetHandler
+	handlers          map[protocol.ConnectionID]packetHandler
 	resetTokens       map[protocol.StatelessResetToken] /* stateless reset token */ packetHandler
 	server            unknownPacketHandler
 	numZeroRTTEntries int
@@ -104,7 +104,7 @@ var receiveBufferWarningOnce sync.Once
 func newPacketHandlerMap(
 	c net.PacketConn,
 	connIDLen int,
-	statelessResetKey []byte,
+	statelessResetKey *StatelessResetKey,
 	tracer logging.Tracer,
 	logger utils.Logger,
 ) (packetHandlerManager, error) {
@@ -129,16 +129,18 @@ func newPacketHandlerMap(
 		conn:                    conn,
 		connIDLen:               connIDLen,
 		listening:               make(chan struct{}),
-		handlers:                make(map[string]packetHandler),
+		handlers:                make(map[protocol.ConnectionID]packetHandler),
 		resetTokens:             make(map[protocol.StatelessResetToken]packetHandler),
 		deleteRetiredConnsAfter: protocol.RetiredConnectionIDDeleteTimeout,
 		zeroRTTQueueDuration:    protocol.Max0RTTQueueingDuration,
 		closeQueue:              make(chan closePacket, 4),
-		statelessResetEnabled:   len(statelessResetKey) > 0,
-		statelessResetHasher:    hmac.New(sha256.New, statelessResetKey),
+		statelessResetEnabled:   statelessResetKey != nil,
 		tracer:                  tracer,
 		logger:                  logger,
 	}
+	if m.statelessResetEnabled {
+		m.statelessResetHasher = hmac.New(sha256.New, statelessResetKey[:])
+	}
 	go m.listen()
 	go m.runCloseQueue()
 
@@ -178,11 +180,11 @@ func (h *packetHandlerMap) Add(id protocol.ConnectionID, handler packetHandler)
 	h.mutex.Lock()
 	defer h.mutex.Unlock()
 
-	if _, ok := h.handlers[string(id)]; ok {
+	if _, ok := h.handlers[id]; ok {
 		h.logger.Debugf("Not adding connection ID %s, as it already exists.", id)
 		return false
 	}
-	h.handlers[string(id)] = handler
+	h.handlers[id] = handler
 	h.logger.Debugf("Adding connection ID %s.", id)
 	return true
 }
@@ -192,7 +194,7 @@ func (h *packetHandlerMap) AddWithConnID(clientDestConnID, newConnID protocol.Co
 	defer h.mutex.Unlock()
 
 	var q *zeroRTTQueue
-	if handler, ok := h.handlers[string(clientDestConnID)]; ok {
+	if handler, ok := h.handlers[clientDestConnID]; ok {
 		q, ok = handler.(*zeroRTTQueue)
 		if !ok {
 			h.logger.Debugf("Not adding connection ID %s for a new connection, as it already exists.", clientDestConnID)
@@ -208,15 +210,15 @@ func (h *packetHandlerMap) AddWithConnID(clientDestConnID, newConnID protocol.Co
 	if q != nil {
 		q.EnqueueAll(conn)
 	}
-	h.handlers[string(clientDestConnID)] = conn
-	h.handlers[string(newConnID)] = conn
+	h.handlers[clientDestConnID] = conn
+	h.handlers[newConnID] = conn
 	h.logger.Debugf("Adding connection IDs %s and %s for a new connection.", clientDestConnID, newConnID)
 	return true
 }
 
 func (h *packetHandlerMap) Remove(id protocol.ConnectionID) {
 	h.mutex.Lock()
-	delete(h.handlers, string(id))
+	delete(h.handlers, id)
 	h.mutex.Unlock()
 	h.logger.Debugf("Removing connection ID %s.", id)
 }
@@ -225,7 +227,7 @@ func (h *packetHandlerMap) Retire(id protocol.ConnectionID) {
 	h.logger.Debugf("Retiring connection ID %s in %s.", id, h.deleteRetiredConnsAfter)
 	time.AfterFunc(h.deleteRetiredConnsAfter, func() {
 		h.mutex.Lock()
-		delete(h.handlers, string(id))
+		delete(h.handlers, id)
 		h.mutex.Unlock()
 		h.logger.Debugf("Removing connection ID %s after it has been retired.", id)
 	})
@@ -256,7 +258,7 @@ func (h *packetHandlerMap) ReplaceWithClosed(ids []protocol.ConnectionID, pers p
 
 	h.mutex.Lock()
 	for _, id := range ids {
-		h.handlers[string(id)] = handler
+		h.handlers[id] = handler
 	}
 	h.mutex.Unlock()
 	h.logger.Debugf("Replacing connection for connection IDs %s with a closed connection.", ids)
@@ -265,7 +267,7 @@ func (h *packetHandlerMap) ReplaceWithClosed(ids []protocol.ConnectionID, pers p
 		h.mutex.Lock()
 		handler.shutdown()
 		for _, id := range ids {
-			delete(h.handlers, string(id))
+			delete(h.handlers, id)
 		}
 		h.mutex.Unlock()
 		h.logger.Debugf("Removing connection IDs %s for a closed connection after it has been retired.", ids)
@@ -396,7 +398,7 @@ func (h *packetHandlerMap) handlePacket(p *receivedPacket) {
 		return
 	}
 
-	if handler, ok := h.handlers[string(connID)]; ok {
+	if handler, ok := h.handlers[connID]; ok {
 		if ha, ok := handler.(*zeroRTTQueue); ok { // only enqueue 0-RTT packets in the 0-RTT queue
 			if wire.Is0RTTPacket(p.data) {
 				ha.handlePacket(p)
@@ -421,15 +423,15 @@ func (h *packetHandlerMap) handlePacket(p *receivedPacket) {
 		}
 		h.numZeroRTTEntries++
 		queue := &zeroRTTQueue{queue: make([]*receivedPacket, 0, 8)}
-		h.handlers[string(connID)] = queue
+		h.handlers[connID] = queue
 		queue.retireTimer = time.AfterFunc(h.zeroRTTQueueDuration, func() {
 			h.mutex.Lock()
 			defer h.mutex.Unlock()
 			// The entry might have been replaced by an actual connection.
 			// Only delete it if it's still a 0-RTT queue.
-			if handler, ok := h.handlers[string(connID)]; ok {
+			if handler, ok := h.handlers[connID]; ok {
 				if q, ok := handler.(*zeroRTTQueue); ok {
-					delete(h.handlers, string(connID))
+					delete(h.handlers, connID)
 					h.numZeroRTTEntries--
 					if h.numZeroRTTEntries < 0 {
 						panic("number of 0-RTT queues < 0")

+ 112 - 179
vendor/github.com/Psiphon-Labs/quic-go/packet_packer.go

@@ -16,10 +16,9 @@ import (
 )
 
 type packer interface {
-	PackCoalescedPacket() (*coalescedPacket, error)
-	PackPacket() (*packedPacket, error)
+	PackCoalescedPacket(onlyAck bool) (*coalescedPacket, error)
+	PackPacket(onlyAck bool) (*packedPacket, error)
 	MaybePackProbePacket(protocol.EncryptionLevel) (*packedPacket, error)
-	MaybePackAckPacket(handshakeConfirmed bool) (*packedPacket, error)
 	PackConnectionClose(*qerr.TransportError) (*coalescedPacket, error)
 	PackApplicationClose(*qerr.ApplicationError) (*coalescedPacket, error)
 
@@ -100,15 +99,16 @@ func (p *packetContents) ToAckHandlerPacket(now time.Time, q *retransmissionQueu
 			p.frames[i].OnLost = q.AddAppData
 		}
 	}
-	return &ackhandler.Packet{
-		PacketNumber:         p.header.PacketNumber,
-		LargestAcked:         largestAcked,
-		Frames:               p.frames,
-		Length:               p.length,
-		EncryptionLevel:      encLevel,
-		SendTime:             now,
-		IsPathMTUProbePacket: p.isMTUProbePacket,
-	}
+
+	ap := ackhandler.GetPacket()
+	ap.PacketNumber = p.header.PacketNumber
+	ap.LargestAcked = largestAcked
+	ap.Frames = p.frames
+	ap.Length = p.length
+	ap.EncryptionLevel = encLevel
+	ap.SendTime = now
+	ap.IsPathMTUProbePacket = p.isMTUProbePacket
+	return ap
 }
 
 func getMaxPacketSize(addr net.Addr, maxPacketSizeAdustment int) protocol.ByteCount {
@@ -339,39 +339,6 @@ func (p *packetPacker) packetLength(hdr *wire.ExtendedHeader, payload *payload)
 	return hdr.GetLength(p.version) + payload.length + paddingLen
 }
 
-func (p *packetPacker) MaybePackAckPacket(handshakeConfirmed bool) (*packedPacket, error) {
-	var encLevel protocol.EncryptionLevel
-	var ack *wire.AckFrame
-	if !handshakeConfirmed {
-		ack = p.acks.GetAckFrame(protocol.EncryptionInitial, true)
-		if ack != nil {
-			encLevel = protocol.EncryptionInitial
-		} else {
-			ack = p.acks.GetAckFrame(protocol.EncryptionHandshake, true)
-			if ack != nil {
-				encLevel = protocol.EncryptionHandshake
-			}
-		}
-	}
-	if ack == nil {
-		ack = p.acks.GetAckFrame(protocol.Encryption1RTT, true)
-		if ack == nil {
-			return nil, nil
-		}
-		encLevel = protocol.Encryption1RTT
-	}
-	payload := &payload{
-		ack:    ack,
-		length: ack.Length(p.version),
-	}
-
-	sealer, hdr, err := p.getSealerAndHeader(encLevel)
-	if err != nil {
-		return nil, err
-	}
-	return p.writeSinglePacket(hdr, payload, encLevel, sealer)
-}
-
 // size is the expected size of the packet, if no padding was applied.
 func (p *packetPacker) initialPaddingLen(frames []ackhandler.Frame, size protocol.ByteCount) protocol.ByteCount {
 	// For the server, only ack-eliciting Initial packets need to be padded.
@@ -387,7 +354,7 @@ func (p *packetPacker) initialPaddingLen(frames []ackhandler.Frame, size protoco
 // PackCoalescedPacket packs a new packet.
 // It packs an Initial / Handshake if there is data to send in these packet number spaces.
 // It should only be called before the handshake is confirmed.
-func (p *packetPacker) PackCoalescedPacket() (*coalescedPacket, error) {
+func (p *packetPacker) PackCoalescedPacket(onlyAck bool) (*coalescedPacket, error) {
 	maxPacketSize := p.maxPacketSize
 	if p.perspective == protocol.PerspectiveClient {
 		maxPacketSize = protocol.MinInitialPacketSize
@@ -402,7 +369,7 @@ func (p *packetPacker) PackCoalescedPacket() (*coalescedPacket, error) {
 	}
 	var size protocol.ByteCount
 	if initialSealer != nil {
-		initialHdr, initialPayload = p.maybeGetCryptoPacket(maxPacketSize-protocol.ByteCount(initialSealer.Overhead()), size, protocol.EncryptionInitial)
+		initialHdr, initialPayload = p.maybeGetCryptoPacket(maxPacketSize-protocol.ByteCount(initialSealer.Overhead()), protocol.EncryptionInitial, onlyAck, true)
 		if initialPayload != nil {
 			size += p.packetLength(initialHdr, initialPayload) + protocol.ByteCount(initialSealer.Overhead())
 			numPackets++
@@ -411,14 +378,14 @@ func (p *packetPacker) PackCoalescedPacket() (*coalescedPacket, error) {
 
 	// Add a Handshake packet.
 	var handshakeSealer sealer
-	if size < maxPacketSize-protocol.MinCoalescedPacketSize {
+	if (onlyAck && size == 0) || (!onlyAck && size < maxPacketSize-protocol.MinCoalescedPacketSize) {
 		var err error
 		handshakeSealer, err = p.cryptoSetup.GetHandshakeSealer()
 		if err != nil && err != handshake.ErrKeysDropped && err != handshake.ErrKeysNotYetAvailable {
 			return nil, err
 		}
 		if handshakeSealer != nil {
-			handshakeHdr, handshakePayload = p.maybeGetCryptoPacket(maxPacketSize-size-protocol.ByteCount(handshakeSealer.Overhead()), size, protocol.EncryptionHandshake)
+			handshakeHdr, handshakePayload = p.maybeGetCryptoPacket(maxPacketSize-size-protocol.ByteCount(handshakeSealer.Overhead()), protocol.EncryptionHandshake, onlyAck, size == 0)
 			if handshakePayload != nil {
 				s := p.packetLength(handshakeHdr, handshakePayload) + protocol.ByteCount(handshakeSealer.Overhead())
 				size += s
@@ -430,17 +397,24 @@ func (p *packetPacker) PackCoalescedPacket() (*coalescedPacket, error) {
 	// Add a 0-RTT / 1-RTT packet.
 	var appDataSealer sealer
 	appDataEncLevel := protocol.Encryption1RTT
-	if size < maxPacketSize-protocol.MinCoalescedPacketSize {
-		var err error
-		appDataSealer, appDataHdr, appDataPayload = p.maybeGetAppDataPacket(maxPacketSize-size, size)
-		if err != nil {
-			return nil, err
-		}
-		if appDataHdr != nil {
-			if appDataHdr.IsLongHeader {
-				appDataEncLevel = protocol.Encryption0RTT
+	if (onlyAck && size == 0) || (!onlyAck && size < maxPacketSize-protocol.MinCoalescedPacketSize) {
+		var sErr error
+		var oneRTTSealer handshake.ShortHeaderSealer
+		oneRTTSealer, sErr = p.cryptoSetup.Get1RTTSealer()
+		appDataSealer = oneRTTSealer
+		if sErr != nil && p.perspective == protocol.PerspectiveClient {
+			appDataSealer, sErr = p.cryptoSetup.Get0RTTSealer()
+			appDataEncLevel = protocol.Encryption0RTT
+		}
+		if appDataSealer != nil && sErr == nil {
+			//nolint:exhaustive // 0-RTT and 1-RTT are the only two application data encryption levels.
+			switch appDataEncLevel {
+			case protocol.Encryption0RTT:
+				appDataHdr, appDataPayload = p.maybeGetAppDataPacketFor0RTT(appDataSealer, maxPacketSize-size)
+			case protocol.Encryption1RTT:
+				appDataHdr, appDataPayload = p.maybeGetShortHeaderPacket(oneRTTSealer, maxPacketSize-size, onlyAck, size == 0)
 			}
-			if appDataPayload != nil {
+			if appDataHdr != nil && appDataPayload != nil {
 				size += p.packetLength(appDataHdr, appDataPayload) + protocol.ByteCount(appDataSealer.Overhead())
 				numPackets++
 			}
@@ -483,17 +457,17 @@ func (p *packetPacker) PackCoalescedPacket() (*coalescedPacket, error) {
 
 // PackPacket packs a packet in the application data packet number space.
 // It should be called after the handshake is confirmed.
-func (p *packetPacker) PackPacket() (*packedPacket, error) {
-	sealer, hdr, payload := p.maybeGetAppDataPacket(p.maxPacketSize, 0)
+func (p *packetPacker) PackPacket(onlyAck bool) (*packedPacket, error) {
+	sealer, err := p.cryptoSetup.Get1RTTSealer()
+	if err != nil {
+		return nil, err
+	}
+	hdr, payload := p.maybeGetShortHeaderPacket(sealer, p.maxPacketSize, onlyAck, true)
 	if payload == nil {
 		return nil, nil
 	}
 	buffer := getPacketBuffer()
-	encLevel := protocol.Encryption1RTT
-	if hdr.IsLongHeader {
-		encLevel = protocol.Encryption0RTT
-	}
-	cont, err := p.appendPacket(buffer, hdr, payload, 0, encLevel, sealer, false)
+	cont, err := p.appendPacket(buffer, hdr, payload, 0, protocol.Encryption1RTT, sealer, false)
 	if err != nil {
 		return nil, err
 	}
@@ -503,7 +477,17 @@ func (p *packetPacker) PackPacket() (*packedPacket, error) {
 	}, nil
 }
 
-func (p *packetPacker) maybeGetCryptoPacket(maxPacketSize, currentSize protocol.ByteCount, encLevel protocol.EncryptionLevel) (*wire.ExtendedHeader, *payload) {
+func (p *packetPacker) maybeGetCryptoPacket(maxPacketSize protocol.ByteCount, encLevel protocol.EncryptionLevel, onlyAck, ackAllowed bool) (*wire.ExtendedHeader, *payload) {
+	if onlyAck {
+		if ack := p.acks.GetAckFrame(encLevel, true); ack != nil {
+			var payload payload
+			payload.ack = ack
+			payload.length = ack.Length(p.version)
+			return p.getLongHeader(encLevel), &payload
+		}
+		return nil, nil
+	}
+
 	var s cryptoStream
 	var hasRetransmission bool
 	//nolint:exhaustive // Initial and Handshake are the only two encryption levels here.
@@ -518,7 +502,7 @@ func (p *packetPacker) maybeGetCryptoPacket(maxPacketSize, currentSize protocol.
 
 	hasData := s.HasData()
 	var ack *wire.AckFrame
-	if encLevel == protocol.EncryptionInitial || currentSize == 0 {
+	if ackAllowed {
 		ack = p.acks.GetAckFrame(encLevel, !hasRetransmission && !hasData)
 	}
 	if !hasData && !hasRetransmission && ack == nil {
@@ -560,35 +544,26 @@ func (p *packetPacker) maybeGetCryptoPacket(maxPacketSize, currentSize protocol.
 	return hdr, &payload
 }
 
-func (p *packetPacker) maybeGetAppDataPacket(maxPacketSize, currentSize protocol.ByteCount) (sealer, *wire.ExtendedHeader, *payload) {
-	var sealer sealer
-	var encLevel protocol.EncryptionLevel
-	var hdr *wire.ExtendedHeader
-	oneRTTSealer, err := p.cryptoSetup.Get1RTTSealer()
-	if err == nil {
-		encLevel = protocol.Encryption1RTT
-		sealer = oneRTTSealer
-		hdr = p.getShortHeader(oneRTTSealer.KeyPhase())
-	} else {
-		// 1-RTT sealer not yet available
-		if p.perspective != protocol.PerspectiveClient {
-			return nil, nil, nil
-		}
-		sealer, err = p.cryptoSetup.Get0RTTSealer()
-		if sealer == nil || err != nil {
-			return nil, nil, nil
-		}
-		encLevel = protocol.Encryption0RTT
-		hdr = p.getLongHeader(protocol.Encryption0RTT)
+func (p *packetPacker) maybeGetAppDataPacketFor0RTT(sealer sealer, maxPacketSize protocol.ByteCount) (*wire.ExtendedHeader, *payload) {
+	if p.perspective != protocol.PerspectiveClient {
+		return nil, nil
 	}
 
+	hdr := p.getLongHeader(protocol.Encryption0RTT)
 	maxPayloadSize := maxPacketSize - hdr.GetLength(p.version) - protocol.ByteCount(sealer.Overhead())
-	payload := p.maybeGetAppDataPacketWithEncLevel(maxPayloadSize, encLevel == protocol.Encryption1RTT && currentSize == 0)
-	return sealer, hdr, payload
+	payload := p.maybeGetAppDataPacket(maxPayloadSize, false, false)
+	return hdr, payload
 }
 
-func (p *packetPacker) maybeGetAppDataPacketWithEncLevel(maxPayloadSize protocol.ByteCount, ackAllowed bool) *payload {
-	payload := p.composeNextPacket(maxPayloadSize, ackAllowed)
+func (p *packetPacker) maybeGetShortHeaderPacket(sealer handshake.ShortHeaderSealer, maxPacketSize protocol.ByteCount, onlyAck, ackAllowed bool) (*wire.ExtendedHeader, *payload) {
+	hdr := p.getShortHeader(sealer.KeyPhase())
+	maxPayloadSize := maxPacketSize - hdr.GetLength(p.version) - protocol.ByteCount(sealer.Overhead())
+	payload := p.maybeGetAppDataPacket(maxPayloadSize, onlyAck, ackAllowed)
+	return hdr, payload
+}
+
+func (p *packetPacker) maybeGetAppDataPacket(maxPayloadSize protocol.ByteCount, onlyAck, ackAllowed bool) *payload {
+	payload := p.composeNextPacket(maxPayloadSize, onlyAck, ackAllowed)
 
 	// check if we have anything to send
 	if len(payload.frames) == 0 {
@@ -611,35 +586,47 @@ func (p *packetPacker) maybeGetAppDataPacketWithEncLevel(maxPayloadSize protocol
 	return payload
 }
 
-func (p *packetPacker) composeNextPacket(maxFrameSize protocol.ByteCount, ackAllowed bool) *payload {
-	payload := &payload{frames: make([]ackhandler.Frame, 0, 1)}
-
-	var hasDatagram bool
-	if p.datagramQueue != nil {
-		if datagram := p.datagramQueue.Get(); datagram != nil {
-			payload.frames = append(payload.frames, ackhandler.Frame{
-				Frame: datagram,
-				// set it to a no-op. Then we won't set the default callback, which would retransmit the frame.
-				OnLost: func(wire.Frame) {},
-			})
-			payload.length += datagram.Length(p.version)
-			hasDatagram = true
+func (p *packetPacker) composeNextPacket(maxFrameSize protocol.ByteCount, onlyAck, ackAllowed bool) *payload {
+	if onlyAck {
+		if ack := p.acks.GetAckFrame(protocol.Encryption1RTT, true); ack != nil {
+			payload := &payload{}
+			payload.ack = ack
+			payload.length += ack.Length(p.version)
+			return payload
 		}
+		return &payload{}
 	}
 
-	var ack *wire.AckFrame
+	payload := &payload{frames: make([]ackhandler.Frame, 0, 1)}
+
 	hasData := p.framer.HasData()
 	hasRetransmission := p.retransmissionQueue.HasAppData()
-	// TODO: make sure ACKs are sent when a lot of DATAGRAMs are queued
-	if !hasDatagram && ackAllowed {
-		ack = p.acks.GetAckFrame(protocol.Encryption1RTT, !hasRetransmission && !hasData)
-		if ack != nil {
+
+	var hasAck bool
+	if ackAllowed {
+		if ack := p.acks.GetAckFrame(protocol.Encryption1RTT, !hasRetransmission && !hasData); ack != nil {
 			payload.ack = ack
 			payload.length += ack.Length(p.version)
+			hasAck = true
 		}
 	}
 
-	if ack == nil && !hasData && !hasRetransmission {
+	if p.datagramQueue != nil {
+		if f := p.datagramQueue.Peek(); f != nil {
+			size := f.Length(p.version)
+			if size <= maxFrameSize-payload.length {
+				payload.frames = append(payload.frames, ackhandler.Frame{
+					Frame: f,
+					// set it to a no-op. Then we won't set the default callback, which would retransmit the frame.
+					OnLost: func(wire.Frame) {},
+				})
+				payload.length += size
+				p.datagramQueue.Pop()
+			}
+		}
+	}
+
+	if hasAck && !hasData && !hasRetransmission {
 		return payload
 	}
 
@@ -681,14 +668,14 @@ func (p *packetPacker) MaybePackProbePacket(encLevel protocol.EncryptionLevel) (
 		if err != nil {
 			return nil, err
 		}
-		hdr, payload = p.maybeGetCryptoPacket(p.maxPacketSize-protocol.ByteCount(sealer.Overhead()), 0, protocol.EncryptionInitial)
+		hdr, payload = p.maybeGetCryptoPacket(p.maxPacketSize-protocol.ByteCount(sealer.Overhead()), protocol.EncryptionInitial, false, true)
 	case protocol.EncryptionHandshake:
 		var err error
 		sealer, err = p.cryptoSetup.GetHandshakeSealer()
 		if err != nil {
 			return nil, err
 		}
-		hdr, payload = p.maybeGetCryptoPacket(p.maxPacketSize-protocol.ByteCount(sealer.Overhead()), 0, protocol.EncryptionHandshake)
+		hdr, payload = p.maybeGetCryptoPacket(p.maxPacketSize-protocol.ByteCount(sealer.Overhead()), protocol.EncryptionHandshake, false, true)
 	case protocol.Encryption1RTT:
 		oneRTTSealer, err := p.cryptoSetup.Get1RTTSealer()
 		if err != nil {
@@ -696,7 +683,7 @@ func (p *packetPacker) MaybePackProbePacket(encLevel protocol.EncryptionLevel) (
 		}
 		sealer = oneRTTSealer
 		hdr = p.getShortHeader(oneRTTSealer.KeyPhase())
-		payload = p.maybeGetAppDataPacketWithEncLevel(p.maxPacketSize-protocol.ByteCount(sealer.Overhead())-hdr.GetLength(p.version), true)
+		payload = p.maybeGetAppDataPacket(p.maxPacketSize-protocol.ByteCount(sealer.Overhead())-hdr.GetLength(p.version), false, true)
 	default:
 		panic("unknown encryption level")
 	}
@@ -742,41 +729,6 @@ func (p *packetPacker) PackMTUProbePacket(ping ackhandler.Frame, size protocol.B
 	}, nil
 }
 
-func (p *packetPacker) getSealerAndHeader(encLevel protocol.EncryptionLevel) (sealer, *wire.ExtendedHeader, error) {
-	switch encLevel {
-	case protocol.EncryptionInitial:
-		sealer, err := p.cryptoSetup.GetInitialSealer()
-		if err != nil {
-			return nil, nil, err
-		}
-		hdr := p.getLongHeader(protocol.EncryptionInitial)
-		return sealer, hdr, nil
-	case protocol.Encryption0RTT:
-		sealer, err := p.cryptoSetup.Get0RTTSealer()
-		if err != nil {
-			return nil, nil, err
-		}
-		hdr := p.getLongHeader(protocol.Encryption0RTT)
-		return sealer, hdr, nil
-	case protocol.EncryptionHandshake:
-		sealer, err := p.cryptoSetup.GetHandshakeSealer()
-		if err != nil {
-			return nil, nil, err
-		}
-		hdr := p.getLongHeader(protocol.EncryptionHandshake)
-		return sealer, hdr, nil
-	case protocol.Encryption1RTT:
-		sealer, err := p.cryptoSetup.Get1RTTSealer()
-		if err != nil {
-			return nil, nil, err
-		}
-		hdr := p.getShortHeader(sealer.KeyPhase())
-		return sealer, hdr, nil
-	default:
-		return nil, nil, fmt.Errorf("unexpected encryption level: %s", encLevel)
-	}
-}
-
 func (p *packetPacker) getShortHeader(kp protocol.KeyPhaseBit) *wire.ExtendedHeader {
 	pn, pnLen := p.pnManager.PeekPacketNumber(protocol.Encryption1RTT)
 	hdr := &wire.ExtendedHeader{}
@@ -811,28 +763,6 @@ func (p *packetPacker) getLongHeader(encLevel protocol.EncryptionLevel) *wire.Ex
 	return hdr
 }
 
-// writeSinglePacket packs a single packet.
-func (p *packetPacker) writeSinglePacket(
-	hdr *wire.ExtendedHeader,
-	payload *payload,
-	encLevel protocol.EncryptionLevel,
-	sealer sealer,
-) (*packedPacket, error) {
-	buffer := getPacketBuffer()
-	var paddingLen protocol.ByteCount
-	if encLevel == protocol.EncryptionInitial {
-		paddingLen = p.initialPaddingLen(payload.frames, hdr.GetLength(p.version)+payload.length+protocol.ByteCount(sealer.Overhead()))
-	}
-	contents, err := p.appendPacket(buffer, hdr, payload, paddingLen, encLevel, sealer, false)
-	if err != nil {
-		return nil, err
-	}
-	return &packedPacket{
-		buffer:         buffer,
-		packetContents: contents,
-	}, nil
-}
-
 func (p *packetPacker) appendPacket(buffer *packetBuffer, header *wire.ExtendedHeader, payload *payload, padding protocol.ByteCount, encLevel protocol.EncryptionLevel, sealer sealer, isMTUProbePacket bool) (*packetContents, error) {
 	var paddingLen protocol.ByteCount
 	pnLen := protocol.ByteCount(header.PacketNumberLen)
@@ -850,35 +780,38 @@ func (p *packetPacker) appendPacket(buffer *packetBuffer, header *wire.ExtendedH
 		return nil, err
 	}
 	payloadOffset := buf.Len()
+	raw := buffer.Data[:payloadOffset]
 
 	if payload.ack != nil {
-		if err := payload.ack.Write(buf, p.version); err != nil {
+		var err error
+		raw, err = payload.ack.Append(raw, p.version)
+		if err != nil {
 			return nil, err
 		}
 	}
 	if paddingLen > 0 {
-		buf.Write(make([]byte, paddingLen))
+		raw = append(raw, make([]byte, paddingLen)...)
 	}
 	for _, frame := range payload.frames {
-		if err := frame.Write(buf, p.version); err != nil {
+		var err error
+		raw, err = frame.Append(raw, p.version)
+		if err != nil {
 			return nil, err
 		}
 	}
 
-	if payloadSize := protocol.ByteCount(buf.Len()-payloadOffset) - paddingLen; payloadSize != payload.length {
+	if payloadSize := protocol.ByteCount(len(raw)-payloadOffset) - paddingLen; payloadSize != payload.length {
 		return nil, fmt.Errorf("PacketPacker BUG: payload size inconsistent (expected %d, got %d bytes)", payload.length, payloadSize)
 	}
 	if !isMTUProbePacket {
-		if size := protocol.ByteCount(buf.Len() + sealer.Overhead()); size > p.maxPacketSize {
+		if size := protocol.ByteCount(len(raw) + sealer.Overhead()); size > p.maxPacketSize {
 			return nil, fmt.Errorf("PacketPacker BUG: packet too large (%d bytes, allowed %d bytes)", size, p.maxPacketSize)
 		}
 	}
 
-	raw := buffer.Data
 	// encrypt the packet
-	raw = raw[:buf.Len()]
 	_ = sealer.Seal(raw[payloadOffset:payloadOffset], raw[payloadOffset:], header.PacketNumber, raw[hdrOffset:payloadOffset])
-	raw = raw[0 : buf.Len()+sealer.Overhead()]
+	raw = raw[0 : len(raw)+sealer.Overhead()]
 	// apply header protection
 	pnOffset := payloadOffset - int(header.PacketNumberLen)
 	sealer.EncryptHeader(raw[pnOffset+4:pnOffset+4+16], &raw[hdrOffset], raw[pnOffset:payloadOffset])

+ 69 - 37
vendor/github.com/Psiphon-Labs/quic-go/packet_unpacker.go

@@ -7,6 +7,7 @@ import (
 
 	"github.com/Psiphon-Labs/quic-go/internal/handshake"
 	"github.com/Psiphon-Labs/quic-go/internal/protocol"
+	"github.com/Psiphon-Labs/quic-go/internal/qerr"
 	"github.com/Psiphon-Labs/quic-go/internal/wire"
 )
 
@@ -27,7 +28,6 @@ func (e *headerParseError) Error() string {
 }
 
 type unpackedPacket struct {
-	packetNumber    protocol.PacketNumber // the decoded packet number
 	hdr             *wire.ExtendedHeader
 	encryptionLevel protocol.EncryptionLevel
 	data            []byte
@@ -37,22 +37,25 @@ type unpackedPacket struct {
 type packetUnpacker struct {
 	cs handshake.CryptoSetup
 
-	version protocol.VersionNumber
+	shortHdrConnIDLen int
+	version           protocol.VersionNumber
 }
 
 var _ unpacker = &packetUnpacker{}
 
-func newPacketUnpacker(cs handshake.CryptoSetup, version protocol.VersionNumber) unpacker {
+func newPacketUnpacker(cs handshake.CryptoSetup, shortHdrConnIDLen int, version protocol.VersionNumber) unpacker {
 	return &packetUnpacker{
-		cs:      cs,
-		version: version,
+		cs:                cs,
+		shortHdrConnIDLen: shortHdrConnIDLen,
+		version:           version,
 	}
 }
 
+// UnpackLongHeader unpacks a Long Header packet.
 // If the reserved bits are invalid, the error is wire.ErrInvalidReservedBits.
 // If any other error occurred when parsing the header, the error is of type headerParseError.
 // If decrypting the payload fails for any reason, the error is the error returned by the AEAD.
-func (u *packetUnpacker) Unpack(hdr *wire.Header, rcvTime time.Time, data []byte) (*unpackedPacket, error) {
+func (u *packetUnpacker) UnpackLongHeader(hdr *wire.Header, rcvTime time.Time, data []byte) (*unpackedPacket, error) {
 	var encLevel protocol.EncryptionLevel
 	var extHdr *wire.ExtendedHeader
 	var decrypted []byte
@@ -89,30 +92,43 @@ func (u *packetUnpacker) Unpack(hdr *wire.Header, rcvTime time.Time, data []byte
 			return nil, err
 		}
 	default:
-		if hdr.IsLongHeader {
-			return nil, fmt.Errorf("unknown packet type: %s", hdr.Type)
-		}
-		encLevel = protocol.Encryption1RTT
-		opener, err := u.cs.Get1RTTOpener()
-		if err != nil {
-			return nil, err
-		}
-		extHdr, decrypted, err = u.unpackShortHeaderPacket(opener, hdr, rcvTime, data)
-		if err != nil {
-			return nil, err
+		return nil, fmt.Errorf("unknown packet type: %s", hdr.Type)
+	}
+
+	if len(decrypted) == 0 {
+		return nil, &qerr.TransportError{
+			ErrorCode:    qerr.ProtocolViolation,
+			ErrorMessage: "empty packet",
 		}
 	}
 
 	return &unpackedPacket{
 		hdr:             extHdr,
-		packetNumber:    extHdr.PacketNumber,
 		encryptionLevel: encLevel,
 		data:            decrypted,
 	}, nil
 }
 
+func (u *packetUnpacker) UnpackShortHeader(rcvTime time.Time, data []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error) {
+	opener, err := u.cs.Get1RTTOpener()
+	if err != nil {
+		return 0, 0, 0, nil, err
+	}
+	pn, pnLen, kp, decrypted, err := u.unpackShortHeaderPacket(opener, rcvTime, data)
+	if err != nil {
+		return 0, 0, 0, nil, err
+	}
+	if len(decrypted) == 0 {
+		return 0, 0, 0, nil, &qerr.TransportError{
+			ErrorCode:    qerr.ProtocolViolation,
+			ErrorMessage: "empty packet",
+		}
+	}
+	return pn, pnLen, kp, decrypted, nil
+}
+
 func (u *packetUnpacker) unpackLongHeaderPacket(opener handshake.LongHeaderOpener, hdr *wire.Header, data []byte) (*wire.ExtendedHeader, []byte, error) {
-	extHdr, parseErr := u.unpackHeader(opener, hdr, data)
+	extHdr, parseErr := u.unpackLongHeader(opener, hdr, data)
 	// If the reserved bits are set incorrectly, we still need to continue unpacking.
 	// This avoids a timing side-channel, which otherwise might allow an attacker
 	// to gain information about the header encryption.
@@ -131,41 +147,57 @@ func (u *packetUnpacker) unpackLongHeaderPacket(opener handshake.LongHeaderOpene
 	return extHdr, decrypted, nil
 }
 
-func (u *packetUnpacker) unpackShortHeaderPacket(
-	opener handshake.ShortHeaderOpener,
-	hdr *wire.Header,
-	rcvTime time.Time,
-	data []byte,
-) (*wire.ExtendedHeader, []byte, error) {
-	extHdr, parseErr := u.unpackHeader(opener, hdr, data)
+func (u *packetUnpacker) unpackShortHeaderPacket(opener handshake.ShortHeaderOpener, rcvTime time.Time, data []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error) {
+	l, pn, pnLen, kp, parseErr := u.unpackShortHeader(opener, data)
 	// If the reserved bits are set incorrectly, we still need to continue unpacking.
 	// This avoids a timing side-channel, which otherwise might allow an attacker
 	// to gain information about the header encryption.
 	if parseErr != nil && parseErr != wire.ErrInvalidReservedBits {
-		return nil, nil, parseErr
+		return 0, 0, 0, nil, &headerParseError{parseErr}
 	}
-	extHdr.PacketNumber = opener.DecodePacketNumber(extHdr.PacketNumber, extHdr.PacketNumberLen)
-	extHdrLen := extHdr.ParsedLen()
-	decrypted, err := opener.Open(data[extHdrLen:extHdrLen], data[extHdrLen:], rcvTime, extHdr.PacketNumber, extHdr.KeyPhase, data[:extHdrLen])
+	pn = opener.DecodePacketNumber(pn, pnLen)
+	decrypted, err := opener.Open(data[l:l], data[l:], rcvTime, pn, kp, data[:l])
 	if err != nil {
-		return nil, nil, err
+		return 0, 0, 0, nil, err
 	}
-	if parseErr != nil {
-		return nil, nil, parseErr
+	return pn, pnLen, kp, decrypted, parseErr
+}
+
+func (u *packetUnpacker) unpackShortHeader(hd headerDecryptor, data []byte) (int, protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, error) {
+	hdrLen := 1 /* first header byte */ + u.shortHdrConnIDLen
+	if len(data) < hdrLen+4+16 {
+		return 0, 0, 0, 0, fmt.Errorf("packet too small, expected at least 20 bytes after the header, got %d", len(data)-hdrLen)
 	}
-	return extHdr, decrypted, nil
+	origPNBytes := make([]byte, 4)
+	copy(origPNBytes, data[hdrLen:hdrLen+4])
+	// 2. decrypt the header, assuming a 4 byte packet number
+	hd.DecryptHeader(
+		data[hdrLen+4:hdrLen+4+16],
+		&data[0],
+		data[hdrLen:hdrLen+4],
+	)
+	// 3. parse the header (and learn the actual length of the packet number)
+	l, pn, pnLen, kp, parseErr := wire.ParseShortHeader(data, u.shortHdrConnIDLen)
+	if parseErr != nil && parseErr != wire.ErrInvalidReservedBits {
+		return l, pn, pnLen, kp, parseErr
+	}
+	// 4. if the packet number is shorter than 4 bytes, replace the remaining bytes with the copy we saved earlier
+	if pnLen != protocol.PacketNumberLen4 {
+		copy(data[hdrLen+int(pnLen):hdrLen+4], origPNBytes[int(pnLen):])
+	}
+	return l, pn, pnLen, kp, parseErr
 }
 
 // The error is either nil, a wire.ErrInvalidReservedBits or of type headerParseError.
-func (u *packetUnpacker) unpackHeader(hd headerDecryptor, hdr *wire.Header, data []byte) (*wire.ExtendedHeader, error) {
-	extHdr, err := unpackHeader(hd, hdr, data, u.version)
+func (u *packetUnpacker) unpackLongHeader(hd headerDecryptor, hdr *wire.Header, data []byte) (*wire.ExtendedHeader, error) {
+	extHdr, err := unpackLongHeader(hd, hdr, data, u.version)
 	if err != nil && err != wire.ErrInvalidReservedBits {
 		return nil, &headerParseError{err: err}
 	}
 	return extHdr, err
 }
 
-func unpackHeader(hd headerDecryptor, hdr *wire.Header, data []byte, version protocol.VersionNumber) (*wire.ExtendedHeader, error) {
+func unpackLongHeader(hd headerDecryptor, hdr *wire.Header, data []byte, version protocol.VersionNumber) (*wire.ExtendedHeader, error) {
 	r := bytes.NewReader(data)
 
 	hdrLen := hdr.ParsedLen()

+ 19 - 0
vendor/github.com/Psiphon-Labs/quic-go/quicvarint/varint.go

@@ -88,6 +88,25 @@ func Write(w Writer, i uint64) {
 	}
 }
 
+func Append(b []byte, i uint64) []byte {
+	if i <= maxVarInt1 {
+		return append(b, uint8(i))
+	}
+	if i <= maxVarInt2 {
+		return append(b, []byte{uint8(i>>8) | 0x40, uint8(i)}...)
+	}
+	if i <= maxVarInt4 {
+		return append(b, []byte{uint8(i>>24) | 0x80, uint8(i >> 16), uint8(i >> 8), uint8(i)}...)
+	}
+	if i <= maxVarInt8 {
+		return append(b, []byte{
+			uint8(i>>56) | 0xc0, uint8(i >> 48), uint8(i >> 40), uint8(i >> 32),
+			uint8(i >> 24), uint8(i >> 16), uint8(i >> 8), uint8(i),
+		}...)
+	}
+	panic(fmt.Sprintf("%#x doesn't fit into 62 bits", i))
+}
+
 // WriteWithLen writes i in the QUIC varint format with the desired length to w.
 func WriteWithLen(w Writer, i uint64, length protocol.ByteCount) {
 	if length != 1 && length != 2 && length != 4 && length != 8 {

+ 7 - 0
vendor/github.com/Psiphon-Labs/quic-go/send_queue.go

@@ -36,6 +36,13 @@ func newSendQueue(conn sendConn) sender {
 func (h *sendQueue) Send(p *packetBuffer) {
 	select {
 	case h.queue <- p:
+		// clear available channel if we've reached capacity
+		if len(h.queue) == sendQueueCapacity {
+			select {
+			case <-h.available:
+			default:
+			}
+		}
 	case <-h.runStopped:
 	default:
 		panic("sendQueue.Send would have blocked")

+ 1 - 1
vendor/github.com/Psiphon-Labs/quic-go/send_stream.go

@@ -122,7 +122,7 @@ func (s *sendStream) Write(p []byte) (int, error) {
 		var copied bool
 		var deadline time.Time
 		// As soon as dataForWriting becomes smaller than a certain size x, we copy all the data to a STREAM frame (s.nextFrame),
-		// which can the be popped the next time we assemble a packet.
+		// which can then be popped the next time we assemble a packet.
 		// This allows us to return Write() when all data but x bytes have been sent out.
 		// When the user now calls Close(), this is much more likely to happen before we popped that last STREAM frame,
 		// allowing us to set the FIN bit on that frame (instead of sending an empty STREAM frame with FIN).

+ 51 - 43
vendor/github.com/Psiphon-Labs/quic-go/server.go

@@ -321,10 +321,37 @@ func (s *baseServer) handlePacketImpl(p *receivedPacket) bool /* is the buffer s
 		}
 		return false
 	}
+	// Short header packets should never end up here in the first place
+	if !wire.IsLongHeaderPacket(p.data[0]) {
+		panic(fmt.Sprintf("misrouted packet: %#v", p.data))
+	}
+	v, err := wire.ParseVersion(p.data)
+	// send a Version Negotiation Packet if the client is speaking a different protocol version
+	if err != nil || !protocol.IsSupportedVersion(s.config.Versions, v) {
+		if err != nil || p.Size() < protocol.MinUnknownVersionPacketSize {
+			s.logger.Debugf("Dropping a packet with an unknown version that is too small (%d bytes)", p.Size())
+			if s.config.Tracer != nil {
+				s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropUnexpectedPacket)
+			}
+			return false
+		}
+		_, src, dest, err := wire.ParseArbitraryLenConnectionIDs(p.data)
+		if err != nil { // should never happen
+			s.logger.Debugf("Dropping a packet with an unknown version for which we failed to parse connection IDs")
+			if s.config.Tracer != nil {
+				s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropUnexpectedPacket)
+			}
+			return false
+		}
+		if !s.config.DisableVersionNegotiationPackets {
+			go s.sendVersionNegotiationPacket(p.remoteAddr, src, dest, p.info.OOB())
+		}
+		return false
+	}
 	// If we're creating a new connection, the packet will be passed to the connection.
 	// The header will then be parsed again.
 	hdr, _, _, err := wire.ParsePacket(p.data, s.config.ConnectionIDGenerator.ConnectionIDLen())
-	if err != nil && err != wire.ErrUnsupportedVersion {
+	if err != nil {
 		if s.config.Tracer != nil {
 			s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropHeaderParseError)
 		}
@@ -344,28 +371,15 @@ func (s *baseServer) handlePacketImpl(p *receivedPacket) bool /* is the buffer s
 	isObfuscated := s.config.ServerMaxPacketSizeAdjustment != nil && s.config.ServerMaxPacketSizeAdjustment(p.remoteAddr) > 0
 
 	if !isObfuscated &&
-
 		hdr.Type == protocol.PacketTypeInitial && p.Size() < protocol.MinInitialPacketSize {
+
 		s.logger.Debugf("Dropping a packet that is too small to be a valid Initial (%d bytes)", p.Size())
 		if s.config.Tracer != nil {
 			s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropUnexpectedPacket)
 		}
 		return false
 	}
-	// send a Version Negotiation Packet if the client is speaking a different protocol version
-	if !protocol.IsSupportedVersion(s.config.Versions, hdr.Version) {
-		if p.Size() < protocol.MinUnknownVersionPacketSize {
-			s.logger.Debugf("Dropping a packet with an unknown version that is too small (%d bytes)", p.Size())
-			if s.config.Tracer != nil {
-				s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropUnexpectedPacket)
-			}
-			return false
-		}
-		if !s.config.DisableVersionNegotiationPackets {
-			go s.sendVersionNegotiationPacket(p, hdr)
-		}
-		return false
-	}
+
 	if hdr.IsLongHeader && hdr.Type != protocol.PacketTypeInitial {
 		// Drop long header packets.
 		// There's little point in sending a Stateless Reset, since the client
@@ -471,23 +485,24 @@ func (s *baseServer) verifyClientHelloRandom(p *receivedPacket, hdr *wire.Header
 	// original packet data must be retained for subsequent processing.
 	data := append([]byte(nil), p.data...)
 
-	unpacker := newPacketUnpacker(cs, protocol.Version1)
-	unpacked, err := unpacker.Unpack(hdr, p.rcvTime, data)
+	unpacker := newPacketUnpacker(cs, 0, protocol.Version1)
+	unpacked, err := unpacker.UnpackLongHeader(hdr, p.rcvTime, data)
 	if err != nil {
-		return fmt.Errorf("verifyClientHelloRandom: Unpack: %w", err)
+		return fmt.Errorf("verifyClientHelloRandom: UnpackLongHeader: %w", err)
 	}
 
 	parser := wire.NewFrameParser(s.config.EnableDatagrams, protocol.Version1)
 
-	r := bytes.NewReader(unpacked.data)
-	for {
-		frame, err := parser.ParseNext(r, protocol.EncryptionInitial)
+	d := unpacked.data
+	for len(d) > 0 {
+		l, frame, err := parser.ParseNext(d, protocol.EncryptionInitial)
 		if err != nil {
 			return fmt.Errorf("verifyClientHelloRandom: ParseNext: %w", err)
 		}
 		if frame == nil {
 			return errors.New("verifyClientHelloRandom: missing CRYPTO frame")
 		}
+		d = d[l:]
 		cryptoFrame, ok := frame.(*wire.CryptoFrame)
 		if !ok {
 			continue
@@ -609,7 +624,7 @@ func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) erro
 	if err != nil {
 		return err
 	}
-	s.logger.Debugf("Changing connection ID to %s.", protocol.ConnectionID(connID))
+	s.logger.Debugf("Changing connection ID to %s.", connID)
 	var conn quicConn
 	tracingID := nextConnTracingID()
 	if added := s.connHandler.AddWithConnID(hdr.DestConnectionID, connID, func() packetHandler {
@@ -707,7 +722,7 @@ func (s *baseServer) sendRetry(remoteAddr net.Addr, hdr *wire.Header, info *pack
 	replyHdr.DestConnectionID = hdr.SrcConnectionID
 	replyHdr.Token = token
 	if s.logger.Debug() {
-		s.logger.Debugf("Changing connection ID to %s.", protocol.ConnectionID(srcConnID))
+		s.logger.Debugf("Changing connection ID to %s.", srcConnID)
 		s.logger.Debugf("-> Sending Retry")
 		replyHdr.Log(s.logger)
 	}
@@ -733,7 +748,7 @@ func (s *baseServer) maybeSendInvalidToken(p *receivedPacket, hdr *wire.Header)
 	// This makes sure that we won't send it for packets that were corrupted.
 	sealer, opener := handshake.NewInitialAEAD(hdr.DestConnectionID, protocol.PerspectiveServer, hdr.Version)
 	data := p.data[:hdr.ParsedLen()+hdr.Length]
-	extHdr, err := unpackHeader(opener, hdr, data, hdr.Version)
+	extHdr, err := unpackLongHeader(opener, hdr, data, hdr.Version)
 	if err != nil {
 		if s.config.Tracer != nil {
 			s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropHeaderParseError)
@@ -781,13 +796,14 @@ func (s *baseServer) sendError(remoteAddr net.Addr, hdr *wire.Header, sealer han
 	}
 	payloadOffset := buf.Len()
 
-	if err := ccf.Write(buf, hdr.Version); err != nil {
+	raw := buf.Bytes()
+	raw, err := ccf.Append(raw, hdr.Version)
+	if err != nil {
 		return err
 	}
 
-	raw := buf.Bytes()
 	_ = sealer.Seal(raw[payloadOffset:payloadOffset], raw[payloadOffset:], replyHdr.PacketNumber, raw[:payloadOffset])
-	raw = raw[0 : buf.Len()+sealer.Overhead()]
+	raw = raw[0 : len(raw)+sealer.Overhead()]
 
 	pnOffset := payloadOffset - int(replyHdr.PacketNumberLen)
 	sealer.EncryptHeader(
@@ -801,26 +817,18 @@ func (s *baseServer) sendError(remoteAddr net.Addr, hdr *wire.Header, sealer han
 	if s.config.Tracer != nil {
 		s.config.Tracer.SentPacket(remoteAddr, &replyHdr.Header, protocol.ByteCount(len(raw)), []logging.Frame{ccf})
 	}
-	_, err := s.conn.WritePacket(raw, remoteAddr, info.OOB())
+	_, err = s.conn.WritePacket(raw, remoteAddr, info.OOB())
 	return err
 }
 
-func (s *baseServer) sendVersionNegotiationPacket(p *receivedPacket, hdr *wire.Header) {
-	s.logger.Debugf("Client offered version %s, sending Version Negotiation", hdr.Version)
-	data := wire.ComposeVersionNegotiation(hdr.SrcConnectionID, hdr.DestConnectionID, s.config.Versions)
+func (s *baseServer) sendVersionNegotiationPacket(remote net.Addr, src, dest protocol.ArbitraryLenConnectionID, oob []byte) {
+	s.logger.Debugf("Client offered version %s, sending Version Negotiation")
+
+	data := wire.ComposeVersionNegotiation(dest, src, s.config.Versions)
 	if s.config.Tracer != nil {
-		s.config.Tracer.SentPacket(
-			p.remoteAddr,
-			&wire.Header{
-				IsLongHeader:     true,
-				DestConnectionID: hdr.SrcConnectionID,
-				SrcConnectionID:  hdr.DestConnectionID,
-			},
-			protocol.ByteCount(len(data)),
-			nil,
-		)
+		s.config.Tracer.SentVersionNegotiationPacket(remote, src, dest, s.config.Versions)
 	}
-	if _, err := s.conn.WritePacket(data, p.remoteAddr, p.info.OOB()); err != nil {
+	if _, err := s.conn.WritePacket(data, remote, oob); err != nil {
 		s.logger.Debugf("Error sending Version Negotiation: %s", err)
 	}
 }

+ 29 - 21
vendor/github.com/Psiphon-Labs/quic-go/sys_conn_oob.go

@@ -123,10 +123,15 @@ func newConn(c OOBCapablePacketConn) (*oobConn, error) {
 		bc = ipv4.NewPacketConn(c)
 	}
 
+	msgs := make([]ipv4.Message, batchSize)
+	for i := range msgs {
+		// preallocate the [][]byte
+		msgs[i].Buffers = make([][]byte, 1)
+	}
 	oobConn := &oobConn{
 		OOBCapablePacketConn: c,
 		batchConn:            bc,
-		messages:             make([]ipv4.Message, batchSize),
+		messages:             msgs,
 		readPos:              batchSize,
 	}
 	for i := 0; i < batchSize; i++ {
@@ -143,7 +148,7 @@ func (c *oobConn) ReadPacket() (*receivedPacket, error) {
 			buffer := getPacketBuffer()
 			buffer.Data = buffer.Data[:protocol.MaxPacketBufferSize]
 			c.buffers[i] = buffer
-			c.messages[i].Buffers = [][]byte{c.buffers[i].Data}
+			c.messages[i].Buffers[0] = c.buffers[i].Data
 		}
 		c.readPos = 0
 
@@ -157,18 +162,20 @@ func (c *oobConn) ReadPacket() (*receivedPacket, error) {
 	msg := c.messages[c.readPos]
 	buffer := c.buffers[c.readPos]
 	c.readPos++
-	ctrlMsgs, err := unix.ParseSocketControlMessage(msg.OOB[:msg.NN])
-	if err != nil {
-		return nil, err
-	}
+
+	data := msg.OOB[:msg.NN]
 	var ecn protocol.ECN
 	var destIP net.IP
 	var ifIndex uint32
-	for _, ctrlMsg := range ctrlMsgs {
-		if ctrlMsg.Header.Level == unix.IPPROTO_IP {
-			switch ctrlMsg.Header.Type {
+	for len(data) > 0 {
+		hdr, body, remainder, err := unix.ParseOneSocketControlMessage(data)
+		if err != nil {
+			return nil, err
+		}
+		if hdr.Level == unix.IPPROTO_IP {
+			switch hdr.Type {
 			case msgTypeIPTOS:
-				ecn = protocol.ECN(ctrlMsg.Data[0] & ecnMask)
+				ecn = protocol.ECN(body[0] & ecnMask)
 			case msgTypeIPv4PKTINFO:
 				// struct in_pktinfo {
 				// 	unsigned int   ipi_ifindex;  /* Interface index */
@@ -177,33 +184,34 @@ func (c *oobConn) ReadPacket() (*receivedPacket, error) {
 				// 									address */
 				// };
 				ip := make([]byte, 4)
-				if len(ctrlMsg.Data) == 12 {
-					ifIndex = binary.LittleEndian.Uint32(ctrlMsg.Data)
-					copy(ip, ctrlMsg.Data[8:12])
-				} else if len(ctrlMsg.Data) == 4 {
+				if len(body) == 12 {
+					ifIndex = binary.LittleEndian.Uint32(body)
+					copy(ip, body[8:12])
+				} else if len(body) == 4 {
 					// FreeBSD
-					copy(ip, ctrlMsg.Data)
+					copy(ip, body)
 				}
 				destIP = net.IP(ip)
 			}
 		}
-		if ctrlMsg.Header.Level == unix.IPPROTO_IPV6 {
-			switch ctrlMsg.Header.Type {
+		if hdr.Level == unix.IPPROTO_IPV6 {
+			switch hdr.Type {
 			case unix.IPV6_TCLASS:
-				ecn = protocol.ECN(ctrlMsg.Data[0] & ecnMask)
+				ecn = protocol.ECN(body[0] & ecnMask)
 			case msgTypeIPv6PKTINFO:
 				// struct in6_pktinfo {
 				// 	struct in6_addr ipi6_addr;    /* src/dst IPv6 address */
 				// 	unsigned int    ipi6_ifindex; /* send/recv interface index */
 				// };
-				if len(ctrlMsg.Data) == 20 {
+				if len(body) == 20 {
 					ip := make([]byte, 16)
-					copy(ip, ctrlMsg.Data[:16])
+					copy(ip, body[:16])
 					destIP = net.IP(ip)
-					ifIndex = binary.LittleEndian.Uint32(ctrlMsg.Data[16:])
+					ifIndex = binary.LittleEndian.Uint32(body[16:])
 				}
 			}
 		}
+		data = remainder
 	}
 	var info *packetInfo
 	if destIP != nil {

+ 2 - 1
vendor/github.com/Psiphon-Labs/quic-go/tools.go

@@ -6,6 +6,7 @@ package quic
 import (
 // [Psiphon]
 // Avoid vendoring testing dependencies
+//
 //	_ "github.com/golang/mock/mockgen"
-//	_ "github.com/onsi/ginkgo/ginkgo"
+//	_ "github.com/onsi/ginkgo/v2/ginkgo"
 )

+ 1 - 2
vendor/github.com/marten-seemann/qpack/README.md

@@ -1,10 +1,9 @@
 # QPACK
 
 [![Godoc Reference](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/marten-seemann/qpack)
-[![CircleCI Build Status](https://img.shields.io/circleci/project/github/marten-seemann/qpack.svg?style=flat-square&label=CircleCI+build)](https://circleci.com/gh/marten-seemann/qpack)
 [![Code Coverage](https://img.shields.io/codecov/c/github/marten-seemann/qpack/master.svg?style=flat-square)](https://codecov.io/gh/marten-seemann/qpack)
 
-This is a minimal QPACK implementation in Go. It is minimal in the sense that it doesn't use the dynamic table at all, but just the static table and (Huffman encoded) string literals. Wherever possible, it reuses code from the [HPACK implementation in the Go standard library](https://github.com/golang/net/tree/master/http2/hpack).
+This is a minimal QPACK ([RFC 9204](https://datatracker.ietf.org/doc/html/rfc9204)) implementation in Go. It is minimal in the sense that it doesn't use the dynamic table at all, but just the static table and (Huffman encoded) string literals. Wherever possible, it reuses code from the [HPACK implementation in the Go standard library](https://github.com/golang/net/tree/master/http2/hpack).
 
 It should be able to interoperate with other QPACK implemetations (both encoders and decoders), however it won't achieve a high compression efficiency.
 

+ 2 - 2
vendor/github.com/marten-seemann/qpack/encoder.go

@@ -51,9 +51,9 @@ func (e *Encoder) WriteField(f HeaderField) error {
 		e.writeLiteralFieldWithoutNameReference(f)
 	}
 
-	e.w.Write(e.buf)
+	_, err := e.w.Write(e.buf)
 	e.buf = e.buf[:0]
-	return nil
+	return err
 }
 
 // Close declares that the encoding is complete and resets the Encoder

+ 1 - 0
vendor/github.com/marten-seemann/qpack/static_table.go

@@ -104,6 +104,7 @@ var staticTableEntries = [...]HeaderField{
 
 // Only needed for tests.
 // use go:linkname to retrieve the static table.
+//
 //nolint:deadcode,unused
 func getStaticTable() []HeaderField {
 	return staticTableEntries[:]

+ 14 - 0
vendor/golang.org/x/sys/unix/sockcmsg_unix.go

@@ -52,6 +52,20 @@ func ParseSocketControlMessage(b []byte) ([]SocketControlMessage, error) {
 	return msgs, nil
 }
 
+// ParseOneSocketControlMessage parses a single socket control message from b, returning the message header,
+// message data (a slice of b), and the remainder of b after that single message.
+// When there are no remaining messages, len(remainder) == 0.
+func ParseOneSocketControlMessage(b []byte) (hdr Cmsghdr, data []byte, remainder []byte, err error) {
+	h, dbuf, err := socketControlMessageHeaderAndData(b)
+	if err != nil {
+		return Cmsghdr{}, nil, nil, err
+	}
+	if i := cmsgAlignOf(int(h.Len)); i < len(b) {
+		remainder = b[i:]
+	}
+	return *h, dbuf, remainder, nil
+}
+
 func socketControlMessageHeaderAndData(b []byte) (*Cmsghdr, []byte, error) {
 	h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
 	if h.Len < SizeofCmsghdr || uint64(h.Len) > uint64(len(b)) {

+ 1 - 0
vendor/golang.org/x/sys/unix/syscall_linux.go

@@ -1554,6 +1554,7 @@ func sendmsgN(fd int, iov []Iovec, oob []byte, ptr unsafe.Pointer, salen _Sockle
 				var iova [1]Iovec
 				iova[0].Base = &dummy
 				iova[0].SetLen(1)
+				iov = iova[:]
 			}
 		}
 		msg.Control = &oob[0]

+ 0 - 16
vendor/gopkg.in/yaml.v3/.travis.yml

@@ -1,16 +0,0 @@
-language: go
-
-go:
-    - "1.4.x"
-    - "1.5.x"
-    - "1.6.x"
-    - "1.7.x"
-    - "1.8.x"
-    - "1.9.x"
-    - "1.10.x"
-    - "1.11.x"
-    - "1.12.x"
-    - "1.13.x"
-    - "tip"
-
-go_import_path: gopkg.in/yaml.v3

+ 1 - 0
vendor/gopkg.in/yaml.v3/apic.go

@@ -108,6 +108,7 @@ func yaml_emitter_initialize(emitter *yaml_emitter_t) {
 		raw_buffer: make([]byte, 0, output_raw_buffer_size),
 		states:     make([]yaml_emitter_state_t, 0, initial_stack_size),
 		events:     make([]yaml_event_t, 0, initial_queue_size),
+		best_width: -1,
 	}
 }
 

+ 106 - 37
vendor/gopkg.in/yaml.v3/decode.go

@@ -35,6 +35,7 @@ type parser struct {
 	doc      *Node
 	anchors  map[string]*Node
 	doneInit bool
+	textless bool
 }
 
 func newParser(b []byte) *parser {
@@ -99,7 +100,10 @@ func (p *parser) peek() yaml_event_type_t {
 	if p.event.typ != yaml_NO_EVENT {
 		return p.event.typ
 	}
-	if !yaml_parser_parse(&p.parser, &p.event) {
+	// It's curious choice from the underlying API to generally return a
+	// positive result on success, but on this case return true in an error
+	// scenario. This was the source of bugs in the past (issue #666).
+	if !yaml_parser_parse(&p.parser, &p.event) || p.parser.error != yaml_NO_ERROR {
 		p.fail()
 	}
 	return p.event.typ
@@ -108,14 +112,18 @@ func (p *parser) peek() yaml_event_type_t {
 func (p *parser) fail() {
 	var where string
 	var line int
-	if p.parser.problem_mark.line != 0 {
+	if p.parser.context_mark.line != 0 {
+		line = p.parser.context_mark.line
+		// Scanner errors don't iterate line before returning error
+		if p.parser.error == yaml_SCANNER_ERROR {
+			line++
+		}
+	} else if p.parser.problem_mark.line != 0 {
 		line = p.parser.problem_mark.line
 		// Scanner errors don't iterate line before returning error
 		if p.parser.error == yaml_SCANNER_ERROR {
 			line++
 		}
-	} else if p.parser.context_mark.line != 0 {
-		line = p.parser.context_mark.line
 	}
 	if line != 0 {
 		where = "line " + strconv.Itoa(line) + ": "
@@ -169,17 +177,20 @@ func (p *parser) node(kind Kind, defaultTag, tag, value string) *Node {
 	} else if kind == ScalarNode {
 		tag, _ = resolve("", value)
 	}
-	return &Node{
-		Kind:        kind,
-		Tag:         tag,
-		Value:       value,
-		Style:       style,
-		Line:        p.event.start_mark.line + 1,
-		Column:      p.event.start_mark.column + 1,
-		HeadComment: string(p.event.head_comment),
-		LineComment: string(p.event.line_comment),
-		FootComment: string(p.event.foot_comment),
+	n := &Node{
+		Kind:  kind,
+		Tag:   tag,
+		Value: value,
+		Style: style,
+	}
+	if !p.textless {
+		n.Line = p.event.start_mark.line + 1
+		n.Column = p.event.start_mark.column + 1
+		n.HeadComment = string(p.event.head_comment)
+		n.LineComment = string(p.event.line_comment)
+		n.FootComment = string(p.event.foot_comment)
 	}
+	return n
 }
 
 func (p *parser) parseChild(parent *Node) *Node {
@@ -312,6 +323,8 @@ type decoder struct {
 	decodeCount int
 	aliasCount  int
 	aliasDepth  int
+
+	mergedFields map[interface{}]bool
 }
 
 var (
@@ -497,8 +510,13 @@ func (d *decoder) unmarshal(n *Node, out reflect.Value) (good bool) {
 		good = d.mapping(n, out)
 	case SequenceNode:
 		good = d.sequence(n, out)
+	case 0:
+		if n.IsZero() {
+			return d.null(out)
+		}
+		fallthrough
 	default:
-		panic("internal error: unknown node kind: " + strconv.Itoa(int(n.Kind)))
+		failf("cannot decode node with unknown kind %d", n.Kind)
 	}
 	return good
 }
@@ -533,6 +551,17 @@ func resetMap(out reflect.Value) {
 	}
 }
 
+func (d *decoder) null(out reflect.Value) bool {
+	if out.CanAddr() {
+		switch out.Kind() {
+		case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
+			out.Set(reflect.Zero(out.Type()))
+			return true
+		}
+	}
+	return false
+}
+
 func (d *decoder) scalar(n *Node, out reflect.Value) bool {
 	var tag string
 	var resolved interface{}
@@ -550,14 +579,7 @@ func (d *decoder) scalar(n *Node, out reflect.Value) bool {
 		}
 	}
 	if resolved == nil {
-		if out.CanAddr() {
-			switch out.Kind() {
-			case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
-				out.Set(reflect.Zero(out.Type()))
-				return true
-			}
-		}
-		return false
+		return d.null(out)
 	}
 	if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() {
 		// We've resolved to exactly the type we want, so use that.
@@ -791,16 +813,30 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
 		}
 	}
 
+	mergedFields := d.mergedFields
+	d.mergedFields = nil
+
+	var mergeNode *Node
+
+	mapIsNew := false
 	if out.IsNil() {
 		out.Set(reflect.MakeMap(outt))
+		mapIsNew = true
 	}
 	for i := 0; i < l; i += 2 {
 		if isMerge(n.Content[i]) {
-			d.merge(n.Content[i+1], out)
+			mergeNode = n.Content[i+1]
 			continue
 		}
 		k := reflect.New(kt).Elem()
 		if d.unmarshal(n.Content[i], k) {
+			if mergedFields != nil {
+				ki := k.Interface()
+				if mergedFields[ki] {
+					continue
+				}
+				mergedFields[ki] = true
+			}
 			kkind := k.Kind()
 			if kkind == reflect.Interface {
 				kkind = k.Elem().Kind()
@@ -809,11 +845,17 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
 				failf("invalid map key: %#v", k.Interface())
 			}
 			e := reflect.New(et).Elem()
-			if d.unmarshal(n.Content[i+1], e) {
+			if d.unmarshal(n.Content[i+1], e) || n.Content[i+1].ShortTag() == nullTag && (mapIsNew || !out.MapIndex(k).IsValid()) {
 				out.SetMapIndex(k, e)
 			}
 		}
 	}
+
+	d.mergedFields = mergedFields
+	if mergeNode != nil {
+		d.merge(n, mergeNode, out)
+	}
+
 	d.stringMapType = stringMapType
 	d.generalMapType = generalMapType
 	return true
@@ -825,7 +867,8 @@ func isStringMap(n *Node) bool {
 	}
 	l := len(n.Content)
 	for i := 0; i < l; i += 2 {
-		if n.Content[i].ShortTag() != strTag {
+		shortTag := n.Content[i].ShortTag()
+		if shortTag != strTag && shortTag != mergeTag {
 			return false
 		}
 	}
@@ -842,7 +885,6 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
 	var elemType reflect.Type
 	if sinfo.InlineMap != -1 {
 		inlineMap = out.Field(sinfo.InlineMap)
-		inlineMap.Set(reflect.New(inlineMap.Type()).Elem())
 		elemType = inlineMap.Type().Elem()
 	}
 
@@ -851,6 +893,9 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
 		d.prepare(n, field)
 	}
 
+	mergedFields := d.mergedFields
+	d.mergedFields = nil
+	var mergeNode *Node
 	var doneFields []bool
 	if d.uniqueKeys {
 		doneFields = make([]bool, len(sinfo.FieldsList))
@@ -860,13 +905,20 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
 	for i := 0; i < l; i += 2 {
 		ni := n.Content[i]
 		if isMerge(ni) {
-			d.merge(n.Content[i+1], out)
+			mergeNode = n.Content[i+1]
 			continue
 		}
 		if !d.unmarshal(ni, name) {
 			continue
 		}
-		if info, ok := sinfo.FieldsMap[name.String()]; ok {
+		sname := name.String()
+		if mergedFields != nil {
+			if mergedFields[sname] {
+				continue
+			}
+			mergedFields[sname] = true
+		}
+		if info, ok := sinfo.FieldsMap[sname]; ok {
 			if d.uniqueKeys {
 				if doneFields[info.Id] {
 					d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type()))
@@ -892,6 +944,11 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
 			d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type()))
 		}
 	}
+
+	d.mergedFields = mergedFields
+	if mergeNode != nil {
+		d.merge(n, mergeNode, out)
+	}
 	return true
 }
 
@@ -899,19 +956,29 @@ func failWantMap() {
 	failf("map merge requires map or sequence of maps as the value")
 }
 
-func (d *decoder) merge(n *Node, out reflect.Value) {
-	switch n.Kind {
+func (d *decoder) merge(parent *Node, merge *Node, out reflect.Value) {
+	mergedFields := d.mergedFields
+	if mergedFields == nil {
+		d.mergedFields = make(map[interface{}]bool)
+		for i := 0; i < len(parent.Content); i += 2 {
+			k := reflect.New(ifaceType).Elem()
+			if d.unmarshal(parent.Content[i], k) {
+				d.mergedFields[k.Interface()] = true
+			}
+		}
+	}
+
+	switch merge.Kind {
 	case MappingNode:
-		d.unmarshal(n, out)
+		d.unmarshal(merge, out)
 	case AliasNode:
-		if n.Alias != nil && n.Alias.Kind != MappingNode {
+		if merge.Alias != nil && merge.Alias.Kind != MappingNode {
 			failWantMap()
 		}
-		d.unmarshal(n, out)
+		d.unmarshal(merge, out)
 	case SequenceNode:
-		// Step backwards as earlier nodes take precedence.
-		for i := len(n.Content) - 1; i >= 0; i-- {
-			ni := n.Content[i]
+		for i := 0; i < len(merge.Content); i++ {
+			ni := merge.Content[i]
 			if ni.Kind == AliasNode {
 				if ni.Alias != nil && ni.Alias.Kind != MappingNode {
 					failWantMap()
@@ -924,6 +991,8 @@ func (d *decoder) merge(n *Node, out reflect.Value) {
 	default:
 		failWantMap()
 	}
+
+	d.mergedFields = mergedFields
 }
 
 func isMerge(n *Node) bool {

+ 43 - 15
vendor/gopkg.in/yaml.v3/emitterc.go

@@ -235,10 +235,13 @@ func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool
 			emitter.indent = 0
 		}
 	} else if !indentless {
-		emitter.indent += emitter.best_indent
-		// [Go] If inside a block sequence item, discount the space taken by the indicator.
-		if emitter.best_indent > 2 && emitter.states[len(emitter.states)-1] == yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE {
-			emitter.indent -= 2
+		// [Go] This was changed so that indentations are more regular.
+		if emitter.states[len(emitter.states)-1] == yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE {
+			// The first indent inside a sequence will just skip the "- " indicator.
+			emitter.indent += 2
+		} else {
+			// Everything else aligns to the chosen indentation.
+			emitter.indent = emitter.best_indent*((emitter.indent+emitter.best_indent)/emitter.best_indent)
 		}
 	}
 	return true
@@ -725,16 +728,9 @@ func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_e
 // Expect a block item node.
 func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
 	if first {
-		// [Go] The original logic here would not indent the sequence when inside a mapping.
-		// In Go we always indent it, but take the sequence indicator out of the indentation.
-		indentless := emitter.best_indent == 2 && emitter.mapping_context && (emitter.column == 0 || !emitter.indention)
-		original := emitter.indent
-		if !yaml_emitter_increase_indent(emitter, false, indentless) {
+		if !yaml_emitter_increase_indent(emitter, false, false) {
 			return false
 		}
-		if emitter.indent > original+2 {
-			emitter.indent -= 2
-		}
 	}
 	if event.typ == yaml_SEQUENCE_END_EVENT {
 		emitter.indent = emitter.indents[len(emitter.indents)-1]
@@ -785,6 +781,13 @@ func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_ev
 	if !yaml_emitter_write_indent(emitter) {
 		return false
 	}
+	if len(emitter.line_comment) > 0 {
+		// [Go] A line comment was provided for the key. That's unusual as the
+		//      scanner associates line comments with the value. Either way,
+		//      save the line comment and render it appropriately later.
+		emitter.key_line_comment = emitter.line_comment
+		emitter.line_comment = nil
+	}
 	if yaml_emitter_check_simple_key(emitter) {
 		emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE)
 		return yaml_emitter_emit_node(emitter, event, false, false, true, true)
@@ -810,6 +813,27 @@ func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_
 			return false
 		}
 	}
+	if len(emitter.key_line_comment) > 0 {
+		// [Go] Line comments are generally associated with the value, but when there's
+		//      no value on the same line as a mapping key they end up attached to the
+		//      key itself.
+		if event.typ == yaml_SCALAR_EVENT {
+			if len(emitter.line_comment) == 0 {
+				// A scalar is coming and it has no line comments by itself yet,
+				// so just let it handle the line comment as usual. If it has a
+				// line comment, we can't have both so the one from the key is lost.
+				emitter.line_comment = emitter.key_line_comment
+				emitter.key_line_comment = nil
+			}
+		} else if event.sequence_style() != yaml_FLOW_SEQUENCE_STYLE && (event.typ == yaml_MAPPING_START_EVENT || event.typ == yaml_SEQUENCE_START_EVENT) {
+			// An indented block follows, so write the comment right now.
+			emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment
+			if !yaml_emitter_process_line_comment(emitter) {
+				return false
+			}
+			emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment
+		}
+	}
 	emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE)
 	if !yaml_emitter_emit_node(emitter, event, false, false, true, false) {
 		return false
@@ -823,6 +847,10 @@ func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_
 	return true
 }
 
+func yaml_emitter_silent_nil_event(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+	return event.typ == yaml_SCALAR_EVENT && event.implicit && !emitter.canonical && len(emitter.scalar_data.value) == 0
+}
+
 // Expect a node.
 func yaml_emitter_emit_node(emitter *yaml_emitter_t, event *yaml_event_t,
 	root bool, sequence bool, mapping bool, simple_key bool) bool {
@@ -1866,7 +1894,7 @@ func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bo
 	if !yaml_emitter_write_block_scalar_hints(emitter, value) {
 		return false
 	}
-	if !put_break(emitter) {
+	if !yaml_emitter_process_line_comment(emitter) {
 		return false
 	}
 	//emitter.indention = true
@@ -1903,10 +1931,10 @@ func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) boo
 	if !yaml_emitter_write_block_scalar_hints(emitter, value) {
 		return false
 	}
-
-	if !put_break(emitter) {
+	if !yaml_emitter_process_line_comment(emitter) {
 		return false
 	}
+
 	//emitter.indention = true
 	emitter.whitespace = true
 

+ 23 - 7
vendor/gopkg.in/yaml.v3/encode.go

@@ -119,6 +119,14 @@ func (e *encoder) marshal(tag string, in reflect.Value) {
 	case *Node:
 		e.nodev(in)
 		return
+	case Node:
+		if !in.CanAddr() {
+			var n = reflect.New(in.Type()).Elem()
+			n.Set(in)
+			in = n
+		}
+		e.nodev(in.Addr())
+		return
 	case time.Time:
 		e.timev(tag, in)
 		return
@@ -422,18 +430,23 @@ func (e *encoder) nodev(in reflect.Value) {
 }
 
 func (e *encoder) node(node *Node, tail string) {
+	// Zero nodes behave as nil.
+	if node.Kind == 0 && node.IsZero() {
+		e.nilv()
+		return
+	}
+
 	// If the tag was not explicitly requested, and dropping it won't change the
 	// implicit tag of the value, don't include it in the presentation.
 	var tag = node.Tag
 	var stag = shortTag(tag)
-	var rtag string
 	var forceQuoting bool
 	if tag != "" && node.Style&TaggedStyle == 0 {
 		if node.Kind == ScalarNode {
 			if stag == strTag && node.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0 {
 				tag = ""
 			} else {
-				rtag, _ = resolve("", node.Value)
+				rtag, _ := resolve("", node.Value)
 				if rtag == stag {
 					tag = ""
 				} else if stag == strTag {
@@ -442,6 +455,7 @@ func (e *encoder) node(node *Node, tail string) {
 				}
 			}
 		} else {
+			var rtag string
 			switch node.Kind {
 			case MappingNode:
 				rtag = mapTag
@@ -471,7 +485,7 @@ func (e *encoder) node(node *Node, tail string) {
 		if node.Style&FlowStyle != 0 {
 			style = yaml_FLOW_SEQUENCE_STYLE
 		}
-		e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(tag), tag == "", style))
+		e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style))
 		e.event.head_comment = []byte(node.HeadComment)
 		e.emit()
 		for _, node := range node.Content {
@@ -487,7 +501,7 @@ func (e *encoder) node(node *Node, tail string) {
 		if node.Style&FlowStyle != 0 {
 			style = yaml_FLOW_MAPPING_STYLE
 		}
-		yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(tag), tag == "", style)
+		yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style)
 		e.event.tail_comment = []byte(tail)
 		e.event.head_comment = []byte(node.HeadComment)
 		e.emit()
@@ -528,11 +542,11 @@ func (e *encoder) node(node *Node, tail string) {
 	case ScalarNode:
 		value := node.Value
 		if !utf8.ValidString(value) {
-			if tag == binaryTag {
+			if stag == binaryTag {
 				failf("explicitly tagged !!binary data must be base64-encoded")
 			}
-			if tag != "" {
-				failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag))
+			if stag != "" {
+				failf("cannot marshal invalid UTF-8 data as %s", stag)
 			}
 			// It can't be encoded directly as YAML so use a binary tag
 			// and encode it as base64.
@@ -557,5 +571,7 @@ func (e *encoder) node(node *Node, tail string) {
 		}
 
 		e.emitScalar(value, node.Anchor, tag, style, []byte(node.HeadComment), []byte(node.LineComment), []byte(node.FootComment), []byte(tail))
+	default:
+		failf("cannot encode node with unknown kind %d", node.Kind)
 	}
 }

+ 43 - 14
vendor/gopkg.in/yaml.v3/parserc.go

@@ -648,6 +648,10 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
 			implicit:   implicit,
 			style:      yaml_style_t(yaml_BLOCK_MAPPING_STYLE),
 		}
+		if parser.stem_comment != nil {
+			event.head_comment = parser.stem_comment
+			parser.stem_comment = nil
+		}
 		return true
 	}
 	if len(anchor) > 0 || len(tag) > 0 {
@@ -683,6 +687,9 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
 func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
 	if first {
 		token := peek_token(parser)
+		if token == nil {
+			return false
+		}
 		parser.marks = append(parser.marks, token.start_mark)
 		skip_token(parser)
 	}
@@ -694,25 +701,13 @@ func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_e
 
 	if token.typ == yaml_BLOCK_ENTRY_TOKEN {
 		mark := token.end_mark
-		prior_head := len(parser.head_comment)
+		prior_head_len := len(parser.head_comment)
 		skip_token(parser)
+		yaml_parser_split_stem_comment(parser, prior_head_len)
 		token = peek_token(parser)
 		if token == nil {
 			return false
 		}
-		if prior_head > 0 && token.typ == yaml_BLOCK_SEQUENCE_START_TOKEN {
-			// [Go] It's a sequence under a sequence entry, so the former head comment
-			//      is for the list itself, not the first list item under it.
-			parser.stem_comment = parser.head_comment[:prior_head]
-			if len(parser.head_comment) == prior_head {
-				parser.head_comment = nil
-			} else {
-				// Copy suffix to prevent very strange bugs if someone ever appends
-				// further bytes to the prefix in the stem_comment slice above.
-				parser.head_comment = append([]byte(nil), parser.head_comment[prior_head+1:]...)
-			}
-
-		}
 		if token.typ != yaml_BLOCK_ENTRY_TOKEN && token.typ != yaml_BLOCK_END_TOKEN {
 			parser.states = append(parser.states, yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE)
 			return yaml_parser_parse_node(parser, event, true, false)
@@ -754,7 +749,9 @@ func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *y
 
 	if token.typ == yaml_BLOCK_ENTRY_TOKEN {
 		mark := token.end_mark
+		prior_head_len := len(parser.head_comment)
 		skip_token(parser)
+		yaml_parser_split_stem_comment(parser, prior_head_len)
 		token = peek_token(parser)
 		if token == nil {
 			return false
@@ -780,6 +777,32 @@ func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *y
 	return true
 }
 
+// Split stem comment from head comment.
+//
+// When a sequence or map is found under a sequence entry, the former head comment
+// is assigned to the underlying sequence or map as a whole, not the individual
+// sequence or map entry as would be expected otherwise. To handle this case the
+// previous head comment is moved aside as the stem comment.
+func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) {
+	if stem_len == 0 {
+		return
+	}
+
+	token := peek_token(parser)
+	if token == nil || token.typ != yaml_BLOCK_SEQUENCE_START_TOKEN && token.typ != yaml_BLOCK_MAPPING_START_TOKEN {
+		return
+	}
+
+	parser.stem_comment = parser.head_comment[:stem_len]
+	if len(parser.head_comment) == stem_len {
+		parser.head_comment = nil
+	} else {
+		// Copy suffix to prevent very strange bugs if someone ever appends
+		// further bytes to the prefix in the stem_comment slice above.
+		parser.head_comment = append([]byte(nil), parser.head_comment[stem_len+1:]...)
+	}
+}
+
 // Parse the productions:
 // block_mapping        ::= BLOCK-MAPPING_START
 //                          *******************
@@ -793,6 +816,9 @@ func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *y
 func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
 	if first {
 		token := peek_token(parser)
+		if token == nil {
+			return false
+		}
 		parser.marks = append(parser.marks, token.start_mark)
 		skip_token(parser)
 	}
@@ -902,6 +928,9 @@ func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_ev
 func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
 	if first {
 		token := peek_token(parser)
+		if token == nil {
+			return false
+		}
 		parser.marks = append(parser.marks, token.start_mark)
 		skip_token(parser)
 	}

+ 31 - 18
vendor/gopkg.in/yaml.v3/scannerc.go

@@ -749,6 +749,11 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) (ok bool) {
 		if !ok {
 			return
 		}
+		if len(parser.tokens) > 0 && parser.tokens[len(parser.tokens)-1].typ == yaml_BLOCK_ENTRY_TOKEN {
+			// Sequence indicators alone have no line comments. It becomes
+			// a head comment for whatever follows.
+			return
+		}
 		if !yaml_parser_scan_line_comment(parser, comment_mark) {
 			ok = false
 			return
@@ -2255,10 +2260,9 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l
 		}
 	}
 	if parser.buffer[parser.buffer_pos] == '#' {
-		// TODO Test this and then re-enable it.
-		//if !yaml_parser_scan_line_comment(parser, start_mark) {
-		//	return false
-		//}
+		if !yaml_parser_scan_line_comment(parser, start_mark) {
+			return false
+		}
 		for !is_breakz(parser.buffer, parser.buffer_pos) {
 			skip(parser)
 			if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
@@ -2856,13 +2860,12 @@ func yaml_parser_scan_line_comment(parser *yaml_parser_t, token_mark yaml_mark_t
 						return false
 					}
 					skip_line(parser)
-				} else {
-					if parser.mark.index >= seen {
-						if len(text) == 0 {
-							start_mark = parser.mark
-						}
-						text = append(text, parser.buffer[parser.buffer_pos])
+				} else if parser.mark.index >= seen {
+					if len(text) == 0 {
+						start_mark = parser.mark
 					}
+					text = read(parser, text)
+				} else {
 					skip(parser)
 				}
 			}
@@ -2888,6 +2891,10 @@ func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) boo
 
 	var token_mark = token.start_mark
 	var start_mark yaml_mark_t
+	var next_indent = parser.indent
+	if next_indent < 0 {
+		next_indent = 0
+	}
 
 	var recent_empty = false
 	var first_empty = parser.newlines <= 1
@@ -2919,15 +2926,18 @@ func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) boo
 			continue
 		}
 		c := parser.buffer[parser.buffer_pos+peek]
-		if is_breakz(parser.buffer, parser.buffer_pos+peek) || parser.flow_level > 0 && (c == ']' || c == '}') {
+		var close_flow = parser.flow_level > 0 && (c == ']' || c == '}')
+		if close_flow || is_breakz(parser.buffer, parser.buffer_pos+peek) {
 			// Got line break or terminator.
-			if !recent_empty {
-				if first_empty && (start_mark.line == foot_line || start_mark.column-1 < parser.indent) {
+			if close_flow || !recent_empty {
+				if close_flow || first_empty && (start_mark.line == foot_line && token.typ != yaml_VALUE_TOKEN || start_mark.column-1 < next_indent) {
 					// This is the first empty line and there were no empty lines before,
 					// so this initial part of the comment is a foot of the prior token
 					// instead of being a head for the following one. Split it up.
+					// Alternatively, this might also be the last comment inside a flow
+					// scope, so it must be a footer.
 					if len(text) > 0 {
-						if start_mark.column-1 < parser.indent {
+						if start_mark.column-1 < next_indent {
 							// If dedented it's unrelated to the prior token.
 							token_mark = start_mark
 						}
@@ -2958,7 +2968,7 @@ func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) boo
 			continue
 		}
 
-		if len(text) > 0 && column < parser.indent+1 && column != start_mark.column {
+		if len(text) > 0 && (close_flow || column-1 < next_indent && column != start_mark.column) {
 			// The comment at the different indentation is a foot of the
 			// preceding data rather than a head of the upcoming one.
 			parser.comments = append(parser.comments, yaml_comment_t{
@@ -2999,10 +3009,9 @@ func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) boo
 					return false
 				}
 				skip_line(parser)
+			} else if parser.mark.index >= seen {
+				text = read(parser, text)
 			} else {
-				if parser.mark.index >= seen {
-					text = append(text, parser.buffer[parser.buffer_pos])
-				}
 				skip(parser)
 			}
 		}
@@ -3010,6 +3019,10 @@ func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) boo
 		peek = 0
 		column = 0
 		line = parser.mark.line
+		next_indent = parser.indent
+		if next_indent < 0 {
+			next_indent = 0
+		}
 	}
 
 	if len(text) > 0 {

+ 38 - 2
vendor/gopkg.in/yaml.v3/yaml.go

@@ -89,7 +89,7 @@ func Unmarshal(in []byte, out interface{}) (err error) {
 	return unmarshal(in, out, false)
 }
 
-// A Decorder reads and decodes YAML values from an input stream.
+// A Decoder reads and decodes YAML values from an input stream.
 type Decoder struct {
 	parser      *parser
 	knownFields bool
@@ -194,7 +194,7 @@ func unmarshal(in []byte, out interface{}, strict bool) (err error) {
 //                  Zero valued structs will be omitted if all their public
 //                  fields are zero, unless they implement an IsZero
 //                  method (see the IsZeroer interface type), in which
-//                  case the field will be included if that method returns true.
+//                  case the field will be excluded if IsZero returns true.
 //
 //     flow         Marshal using a flow style (useful for structs,
 //                  sequences and maps).
@@ -252,6 +252,24 @@ func (e *Encoder) Encode(v interface{}) (err error) {
 	return nil
 }
 
+// Encode encodes value v and stores its representation in n.
+//
+// See the documentation for Marshal for details about the
+// conversion of Go values into YAML.
+func (n *Node) Encode(v interface{}) (err error) {
+	defer handleErr(&err)
+	e := newEncoder()
+	defer e.destroy()
+	e.marshalDoc("", reflect.ValueOf(v))
+	e.finish()
+	p := newParser(e.out)
+	p.textless = true
+	defer p.destroy()
+	doc := p.parse()
+	*n = *doc.Content[0]
+	return nil
+}
+
 // SetIndent changes the used indentation used when encoding.
 func (e *Encoder) SetIndent(spaces int) {
 	if spaces < 0 {
@@ -328,6 +346,12 @@ const (
 // and maps, Node is an intermediate representation that allows detailed
 // control over the content being decoded or encoded.
 //
+// It's worth noting that although Node offers access into details such as
+// line numbers, colums, and comments, the content when re-encoded will not
+// have its original textual representation preserved. An effort is made to
+// render the data plesantly, and to preserve comments near the data they
+// describe, though.
+//
 // Values that make use of the Node type interact with the yaml package in the
 // same way any other type would do, by encoding and decoding yaml data
 // directly or indirectly into them.
@@ -391,6 +415,13 @@ type Node struct {
 	Column int
 }
 
+// IsZero returns whether the node has all of its fields unset.
+func (n *Node) IsZero() bool {
+	return n.Kind == 0 && n.Style == 0 && n.Tag == "" && n.Value == "" && n.Anchor == "" && n.Alias == nil && n.Content == nil &&
+		n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0
+}
+
+
 // LongTag returns the long form of the tag that indicates the data type for
 // the node. If the Tag field isn't explicitly defined, one will be computed
 // based on the node properties.
@@ -418,6 +449,11 @@ func (n *Node) ShortTag() string {
 		case ScalarNode:
 			tag, _ := resolve("", n.Value)
 			return tag
+		case 0:
+			// Special case to make the zero value convenient.
+			if n.IsZero() {
+				return nullTag
+			}
 		}
 		return ""
 	}

+ 2 - 0
vendor/gopkg.in/yaml.v3/yamlh.go

@@ -787,6 +787,8 @@ type yaml_emitter_t struct {
 	foot_comment []byte
 	tail_comment []byte
 
+	key_line_comment []byte
+
 	// Dumper stuff
 
 	opened bool // If the stream was already opened?

Некоторые файлы не были показаны из-за большого количества измененных файлов