1
2
3
4
5
6
7 package quic
8
9 import (
10 "bytes"
11 "crypto/tls"
12 "fmt"
13 "net/netip"
14 "strings"
15 "testing"
16 )
17
18 func TestConnIDClientHandshake(t *testing.T) {
19 tc := newTestConn(t, clientSide)
20
21
22
23
24 if got, want := tc.conn.connIDState.srcConnID(), testLocalConnID(0); !bytes.Equal(got, want) {
25 t.Errorf("after initialization: srcConnID = %x, want %x", got, want)
26 }
27 dstConnID, _ := tc.conn.connIDState.dstConnID()
28 if got, want := dstConnID, testLocalConnID(-1); !bytes.Equal(got, want) {
29 t.Errorf("after initialization: dstConnID = %x, want %x", got, want)
30 }
31
32
33
34 tc.writeFrames(packetTypeInitial,
35 debugFrameCrypto{
36 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
37 })
38 dstConnID, _ = tc.conn.connIDState.dstConnID()
39 if got, want := dstConnID, testPeerConnID(0); !bytes.Equal(got, want) {
40 t.Errorf("after receiving Initial: dstConnID = %x, want %x", got, want)
41 }
42
43 wantLocal := []connID{{
44 cid: testLocalConnID(0),
45 seq: 0,
46 }}
47 if got := tc.conn.connIDState.local; !connIDListEqual(got, wantLocal) {
48 t.Errorf("local ids: %v, want %v", fmtConnIDList(got), fmtConnIDList(wantLocal))
49 }
50 wantRemote := []remoteConnID{{
51 connID: connID{
52 cid: testPeerConnID(0),
53 seq: 0,
54 },
55 }}
56 if got := tc.conn.connIDState.remote; !remoteConnIDListEqual(got, wantRemote) {
57 t.Errorf("remote ids: %v, want %v", fmtRemoteConnIDList(got), fmtRemoteConnIDList(wantRemote))
58 }
59 }
60
61 func TestConnIDServerHandshake(t *testing.T) {
62 tc := newTestConn(t, serverSide)
63
64
65
66 tc.writeFrames(packetTypeInitial,
67 debugFrameCrypto{
68 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial][:1],
69 })
70 if got, want := tc.conn.connIDState.srcConnID(), testLocalConnID(0); !bytes.Equal(got, want) {
71 t.Errorf("after initClient: srcConnID = %q, want %q", got, want)
72 }
73 dstConnID, _ := tc.conn.connIDState.dstConnID()
74 if got, want := dstConnID, testPeerConnID(0); !bytes.Equal(got, want) {
75 t.Errorf("after initClient: dstConnID = %q, want %q", got, want)
76 }
77
78
79
80 tc.writeFrames(packetTypeInitial,
81 debugFrameCrypto{
82 off: 1,
83 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial][1:],
84 })
85 wantLocal := []connID{{
86 cid: testPeerConnID(-1),
87 seq: -1,
88 }, {
89 cid: testLocalConnID(0),
90 seq: 0,
91 }, {
92 cid: testLocalConnID(1),
93 seq: 1,
94 }}
95 if got := tc.conn.connIDState.local; !connIDListEqual(got, wantLocal) {
96 t.Errorf("local ids: %v, want %v", fmtConnIDList(got), fmtConnIDList(wantLocal))
97 }
98 wantRemote := []remoteConnID{{
99 connID: connID{
100 cid: testPeerConnID(0),
101 seq: 0,
102 },
103 }}
104 if got := tc.conn.connIDState.remote; !remoteConnIDListEqual(got, wantRemote) {
105 t.Errorf("remote ids: %v, want %v", fmtRemoteConnIDList(got), fmtRemoteConnIDList(wantRemote))
106 }
107
108
109
110 tc.writeFrames(packetTypeHandshake,
111 debugFrameCrypto{
112 data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake],
113 })
114 wantLocal = []connID{{
115 cid: testLocalConnID(0),
116 seq: 0,
117 }, {
118 cid: testLocalConnID(1),
119 seq: 1,
120 }}
121 if got := tc.conn.connIDState.local; !connIDListEqual(got, wantLocal) {
122 t.Errorf("local ids: %v, want %v", fmtConnIDList(got), fmtConnIDList(wantLocal))
123 }
124 }
125
126 func connIDListEqual(a, b []connID) bool {
127 if len(a) != len(b) {
128 return false
129 }
130 for i := range a {
131 if a[i].seq != b[i].seq {
132 return false
133 }
134 if !bytes.Equal(a[i].cid, b[i].cid) {
135 return false
136 }
137 }
138 return true
139 }
140
141 func remoteConnIDListEqual(a, b []remoteConnID) bool {
142 if len(a) != len(b) {
143 return false
144 }
145 for i := range a {
146 if a[i].seq != b[i].seq {
147 return false
148 }
149 if !bytes.Equal(a[i].cid, b[i].cid) {
150 return false
151 }
152 if a[i].resetToken != b[i].resetToken {
153 return false
154 }
155 }
156 return true
157 }
158
159 func fmtConnIDList(s []connID) string {
160 var strs []string
161 for _, cid := range s {
162 strs = append(strs, fmt.Sprintf("[seq:%v cid:{%x}]", cid.seq, cid.cid))
163 }
164 return "{" + strings.Join(strs, " ") + "}"
165 }
166
167 func fmtRemoteConnIDList(s []remoteConnID) string {
168 var strs []string
169 for _, cid := range s {
170 strs = append(strs, fmt.Sprintf("[seq:%v cid:{%x} token:{%x}]", cid.seq, cid.cid, cid.resetToken))
171 }
172 return "{" + strings.Join(strs, " ") + "}"
173 }
174
175 func TestNewRandomConnID(t *testing.T) {
176 cid, err := newRandomConnID(0)
177 if len(cid) != connIDLen || err != nil {
178 t.Fatalf("newConnID() = %x, %v; want %v bytes", cid, connIDLen, err)
179 }
180 }
181
182 func TestConnIDPeerRequestsManyIDs(t *testing.T) {
183
184
185
186
187
188
189
190
191
192
193 tc := newTestConn(t, serverSide, func(p *transportParameters) {
194 p.activeConnIDLimit = 100
195 })
196 tc.ignoreFrame(frameTypeAck)
197 tc.ignoreFrame(frameTypeCrypto)
198
199 tc.writeFrames(packetTypeInitial,
200 debugFrameCrypto{
201 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
202 })
203 tc.wantFrame("provide additional connection ID 1",
204 packetType1RTT, debugFrameNewConnectionID{
205 seq: 1,
206 connID: testLocalConnID(1),
207 token: testLocalStatelessResetToken(1),
208 })
209 tc.wantFrame("provide additional connection ID 2",
210 packetType1RTT, debugFrameNewConnectionID{
211 seq: 2,
212 connID: testLocalConnID(2),
213 token: testLocalStatelessResetToken(2),
214 })
215 tc.wantFrame("provide additional connection ID 3",
216 packetType1RTT, debugFrameNewConnectionID{
217 seq: 3,
218 connID: testLocalConnID(3),
219 token: testLocalStatelessResetToken(3),
220 })
221 tc.wantIdle("connection ID limit reached, no more to provide")
222 }
223
224 func TestConnIDPeerProvidesTooManyIDs(t *testing.T) {
225
226
227 tc := newTestConn(t, serverSide)
228 tc.handshake()
229 tc.ignoreFrame(frameTypeAck)
230
231 tc.writeFrames(packetType1RTT,
232 debugFrameNewConnectionID{
233 seq: 2,
234 connID: testLocalConnID(2),
235 })
236 tc.wantFrame("peer provided 3 connection IDs, our limit is 2",
237 packetType1RTT, debugFrameConnectionCloseTransport{
238 code: errConnectionIDLimit,
239 })
240 }
241
242 func TestConnIDPeerTemporarilyExceedsActiveConnIDLimit(t *testing.T) {
243
244
245
246 tc := newTestConn(t, serverSide)
247 tc.handshake()
248 tc.ignoreFrame(frameTypeAck)
249
250 tc.writeFrames(packetType1RTT,
251 debugFrameNewConnectionID{
252 retirePriorTo: 2,
253 seq: 2,
254 connID: testPeerConnID(2),
255 }, debugFrameNewConnectionID{
256 retirePriorTo: 2,
257 seq: 3,
258 connID: testPeerConnID(3),
259 })
260 tc.wantFrame("peer requested we retire conn id 0",
261 packetType1RTT, debugFrameRetireConnectionID{
262 seq: 0,
263 })
264 tc.wantFrame("peer requested we retire conn id 1",
265 packetType1RTT, debugFrameRetireConnectionID{
266 seq: 1,
267 })
268 }
269
270 func TestConnIDPeerRetiresConnID(t *testing.T) {
271
272
273 for _, side := range []connSide{
274 clientSide,
275 serverSide,
276 } {
277 t.Run(side.String(), func(t *testing.T) {
278 tc := newTestConn(t, side)
279 tc.handshake()
280 tc.ignoreFrame(frameTypeAck)
281
282 tc.writeFrames(packetType1RTT,
283 debugFrameRetireConnectionID{
284 seq: 0,
285 })
286 tc.wantFrame("provide replacement connection ID",
287 packetType1RTT, debugFrameNewConnectionID{
288 seq: 2,
289 retirePriorTo: 1,
290 connID: testLocalConnID(2),
291 token: testLocalStatelessResetToken(2),
292 })
293 })
294 }
295 }
296
297 func TestConnIDPeerWithZeroLengthConnIDSendsNewConnectionID(t *testing.T) {
298
299
300
301 tc := newTestConn(t, clientSide, func(p *transportParameters) {
302 p.initialSrcConnID = []byte{}
303 })
304 tc.peerConnID = []byte{}
305 tc.ignoreFrame(frameTypeAck)
306 tc.uncheckedHandshake()
307
308 tc.writeFrames(packetType1RTT,
309 debugFrameNewConnectionID{
310 seq: 1,
311 connID: testPeerConnID(1),
312 })
313 tc.wantFrame("invalid NEW_CONNECTION_ID: previous conn id is zero-length",
314 packetType1RTT, debugFrameConnectionCloseTransport{
315 code: errProtocolViolation,
316 })
317 }
318
319 func TestConnIDPeerRequestsRetirement(t *testing.T) {
320
321
322
323
324 tc := newTestConn(t, clientSide)
325 tc.handshake()
326 tc.ignoreFrame(frameTypeAck)
327
328 tc.writeFrames(packetType1RTT,
329 debugFrameNewConnectionID{
330 seq: 2,
331 retirePriorTo: 1,
332 connID: testPeerConnID(2),
333 })
334 tc.wantFrame("peer asked for conn id 0 to be retired",
335 packetType1RTT, debugFrameRetireConnectionID{
336 seq: 0,
337 })
338 if got, want := tc.lastPacket.dstConnID, testPeerConnID(1); !bytes.Equal(got, want) {
339 t.Fatalf("used destination conn id {%x}, want {%x}", got, want)
340 }
341 }
342
343 func TestConnIDPeerDoesNotAcknowledgeRetirement(t *testing.T) {
344
345
346
347 tc := newTestConn(t, clientSide)
348 tc.handshake()
349 tc.ignoreFrame(frameTypeAck)
350 tc.ignoreFrame(frameTypeRetireConnectionID)
351
352
353 for seq := int64(0); seq < 7; seq++ {
354 tc.writeFrames(packetType1RTT,
355 debugFrameNewConnectionID{
356 seq: seq + 2,
357 retirePriorTo: seq + 1,
358 connID: testPeerConnID(seq + 2),
359 })
360
361 }
362 tc.wantFrame("number of retired, unacked conn ids is too large",
363 packetType1RTT, debugFrameConnectionCloseTransport{
364 code: errConnectionIDLimit,
365 })
366 }
367
368 func TestConnIDRepeatedNewConnectionIDFrame(t *testing.T) {
369
370
371
372 tc := newTestConn(t, clientSide)
373 tc.handshake()
374 tc.ignoreFrame(frameTypeAck)
375
376 for i := 0; i < 4; i++ {
377 tc.writeFrames(packetType1RTT,
378 debugFrameNewConnectionID{
379 seq: 2,
380 retirePriorTo: 1,
381 connID: testPeerConnID(2),
382 })
383 }
384 tc.wantFrame("peer asked for conn id to be retired",
385 packetType1RTT, debugFrameRetireConnectionID{
386 seq: 0,
387 })
388 tc.wantIdle("repeated NEW_CONNECTION_ID frames are not an error")
389 }
390
391 func TestConnIDForSequenceNumberChanges(t *testing.T) {
392
393
394
395
396 tc := newTestConn(t, clientSide)
397 tc.handshake()
398 tc.ignoreFrame(frameTypeAck)
399 tc.ignoreFrame(frameTypeRetireConnectionID)
400
401 tc.writeFrames(packetType1RTT,
402 debugFrameNewConnectionID{
403 seq: 2,
404 retirePriorTo: 1,
405 connID: testPeerConnID(2),
406 })
407 tc.writeFrames(packetType1RTT,
408 debugFrameNewConnectionID{
409 seq: 2,
410 retirePriorTo: 1,
411 connID: testPeerConnID(3),
412 })
413 tc.wantFrame("connection ID for sequence 0 has changed",
414 packetType1RTT, debugFrameConnectionCloseTransport{
415 code: errProtocolViolation,
416 })
417 }
418
419 func TestConnIDRetirePriorToAfterNewConnID(t *testing.T) {
420
421
422
423
424 tc := newTestConn(t, serverSide)
425 tc.handshake()
426 tc.ignoreFrame(frameTypeAck)
427
428 tc.writeFrames(packetType1RTT,
429 debugFrameNewConnectionID{
430 retirePriorTo: 3,
431 seq: 2,
432 connID: testPeerConnID(2),
433 })
434 tc.wantFrame("invalid NEW_CONNECTION_ID: retired the new conn id",
435 packetType1RTT, debugFrameConnectionCloseTransport{
436 code: errFrameEncoding,
437 })
438 }
439
440 func TestConnIDAlreadyRetired(t *testing.T) {
441
442
443
444
445
446 tc := newTestConn(t, clientSide)
447 tc.handshake()
448 tc.ignoreFrame(frameTypeAck)
449
450 tc.writeFrames(packetType1RTT,
451 debugFrameNewConnectionID{
452 seq: 4,
453 retirePriorTo: 3,
454 connID: testPeerConnID(4),
455 })
456 tc.wantFrame("peer asked for conn id to be retired",
457 packetType1RTT, debugFrameRetireConnectionID{
458 seq: 0,
459 })
460 tc.wantFrame("peer asked for conn id to be retired",
461 packetType1RTT, debugFrameRetireConnectionID{
462 seq: 1,
463 })
464 tc.writeFrames(packetType1RTT,
465 debugFrameNewConnectionID{
466 seq: 2,
467 retirePriorTo: 0,
468 connID: testPeerConnID(2),
469 })
470 tc.wantFrame("NEW_CONNECTION_ID was for an already-retired ID",
471 packetType1RTT, debugFrameRetireConnectionID{
472 seq: 2,
473 })
474 }
475
476 func TestConnIDRepeatedRetireConnectionIDFrame(t *testing.T) {
477 tc := newTestConn(t, clientSide)
478 tc.handshake()
479 tc.ignoreFrame(frameTypeAck)
480
481 for i := 0; i < 4; i++ {
482 tc.writeFrames(packetType1RTT,
483 debugFrameRetireConnectionID{
484 seq: 0,
485 })
486 }
487 tc.wantFrame("issue new conn id after peer retires one",
488 packetType1RTT, debugFrameNewConnectionID{
489 retirePriorTo: 1,
490 seq: 2,
491 connID: testLocalConnID(2),
492 token: testLocalStatelessResetToken(2),
493 })
494 tc.wantIdle("repeated RETIRE_CONNECTION_ID frames are not an error")
495 }
496
497 func TestConnIDRetiredUnsent(t *testing.T) {
498
499
500
501
502 tc := newTestConn(t, clientSide)
503 tc.handshake()
504 tc.ignoreFrame(frameTypeAck)
505
506 tc.writeFrames(packetType1RTT,
507 debugFrameRetireConnectionID{
508 seq: 2,
509 })
510 tc.wantFrame("invalid NEW_CONNECTION_ID: previous conn id is zero-length",
511 packetType1RTT, debugFrameConnectionCloseTransport{
512 code: errProtocolViolation,
513 })
514 }
515
516 func TestConnIDUsePreferredAddressConnID(t *testing.T) {
517
518
519
520
521
522 cid := testPeerConnID(10)
523 tc := newTestConn(t, serverSide, func(p *transportParameters) {
524 p.preferredAddrV4 = netip.MustParseAddrPort("0.0.0.0:0")
525 p.preferredAddrV6 = netip.MustParseAddrPort("[::0]:0")
526 p.preferredAddrConnID = cid
527 p.preferredAddrResetToken = make([]byte, 16)
528 })
529 tc.uncheckedHandshake()
530 tc.ignoreFrame(frameTypeAck)
531
532 tc.writeFrames(packetType1RTT,
533 debugFrameNewConnectionID{
534 seq: 2,
535 retirePriorTo: 1,
536 connID: []byte{0xff},
537 })
538 tc.wantFrame("peer asked for conn id 0 to be retired",
539 packetType1RTT, debugFrameRetireConnectionID{
540 seq: 0,
541 })
542 if got, want := tc.lastPacket.dstConnID, cid; !bytes.Equal(got, want) {
543 t.Fatalf("used destination conn id {%x}, want {%x} from preferred address transport parameter", got, want)
544 }
545 }
546
547 func TestConnIDPeerProvidesPreferredAddrAndTooManyConnIDs(t *testing.T) {
548
549
550 cid := testPeerConnID(10)
551 tc := newTestConn(t, serverSide, func(p *transportParameters) {
552 p.preferredAddrV4 = netip.MustParseAddrPort("0.0.0.0:0")
553 p.preferredAddrV6 = netip.MustParseAddrPort("[::0]:0")
554 p.preferredAddrConnID = cid
555 p.preferredAddrResetToken = make([]byte, 16)
556 })
557 tc.uncheckedHandshake()
558 tc.ignoreFrame(frameTypeAck)
559
560 tc.writeFrames(packetType1RTT,
561 debugFrameNewConnectionID{
562 seq: 2,
563 retirePriorTo: 0,
564 connID: testPeerConnID(2),
565 })
566 tc.wantFrame("peer provided 3 connection IDs, our limit is 2",
567 packetType1RTT, debugFrameConnectionCloseTransport{
568 code: errConnectionIDLimit,
569 })
570 }
571
572 func TestConnIDPeerWithZeroLengthIDProvidesPreferredAddr(t *testing.T) {
573
574
575 tc := newTestConn(t, serverSide, func(p *transportParameters) {
576 p.initialSrcConnID = []byte{}
577 p.preferredAddrV4 = netip.MustParseAddrPort("0.0.0.0:0")
578 p.preferredAddrV6 = netip.MustParseAddrPort("[::0]:0")
579 p.preferredAddrConnID = testPeerConnID(1)
580 p.preferredAddrResetToken = make([]byte, 16)
581 }, func(cids *newServerConnIDs) {
582 cids.srcConnID = []byte{}
583 }, func(tc *testConn) {
584 tc.peerConnID = []byte{}
585 })
586
587 tc.writeFrames(packetTypeInitial,
588 debugFrameCrypto{
589 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
590 })
591 tc.wantFrame("peer with zero-length connection ID tried to provide another in transport parameters",
592 packetTypeInitial, debugFrameConnectionCloseTransport{
593 code: errProtocolViolation,
594 })
595 }
596
597 func TestConnIDInitialSrcConnIDMismatch(t *testing.T) {
598
599
600
601 testSides(t, "", func(t *testing.T, side connSide) {
602 tc := newTestConn(t, side, func(p *transportParameters) {
603 p.initialSrcConnID = []byte("invalid")
604 })
605 tc.ignoreFrame(frameTypeAck)
606 tc.ignoreFrame(frameTypeCrypto)
607 tc.writeFrames(packetTypeInitial,
608 debugFrameCrypto{
609 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
610 })
611 if side == clientSide {
612
613 tc.writeFrames(packetTypeHandshake,
614 debugFrameCrypto{
615 data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake],
616 })
617 }
618 tc.wantFrame("initial_source_connection_id transport parameter mismatch",
619 packetTypeInitial, debugFrameConnectionCloseTransport{
620 code: errTransportParameter,
621 })
622 })
623 }
624
625 func TestConnIDsCleanedUpAfterClose(t *testing.T) {
626 testSides(t, "", func(t *testing.T, side connSide) {
627 tc := newTestConn(t, side, func(p *transportParameters) {
628 if side == clientSide {
629 token := testPeerStatelessResetToken(0)
630 p.statelessResetToken = token[:]
631 }
632 })
633 tc.handshake()
634 tc.ignoreFrame(frameTypeAck)
635 tc.writeFrames(packetType1RTT,
636 debugFrameNewConnectionID{
637 seq: 2,
638 retirePriorTo: 1,
639 connID: testPeerConnID(2),
640 token: testPeerStatelessResetToken(0),
641 })
642 tc.wantFrame("peer asked for conn id 0 to be retired",
643 packetType1RTT, debugFrameRetireConnectionID{
644 seq: 0,
645 })
646 tc.writeFrames(packetType1RTT, debugFrameConnectionCloseTransport{})
647 tc.conn.Abort(nil)
648 tc.wantFrame("CONN_CLOSE sent after user closes connection",
649 packetType1RTT, debugFrameConnectionCloseTransport{})
650
651
652
653
654
655 tc.advanceToTimer()
656 <-tc.conn.donec
657 tc.endpoint.e.connsMap.applyUpdates()
658
659 if got := len(tc.endpoint.e.connsMap.byConnID); got != 0 {
660 t.Errorf("%v conn ids in endpoint map after closing, want 0", got)
661 }
662 if got := len(tc.endpoint.e.connsMap.byResetToken); got != 0 {
663 t.Errorf("%v reset tokens in endpoint map after closing, want 0", got)
664 }
665 })
666 }
667
View as plain text