github.com/metacubex/quic-go@v0.44.1-0.20240520163451-20b689a59136/internal/wire/transport_parameters.go (about) 1 package wire 2 3 import ( 4 "crypto/rand" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "golang.org/x/exp/slices" 9 "io" 10 "net/netip" 11 "time" 12 13 "github.com/metacubex/quic-go/internal/protocol" 14 "github.com/metacubex/quic-go/internal/qerr" 15 "github.com/metacubex/quic-go/internal/utils" 16 "github.com/metacubex/quic-go/quicvarint" 17 ) 18 19 // AdditionalTransportParametersClient are additional transport parameters that will be added 20 // to the client's transport parameters. 21 // This is not intended for production use, but _only_ to increase the size of the ClientHello beyond 22 // the usual size of less than 1 MTU. 23 var AdditionalTransportParametersClient map[uint64][]byte 24 25 const transportParameterMarshalingVersion = 1 26 27 type transportParameterID uint64 28 29 const ( 30 originalDestinationConnectionIDParameterID transportParameterID = 0x0 31 maxIdleTimeoutParameterID transportParameterID = 0x1 32 statelessResetTokenParameterID transportParameterID = 0x2 33 maxUDPPayloadSizeParameterID transportParameterID = 0x3 34 initialMaxDataParameterID transportParameterID = 0x4 35 initialMaxStreamDataBidiLocalParameterID transportParameterID = 0x5 36 initialMaxStreamDataBidiRemoteParameterID transportParameterID = 0x6 37 initialMaxStreamDataUniParameterID transportParameterID = 0x7 38 initialMaxStreamsBidiParameterID transportParameterID = 0x8 39 initialMaxStreamsUniParameterID transportParameterID = 0x9 40 ackDelayExponentParameterID transportParameterID = 0xa 41 maxAckDelayParameterID transportParameterID = 0xb 42 disableActiveMigrationParameterID transportParameterID = 0xc 43 preferredAddressParameterID transportParameterID = 0xd 44 activeConnectionIDLimitParameterID transportParameterID = 0xe 45 initialSourceConnectionIDParameterID transportParameterID = 0xf 46 retrySourceConnectionIDParameterID transportParameterID = 0x10 47 // RFC 9221 48 maxDatagramFrameSizeParameterID transportParameterID = 0x20 49 ) 50 51 // PreferredAddress is the value encoding in the preferred_address transport parameter 52 type PreferredAddress struct { 53 IPv4, IPv6 netip.AddrPort 54 ConnectionID protocol.ConnectionID 55 StatelessResetToken protocol.StatelessResetToken 56 } 57 58 // TransportParameters are parameters sent to the peer during the handshake 59 type TransportParameters struct { 60 InitialMaxStreamDataBidiLocal protocol.ByteCount 61 InitialMaxStreamDataBidiRemote protocol.ByteCount 62 InitialMaxStreamDataUni protocol.ByteCount 63 InitialMaxData protocol.ByteCount 64 65 MaxAckDelay time.Duration 66 AckDelayExponent uint8 67 68 DisableActiveMigration bool 69 70 MaxUDPPayloadSize protocol.ByteCount 71 72 MaxUniStreamNum protocol.StreamNum 73 MaxBidiStreamNum protocol.StreamNum 74 75 MaxIdleTimeout time.Duration 76 77 PreferredAddress *PreferredAddress 78 79 OriginalDestinationConnectionID protocol.ConnectionID 80 InitialSourceConnectionID protocol.ConnectionID 81 RetrySourceConnectionID *protocol.ConnectionID // use a pointer here to distinguish zero-length connection IDs from missing transport parameters 82 83 StatelessResetToken *protocol.StatelessResetToken 84 ActiveConnectionIDLimit uint64 85 86 MaxDatagramFrameSize protocol.ByteCount 87 } 88 89 // Unmarshal the transport parameters 90 func (p *TransportParameters) Unmarshal(data []byte, sentBy protocol.Perspective) error { 91 if err := p.unmarshal(data, sentBy, false); err != nil { 92 return &qerr.TransportError{ 93 ErrorCode: qerr.TransportParameterError, 94 ErrorMessage: err.Error(), 95 } 96 } 97 return nil 98 } 99 100 func (p *TransportParameters) unmarshal(b []byte, sentBy protocol.Perspective, fromSessionTicket bool) error { 101 // needed to check that every parameter is only sent at most once 102 parameterIDs := make([]transportParameterID, 0, 32) 103 104 var ( 105 readOriginalDestinationConnectionID bool 106 readInitialSourceConnectionID bool 107 readActiveConnectionIDLimit bool 108 ) 109 110 p.AckDelayExponent = protocol.DefaultAckDelayExponent 111 p.MaxAckDelay = protocol.DefaultMaxAckDelay 112 p.MaxDatagramFrameSize = protocol.InvalidByteCount 113 114 for len(b) > 0 { 115 paramIDInt, l, err := quicvarint.Parse(b) 116 if err != nil { 117 return err 118 } 119 paramID := transportParameterID(paramIDInt) 120 b = b[l:] 121 paramLen, l, err := quicvarint.Parse(b) 122 if err != nil { 123 return err 124 } 125 b = b[l:] 126 if uint64(len(b)) < paramLen { 127 return fmt.Errorf("remaining length (%d) smaller than parameter length (%d)", len(b), paramLen) 128 } 129 parameterIDs = append(parameterIDs, paramID) 130 switch paramID { 131 case activeConnectionIDLimitParameterID: 132 readActiveConnectionIDLimit = true 133 fallthrough 134 case maxIdleTimeoutParameterID, 135 maxUDPPayloadSizeParameterID, 136 initialMaxDataParameterID, 137 initialMaxStreamDataBidiLocalParameterID, 138 initialMaxStreamDataBidiRemoteParameterID, 139 initialMaxStreamDataUniParameterID, 140 initialMaxStreamsBidiParameterID, 141 initialMaxStreamsUniParameterID, 142 maxAckDelayParameterID, 143 maxDatagramFrameSizeParameterID, 144 ackDelayExponentParameterID: 145 if err := p.readNumericTransportParameter(b, paramID, int(paramLen)); err != nil { 146 return err 147 } 148 b = b[paramLen:] 149 case preferredAddressParameterID: 150 if sentBy == protocol.PerspectiveClient { 151 return errors.New("client sent a preferred_address") 152 } 153 if err := p.readPreferredAddress(b, int(paramLen)); err != nil { 154 return err 155 } 156 b = b[paramLen:] 157 case disableActiveMigrationParameterID: 158 if paramLen != 0 { 159 return fmt.Errorf("wrong length for disable_active_migration: %d (expected empty)", paramLen) 160 } 161 p.DisableActiveMigration = true 162 case statelessResetTokenParameterID: 163 if sentBy == protocol.PerspectiveClient { 164 return errors.New("client sent a stateless_reset_token") 165 } 166 if paramLen != 16 { 167 return fmt.Errorf("wrong length for stateless_reset_token: %d (expected 16)", paramLen) 168 } 169 var token protocol.StatelessResetToken 170 if len(b) < len(token) { 171 return io.EOF 172 } 173 copy(token[:], b) 174 b = b[len(token):] 175 p.StatelessResetToken = &token 176 case originalDestinationConnectionIDParameterID: 177 if sentBy == protocol.PerspectiveClient { 178 return errors.New("client sent an original_destination_connection_id") 179 } 180 if paramLen > protocol.MaxConnIDLen { 181 return protocol.ErrInvalidConnectionIDLen 182 } 183 p.OriginalDestinationConnectionID = protocol.ParseConnectionID(b[:paramLen]) 184 b = b[paramLen:] 185 readOriginalDestinationConnectionID = true 186 case initialSourceConnectionIDParameterID: 187 if paramLen > protocol.MaxConnIDLen { 188 return protocol.ErrInvalidConnectionIDLen 189 } 190 p.InitialSourceConnectionID = protocol.ParseConnectionID(b[:paramLen]) 191 b = b[paramLen:] 192 readInitialSourceConnectionID = true 193 case retrySourceConnectionIDParameterID: 194 if sentBy == protocol.PerspectiveClient { 195 return errors.New("client sent a retry_source_connection_id") 196 } 197 if paramLen > protocol.MaxConnIDLen { 198 return protocol.ErrInvalidConnectionIDLen 199 } 200 connID := protocol.ParseConnectionID(b[:paramLen]) 201 b = b[paramLen:] 202 p.RetrySourceConnectionID = &connID 203 default: 204 b = b[paramLen:] 205 } 206 } 207 208 if !readActiveConnectionIDLimit { 209 p.ActiveConnectionIDLimit = protocol.DefaultActiveConnectionIDLimit 210 } 211 if !fromSessionTicket { 212 if sentBy == protocol.PerspectiveServer && !readOriginalDestinationConnectionID { 213 return errors.New("missing original_destination_connection_id") 214 } 215 if p.MaxUDPPayloadSize == 0 { 216 p.MaxUDPPayloadSize = protocol.MaxByteCount 217 } 218 if !readInitialSourceConnectionID { 219 return errors.New("missing initial_source_connection_id") 220 } 221 } 222 223 // check that every transport parameter was sent at most once 224 slices.SortFunc(parameterIDs, func(a, b transportParameterID) int { 225 if a < b { 226 return -1 227 } 228 return 1 229 }) 230 for i := 0; i < len(parameterIDs)-1; i++ { 231 if parameterIDs[i] == parameterIDs[i+1] { 232 return fmt.Errorf("received duplicate transport parameter %#x", parameterIDs[i]) 233 } 234 } 235 236 return nil 237 } 238 239 func (p *TransportParameters) readPreferredAddress(b []byte, expectedLen int) error { 240 remainingLen := len(b) 241 pa := &PreferredAddress{} 242 if len(b) < 4+2+16+2+1 { 243 return io.EOF 244 } 245 var ipv4 [4]byte 246 copy(ipv4[:], b[:4]) 247 port4 := binary.BigEndian.Uint16(b[4:]) 248 b = b[4+2:] 249 pa.IPv4 = netip.AddrPortFrom(netip.AddrFrom4(ipv4), port4) 250 var ipv6 [16]byte 251 copy(ipv6[:], b[:16]) 252 port6 := binary.BigEndian.Uint16(b[16:]) 253 pa.IPv6 = netip.AddrPortFrom(netip.AddrFrom16(ipv6), port6) 254 b = b[16+2:] 255 connIDLen := int(b[0]) 256 b = b[1:] 257 if connIDLen == 0 || connIDLen > protocol.MaxConnIDLen { 258 return fmt.Errorf("invalid connection ID length: %d", connIDLen) 259 } 260 if len(b) < connIDLen+len(pa.StatelessResetToken) { 261 return io.EOF 262 } 263 pa.ConnectionID = protocol.ParseConnectionID(b[:connIDLen]) 264 b = b[connIDLen:] 265 copy(pa.StatelessResetToken[:], b) 266 b = b[len(pa.StatelessResetToken):] 267 if bytesRead := remainingLen - len(b); bytesRead != expectedLen { 268 return fmt.Errorf("expected preferred_address to be %d long, read %d bytes", expectedLen, bytesRead) 269 } 270 p.PreferredAddress = pa 271 return nil 272 } 273 274 func (p *TransportParameters) readNumericTransportParameter(b []byte, paramID transportParameterID, expectedLen int) error { 275 val, l, err := quicvarint.Parse(b) 276 if err != nil { 277 return fmt.Errorf("error while reading transport parameter %d: %s", paramID, err) 278 } 279 if l != expectedLen { 280 return fmt.Errorf("inconsistent transport parameter length for transport parameter %#x", paramID) 281 } 282 //nolint:exhaustive // This only covers the numeric transport parameters. 283 switch paramID { 284 case initialMaxStreamDataBidiLocalParameterID: 285 p.InitialMaxStreamDataBidiLocal = protocol.ByteCount(val) 286 case initialMaxStreamDataBidiRemoteParameterID: 287 p.InitialMaxStreamDataBidiRemote = protocol.ByteCount(val) 288 case initialMaxStreamDataUniParameterID: 289 p.InitialMaxStreamDataUni = protocol.ByteCount(val) 290 case initialMaxDataParameterID: 291 p.InitialMaxData = protocol.ByteCount(val) 292 case initialMaxStreamsBidiParameterID: 293 p.MaxBidiStreamNum = protocol.StreamNum(val) 294 if p.MaxBidiStreamNum > protocol.MaxStreamCount { 295 return fmt.Errorf("initial_max_streams_bidi too large: %d (maximum %d)", p.MaxBidiStreamNum, protocol.MaxStreamCount) 296 } 297 case initialMaxStreamsUniParameterID: 298 p.MaxUniStreamNum = protocol.StreamNum(val) 299 if p.MaxUniStreamNum > protocol.MaxStreamCount { 300 return fmt.Errorf("initial_max_streams_uni too large: %d (maximum %d)", p.MaxUniStreamNum, protocol.MaxStreamCount) 301 } 302 case maxIdleTimeoutParameterID: 303 p.MaxIdleTimeout = utils.Max(protocol.MinRemoteIdleTimeout, time.Duration(val)*time.Millisecond) 304 case maxUDPPayloadSizeParameterID: 305 if val < 1200 { 306 return fmt.Errorf("invalid value for max_udp_payload_size: %d (minimum 1200)", val) 307 } 308 p.MaxUDPPayloadSize = protocol.ByteCount(val) 309 case ackDelayExponentParameterID: 310 if val > protocol.MaxAckDelayExponent { 311 return fmt.Errorf("invalid value for ack_delay_exponent: %d (maximum %d)", val, protocol.MaxAckDelayExponent) 312 } 313 p.AckDelayExponent = uint8(val) 314 case maxAckDelayParameterID: 315 if val > uint64(protocol.MaxMaxAckDelay/time.Millisecond) { 316 return fmt.Errorf("invalid value for max_ack_delay: %dms (maximum %dms)", val, protocol.MaxMaxAckDelay/time.Millisecond) 317 } 318 p.MaxAckDelay = time.Duration(val) * time.Millisecond 319 case activeConnectionIDLimitParameterID: 320 if val < 2 { 321 return fmt.Errorf("invalid value for active_connection_id_limit: %d (minimum 2)", val) 322 } 323 p.ActiveConnectionIDLimit = val 324 case maxDatagramFrameSizeParameterID: 325 p.MaxDatagramFrameSize = protocol.ByteCount(val) 326 default: 327 return fmt.Errorf("TransportParameter BUG: transport parameter %d not found", paramID) 328 } 329 return nil 330 } 331 332 // Marshal the transport parameters 333 func (p *TransportParameters) Marshal(pers protocol.Perspective) []byte { 334 // Typical Transport Parameters consume around 110 bytes, depending on the exact values, 335 // especially the lengths of the Connection IDs. 336 // Allocate 256 bytes, so we won't have to grow the slice in any case. 337 b := make([]byte, 0, 256) 338 339 // add a greased value 340 random := make([]byte, 18) 341 rand.Read(random) 342 b = quicvarint.Append(b, 27+31*uint64(random[0])) 343 length := random[1] % 16 344 b = quicvarint.Append(b, uint64(length)) 345 b = append(b, random[2:2+length]...) 346 347 // initial_max_stream_data_bidi_local 348 b = p.marshalVarintParam(b, initialMaxStreamDataBidiLocalParameterID, uint64(p.InitialMaxStreamDataBidiLocal)) 349 // initial_max_stream_data_bidi_remote 350 b = p.marshalVarintParam(b, initialMaxStreamDataBidiRemoteParameterID, uint64(p.InitialMaxStreamDataBidiRemote)) 351 // initial_max_stream_data_uni 352 b = p.marshalVarintParam(b, initialMaxStreamDataUniParameterID, uint64(p.InitialMaxStreamDataUni)) 353 // initial_max_data 354 b = p.marshalVarintParam(b, initialMaxDataParameterID, uint64(p.InitialMaxData)) 355 // initial_max_bidi_streams 356 b = p.marshalVarintParam(b, initialMaxStreamsBidiParameterID, uint64(p.MaxBidiStreamNum)) 357 // initial_max_uni_streams 358 b = p.marshalVarintParam(b, initialMaxStreamsUniParameterID, uint64(p.MaxUniStreamNum)) 359 // idle_timeout 360 b = p.marshalVarintParam(b, maxIdleTimeoutParameterID, uint64(p.MaxIdleTimeout/time.Millisecond)) 361 // max_udp_payload_size 362 if p.MaxUDPPayloadSize > 0 { 363 b = p.marshalVarintParam(b, maxUDPPayloadSizeParameterID, uint64(p.MaxUDPPayloadSize)) 364 } 365 // max_ack_delay 366 // Only send it if is different from the default value. 367 if p.MaxAckDelay != protocol.DefaultMaxAckDelay { 368 b = p.marshalVarintParam(b, maxAckDelayParameterID, uint64(p.MaxAckDelay/time.Millisecond)) 369 } 370 // ack_delay_exponent 371 // Only send it if is different from the default value. 372 if p.AckDelayExponent != protocol.DefaultAckDelayExponent { 373 b = p.marshalVarintParam(b, ackDelayExponentParameterID, uint64(p.AckDelayExponent)) 374 } 375 // disable_active_migration 376 if p.DisableActiveMigration { 377 b = quicvarint.Append(b, uint64(disableActiveMigrationParameterID)) 378 b = quicvarint.Append(b, 0) 379 } 380 if pers == protocol.PerspectiveServer { 381 // stateless_reset_token 382 if p.StatelessResetToken != nil { 383 b = quicvarint.Append(b, uint64(statelessResetTokenParameterID)) 384 b = quicvarint.Append(b, 16) 385 b = append(b, p.StatelessResetToken[:]...) 386 } 387 // original_destination_connection_id 388 b = quicvarint.Append(b, uint64(originalDestinationConnectionIDParameterID)) 389 b = quicvarint.Append(b, uint64(p.OriginalDestinationConnectionID.Len())) 390 b = append(b, p.OriginalDestinationConnectionID.Bytes()...) 391 // preferred_address 392 if p.PreferredAddress != nil { 393 b = quicvarint.Append(b, uint64(preferredAddressParameterID)) 394 b = quicvarint.Append(b, 4+2+16+2+1+uint64(p.PreferredAddress.ConnectionID.Len())+16) 395 ip4 := p.PreferredAddress.IPv4.Addr().As4() 396 b = append(b, ip4[:]...) 397 b = binary.BigEndian.AppendUint16(b, p.PreferredAddress.IPv4.Port()) 398 ip6 := p.PreferredAddress.IPv6.Addr().As16() 399 b = append(b, ip6[:]...) 400 b = binary.BigEndian.AppendUint16(b, p.PreferredAddress.IPv6.Port()) 401 b = append(b, uint8(p.PreferredAddress.ConnectionID.Len())) 402 b = append(b, p.PreferredAddress.ConnectionID.Bytes()...) 403 b = append(b, p.PreferredAddress.StatelessResetToken[:]...) 404 } 405 } 406 // active_connection_id_limit 407 if p.ActiveConnectionIDLimit != protocol.DefaultActiveConnectionIDLimit { 408 b = p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit) 409 } 410 // initial_source_connection_id 411 b = quicvarint.Append(b, uint64(initialSourceConnectionIDParameterID)) 412 b = quicvarint.Append(b, uint64(p.InitialSourceConnectionID.Len())) 413 b = append(b, p.InitialSourceConnectionID.Bytes()...) 414 // retry_source_connection_id 415 if pers == protocol.PerspectiveServer && p.RetrySourceConnectionID != nil { 416 b = quicvarint.Append(b, uint64(retrySourceConnectionIDParameterID)) 417 b = quicvarint.Append(b, uint64(p.RetrySourceConnectionID.Len())) 418 b = append(b, p.RetrySourceConnectionID.Bytes()...) 419 } 420 if p.MaxDatagramFrameSize != protocol.InvalidByteCount { 421 b = p.marshalVarintParam(b, maxDatagramFrameSizeParameterID, uint64(p.MaxDatagramFrameSize)) 422 } 423 424 if pers == protocol.PerspectiveClient && len(AdditionalTransportParametersClient) > 0 { 425 for k, v := range AdditionalTransportParametersClient { 426 b = quicvarint.Append(b, k) 427 b = quicvarint.Append(b, uint64(len(v))) 428 b = append(b, v...) 429 } 430 } 431 432 return b 433 } 434 435 func (p *TransportParameters) marshalVarintParam(b []byte, id transportParameterID, val uint64) []byte { 436 b = quicvarint.Append(b, uint64(id)) 437 b = quicvarint.Append(b, uint64(quicvarint.Len(val))) 438 return quicvarint.Append(b, val) 439 } 440 441 // MarshalForSessionTicket marshals the transport parameters we save in the session ticket. 442 // When sending a 0-RTT enabled TLS session tickets, we need to save the transport parameters. 443 // The client will remember the transport parameters used in the last session, 444 // and apply those to the 0-RTT data it sends. 445 // Saving the transport parameters in the ticket gives the server the option to reject 0-RTT 446 // if the transport parameters changed. 447 // Since the session ticket is encrypted, the serialization format is defined by the server. 448 // For convenience, we use the same format that we also use for sending the transport parameters. 449 func (p *TransportParameters) MarshalForSessionTicket(b []byte) []byte { 450 b = quicvarint.Append(b, transportParameterMarshalingVersion) 451 452 // initial_max_stream_data_bidi_local 453 b = p.marshalVarintParam(b, initialMaxStreamDataBidiLocalParameterID, uint64(p.InitialMaxStreamDataBidiLocal)) 454 // initial_max_stream_data_bidi_remote 455 b = p.marshalVarintParam(b, initialMaxStreamDataBidiRemoteParameterID, uint64(p.InitialMaxStreamDataBidiRemote)) 456 // initial_max_stream_data_uni 457 b = p.marshalVarintParam(b, initialMaxStreamDataUniParameterID, uint64(p.InitialMaxStreamDataUni)) 458 // initial_max_data 459 b = p.marshalVarintParam(b, initialMaxDataParameterID, uint64(p.InitialMaxData)) 460 // initial_max_bidi_streams 461 b = p.marshalVarintParam(b, initialMaxStreamsBidiParameterID, uint64(p.MaxBidiStreamNum)) 462 // initial_max_uni_streams 463 b = p.marshalVarintParam(b, initialMaxStreamsUniParameterID, uint64(p.MaxUniStreamNum)) 464 // max_datagram_frame_size 465 if p.MaxDatagramFrameSize != protocol.InvalidByteCount { 466 b = p.marshalVarintParam(b, maxDatagramFrameSizeParameterID, uint64(p.MaxDatagramFrameSize)) 467 } 468 // active_connection_id_limit 469 return p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit) 470 } 471 472 // UnmarshalFromSessionTicket unmarshals transport parameters from a session ticket. 473 func (p *TransportParameters) UnmarshalFromSessionTicket(b []byte) error { 474 version, l, err := quicvarint.Parse(b) 475 if err != nil { 476 return err 477 } 478 if version != transportParameterMarshalingVersion { 479 return fmt.Errorf("unknown transport parameter marshaling version: %d", version) 480 } 481 return p.unmarshal(b[l:], protocol.PerspectiveServer, true) 482 } 483 484 // ValidFor0RTT checks if the transport parameters match those saved in the session ticket. 485 func (p *TransportParameters) ValidFor0RTT(saved *TransportParameters) bool { 486 if saved.MaxDatagramFrameSize != protocol.InvalidByteCount && (p.MaxDatagramFrameSize == protocol.InvalidByteCount || p.MaxDatagramFrameSize < saved.MaxDatagramFrameSize) { 487 return false 488 } 489 return p.InitialMaxStreamDataBidiLocal >= saved.InitialMaxStreamDataBidiLocal && 490 p.InitialMaxStreamDataBidiRemote >= saved.InitialMaxStreamDataBidiRemote && 491 p.InitialMaxStreamDataUni >= saved.InitialMaxStreamDataUni && 492 p.InitialMaxData >= saved.InitialMaxData && 493 p.MaxBidiStreamNum >= saved.MaxBidiStreamNum && 494 p.MaxUniStreamNum >= saved.MaxUniStreamNum && 495 p.ActiveConnectionIDLimit == saved.ActiveConnectionIDLimit 496 } 497 498 // ValidForUpdate checks that the new transport parameters don't reduce limits after resuming a 0-RTT connection. 499 // It is only used on the client side. 500 func (p *TransportParameters) ValidForUpdate(saved *TransportParameters) bool { 501 if saved.MaxDatagramFrameSize != protocol.InvalidByteCount && (p.MaxDatagramFrameSize == protocol.InvalidByteCount || p.MaxDatagramFrameSize < saved.MaxDatagramFrameSize) { 502 return false 503 } 504 return p.ActiveConnectionIDLimit >= saved.ActiveConnectionIDLimit && 505 p.InitialMaxData >= saved.InitialMaxData && 506 p.InitialMaxStreamDataBidiLocal >= saved.InitialMaxStreamDataBidiLocal && 507 p.InitialMaxStreamDataBidiRemote >= saved.InitialMaxStreamDataBidiRemote && 508 p.InitialMaxStreamDataUni >= saved.InitialMaxStreamDataUni && 509 p.MaxBidiStreamNum >= saved.MaxBidiStreamNum && 510 p.MaxUniStreamNum >= saved.MaxUniStreamNum 511 } 512 513 // String returns a string representation, intended for logging. 514 func (p *TransportParameters) String() string { 515 logString := "&wire.TransportParameters{OriginalDestinationConnectionID: %s, InitialSourceConnectionID: %s, " 516 logParams := []interface{}{p.OriginalDestinationConnectionID, p.InitialSourceConnectionID} 517 if p.RetrySourceConnectionID != nil { 518 logString += "RetrySourceConnectionID: %s, " 519 logParams = append(logParams, p.RetrySourceConnectionID) 520 } 521 logString += "InitialMaxStreamDataBidiLocal: %d, InitialMaxStreamDataBidiRemote: %d, InitialMaxStreamDataUni: %d, InitialMaxData: %d, MaxBidiStreamNum: %d, MaxUniStreamNum: %d, MaxIdleTimeout: %s, AckDelayExponent: %d, MaxAckDelay: %s, ActiveConnectionIDLimit: %d" 522 logParams = append(logParams, []interface{}{p.InitialMaxStreamDataBidiLocal, p.InitialMaxStreamDataBidiRemote, p.InitialMaxStreamDataUni, p.InitialMaxData, p.MaxBidiStreamNum, p.MaxUniStreamNum, p.MaxIdleTimeout, p.AckDelayExponent, p.MaxAckDelay, p.ActiveConnectionIDLimit}...) 523 if p.StatelessResetToken != nil { // the client never sends a stateless reset token 524 logString += ", StatelessResetToken: %#x" 525 logParams = append(logParams, *p.StatelessResetToken) 526 } 527 if p.MaxDatagramFrameSize != protocol.InvalidByteCount { 528 logString += ", MaxDatagramFrameSize: %d" 529 logParams = append(logParams, p.MaxDatagramFrameSize) 530 } 531 logString += "}" 532 return fmt.Sprintf(logString, logParams...) 533 }