1
2
3
4
5
6
7 package quic
8
9 import (
10 "bytes"
11 "context"
12 "crypto/rand"
13 "crypto/tls"
14 "errors"
15 "net/netip"
16 "testing"
17 "time"
18 )
19
20 func TestStatelessResetClientSendsStatelessResetTokenTransportParameter(t *testing.T) {
21
22
23 resetToken := testPeerStatelessResetToken(0)
24 tc := newTestConn(t, serverSide, func(p *transportParameters) {
25 p.statelessResetToken = resetToken[:]
26 })
27 tc.writeFrames(packetTypeInitial,
28 debugFrameCrypto{
29 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
30 })
31 tc.wantFrame("client provided stateless_reset_token transport parameter",
32 packetTypeInitial, debugFrameConnectionCloseTransport{
33 code: errTransportParameter,
34 })
35 }
36
37 var testStatelessResetKey = func() (key [32]byte) {
38 if _, err := rand.Read(key[:]); err != nil {
39 panic(err)
40 }
41 return key
42 }()
43
44 func testStatelessResetToken(cid []byte) statelessResetToken {
45 var gen statelessResetTokenGenerator
46 gen.init(testStatelessResetKey)
47 return gen.tokenForConnID(cid)
48 }
49
50 func testLocalStatelessResetToken(seq int64) statelessResetToken {
51 return testStatelessResetToken(testLocalConnID(seq))
52 }
53
54 func newDatagramForReset(cid []byte, size int, addr netip.AddrPort) *datagram {
55 dgram := append([]byte{headerFormShort | fixedBit}, cid...)
56 for len(dgram) < size {
57 dgram = append(dgram, byte(len(dgram)))
58 }
59 return &datagram{
60 b: dgram,
61 addr: addr,
62 }
63 }
64
65 func TestStatelessResetSentSizes(t *testing.T) {
66 config := &Config{
67 TLSConfig: newTestTLSConfig(serverSide),
68 StatelessResetKey: testStatelessResetKey,
69 }
70 addr := netip.MustParseAddr("127.0.0.1")
71 te := newTestEndpoint(t, config)
72 for i, test := range []struct {
73 reqSize int
74 wantSize int
75 }{{
76
77
78
79 reqSize: 1200,
80 wantSize: 42,
81 }, {
82
83
84
85
86 reqSize: 43,
87 wantSize: 42,
88 }, {
89 reqSize: 42,
90 wantSize: 41,
91 }, {
92
93
94
95
96 reqSize: 1 + connIDLen + 1 + 1 + 16,
97 wantSize: 1 + connIDLen + 1 + 1 + 16 - 1,
98 }, {
99
100
101
102 reqSize: 21,
103 wantSize: 0,
104 }} {
105 cid := testLocalConnID(int64(i))
106 token := testStatelessResetToken(cid)
107 addrport := netip.AddrPortFrom(addr, uint16(8000+i))
108 te.write(newDatagramForReset(cid, test.reqSize, addrport))
109
110 got := te.read()
111 if len(got) != test.wantSize {
112 t.Errorf("got %v-byte response to %v-byte req, want %v",
113 len(got), test.reqSize, test.wantSize)
114 }
115 if len(got) == 0 {
116 continue
117 }
118
119
120
121 if isLongHeader(got[0]) {
122 t.Errorf("response to %v-byte request is not a short-header packet\ngot: %x", test.reqSize, got)
123 }
124 if !bytes.HasSuffix(got, token[:]) {
125 t.Errorf("response to %v-byte request does not end in stateless reset token\ngot: %x\nwant suffix: %x", test.reqSize, got, token)
126 }
127 }
128 }
129
130 func TestStatelessResetSuccessfulNewConnectionID(t *testing.T) {
131
132
133 qr := &qlogRecord{}
134 tc := newTestConn(t, clientSide, qr.config)
135 tc.handshake()
136 tc.ignoreFrame(frameTypeAck)
137
138
139 tc.writeFrames(packetType1RTT,
140 debugFrameNewConnectionID{
141 retirePriorTo: 1,
142 seq: 2,
143 connID: testPeerConnID(2),
144 })
145 tc.wantFrame("peer requested we retire conn id 0",
146 packetType1RTT, debugFrameRetireConnectionID{
147 seq: 0,
148 })
149
150 resetToken := testPeerStatelessResetToken(1)
151 dgram := append(make([]byte, 100), resetToken[:]...)
152 tc.endpoint.write(&datagram{
153 b: dgram,
154 })
155
156 if err := tc.conn.Wait(canceledContext()); !errors.Is(err, errStatelessReset) {
157 t.Errorf("conn.Wait() = %v, want errStatelessReset", err)
158 }
159 tc.wantIdle("closed connection is idle in draining")
160 tc.advance(1 * time.Second)
161 tc.wantIdle("closed connection is idle after draining")
162
163 qr.wantEvents(t, jsonEvent{
164 "name": "connectivity:connection_closed",
165 "data": map[string]any{
166 "trigger": "stateless_reset",
167 },
168 })
169 }
170
171 func TestStatelessResetSuccessfulTransportParameter(t *testing.T) {
172
173
174
175 resetToken := testPeerStatelessResetToken(0)
176 tc := newTestConn(t, clientSide, func(p *transportParameters) {
177 p.statelessResetToken = resetToken[:]
178 })
179 tc.handshake()
180
181 dgram := append(make([]byte, 100), resetToken[:]...)
182 tc.endpoint.write(&datagram{
183 b: dgram,
184 })
185
186 if err := tc.conn.Wait(canceledContext()); !errors.Is(err, errStatelessReset) {
187 t.Errorf("conn.Wait() = %v, want errStatelessReset", err)
188 }
189 tc.wantIdle("closed connection is idle")
190 }
191
192 func TestStatelessResetSuccessfulPrefix(t *testing.T) {
193 for _, test := range []struct {
194 name string
195 prefix []byte
196 size int
197 }{{
198 name: "short header and fixed bit",
199 prefix: []byte{
200 headerFormShort | fixedBit,
201 },
202 size: 100,
203 }, {
204
205
206
207 name: "long header no fixed bit",
208 prefix: []byte{
209 headerFormLong,
210 },
211 size: 100,
212 }, {
213
214
215
216 name: "short header valid DCID",
217 prefix: append([]byte{
218 headerFormShort | fixedBit,
219 }, testLocalConnID(0)...),
220 size: 100,
221 }, {
222 name: "handshake valid DCID",
223 prefix: append([]byte{
224 headerFormLong | fixedBit | longPacketTypeHandshake,
225 }, testLocalConnID(0)...),
226 size: 100,
227 }, {
228 name: "no fixed bit valid DCID",
229 prefix: append([]byte{
230 0,
231 }, testLocalConnID(0)...),
232 size: 100,
233 }} {
234 t.Run(test.name, func(t *testing.T) {
235 resetToken := testPeerStatelessResetToken(0)
236 tc := newTestConn(t, clientSide, func(p *transportParameters) {
237 p.statelessResetToken = resetToken[:]
238 })
239 tc.handshake()
240
241 dgram := test.prefix
242 for len(dgram) < test.size-len(resetToken) {
243 dgram = append(dgram, byte(len(dgram)))
244 }
245 dgram = append(dgram, resetToken[:]...)
246 tc.endpoint.write(&datagram{
247 b: dgram,
248 })
249 if err := tc.conn.Wait(canceledContext()); !errors.Is(err, errStatelessReset) {
250 t.Errorf("conn.Wait() = %v, want errStatelessReset", err)
251 }
252 })
253 }
254 }
255
256 func TestStatelessResetRetiredConnID(t *testing.T) {
257
258
259
260 resetToken := testPeerStatelessResetToken(0)
261 tc := newTestConn(t, clientSide, func(p *transportParameters) {
262 p.statelessResetToken = resetToken[:]
263 })
264 tc.handshake()
265 tc.ignoreFrame(frameTypeAck)
266
267
268 tc.writeFrames(packetType1RTT,
269 debugFrameNewConnectionID{
270 seq: 2,
271 retirePriorTo: 1,
272 connID: testPeerConnID(2),
273 })
274 tc.wantFrame("peer asked for conn id 0 to be retired",
275 packetType1RTT, debugFrameRetireConnectionID{
276 seq: 0,
277 })
278
279
280 dgram := append(make([]byte, 100), resetToken[:]...)
281 tc.endpoint.write(&datagram{
282 b: dgram,
283 })
284
285 if err := tc.conn.Wait(canceledContext()); !errors.Is(err, context.Canceled) {
286 t.Errorf("conn.Wait() = %v, want connection to be alive", err)
287 }
288 }
289
View as plain text