golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/quic/packet_codec_test.go (about) 1 // Copyright 2023 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build go1.21 6 7 package quic 8 9 import ( 10 "bytes" 11 "crypto/tls" 12 "io" 13 "log/slog" 14 "reflect" 15 "testing" 16 "time" 17 18 "golang.org/x/net/quic/qlog" 19 ) 20 21 func TestParseLongHeaderPacket(t *testing.T) { 22 // Example Initial packet from: 23 // https://www.rfc-editor.org/rfc/rfc9001.html#section-a.3 24 cid := unhex(`8394c8f03e515708`) 25 initialServerKeys := initialKeys(cid, clientSide).r 26 pkt := unhex(` 27 cf000000010008f067a5502a4262b500 4075c0d95a482cd0991cd25b0aac406a 28 5816b6394100f37a1c69797554780bb3 8cc5a99f5ede4cf73c3ec2493a1839b3 29 dbcba3f6ea46c5b7684df3548e7ddeb9 c3bf9c73cc3f3bded74b562bfb19fb84 30 022f8ef4cdd93795d77d06edbb7aaf2f 58891850abbdca3d20398c276456cbc4 31 2158407dd074ee 32 `) 33 want := longPacket{ 34 ptype: packetTypeInitial, 35 version: 1, 36 num: 1, 37 dstConnID: []byte{}, 38 srcConnID: unhex(`f067a5502a4262b5`), 39 payload: unhex(` 40 02000000000600405a020000560303ee fce7f7b37ba1d1632e96677825ddf739 41 88cfc79825df566dc5430b9a045a1200 130100002e00330024001d00209d3c94 42 0d89690b84d08a60993c144eca684d10 81287c834d5311bcf32bb9da1a002b00 43 020304 44 `), 45 extra: []byte{}, 46 } 47 48 // Parse the packet. 49 got, n := parseLongHeaderPacket(pkt, initialServerKeys, 0) 50 if n != len(pkt) { 51 t.Errorf("parseLongHeaderPacket: n=%v, want %v", n, len(pkt)) 52 } 53 if !reflect.DeepEqual(got, want) { 54 t.Errorf("parseLongHeaderPacket:\n got: %+v\nwant: %+v", got, want) 55 } 56 57 // Skip the packet. 58 if got, want := skipLongHeaderPacket(pkt), len(pkt); got != want { 59 t.Errorf("skipLongHeaderPacket: n=%v, want %v", got, want) 60 } 61 62 // Parse truncated versions of the packet; every attempt should fail. 63 for i := 0; i < len(pkt); i++ { 64 if _, n := parseLongHeaderPacket(pkt[:i], initialServerKeys, 0); n != -1 { 65 t.Fatalf("parse truncated long header packet: n=%v, want -1\ninput: %x", n, pkt[:i]) 66 } 67 if n := skipLongHeaderPacket(pkt[:i]); n != -1 { 68 t.Errorf("skip truncated long header packet: n=%v, want -1", n) 69 } 70 } 71 72 // Parse with the wrong keys. 73 invalidKeys := initialKeys([]byte{}, clientSide).w 74 if _, n := parseLongHeaderPacket(pkt, invalidKeys, 0); n != -1 { 75 t.Fatalf("parse long header packet with wrong keys: n=%v, want -1", n) 76 } 77 } 78 79 func TestRoundtripEncodeLongPacket(t *testing.T) { 80 var aes128Keys, aes256Keys, chachaKeys fixedKeys 81 aes128Keys.init(tls.TLS_AES_128_GCM_SHA256, []byte("secret")) 82 aes256Keys.init(tls.TLS_AES_256_GCM_SHA384, []byte("secret")) 83 chachaKeys.init(tls.TLS_CHACHA20_POLY1305_SHA256, []byte("secret")) 84 for _, test := range []struct { 85 desc string 86 p longPacket 87 k fixedKeys 88 }{{ 89 desc: "Initial, 1-byte number, AES128", 90 p: longPacket{ 91 ptype: packetTypeInitial, 92 version: 0x11223344, 93 num: 0, // 1-byte encodeing 94 dstConnID: []byte{1, 2, 3, 4}, 95 srcConnID: []byte{5, 6, 7, 8}, 96 payload: []byte("payload"), 97 extra: []byte("token"), 98 }, 99 k: aes128Keys, 100 }, { 101 desc: "0-RTT, 2-byte number, AES256", 102 p: longPacket{ 103 ptype: packetType0RTT, 104 version: 0x11223344, 105 num: 0x100, // 2-byte encoding 106 dstConnID: []byte{1, 2, 3, 4}, 107 srcConnID: []byte{5, 6, 7, 8}, 108 payload: []byte("payload"), 109 }, 110 k: aes256Keys, 111 }, { 112 desc: "0-RTT, 3-byte number, AES256", 113 p: longPacket{ 114 ptype: packetType0RTT, 115 version: 0x11223344, 116 num: 0x10000, // 2-byte encoding 117 dstConnID: []byte{1, 2, 3, 4}, 118 srcConnID: []byte{5, 6, 7, 8}, 119 payload: []byte{0}, 120 }, 121 k: aes256Keys, 122 }, { 123 desc: "Handshake, 4-byte number, ChaCha20Poly1305", 124 p: longPacket{ 125 ptype: packetTypeHandshake, 126 version: 0x11223344, 127 num: 0x1000000, // 4-byte encoding 128 dstConnID: []byte{1, 2, 3, 4}, 129 srcConnID: []byte{5, 6, 7, 8}, 130 payload: []byte("payload"), 131 }, 132 k: chachaKeys, 133 }} { 134 t.Run(test.desc, func(t *testing.T) { 135 var w packetWriter 136 w.reset(1200) 137 w.startProtectedLongHeaderPacket(0, test.p) 138 w.b = append(w.b, test.p.payload...) 139 w.finishProtectedLongHeaderPacket(0, test.k, test.p) 140 pkt := w.datagram() 141 142 got, n := parseLongHeaderPacket(pkt, test.k, 0) 143 if n != len(pkt) { 144 t.Errorf("parseLongHeaderPacket: n=%v, want %v", n, len(pkt)) 145 } 146 if !reflect.DeepEqual(got, test.p) { 147 t.Errorf("Round-trip encode/decode did not preserve packet.\nsent: %+v\n got: %+v\nwire: %x", test.p, got, pkt) 148 } 149 }) 150 } 151 } 152 153 func TestRoundtripEncodeShortPacket(t *testing.T) { 154 var aes128Keys, aes256Keys, chachaKeys updatingKeyPair 155 aes128Keys.r.init(tls.TLS_AES_128_GCM_SHA256, []byte("secret")) 156 aes256Keys.r.init(tls.TLS_AES_256_GCM_SHA384, []byte("secret")) 157 chachaKeys.r.init(tls.TLS_CHACHA20_POLY1305_SHA256, []byte("secret")) 158 aes128Keys.w = aes128Keys.r 159 aes256Keys.w = aes256Keys.r 160 chachaKeys.w = chachaKeys.r 161 aes128Keys.updateAfter = maxPacketNumber 162 aes256Keys.updateAfter = maxPacketNumber 163 chachaKeys.updateAfter = maxPacketNumber 164 connID := make([]byte, connIDLen) 165 for i := range connID { 166 connID[i] = byte(i) 167 } 168 for _, test := range []struct { 169 desc string 170 num packetNumber 171 payload []byte 172 k updatingKeyPair 173 }{{ 174 desc: "1-byte number, AES128", 175 num: 0, // 1-byte encoding, 176 payload: []byte("payload"), 177 k: aes128Keys, 178 }, { 179 desc: "2-byte number, AES256", 180 num: 0x100, // 2-byte encoding 181 payload: []byte("payload"), 182 k: aes256Keys, 183 }, { 184 desc: "3-byte number, ChaCha20Poly1305", 185 num: 0x10000, // 3-byte encoding 186 payload: []byte("payload"), 187 k: chachaKeys, 188 }, { 189 desc: "4-byte number, ChaCha20Poly1305", 190 num: 0x1000000, // 4-byte encoding 191 payload: []byte{0}, 192 k: chachaKeys, 193 }} { 194 t.Run(test.desc, func(t *testing.T) { 195 var w packetWriter 196 w.reset(1200) 197 w.start1RTTPacket(test.num, 0, connID) 198 w.b = append(w.b, test.payload...) 199 w.finish1RTTPacket(test.num, 0, connID, &test.k) 200 pkt := w.datagram() 201 p, err := parse1RTTPacket(pkt, &test.k, connIDLen, 0) 202 if err != nil { 203 t.Errorf("parse1RTTPacket: err=%v, want nil", err) 204 } 205 if p.num != test.num || !bytes.Equal(p.payload, test.payload) { 206 t.Errorf("Round-trip encode/decode did not preserve packet.\nsent: num=%v, payload={%x}\ngot: num=%v, payload={%x}", test.num, test.payload, p.num, p.payload) 207 } 208 }) 209 } 210 } 211 212 func TestFrameEncodeDecode(t *testing.T) { 213 for _, test := range []struct { 214 s string 215 j string 216 f debugFrame 217 b []byte 218 truncated []byte 219 }{{ 220 s: "PADDING*1", 221 j: `{"frame_type":"padding","length":1}`, 222 f: debugFramePadding{ 223 size: 1, 224 }, 225 b: []byte{ 226 0x00, // Type (i) = 0x00, 227 228 }, 229 }, { 230 s: "PING", 231 j: `{"frame_type":"ping"}`, 232 f: debugFramePing{}, 233 b: []byte{ 234 0x01, // TYPE(i) = 0x01 235 }, 236 }, { 237 s: "ACK Delay=10 [0,16) [17,32) [48,64)", 238 j: `"error: debugFrameAck should not appear as a slog Value"`, 239 f: debugFrameAck{ 240 ackDelay: 10, 241 ranges: []i64range[packetNumber]{ 242 {0x00, 0x10}, 243 {0x11, 0x20}, 244 {0x30, 0x40}, 245 }, 246 }, 247 b: []byte{ 248 0x02, // TYPE (i) = 0x02 249 0x3f, // Largest Acknowledged (i) 250 10, // ACK Delay (i) 251 0x02, // ACK Range Count (i) 252 0x0f, // First ACK Range (i) 253 0x0f, // Gap (i) 254 0x0e, // ACK Range Length (i) 255 0x00, // Gap (i) 256 0x0f, // ACK Range Length (i) 257 }, 258 truncated: []byte{ 259 0x02, // TYPE (i) = 0x02 260 0x3f, // Largest Acknowledged (i) 261 10, // ACK Delay (i) 262 0x01, // ACK Range Count (i) 263 0x0f, // First ACK Range (i) 264 0x0f, // Gap (i) 265 0x0e, // ACK Range Length (i) 266 }, 267 }, { 268 s: "RESET_STREAM ID=1 Code=2 FinalSize=3", 269 j: `{"frame_type":"reset_stream","stream_id":1,"final_size":3}`, 270 f: debugFrameResetStream{ 271 id: 1, 272 code: 2, 273 finalSize: 3, 274 }, 275 b: []byte{ 276 0x04, // TYPE(i) = 0x04 277 0x01, // Stream ID (i), 278 0x02, // Application Protocol Error Code (i), 279 0x03, // Final Size (i), 280 }, 281 }, { 282 s: "STOP_SENDING ID=1 Code=2", 283 j: `{"frame_type":"stop_sending","stream_id":1,"error_code":2}`, 284 f: debugFrameStopSending{ 285 id: 1, 286 code: 2, 287 }, 288 b: []byte{ 289 0x05, // TYPE(i) = 0x05 290 0x01, // Stream ID (i), 291 0x02, // Application Protocol Error Code (i), 292 }, 293 }, { 294 s: "CRYPTO Offset=1 Length=2", 295 j: `{"frame_type":"crypto","offset":1,"length":2}`, 296 f: debugFrameCrypto{ 297 off: 1, 298 data: []byte{3, 4}, 299 }, 300 b: []byte{ 301 0x06, // Type (i) = 0x06, 302 0x01, // Offset (i), 303 0x02, // Length (i), 304 0x03, 0x04, // Crypto Data (..), 305 }, 306 truncated: []byte{ 307 0x06, // Type (i) = 0x06, 308 0x01, // Offset (i), 309 0x01, // Length (i), 310 0x03, 311 }, 312 }, { 313 s: "NEW_TOKEN Token=0304", 314 j: `{"frame_type":"new_token","token":"0304"}`, 315 f: debugFrameNewToken{ 316 token: []byte{3, 4}, 317 }, 318 b: []byte{ 319 0x07, // Type (i) = 0x07, 320 0x02, // Token Length (i), 321 0x03, 0x04, // Token (..), 322 }, 323 }, { 324 s: "STREAM ID=1 Offset=0 Length=0", 325 j: `{"frame_type":"stream","stream_id":1,"offset":0,"length":0}`, 326 f: debugFrameStream{ 327 id: 1, 328 fin: false, 329 off: 0, 330 data: []byte{}, 331 }, 332 b: []byte{ 333 0x0a, // Type (i) = 0x08..0x0f, 334 0x01, // Stream ID (i), 335 // [Offset (i)], 336 0x00, // [Length (i)], 337 // Stream Data (..), 338 }, 339 }, { 340 s: "STREAM ID=100 Offset=4 Length=3", 341 j: `{"frame_type":"stream","stream_id":100,"offset":4,"length":3}`, 342 f: debugFrameStream{ 343 id: 100, 344 fin: false, 345 off: 4, 346 data: []byte{0xa0, 0xa1, 0xa2}, 347 }, 348 b: []byte{ 349 0x0e, // Type (i) = 0x08..0x0f, 350 0x40, 0x64, // Stream ID (i), 351 0x04, // [Offset (i)], 352 0x03, // [Length (i)], 353 0xa0, 0xa1, 0xa2, // Stream Data (..), 354 }, 355 truncated: []byte{ 356 0x0e, // Type (i) = 0x08..0x0f, 357 0x40, 0x64, // Stream ID (i), 358 0x04, // [Offset (i)], 359 0x02, // [Length (i)], 360 0xa0, 0xa1, // Stream Data (..), 361 }, 362 }, { 363 s: "STREAM ID=100 FIN Offset=4 Length=3", 364 j: `{"frame_type":"stream","stream_id":100,"offset":4,"length":3,"fin":true}`, 365 f: debugFrameStream{ 366 id: 100, 367 fin: true, 368 off: 4, 369 data: []byte{0xa0, 0xa1, 0xa2}, 370 }, 371 b: []byte{ 372 0x0f, // Type (i) = 0x08..0x0f, 373 0x40, 0x64, // Stream ID (i), 374 0x04, // [Offset (i)], 375 0x03, // [Length (i)], 376 0xa0, 0xa1, 0xa2, // Stream Data (..), 377 }, 378 truncated: []byte{ 379 0x0e, // Type (i) = 0x08..0x0f, 380 0x40, 0x64, // Stream ID (i), 381 0x04, // [Offset (i)], 382 0x02, // [Length (i)], 383 0xa0, 0xa1, // Stream Data (..), 384 }, 385 }, { 386 s: "STREAM ID=1 FIN Offset=100 Length=0", 387 j: `{"frame_type":"stream","stream_id":1,"offset":100,"length":0,"fin":true}`, 388 f: debugFrameStream{ 389 id: 1, 390 fin: true, 391 off: 100, 392 data: []byte{}, 393 }, 394 b: []byte{ 395 0x0f, // Type (i) = 0x08..0x0f, 396 0x01, // Stream ID (i), 397 0x40, 0x64, // [Offset (i)], 398 0x00, // [Length (i)], 399 // Stream Data (..), 400 }, 401 }, { 402 s: "MAX_DATA Max=10", 403 j: `{"frame_type":"max_data","maximum":10}`, 404 f: debugFrameMaxData{ 405 max: 10, 406 }, 407 b: []byte{ 408 0x10, // Type (i) = 0x10, 409 0x0a, // Maximum Data (i), 410 }, 411 }, { 412 s: "MAX_STREAM_DATA ID=1 Max=10", 413 j: `{"frame_type":"max_stream_data","stream_id":1,"maximum":10}`, 414 f: debugFrameMaxStreamData{ 415 id: 1, 416 max: 10, 417 }, 418 b: []byte{ 419 0x11, // Type (i) = 0x11, 420 0x01, // Stream ID (i), 421 0x0a, // Maximum Stream Data (i), 422 }, 423 }, { 424 s: "MAX_STREAMS Type=bidi Max=1", 425 j: `{"frame_type":"max_streams","stream_type":"bidirectional","maximum":1}`, 426 f: debugFrameMaxStreams{ 427 streamType: bidiStream, 428 max: 1, 429 }, 430 b: []byte{ 431 0x12, // Type (i) = 0x12..0x13, 432 0x01, // Maximum Streams (i), 433 }, 434 }, { 435 s: "MAX_STREAMS Type=uni Max=1", 436 j: `{"frame_type":"max_streams","stream_type":"unidirectional","maximum":1}`, 437 f: debugFrameMaxStreams{ 438 streamType: uniStream, 439 max: 1, 440 }, 441 b: []byte{ 442 0x13, // Type (i) = 0x12..0x13, 443 0x01, // Maximum Streams (i), 444 }, 445 }, { 446 s: "DATA_BLOCKED Max=1", 447 j: `{"frame_type":"data_blocked","limit":1}`, 448 f: debugFrameDataBlocked{ 449 max: 1, 450 }, 451 b: []byte{ 452 0x14, // Type (i) = 0x14, 453 0x01, // Maximum Data (i), 454 }, 455 }, { 456 s: "STREAM_DATA_BLOCKED ID=1 Max=2", 457 j: `{"frame_type":"stream_data_blocked","stream_id":1,"limit":2}`, 458 f: debugFrameStreamDataBlocked{ 459 id: 1, 460 max: 2, 461 }, 462 b: []byte{ 463 0x15, // Type (i) = 0x15, 464 0x01, // Stream ID (i), 465 0x02, // Maximum Stream Data (i), 466 }, 467 }, { 468 s: "STREAMS_BLOCKED Type=bidi Max=1", 469 j: `{"frame_type":"streams_blocked","stream_type":"bidirectional","limit":1}`, 470 f: debugFrameStreamsBlocked{ 471 streamType: bidiStream, 472 max: 1, 473 }, 474 b: []byte{ 475 0x16, // Type (i) = 0x16..0x17, 476 0x01, // Maximum Streams (i), 477 }, 478 }, { 479 s: "STREAMS_BLOCKED Type=uni Max=1", 480 j: `{"frame_type":"streams_blocked","stream_type":"unidirectional","limit":1}`, 481 f: debugFrameStreamsBlocked{ 482 streamType: uniStream, 483 max: 1, 484 }, 485 b: []byte{ 486 0x17, // Type (i) = 0x16..0x17, 487 0x01, // Maximum Streams (i), 488 }, 489 }, { 490 s: "NEW_CONNECTION_ID Seq=3 Retire=2 ID=a0a1a2a3 Token=0102030405060708090a0b0c0d0e0f10", 491 j: `{"frame_type":"new_connection_id","sequence_number":3,"retire_prior_to":2,"connection_id":"a0a1a2a3","stateless_reset_token":"0102030405060708090a0b0c0d0e0f10"}`, 492 f: debugFrameNewConnectionID{ 493 seq: 3, 494 retirePriorTo: 2, 495 connID: []byte{0xa0, 0xa1, 0xa2, 0xa3}, 496 token: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, 497 }, 498 b: []byte{ 499 0x18, // Type (i) = 0x18, 500 0x03, // Sequence Number (i), 501 0x02, // Retire Prior To (i), 502 0x04, // Length (8), 503 0xa0, 0xa1, 0xa2, 0xa3, // Connection ID (8..160), 504 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, // Stateless Reset Token (128), 505 }, 506 }, { 507 s: "RETIRE_CONNECTION_ID Seq=1", 508 j: `{"frame_type":"retire_connection_id","sequence_number":1}`, 509 f: debugFrameRetireConnectionID{ 510 seq: 1, 511 }, 512 b: []byte{ 513 0x19, // Type (i) = 0x19, 514 0x01, // Sequence Number (i), 515 }, 516 }, { 517 s: "PATH_CHALLENGE Data=0123456789abcdef", 518 j: `{"frame_type":"path_challenge","data":"0123456789abcdef"}`, 519 f: debugFramePathChallenge{ 520 data: pathChallengeData{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, 521 }, 522 b: []byte{ 523 0x1a, // Type (i) = 0x1a, 524 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, // Data (64), 525 }, 526 }, { 527 s: "PATH_RESPONSE Data=0123456789abcdef", 528 j: `{"frame_type":"path_response","data":"0123456789abcdef"}`, 529 f: debugFramePathResponse{ 530 data: pathChallengeData{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, 531 }, 532 b: []byte{ 533 0x1b, // Type (i) = 0x1b, 534 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, // Data (64), 535 }, 536 }, { 537 s: `CONNECTION_CLOSE Code=INTERNAL_ERROR FrameType=2 Reason="oops"`, 538 j: `{"frame_type":"connection_close","error_space":"transport","error_code_value":1,"reason":"oops"}`, 539 f: debugFrameConnectionCloseTransport{ 540 code: 1, 541 frameType: 2, 542 reason: "oops", 543 }, 544 b: []byte{ 545 0x1c, // Type (i) = 0x1c..0x1d, 546 0x01, // Error Code (i), 547 0x02, // [Frame Type (i)], 548 0x04, // Reason Phrase Length (i), 549 'o', 'o', 'p', 's', // Reason Phrase (..), 550 }, 551 }, { 552 s: `CONNECTION_CLOSE AppCode=1 Reason="oops"`, 553 j: `{"frame_type":"connection_close","error_space":"application","error_code_value":1,"reason":"oops"}`, 554 f: debugFrameConnectionCloseApplication{ 555 code: 1, 556 reason: "oops", 557 }, 558 b: []byte{ 559 0x1d, // Type (i) = 0x1c..0x1d, 560 0x01, // Error Code (i), 561 0x04, // Reason Phrase Length (i), 562 'o', 'o', 'p', 's', // Reason Phrase (..), 563 }, 564 }, { 565 s: "HANDSHAKE_DONE", 566 j: `{"frame_type":"handshake_done"}`, 567 f: debugFrameHandshakeDone{}, 568 b: []byte{ 569 0x1e, // Type (i) = 0x1e, 570 }, 571 }} { 572 var w packetWriter 573 w.reset(1200) 574 w.start1RTTPacket(0, 0, nil) 575 w.pktLim = w.payOff + len(test.b) 576 if added := test.f.write(&w); !added { 577 t.Errorf("encoding %v with %v bytes available: write unexpectedly failed", test.f, len(test.b)) 578 } 579 if got, want := w.payload(), test.b; !bytes.Equal(got, want) { 580 t.Errorf("encoding %v:\ngot {%x}\nwant {%x}", test.f, got, want) 581 } 582 gotf, n := parseDebugFrame(test.b) 583 if n != len(test.b) || !reflect.DeepEqual(gotf, test.f) { 584 t.Errorf("decoding {%x}:\ndecoded %v bytes, want %v\ngot: %v\nwant: %v", test.b, n, len(test.b), gotf, test.f) 585 } 586 if got, want := test.f.String(), test.s; got != want { 587 t.Errorf("frame.String():\ngot %q\nwant %q", got, want) 588 } 589 if got, want := frameJSON(test.f), test.j; got != want { 590 t.Errorf("frame.LogValue():\ngot %q\nwant %q", got, want) 591 } 592 593 // Try encoding the frame into too little space. 594 // Most frames will result in an error; some (like STREAM frames) will truncate 595 // the data written. 596 w.reset(1200) 597 w.start1RTTPacket(0, 0, nil) 598 w.pktLim = w.payOff + len(test.b) - 1 599 if added := test.f.write(&w); added { 600 if test.truncated == nil { 601 t.Errorf("encoding %v with %v-1 bytes available: write unexpectedly succeeded", test.f, len(test.b)) 602 } else if got, want := w.payload(), test.truncated; !bytes.Equal(got, want) { 603 t.Errorf("encoding %v with %v-1 bytes available:\ngot {%x}\nwant {%x}", test.f, len(test.b), got, want) 604 } 605 } 606 607 // Try parsing truncated data. 608 for i := 0; i < len(test.b); i++ { 609 f, n := parseDebugFrame(test.b[:i]) 610 if n >= 0 { 611 t.Errorf("decoding truncated frame {%x}:\ngot: %v\nwant error", test.b[:i], f) 612 } 613 } 614 } 615 } 616 617 func TestFrameScaledAck(t *testing.T) { 618 for _, test := range []struct { 619 j string 620 f debugFrameScaledAck 621 }{{ 622 j: `{"frame_type":"ack","acked_ranges":[[0,15],[17],[48,63]],"ack_delay":10.000000}`, 623 f: debugFrameScaledAck{ 624 ackDelay: 10 * time.Millisecond, 625 ranges: []i64range[packetNumber]{ 626 {0x00, 0x10}, 627 {0x11, 0x12}, 628 {0x30, 0x40}, 629 }, 630 }, 631 }} { 632 if got, want := frameJSON(test.f), test.j; got != want { 633 t.Errorf("frame.LogValue():\ngot %q\nwant %q", got, want) 634 } 635 } 636 } 637 638 func frameJSON(f slog.LogValuer) string { 639 var buf bytes.Buffer 640 h := qlog.NewJSONHandler(qlog.HandlerOptions{ 641 Level: QLogLevelFrame, 642 NewTrace: func(info qlog.TraceInfo) (io.WriteCloser, error) { 643 return nopCloseWriter{&buf}, nil 644 }, 645 }) 646 // Log the frame, and then trim out everything but the frame from the log. 647 slog.New(h).Info("message", slog.Any("frame", f)) 648 _, b, _ := bytes.Cut(buf.Bytes(), []byte(`"frame":`)) 649 b = bytes.TrimSuffix(b, []byte("}}\n")) 650 return string(b) 651 } 652 653 func TestFrameDecode(t *testing.T) { 654 for _, test := range []struct { 655 desc string 656 want debugFrame 657 b []byte 658 }{{ 659 desc: "STREAM frame with LEN bit unset", 660 want: debugFrameStream{ 661 id: 1, 662 fin: false, 663 data: []byte{0x01, 0x02, 0x03}, 664 }, 665 b: []byte{ 666 0x08, // Type (i) = 0x08..0x0f, 667 0x01, // Stream ID (i), 668 // [Offset (i)], 669 // [Length (i)], 670 0x01, 0x02, 0x03, // Stream Data (..), 671 }, 672 }, { 673 desc: "ACK frame with ECN counts", 674 want: debugFrameAck{ 675 ackDelay: 10, 676 ranges: []i64range[packetNumber]{ 677 {0, 1}, 678 }, 679 }, 680 b: []byte{ 681 0x03, // TYPE (i) = 0x02..0x03 682 0x00, // Largest Acknowledged (i) 683 10, // ACK Delay (i) 684 0x00, // ACK Range Count (i) 685 0x00, // First ACK Range (i) 686 0x01, 0x02, 0x03, // [ECN Counts (..)], 687 }, 688 }} { 689 got, n := parseDebugFrame(test.b) 690 if n != len(test.b) || !reflect.DeepEqual(got, test.want) { 691 t.Errorf("decoding {%x}:\ndecoded %v bytes, want %v\ngot: %v\nwant: %v", test.b, n, len(test.b), got, test.want) 692 } 693 } 694 } 695 696 func TestFrameDecodeErrors(t *testing.T) { 697 for _, test := range []struct { 698 name string 699 b []byte 700 }{{ 701 name: "ACK [-1,0]", 702 b: []byte{ 703 0x02, // TYPE (i) = 0x02 704 0x00, // Largest Acknowledged (i) 705 0x00, // ACK Delay (i) 706 0x00, // ACK Range Count (i) 707 0x01, // First ACK Range (i) 708 }, 709 }, { 710 name: "ACK [-1,16]", 711 b: []byte{ 712 0x02, // TYPE (i) = 0x02 713 0x10, // Largest Acknowledged (i) 714 0x00, // ACK Delay (i) 715 0x00, // ACK Range Count (i) 716 0x11, // First ACK Range (i) 717 }, 718 }, { 719 name: "ACK [-1,0],[1,2]", 720 b: []byte{ 721 0x02, // TYPE (i) = 0x02 722 0x02, // Largest Acknowledged (i) 723 0x00, // ACK Delay (i) 724 0x01, // ACK Range Count (i) 725 0x00, // First ACK Range (i) 726 0x01, // Gap (i) 727 0x01, // ACK Range Length (i) 728 }, 729 }, { 730 name: "NEW_TOKEN with zero-length token", 731 b: []byte{ 732 0x07, // Type (i) = 0x07, 733 0x00, // Token Length (i), 734 }, 735 }, { 736 name: "MAX_STREAMS with too many streams", 737 b: func() []byte { 738 // https://www.rfc-editor.org/rfc/rfc9000.html#section-19.11-5.2.1 739 return appendVarint([]byte{frameTypeMaxStreamsBidi}, (1<<60)+1) 740 }(), 741 }, { 742 name: "NEW_CONNECTION_ID too small", 743 b: []byte{ 744 0x18, // Type (i) = 0x18, 745 0x03, // Sequence Number (i), 746 0x02, // Retire Prior To (i), 747 0x00, // Length (8), 748 // Connection ID (8..160), 749 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, // Stateless Reset Token (128), 750 }, 751 }, { 752 name: "NEW_CONNECTION_ID too large", 753 b: []byte{ 754 0x18, // Type (i) = 0x18, 755 0x03, // Sequence Number (i), 756 0x02, // Retire Prior To (i), 757 21, // Length (8), 758 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 759 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, // Connection ID (8..160), 760 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, // Stateless Reset Token (128), 761 }, 762 }, { 763 name: "NEW_CONNECTION_ID sequence smaller than retire", 764 b: []byte{ 765 0x18, // Type (i) = 0x18, 766 0x02, // Sequence Number (i), 767 0x03, // Retire Prior To (i), 768 0x02, // Length (8), 769 0xff, 0xff, // Connection ID (8..160), 770 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, // Stateless Reset Token (128), 771 }, 772 }} { 773 f, n := parseDebugFrame(test.b) 774 if n >= 0 { 775 t.Errorf("%v: no error when parsing invalid frame {%x}\ngot: %v", test.name, test.b, f) 776 } 777 } 778 } 779 780 func FuzzParseLongHeaderPacket(f *testing.F) { 781 cid := unhex(`0000000000000000`) 782 initialServerKeys := initialKeys(cid, clientSide).r 783 f.Fuzz(func(t *testing.T, in []byte) { 784 parseLongHeaderPacket(in, initialServerKeys, 0) 785 }) 786 } 787 788 func FuzzFrameDecode(f *testing.F) { 789 f.Fuzz(func(t *testing.T, in []byte) { 790 parseDebugFrame(in) 791 }) 792 }