github.com/metacubex/quic-go@v0.44.1-0.20240520163451-20b689a59136/internal/wire/transport_parameter_test.go (about) 1 package wire 2 3 import ( 4 "bytes" 5 "fmt" 6 "math" 7 "net/netip" 8 "testing" 9 "time" 10 11 "golang.org/x/exp/rand" 12 13 "github.com/metacubex/quic-go/internal/protocol" 14 "github.com/metacubex/quic-go/internal/qerr" 15 "github.com/metacubex/quic-go/quicvarint" 16 17 . "github.com/onsi/ginkgo/v2" 18 . "github.com/onsi/gomega" 19 ) 20 21 func getRandomValueUpTo(max int64) uint64 { 22 maxVals := []int64{math.MaxUint8 / 4, math.MaxUint16 / 4, math.MaxUint32 / 4, math.MaxUint64 / 4} 23 m := maxVals[int(rand.Int31n(4))] 24 if m > max { 25 m = max 26 } 27 return uint64(rand.Int63n(m)) 28 } 29 30 func getRandomValue() uint64 { 31 return getRandomValueUpTo(quicvarint.Max) 32 } 33 34 var _ = Describe("Transport Parameters", func() { 35 BeforeEach(func() { 36 rand.Seed(uint64(GinkgoRandomSeed())) 37 }) 38 39 appendInitialSourceConnectionID := func(b []byte) []byte { 40 b = quicvarint.Append(b, uint64(initialSourceConnectionIDParameterID)) 41 b = quicvarint.Append(b, 6) 42 return append(b, []byte("foobar")...) 43 } 44 45 It("has a string representation", func() { 46 rcid := protocol.ParseConnectionID([]byte{0xde, 0xad, 0xc0, 0xde}) 47 p := &TransportParameters{ 48 InitialMaxStreamDataBidiLocal: 1234, 49 InitialMaxStreamDataBidiRemote: 2345, 50 InitialMaxStreamDataUni: 3456, 51 InitialMaxData: 4567, 52 MaxBidiStreamNum: 1337, 53 MaxUniStreamNum: 7331, 54 MaxIdleTimeout: 42 * time.Second, 55 OriginalDestinationConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), 56 InitialSourceConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad}), 57 RetrySourceConnectionID: &rcid, 58 AckDelayExponent: 14, 59 MaxAckDelay: 37 * time.Millisecond, 60 StatelessResetToken: &protocol.StatelessResetToken{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00}, 61 ActiveConnectionIDLimit: 123, 62 MaxDatagramFrameSize: 876, 63 } 64 Expect(p.String()).To(Equal("&wire.TransportParameters{OriginalDestinationConnectionID: deadbeef, InitialSourceConnectionID: decafbad, RetrySourceConnectionID: deadc0de, InitialMaxStreamDataBidiLocal: 1234, InitialMaxStreamDataBidiRemote: 2345, InitialMaxStreamDataUni: 3456, InitialMaxData: 4567, MaxBidiStreamNum: 1337, MaxUniStreamNum: 7331, MaxIdleTimeout: 42s, AckDelayExponent: 14, MaxAckDelay: 37ms, ActiveConnectionIDLimit: 123, StatelessResetToken: 0x112233445566778899aabbccddeeff00, MaxDatagramFrameSize: 876}")) 65 }) 66 67 It("has a string representation, if there's no stateless reset token, no Retry source connection id and no datagram support", func() { 68 p := &TransportParameters{ 69 InitialMaxStreamDataBidiLocal: 1234, 70 InitialMaxStreamDataBidiRemote: 2345, 71 InitialMaxStreamDataUni: 3456, 72 InitialMaxData: 4567, 73 MaxBidiStreamNum: 1337, 74 MaxUniStreamNum: 7331, 75 MaxIdleTimeout: 42 * time.Second, 76 OriginalDestinationConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), 77 InitialSourceConnectionID: protocol.ParseConnectionID([]byte{}), 78 AckDelayExponent: 14, 79 MaxAckDelay: 37 * time.Second, 80 ActiveConnectionIDLimit: 89, 81 MaxDatagramFrameSize: protocol.InvalidByteCount, 82 } 83 Expect(p.String()).To(Equal("&wire.TransportParameters{OriginalDestinationConnectionID: deadbeef, InitialSourceConnectionID: (empty), InitialMaxStreamDataBidiLocal: 1234, InitialMaxStreamDataBidiRemote: 2345, InitialMaxStreamDataUni: 3456, InitialMaxData: 4567, MaxBidiStreamNum: 1337, MaxUniStreamNum: 7331, MaxIdleTimeout: 42s, AckDelayExponent: 14, MaxAckDelay: 37s, ActiveConnectionIDLimit: 89}")) 84 }) 85 86 It("marshals and unmarshals", func() { 87 var token protocol.StatelessResetToken 88 rand.Read(token[:]) 89 rcid := protocol.ParseConnectionID([]byte{0xde, 0xad, 0xc0, 0xde}) 90 params := &TransportParameters{ 91 InitialMaxStreamDataBidiLocal: protocol.ByteCount(getRandomValue()), 92 InitialMaxStreamDataBidiRemote: protocol.ByteCount(getRandomValue()), 93 InitialMaxStreamDataUni: protocol.ByteCount(getRandomValue()), 94 InitialMaxData: protocol.ByteCount(getRandomValue()), 95 MaxIdleTimeout: 0xcafe * time.Second, 96 MaxBidiStreamNum: protocol.StreamNum(getRandomValueUpTo(int64(protocol.MaxStreamCount))), 97 MaxUniStreamNum: protocol.StreamNum(getRandomValueUpTo(int64(protocol.MaxStreamCount))), 98 DisableActiveMigration: true, 99 StatelessResetToken: &token, 100 OriginalDestinationConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), 101 InitialSourceConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad}), 102 RetrySourceConnectionID: &rcid, 103 AckDelayExponent: 13, 104 MaxAckDelay: 42 * time.Millisecond, 105 ActiveConnectionIDLimit: 2 + getRandomValueUpTo(quicvarint.Max-2), 106 MaxUDPPayloadSize: 1200 + protocol.ByteCount(getRandomValueUpTo(quicvarint.Max-1200)), 107 MaxDatagramFrameSize: protocol.ByteCount(getRandomValue()), 108 } 109 data := params.Marshal(protocol.PerspectiveServer) 110 111 p := &TransportParameters{} 112 Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(Succeed()) 113 Expect(p.InitialMaxStreamDataBidiLocal).To(Equal(params.InitialMaxStreamDataBidiLocal)) 114 Expect(p.InitialMaxStreamDataBidiRemote).To(Equal(params.InitialMaxStreamDataBidiRemote)) 115 Expect(p.InitialMaxStreamDataUni).To(Equal(params.InitialMaxStreamDataUni)) 116 Expect(p.InitialMaxData).To(Equal(params.InitialMaxData)) 117 Expect(p.MaxUniStreamNum).To(Equal(params.MaxUniStreamNum)) 118 Expect(p.MaxBidiStreamNum).To(Equal(params.MaxBidiStreamNum)) 119 Expect(p.MaxIdleTimeout).To(Equal(params.MaxIdleTimeout)) 120 Expect(p.DisableActiveMigration).To(Equal(params.DisableActiveMigration)) 121 Expect(p.StatelessResetToken).To(Equal(params.StatelessResetToken)) 122 Expect(p.OriginalDestinationConnectionID).To(Equal(protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}))) 123 Expect(p.InitialSourceConnectionID).To(Equal(protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad}))) 124 Expect(p.RetrySourceConnectionID).To(Equal(&rcid)) 125 Expect(p.AckDelayExponent).To(Equal(uint8(13))) 126 Expect(p.MaxAckDelay).To(Equal(42 * time.Millisecond)) 127 Expect(p.ActiveConnectionIDLimit).To(Equal(params.ActiveConnectionIDLimit)) 128 Expect(p.MaxUDPPayloadSize).To(Equal(params.MaxUDPPayloadSize)) 129 Expect(p.MaxDatagramFrameSize).To(Equal(params.MaxDatagramFrameSize)) 130 }) 131 132 It("marshals additional transport parameters (used for testing large ClientHellos)", func() { 133 origAdditionalTransportParametersClient := AdditionalTransportParametersClient 134 defer func() { 135 AdditionalTransportParametersClient = origAdditionalTransportParametersClient 136 }() 137 AdditionalTransportParametersClient = map[uint64][]byte{1337: []byte("foobar")} 138 139 result := quicvarint.Append([]byte{}, 1337) 140 result = quicvarint.Append(result, 6) 141 result = append(result, []byte("foobar")...) 142 143 params := &TransportParameters{} 144 Expect(bytes.Contains(params.Marshal(protocol.PerspectiveClient), result)).To(BeTrue()) 145 Expect(bytes.Contains(params.Marshal(protocol.PerspectiveServer), result)).To(BeFalse()) 146 }) 147 148 It("doesn't marshal a retry_source_connection_id, if no Retry was performed", func() { 149 data := (&TransportParameters{ 150 StatelessResetToken: &protocol.StatelessResetToken{}, 151 ActiveConnectionIDLimit: 2, 152 }).Marshal(protocol.PerspectiveServer) 153 p := &TransportParameters{} 154 Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(Succeed()) 155 Expect(p.RetrySourceConnectionID).To(BeNil()) 156 }) 157 158 It("marshals a zero-length retry_source_connection_id", func() { 159 rcid := protocol.ParseConnectionID([]byte{}) 160 data := (&TransportParameters{ 161 RetrySourceConnectionID: &rcid, 162 StatelessResetToken: &protocol.StatelessResetToken{}, 163 ActiveConnectionIDLimit: 2, 164 }).Marshal(protocol.PerspectiveServer) 165 p := &TransportParameters{} 166 Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(Succeed()) 167 Expect(p.RetrySourceConnectionID).ToNot(BeNil()) 168 Expect(p.RetrySourceConnectionID.Len()).To(BeZero()) 169 }) 170 171 It("errors when the stateless_reset_token has the wrong length", func() { 172 b := quicvarint.Append(nil, uint64(statelessResetTokenParameterID)) 173 b = quicvarint.Append(b, 15) 174 b = append(b, make([]byte, 15)...) 175 Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ 176 ErrorCode: qerr.TransportParameterError, 177 ErrorMessage: "wrong length for stateless_reset_token: 15 (expected 16)", 178 })) 179 }) 180 181 It("errors when the max_udp_payload_size is too small", func() { 182 b := quicvarint.Append(nil, uint64(maxUDPPayloadSizeParameterID)) 183 b = quicvarint.Append(b, uint64(quicvarint.Len(1199))) 184 b = quicvarint.Append(b, 1199) 185 Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ 186 ErrorCode: qerr.TransportParameterError, 187 ErrorMessage: "invalid value for max_udp_payload_size: 1199 (minimum 1200)", 188 })) 189 }) 190 191 It("errors when disable_active_migration has content", func() { 192 b := quicvarint.Append(nil, uint64(disableActiveMigrationParameterID)) 193 b = quicvarint.Append(b, 6) 194 b = append(b, []byte("foobar")...) 195 Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ 196 ErrorCode: qerr.TransportParameterError, 197 ErrorMessage: "wrong length for disable_active_migration: 6 (expected empty)", 198 })) 199 }) 200 201 It("errors when the server doesn't set the original_destination_connection_id", func() { 202 b := quicvarint.Append(nil, uint64(statelessResetTokenParameterID)) 203 b = quicvarint.Append(b, 16) 204 b = append(b, make([]byte, 16)...) 205 b = appendInitialSourceConnectionID(b) 206 Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ 207 ErrorCode: qerr.TransportParameterError, 208 ErrorMessage: "missing original_destination_connection_id", 209 })) 210 }) 211 212 It("errors when the initial_source_connection_id is missing", func() { 213 Expect((&TransportParameters{}).Unmarshal([]byte{}, protocol.PerspectiveClient)).To(MatchError(&qerr.TransportError{ 214 ErrorCode: qerr.TransportParameterError, 215 ErrorMessage: "missing initial_source_connection_id", 216 })) 217 }) 218 219 It("errors when the max_ack_delay is too large", func() { 220 data := (&TransportParameters{ 221 MaxAckDelay: 1 << 14 * time.Millisecond, 222 StatelessResetToken: &protocol.StatelessResetToken{}, 223 }).Marshal(protocol.PerspectiveServer) 224 p := &TransportParameters{} 225 Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ 226 ErrorCode: qerr.TransportParameterError, 227 ErrorMessage: "invalid value for max_ack_delay: 16384ms (maximum 16383ms)", 228 })) 229 }) 230 231 It("doesn't send the max_ack_delay, if it has the default value", func() { 232 const num = 1000 233 var defaultLen, dataLen int 234 // marshal 1000 times to average out the greasing transport parameter 235 maxAckDelay := protocol.DefaultMaxAckDelay + time.Millisecond 236 for i := 0; i < num; i++ { 237 dataDefault := (&TransportParameters{ 238 MaxAckDelay: protocol.DefaultMaxAckDelay, 239 StatelessResetToken: &protocol.StatelessResetToken{}, 240 }).Marshal(protocol.PerspectiveServer) 241 defaultLen += len(dataDefault) 242 data := (&TransportParameters{ 243 MaxAckDelay: maxAckDelay, 244 StatelessResetToken: &protocol.StatelessResetToken{}, 245 }).Marshal(protocol.PerspectiveServer) 246 dataLen += len(data) 247 } 248 entryLen := quicvarint.Len(uint64(ackDelayExponentParameterID)) /* parameter id */ + quicvarint.Len(uint64(quicvarint.Len(uint64(maxAckDelay.Milliseconds())))) /*length */ + quicvarint.Len(uint64(maxAckDelay.Milliseconds())) /* value */ 249 Expect(float32(dataLen) / num).To(BeNumerically("~", float32(defaultLen)/num+float32(entryLen), 1)) 250 }) 251 252 It("errors when the active_connection_id_limit is too small", func() { 253 data := (&TransportParameters{ 254 ActiveConnectionIDLimit: 1, 255 StatelessResetToken: &protocol.StatelessResetToken{}, 256 }).Marshal(protocol.PerspectiveServer) 257 p := &TransportParameters{} 258 Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ 259 ErrorCode: qerr.TransportParameterError, 260 ErrorMessage: "invalid value for active_connection_id_limit: 1 (minimum 2)", 261 })) 262 }) 263 264 It("errors when the ack_delay_exponenent is too large", func() { 265 data := (&TransportParameters{ 266 AckDelayExponent: 21, 267 StatelessResetToken: &protocol.StatelessResetToken{}, 268 }).Marshal(protocol.PerspectiveServer) 269 p := &TransportParameters{} 270 Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ 271 ErrorCode: qerr.TransportParameterError, 272 ErrorMessage: "invalid value for ack_delay_exponent: 21 (maximum 20)", 273 })) 274 }) 275 276 It("doesn't send the ack_delay_exponent, if it has the default value", func() { 277 const num = 1000 278 var defaultLen, dataLen int 279 // marshal 1000 times to average out the greasing transport parameter 280 for i := 0; i < num; i++ { 281 dataDefault := (&TransportParameters{ 282 AckDelayExponent: protocol.DefaultAckDelayExponent, 283 StatelessResetToken: &protocol.StatelessResetToken{}, 284 }).Marshal(protocol.PerspectiveServer) 285 defaultLen += len(dataDefault) 286 data := (&TransportParameters{ 287 AckDelayExponent: protocol.DefaultAckDelayExponent + 1, 288 StatelessResetToken: &protocol.StatelessResetToken{}, 289 }).Marshal(protocol.PerspectiveServer) 290 dataLen += len(data) 291 } 292 entryLen := quicvarint.Len(uint64(ackDelayExponentParameterID)) /* parameter id */ + quicvarint.Len(uint64(quicvarint.Len(protocol.DefaultAckDelayExponent+1))) /* length */ + quicvarint.Len(protocol.DefaultAckDelayExponent+1) /* value */ 293 Expect(float32(dataLen) / num).To(BeNumerically("~", float32(defaultLen)/num+float32(entryLen), 1)) 294 }) 295 296 It("sets the default value for the ack_delay_exponent and max_active_connection_id_limit, when no values were sent", func() { 297 data := (&TransportParameters{ 298 AckDelayExponent: protocol.DefaultAckDelayExponent, 299 StatelessResetToken: &protocol.StatelessResetToken{}, 300 ActiveConnectionIDLimit: protocol.DefaultActiveConnectionIDLimit, 301 }).Marshal(protocol.PerspectiveServer) 302 p := &TransportParameters{} 303 Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(Succeed()) 304 Expect(p.AckDelayExponent).To(BeEquivalentTo(protocol.DefaultAckDelayExponent)) 305 Expect(p.ActiveConnectionIDLimit).To(BeEquivalentTo(protocol.DefaultActiveConnectionIDLimit)) 306 }) 307 308 It("errors when the varint value has the wrong length", func() { 309 b := quicvarint.Append(nil, uint64(initialMaxStreamDataBidiLocalParameterID)) 310 b = quicvarint.Append(b, 2) 311 val := uint64(0xdeadbeef) 312 Expect(quicvarint.Len(val)).ToNot(BeEquivalentTo(2)) 313 b = quicvarint.Append(b, val) 314 b = appendInitialSourceConnectionID(b) 315 Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ 316 ErrorCode: qerr.TransportParameterError, 317 ErrorMessage: fmt.Sprintf("inconsistent transport parameter length for transport parameter %#x", initialMaxStreamDataBidiLocalParameterID), 318 })) 319 }) 320 321 It("errors if initial_max_streams_bidi is too large", func() { 322 b := quicvarint.Append(nil, uint64(initialMaxStreamsBidiParameterID)) 323 b = quicvarint.Append(b, uint64(quicvarint.Len(uint64(protocol.MaxStreamCount+1)))) 324 b = quicvarint.Append(b, uint64(protocol.MaxStreamCount+1)) 325 b = appendInitialSourceConnectionID(b) 326 Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ 327 ErrorCode: qerr.TransportParameterError, 328 ErrorMessage: "initial_max_streams_bidi too large: 1152921504606846977 (maximum 1152921504606846976)", 329 })) 330 }) 331 332 It("errors if initial_max_streams_uni is too large", func() { 333 b := quicvarint.Append(nil, uint64(initialMaxStreamsUniParameterID)) 334 b = quicvarint.Append(b, uint64(quicvarint.Len(uint64(protocol.MaxStreamCount+1)))) 335 b = quicvarint.Append(b, uint64(protocol.MaxStreamCount+1)) 336 b = appendInitialSourceConnectionID(b) 337 Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ 338 ErrorCode: qerr.TransportParameterError, 339 ErrorMessage: "initial_max_streams_uni too large: 1152921504606846977 (maximum 1152921504606846976)", 340 })) 341 }) 342 343 It("handles huge max_ack_delay values", func() { 344 val := uint64(math.MaxUint64) / 5 345 b := quicvarint.Append(nil, uint64(maxAckDelayParameterID)) 346 b = quicvarint.Append(b, uint64(quicvarint.Len(val))) 347 b = quicvarint.Append(b, val) 348 b = appendInitialSourceConnectionID(b) 349 Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveClient)).To(MatchError(&qerr.TransportError{ 350 ErrorCode: qerr.TransportParameterError, 351 ErrorMessage: "invalid value for max_ack_delay: 3689348814741910323ms (maximum 16383ms)", 352 })) 353 }) 354 355 It("skips unknown parameters", func() { 356 // write a known parameter 357 b := quicvarint.Append(nil, uint64(initialMaxStreamDataBidiLocalParameterID)) 358 b = quicvarint.Append(b, uint64(quicvarint.Len(0x1337))) 359 b = quicvarint.Append(b, 0x1337) 360 // write an unknown parameter 361 b = quicvarint.Append(b, 0x42) 362 b = quicvarint.Append(b, 6) 363 b = append(b, []byte("foobar")...) 364 // write a known parameter 365 b = quicvarint.Append(b, uint64(initialMaxStreamDataBidiRemoteParameterID)) 366 b = quicvarint.Append(b, uint64(quicvarint.Len(0x42))) 367 b = quicvarint.Append(b, 0x42) 368 b = appendInitialSourceConnectionID(b) 369 p := &TransportParameters{} 370 Expect(p.Unmarshal(b, protocol.PerspectiveClient)).To(Succeed()) 371 Expect(p.InitialMaxStreamDataBidiLocal).To(Equal(protocol.ByteCount(0x1337))) 372 Expect(p.InitialMaxStreamDataBidiRemote).To(Equal(protocol.ByteCount(0x42))) 373 }) 374 375 It("rejects duplicate parameters", func() { 376 // write first parameter 377 b := quicvarint.Append(nil, uint64(initialMaxStreamDataBidiLocalParameterID)) 378 b = quicvarint.Append(b, uint64(quicvarint.Len(0x1337))) 379 b = quicvarint.Append(b, 0x1337) 380 // write a second parameter 381 b = quicvarint.Append(b, uint64(initialMaxStreamDataBidiRemoteParameterID)) 382 b = quicvarint.Append(b, uint64(quicvarint.Len(0x42))) 383 b = quicvarint.Append(b, 0x42) 384 // write first parameter again 385 b = quicvarint.Append(b, uint64(initialMaxStreamDataBidiLocalParameterID)) 386 b = quicvarint.Append(b, uint64(quicvarint.Len(0x1337))) 387 b = quicvarint.Append(b, 0x1337) 388 b = appendInitialSourceConnectionID(b) 389 Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveClient)).To(MatchError(&qerr.TransportError{ 390 ErrorCode: qerr.TransportParameterError, 391 ErrorMessage: fmt.Sprintf("received duplicate transport parameter %#x", initialMaxStreamDataBidiLocalParameterID), 392 })) 393 }) 394 395 It("errors if there's not enough data to read", func() { 396 b := quicvarint.Append(nil, 0x42) 397 b = quicvarint.Append(b, 7) 398 b = append(b, []byte("foobar")...) 399 p := &TransportParameters{} 400 Expect(p.Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ 401 ErrorCode: qerr.TransportParameterError, 402 ErrorMessage: "remaining length (6) smaller than parameter length (7)", 403 })) 404 }) 405 406 It("errors if the client sent a stateless_reset_token", func() { 407 b := quicvarint.Append(nil, uint64(statelessResetTokenParameterID)) 408 b = quicvarint.Append(b, uint64(quicvarint.Len(16))) 409 b = append(b, make([]byte, 16)...) 410 Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveClient)).To(MatchError(&qerr.TransportError{ 411 ErrorCode: qerr.TransportParameterError, 412 ErrorMessage: "client sent a stateless_reset_token", 413 })) 414 }) 415 416 It("errors if the client sent the original_destination_connection_id", func() { 417 b := quicvarint.Append(nil, uint64(originalDestinationConnectionIDParameterID)) 418 b = quicvarint.Append(b, 6) 419 b = append(b, []byte("foobar")...) 420 Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveClient)).To(MatchError(&qerr.TransportError{ 421 ErrorCode: qerr.TransportParameterError, 422 ErrorMessage: "client sent an original_destination_connection_id", 423 })) 424 }) 425 426 Context("preferred address", func() { 427 var pa *PreferredAddress 428 429 BeforeEach(func() { 430 pa = &PreferredAddress{ 431 IPv4: netip.AddrPortFrom(netip.AddrFrom4([4]byte{127, 0, 0, 1}), 42), 432 IPv6: netip.AddrPortFrom(netip.AddrFrom16([16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}), 13), 433 ConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), 434 StatelessResetToken: protocol.StatelessResetToken{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, 435 } 436 }) 437 438 It("marshals and unmarshals", func() { 439 data := (&TransportParameters{ 440 PreferredAddress: pa, 441 StatelessResetToken: &protocol.StatelessResetToken{}, 442 ActiveConnectionIDLimit: 2, 443 }).Marshal(protocol.PerspectiveServer) 444 p := &TransportParameters{} 445 Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(Succeed()) 446 Expect(p.PreferredAddress.IPv4).To(Equal(pa.IPv4)) 447 Expect(p.PreferredAddress.IPv6).To(Equal(pa.IPv6)) 448 Expect(p.PreferredAddress.ConnectionID).To(Equal(pa.ConnectionID)) 449 Expect(p.PreferredAddress.StatelessResetToken).To(Equal(pa.StatelessResetToken)) 450 }) 451 452 It("errors if the client sent a preferred_address", func() { 453 b := quicvarint.Append(nil, uint64(preferredAddressParameterID)) 454 b = quicvarint.Append(b, 6) 455 b = append(b, []byte("foobar")...) 456 p := &TransportParameters{} 457 Expect(p.Unmarshal(b, protocol.PerspectiveClient)).To(MatchError(&qerr.TransportError{ 458 ErrorCode: qerr.TransportParameterError, 459 ErrorMessage: "client sent a preferred_address", 460 })) 461 }) 462 463 It("errors on zero-length connection IDs", func() { 464 pa.ConnectionID = protocol.ParseConnectionID([]byte{}) 465 data := (&TransportParameters{ 466 PreferredAddress: pa, 467 StatelessResetToken: &protocol.StatelessResetToken{}, 468 }).Marshal(protocol.PerspectiveServer) 469 p := &TransportParameters{} 470 Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ 471 ErrorCode: qerr.TransportParameterError, 472 ErrorMessage: "invalid connection ID length: 0", 473 })) 474 }) 475 476 It("errors on EOF", func() { 477 raw := []byte{ 478 127, 0, 0, 1, // IPv4 479 0, 42, // IPv4 Port 480 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, // IPv6 481 13, 37, // IPv6 Port, 482 4, // conn ID len 483 0xde, 0xad, 0xbe, 0xef, 484 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, // stateless reset token 485 } 486 for i := 1; i < len(raw); i++ { 487 b := quicvarint.Append(nil, uint64(preferredAddressParameterID)) 488 b = append(b, raw[:i]...) 489 p := &TransportParameters{} 490 Expect(p.Unmarshal(b, protocol.PerspectiveServer)).ToNot(Succeed()) 491 } 492 }) 493 }) 494 495 Context("saving and retrieving from a session ticket", func() { 496 It("saves and retrieves the parameters", func() { 497 params := &TransportParameters{ 498 InitialMaxStreamDataBidiLocal: protocol.ByteCount(getRandomValue()), 499 InitialMaxStreamDataBidiRemote: protocol.ByteCount(getRandomValue()), 500 InitialMaxStreamDataUni: protocol.ByteCount(getRandomValue()), 501 InitialMaxData: protocol.ByteCount(getRandomValue()), 502 MaxBidiStreamNum: protocol.StreamNum(getRandomValueUpTo(int64(protocol.MaxStreamCount))), 503 MaxUniStreamNum: protocol.StreamNum(getRandomValueUpTo(int64(protocol.MaxStreamCount))), 504 ActiveConnectionIDLimit: 2 + getRandomValueUpTo(quicvarint.Max-2), 505 MaxDatagramFrameSize: protocol.ByteCount(getRandomValueUpTo(int64(MaxDatagramSize))), 506 } 507 Expect(params.ValidFor0RTT(params)).To(BeTrue()) 508 b := params.MarshalForSessionTicket(nil) 509 var tp TransportParameters 510 Expect(tp.UnmarshalFromSessionTicket(b)).To(Succeed()) 511 Expect(tp.InitialMaxStreamDataBidiLocal).To(Equal(params.InitialMaxStreamDataBidiLocal)) 512 Expect(tp.InitialMaxStreamDataBidiRemote).To(Equal(params.InitialMaxStreamDataBidiRemote)) 513 Expect(tp.InitialMaxStreamDataUni).To(Equal(params.InitialMaxStreamDataUni)) 514 Expect(tp.InitialMaxData).To(Equal(params.InitialMaxData)) 515 Expect(tp.MaxBidiStreamNum).To(Equal(params.MaxBidiStreamNum)) 516 Expect(tp.MaxUniStreamNum).To(Equal(params.MaxUniStreamNum)) 517 Expect(tp.ActiveConnectionIDLimit).To(Equal(params.ActiveConnectionIDLimit)) 518 Expect(tp.MaxDatagramFrameSize).To(Equal(params.MaxDatagramFrameSize)) 519 }) 520 521 It("rejects the parameters if it can't parse them", func() { 522 var p TransportParameters 523 Expect(p.UnmarshalFromSessionTicket([]byte("foobar"))).ToNot(Succeed()) 524 }) 525 526 It("rejects the parameters if the version changed", func() { 527 var p TransportParameters 528 data := p.MarshalForSessionTicket(nil) 529 b := quicvarint.Append(nil, transportParameterMarshalingVersion+1) 530 b = append(b, data[quicvarint.Len(transportParameterMarshalingVersion):]...) 531 Expect(p.UnmarshalFromSessionTicket(b)).To(MatchError(fmt.Sprintf("unknown transport parameter marshaling version: %d", transportParameterMarshalingVersion+1))) 532 }) 533 534 Context("rejects the parameters if they changed", func() { 535 var p TransportParameters 536 saved := &TransportParameters{ 537 InitialMaxStreamDataBidiLocal: 1, 538 InitialMaxStreamDataBidiRemote: 2, 539 InitialMaxStreamDataUni: 3, 540 InitialMaxData: 4, 541 MaxBidiStreamNum: 5, 542 MaxUniStreamNum: 6, 543 ActiveConnectionIDLimit: 7, 544 MaxDatagramFrameSize: 1000, 545 } 546 547 BeforeEach(func() { 548 p = *saved 549 Expect(p.ValidFor0RTT(saved)).To(BeTrue()) 550 }) 551 552 It("rejects the parameters if the InitialMaxStreamDataBidiLocal was reduced", func() { 553 p.InitialMaxStreamDataBidiLocal = saved.InitialMaxStreamDataBidiLocal - 1 554 Expect(p.ValidFor0RTT(saved)).To(BeFalse()) 555 }) 556 557 It("doesn't reject the parameters if the InitialMaxStreamDataBidiLocal was increased", func() { 558 p.InitialMaxStreamDataBidiLocal = saved.InitialMaxStreamDataBidiLocal + 1 559 Expect(p.ValidFor0RTT(saved)).To(BeTrue()) 560 }) 561 562 It("rejects the parameters if the InitialMaxStreamDataBidiRemote was reduced", func() { 563 p.InitialMaxStreamDataBidiRemote = saved.InitialMaxStreamDataBidiRemote - 1 564 Expect(p.ValidFor0RTT(saved)).To(BeFalse()) 565 }) 566 567 It("doesn't reject the parameters if the InitialMaxStreamDataBidiRemote was increased", func() { 568 p.InitialMaxStreamDataBidiRemote = saved.InitialMaxStreamDataBidiRemote + 1 569 Expect(p.ValidFor0RTT(saved)).To(BeTrue()) 570 }) 571 572 It("rejects the parameters if the InitialMaxStreamDataUni was reduced", func() { 573 p.InitialMaxStreamDataUni = saved.InitialMaxStreamDataUni - 1 574 Expect(p.ValidFor0RTT(saved)).To(BeFalse()) 575 }) 576 577 It("doesn't reject the parameters if the InitialMaxStreamDataUni was increased", func() { 578 p.InitialMaxStreamDataUni = saved.InitialMaxStreamDataUni + 1 579 Expect(p.ValidFor0RTT(saved)).To(BeTrue()) 580 }) 581 582 It("rejects the parameters if the InitialMaxData was reduced", func() { 583 p.InitialMaxData = saved.InitialMaxData - 1 584 Expect(p.ValidFor0RTT(saved)).To(BeFalse()) 585 }) 586 587 It("doesn't reject the parameters if the InitialMaxData was increased", func() { 588 p.InitialMaxData = saved.InitialMaxData + 1 589 Expect(p.ValidFor0RTT(saved)).To(BeTrue()) 590 }) 591 592 It("rejects the parameters if the MaxBidiStreamNum was reduced", func() { 593 p.MaxBidiStreamNum = saved.MaxBidiStreamNum - 1 594 Expect(p.ValidFor0RTT(saved)).To(BeFalse()) 595 }) 596 597 It("accepts the parameters if the MaxBidiStreamNum was increased", func() { 598 p.MaxBidiStreamNum = saved.MaxBidiStreamNum + 1 599 Expect(p.ValidFor0RTT(saved)).To(BeTrue()) 600 }) 601 602 It("rejects the parameters if the MaxUniStreamNum changed", func() { 603 p.MaxUniStreamNum = saved.MaxUniStreamNum - 1 604 Expect(p.ValidFor0RTT(saved)).To(BeFalse()) 605 }) 606 607 It("accepts the parameters if the MaxUniStreamNum was increased", func() { 608 p.MaxUniStreamNum = saved.MaxUniStreamNum + 1 609 Expect(p.ValidFor0RTT(saved)).To(BeTrue()) 610 }) 611 612 It("rejects the parameters if the ActiveConnectionIDLimit changed", func() { 613 p.ActiveConnectionIDLimit = 0 614 Expect(p.ValidFor0RTT(saved)).To(BeFalse()) 615 }) 616 617 It("accepts the parameters if the MaxDatagramFrameSize was increased", func() { 618 p.MaxDatagramFrameSize = saved.MaxDatagramFrameSize + 1 619 Expect(p.ValidFor0RTT(saved)).To(BeTrue()) 620 }) 621 622 It("rejects the parameters if the MaxDatagramFrameSize reduced", func() { 623 p.MaxDatagramFrameSize = saved.MaxDatagramFrameSize - 1 624 Expect(p.ValidFor0RTT(saved)).To(BeFalse()) 625 }) 626 }) 627 628 Context("client checks the parameters after successfully sending 0-RTT data", func() { 629 var p TransportParameters 630 saved := &TransportParameters{ 631 InitialMaxStreamDataBidiLocal: 1, 632 InitialMaxStreamDataBidiRemote: 2, 633 InitialMaxStreamDataUni: 3, 634 InitialMaxData: 4, 635 MaxBidiStreamNum: 5, 636 MaxUniStreamNum: 6, 637 ActiveConnectionIDLimit: 7, 638 MaxDatagramFrameSize: 1000, 639 } 640 641 BeforeEach(func() { 642 p = *saved 643 Expect(p.ValidForUpdate(saved)).To(BeTrue()) 644 }) 645 646 It("rejects the parameters if the InitialMaxStreamDataBidiLocal was reduced", func() { 647 p.InitialMaxStreamDataBidiLocal = saved.InitialMaxStreamDataBidiLocal - 1 648 Expect(p.ValidForUpdate(saved)).To(BeFalse()) 649 }) 650 651 It("doesn't reject the parameters if the InitialMaxStreamDataBidiLocal was increased", func() { 652 p.InitialMaxStreamDataBidiLocal = saved.InitialMaxStreamDataBidiLocal + 1 653 Expect(p.ValidForUpdate(saved)).To(BeTrue()) 654 }) 655 656 It("rejects the parameters if the InitialMaxStreamDataBidiRemote was reduced", func() { 657 p.InitialMaxStreamDataBidiRemote = saved.InitialMaxStreamDataBidiRemote - 1 658 Expect(p.ValidForUpdate(saved)).To(BeFalse()) 659 }) 660 661 It("doesn't reject the parameters if the InitialMaxStreamDataBidiRemote was increased", func() { 662 p.InitialMaxStreamDataBidiRemote = saved.InitialMaxStreamDataBidiRemote + 1 663 Expect(p.ValidForUpdate(saved)).To(BeTrue()) 664 }) 665 666 It("rejects the parameters if the InitialMaxStreamDataUni was reduced", func() { 667 p.InitialMaxStreamDataUni = saved.InitialMaxStreamDataUni - 1 668 Expect(p.ValidForUpdate(saved)).To(BeFalse()) 669 }) 670 671 It("doesn't reject the parameters if the InitialMaxStreamDataUni was increased", func() { 672 p.InitialMaxStreamDataUni = saved.InitialMaxStreamDataUni + 1 673 Expect(p.ValidForUpdate(saved)).To(BeTrue()) 674 }) 675 676 It("rejects the parameters if the InitialMaxData was reduced", func() { 677 p.InitialMaxData = saved.InitialMaxData - 1 678 Expect(p.ValidForUpdate(saved)).To(BeFalse()) 679 }) 680 681 It("doesn't reject the parameters if the InitialMaxData was increased", func() { 682 p.InitialMaxData = saved.InitialMaxData + 1 683 Expect(p.ValidForUpdate(saved)).To(BeTrue()) 684 }) 685 686 It("rejects the parameters if the MaxBidiStreamNum was reduced", func() { 687 p.MaxBidiStreamNum = saved.MaxBidiStreamNum - 1 688 Expect(p.ValidForUpdate(saved)).To(BeFalse()) 689 }) 690 691 It("doesn't reject the parameters if the MaxBidiStreamNum was increased", func() { 692 p.MaxBidiStreamNum = saved.MaxBidiStreamNum + 1 693 Expect(p.ValidForUpdate(saved)).To(BeTrue()) 694 }) 695 696 It("rejects the parameters if the MaxUniStreamNum reduced", func() { 697 p.MaxUniStreamNum = saved.MaxUniStreamNum - 1 698 Expect(p.ValidForUpdate(saved)).To(BeFalse()) 699 }) 700 701 It("doesn't reject the parameters if the MaxUniStreamNum was increased", func() { 702 p.MaxUniStreamNum = saved.MaxUniStreamNum + 1 703 Expect(p.ValidForUpdate(saved)).To(BeTrue()) 704 }) 705 706 It("rejects the parameters if the ActiveConnectionIDLimit reduced", func() { 707 p.ActiveConnectionIDLimit = saved.ActiveConnectionIDLimit - 1 708 Expect(p.ValidForUpdate(saved)).To(BeFalse()) 709 }) 710 711 It("doesn't reject the parameters if the ActiveConnectionIDLimit increased", func() { 712 p.ActiveConnectionIDLimit = saved.ActiveConnectionIDLimit + 1 713 Expect(p.ValidForUpdate(saved)).To(BeTrue()) 714 }) 715 716 It("rejects the parameters if the MaxDatagramFrameSize reduced", func() { 717 p.MaxDatagramFrameSize = saved.MaxDatagramFrameSize - 1 718 Expect(p.ValidForUpdate(saved)).To(BeFalse()) 719 }) 720 721 It("doesn't reject the parameters if the MaxDatagramFrameSize increased", func() { 722 p.MaxDatagramFrameSize = saved.MaxDatagramFrameSize + 1 723 Expect(p.ValidForUpdate(saved)).To(BeTrue()) 724 }) 725 }) 726 }) 727 }) 728 729 func BenchmarkTransportParameters(b *testing.B) { 730 b.Run("without preferred address", func(b *testing.B) { benchmarkTransportParameters(b, false) }) 731 b.Run("with preferred address", func(b *testing.B) { benchmarkTransportParameters(b, true) }) 732 } 733 734 func benchmarkTransportParameters(b *testing.B, withPreferredAddress bool) { 735 var token protocol.StatelessResetToken 736 rand.Read(token[:]) 737 rcid := protocol.ParseConnectionID([]byte{0xde, 0xad, 0xc0, 0xde}) 738 params := &TransportParameters{ 739 InitialMaxStreamDataBidiLocal: protocol.ByteCount(getRandomValue()), 740 InitialMaxStreamDataBidiRemote: protocol.ByteCount(getRandomValue()), 741 InitialMaxStreamDataUni: protocol.ByteCount(getRandomValue()), 742 InitialMaxData: protocol.ByteCount(getRandomValue()), 743 MaxIdleTimeout: 0xcafe * time.Second, 744 MaxBidiStreamNum: protocol.StreamNum(getRandomValueUpTo(int64(protocol.MaxStreamCount))), 745 MaxUniStreamNum: protocol.StreamNum(getRandomValueUpTo(int64(protocol.MaxStreamCount))), 746 DisableActiveMigration: true, 747 StatelessResetToken: &token, 748 OriginalDestinationConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), 749 InitialSourceConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad}), 750 RetrySourceConnectionID: &rcid, 751 AckDelayExponent: 13, 752 MaxAckDelay: 42 * time.Millisecond, 753 ActiveConnectionIDLimit: 2 + getRandomValueUpTo(quicvarint.Max-2), 754 MaxDatagramFrameSize: protocol.ByteCount(getRandomValue()), 755 } 756 var token2 protocol.StatelessResetToken 757 rand.Read(token2[:]) 758 if withPreferredAddress { 759 var ip4 [4]byte 760 var ip6 [16]byte 761 rand.Read(ip4[:]) 762 rand.Read(ip6[:]) 763 params.PreferredAddress = &PreferredAddress{ 764 IPv4: netip.AddrPortFrom(netip.AddrFrom4(ip4), 1234), 765 IPv6: netip.AddrPortFrom(netip.AddrFrom16(ip6), 4321), 766 ConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), 767 StatelessResetToken: token2, 768 } 769 } 770 data := params.Marshal(protocol.PerspectiveServer) 771 772 b.ResetTimer() 773 b.ReportAllocs() 774 var p TransportParameters 775 for i := 0; i < b.N; i++ { 776 if err := p.Unmarshal(data, protocol.PerspectiveServer); err != nil { 777 b.Fatal(err) 778 } 779 // check a few fields 780 if p.DisableActiveMigration != params.DisableActiveMigration || 781 p.InitialMaxStreamDataBidiLocal != params.InitialMaxStreamDataBidiLocal || 782 *p.StatelessResetToken != *params.StatelessResetToken || 783 p.AckDelayExponent != params.AckDelayExponent { 784 b.Fatalf("params mismatch: %v vs %v", p, params) 785 } 786 if withPreferredAddress && *p.PreferredAddress != *params.PreferredAddress { 787 b.Fatalf("preferred address mismatch: %v vs %v", p.PreferredAddress, params.PreferredAddress) 788 } 789 } 790 }