Refactor SerializeTransportParameters
This CL is a no-op. It refactors SerializeTransportParameters such that it starts with a sorted vector of which transport parameter to serialize. That'll make it easier to randomize the order of serialized transport parameters in a subsequent CL.
PiperOrigin-RevId: 390710640
diff --git a/quic/core/crypto/transport_parameters.cc b/quic/core/crypto/transport_parameters.cc
index 14df9c3..1d7ecc4 100644
--- a/quic/core/crypto/transport_parameters.cc
+++ b/quic/core/crypto/transport_parameters.cc
@@ -609,15 +609,25 @@
std::vector<uint8_t>* out) {
std::string error_details;
if (!in.AreValid(&error_details)) {
- QUIC_BUG(quic_bug_10743_5)
+ QUIC_BUG(invalid transport parameters)
<< "Not serializing invalid transport parameters: " << error_details;
return false;
}
if (in.version == 0 || (in.perspective == Perspective::IS_SERVER &&
in.supported_versions.empty())) {
- QUIC_BUG(quic_bug_10743_6) << "Refusing to serialize without versions";
+ QUIC_BUG(missing versions) << "Refusing to serialize without versions";
return false;
}
+ TransportParameters::ParameterMap custom_parameters = in.custom_parameters;
+ for (const auto& kv : custom_parameters) {
+ if (kv.first % 31 == 27) {
+ // See the "Reserved Transport Parameters" section of RFC 9000.
+ QUIC_BUG(custom_parameters with GREASE)
+ << "Serializing custom_parameters with GREASE ID " << kv.first
+ << " is not allowed";
+ return false;
+ }
+ }
// Maximum length of the GREASE transport parameter (see below).
static constexpr size_t kMaxGreaseLength = 16;
@@ -637,8 +647,6 @@
kTypeAndValueLength + 4 /*IPv4 address */ + 2 /* IPv4 port */ +
16 /* IPv6 address */ + 1 /* Connection ID length */ +
255 /* maximum connection ID length */ + 16 /* stateless reset token */;
- static constexpr size_t kGreaseParameterLength =
- kTypeAndValueLength + kMaxGreaseLength;
static constexpr size_t kKnownTransportParamLength =
kConnectionIdParameterLength + // original_destination_connection_id
kIntegerParameterLength + // max_idle_timeout
@@ -663,8 +671,34 @@
kTypeAndValueLength + // google_connection_options
kTypeAndValueLength + // user_agent_id
kTypeAndValueLength + // key_update_not_yet_supported
- kTypeAndValueLength + // google-version
- kGreaseParameterLength; // GREASE
+ kTypeAndValueLength; // google-version
+
+ std::vector<TransportParameters::TransportParameterId> parameter_ids = {
+ TransportParameters::kOriginalDestinationConnectionId,
+ TransportParameters::kMaxIdleTimeout,
+ TransportParameters::kStatelessResetToken,
+ TransportParameters::kMaxPacketSize,
+ TransportParameters::kInitialMaxData,
+ TransportParameters::kInitialMaxStreamDataBidiLocal,
+ TransportParameters::kInitialMaxStreamDataBidiRemote,
+ TransportParameters::kInitialMaxStreamDataUni,
+ TransportParameters::kInitialMaxStreamsBidi,
+ TransportParameters::kInitialMaxStreamsUni,
+ TransportParameters::kAckDelayExponent,
+ TransportParameters::kMaxAckDelay,
+ TransportParameters::kMinAckDelay,
+ TransportParameters::kActiveConnectionIdLimit,
+ TransportParameters::kMaxDatagramFrameSize,
+ TransportParameters::kInitialRoundTripTime,
+ TransportParameters::kDisableActiveMigration,
+ TransportParameters::kPreferredAddress,
+ TransportParameters::kInitialSourceConnectionId,
+ TransportParameters::kRetrySourceConnectionId,
+ TransportParameters::kGoogleConnectionOptions,
+ TransportParameters::kGoogleUserAgentId,
+ TransportParameters::kGoogleKeyUpdateNotYetSupported,
+ TransportParameters::kGoogleQuicVersion,
+ };
size_t max_transport_param_length = kKnownTransportParamLength;
// google_connection_options.
@@ -680,269 +714,379 @@
max_transport_param_length +=
sizeof(in.version) + 1 /* versions length */ +
in.supported_versions.size() * sizeof(QuicVersionLabel);
+
+ // Add a random GREASE transport parameter, as defined in the
+ // "Reserved Transport Parameters" section of RFC 9000.
+ // This forces receivers to support unexpected input.
+ QuicRandom* random = QuicRandom::GetInstance();
+ // Transport parameter identifiers are 62 bits long so we need to
+ // ensure that the output of the computation below fits in 62 bits.
+ uint64_t grease_id64 = random->RandUint64() % ((1ULL << 62) - 31);
+ // Make sure grease_id % 31 == 27. Note that this is not uniformely
+ // distributed but is acceptable since no security depends on this
+ // randomness.
+ grease_id64 = (grease_id64 / 31) * 31 + 27;
+ TransportParameters::TransportParameterId grease_id =
+ static_cast<TransportParameters::TransportParameterId>(grease_id64);
+ const size_t grease_length = random->RandUint64() % kMaxGreaseLength;
+ QUICHE_DCHECK_GE(kMaxGreaseLength, grease_length);
+ char grease_contents[kMaxGreaseLength];
+ random->RandBytes(grease_contents, grease_length);
+ custom_parameters[grease_id] = std::string(grease_contents, grease_length);
+
// Custom parameters.
- for (const auto& kv : in.custom_parameters) {
+ for (const auto& kv : custom_parameters) {
max_transport_param_length += kTypeAndValueLength + kv.second.length();
+ parameter_ids.push_back(kv.first);
}
out->resize(max_transport_param_length);
QuicDataWriter writer(out->size(), reinterpret_cast<char*>(out->data()));
- // original_destination_connection_id
- if (in.original_destination_connection_id.has_value()) {
- QUICHE_DCHECK_EQ(Perspective::IS_SERVER, in.perspective);
- QuicConnectionId original_destination_connection_id =
- in.original_destination_connection_id.value();
- if (!writer.WriteVarInt62(
- TransportParameters::kOriginalDestinationConnectionId) ||
- !writer.WriteStringPieceVarInt62(
- absl::string_view(original_destination_connection_id.data(),
- original_destination_connection_id.length()))) {
- QUIC_BUG(quic_bug_10743_7)
- << "Failed to write original_destination_connection_id "
- << original_destination_connection_id << " for " << in;
- return false;
- }
- }
-
- if (!in.max_idle_timeout_ms.Write(&writer)) {
- QUIC_BUG(quic_bug_10743_8) << "Failed to write idle_timeout for " << in;
- return false;
- }
-
- // stateless_reset_token
- if (!in.stateless_reset_token.empty()) {
- QUICHE_DCHECK_EQ(kStatelessResetTokenLength,
- in.stateless_reset_token.size());
- QUICHE_DCHECK_EQ(Perspective::IS_SERVER, in.perspective);
- if (!writer.WriteVarInt62(TransportParameters::kStatelessResetToken) ||
- !writer.WriteStringPieceVarInt62(absl::string_view(
- reinterpret_cast<const char*>(in.stateless_reset_token.data()),
- in.stateless_reset_token.size()))) {
- QUIC_BUG(quic_bug_10743_9)
- << "Failed to write stateless_reset_token of length "
- << in.stateless_reset_token.size() << " for " << in;
- return false;
- }
- }
-
- if (!in.max_udp_payload_size.Write(&writer) ||
- !in.initial_max_data.Write(&writer) ||
- !in.initial_max_stream_data_bidi_local.Write(&writer) ||
- !in.initial_max_stream_data_bidi_remote.Write(&writer) ||
- !in.initial_max_stream_data_uni.Write(&writer) ||
- !in.initial_max_streams_bidi.Write(&writer) ||
- !in.initial_max_streams_uni.Write(&writer) ||
- !in.ack_delay_exponent.Write(&writer) ||
- !in.max_ack_delay.Write(&writer) || !in.min_ack_delay_us.Write(&writer) ||
- !in.active_connection_id_limit.Write(&writer) ||
- !in.max_datagram_frame_size.Write(&writer) ||
- !in.initial_round_trip_time_us.Write(&writer)) {
- QUIC_BUG(quic_bug_10743_10) << "Failed to write integers for " << in;
- return false;
- }
-
- // disable_active_migration
- if (in.disable_active_migration) {
- if (!writer.WriteVarInt62(TransportParameters::kDisableActiveMigration) ||
- !writer.WriteVarInt62(/* transport parameter length */ 0)) {
- QUIC_BUG(quic_bug_10743_11)
- << "Failed to write disable_active_migration for " << in;
- return false;
- }
- }
-
- // preferred_address
- if (in.preferred_address) {
- std::string v4_address_bytes =
- in.preferred_address->ipv4_socket_address.host().ToPackedString();
- std::string v6_address_bytes =
- in.preferred_address->ipv6_socket_address.host().ToPackedString();
- if (v4_address_bytes.length() != 4 || v6_address_bytes.length() != 16 ||
- in.preferred_address->stateless_reset_token.size() !=
- kStatelessResetTokenLength) {
- QUIC_BUG(quic_bug_10743_12) << "Bad lengths " << *in.preferred_address;
- return false;
- }
- const uint64_t preferred_address_length =
- v4_address_bytes.length() + /* IPv4 port */ sizeof(uint16_t) +
- v6_address_bytes.length() + /* IPv6 port */ sizeof(uint16_t) +
- /* connection ID length byte */ sizeof(uint8_t) +
- in.preferred_address->connection_id.length() +
- in.preferred_address->stateless_reset_token.size();
- if (!writer.WriteVarInt62(TransportParameters::kPreferredAddress) ||
- !writer.WriteVarInt62(
- /* transport parameter length */ preferred_address_length) ||
- !writer.WriteStringPiece(v4_address_bytes) ||
- !writer.WriteUInt16(in.preferred_address->ipv4_socket_address.port()) ||
- !writer.WriteStringPiece(v6_address_bytes) ||
- !writer.WriteUInt16(in.preferred_address->ipv6_socket_address.port()) ||
- !writer.WriteUInt8(in.preferred_address->connection_id.length()) ||
- !writer.WriteBytes(in.preferred_address->connection_id.data(),
- in.preferred_address->connection_id.length()) ||
- !writer.WriteBytes(
- in.preferred_address->stateless_reset_token.data(),
- in.preferred_address->stateless_reset_token.size())) {
- QUIC_BUG(quic_bug_10743_13)
- << "Failed to write preferred_address for " << in;
- return false;
- }
- }
-
- // initial_source_connection_id
- if (in.initial_source_connection_id.has_value()) {
- QuicConnectionId initial_source_connection_id =
- in.initial_source_connection_id.value();
- if (!writer.WriteVarInt62(
- TransportParameters::kInitialSourceConnectionId) ||
- !writer.WriteStringPieceVarInt62(
- absl::string_view(initial_source_connection_id.data(),
- initial_source_connection_id.length()))) {
- QUIC_BUG(quic_bug_10743_14)
- << "Failed to write initial_source_connection_id "
- << initial_source_connection_id << " for " << in;
- return false;
- }
- }
-
- // retry_source_connection_id
- if (in.retry_source_connection_id.has_value()) {
- QUICHE_DCHECK_EQ(Perspective::IS_SERVER, in.perspective);
- QuicConnectionId retry_source_connection_id =
- in.retry_source_connection_id.value();
- if (!writer.WriteVarInt62(TransportParameters::kRetrySourceConnectionId) ||
- !writer.WriteStringPieceVarInt62(
- absl::string_view(retry_source_connection_id.data(),
- retry_source_connection_id.length()))) {
- QUIC_BUG(quic_bug_10743_15)
- << "Failed to write retry_source_connection_id "
- << retry_source_connection_id << " for " << in;
- return false;
- }
- }
-
- // Google-specific connection options.
- if (in.google_connection_options.has_value()) {
- static_assert(sizeof(in.google_connection_options.value().front()) == 4,
- "bad size");
- uint64_t connection_options_length =
- in.google_connection_options.value().size() * 4;
- if (!writer.WriteVarInt62(TransportParameters::kGoogleConnectionOptions) ||
- !writer.WriteVarInt62(
- /* transport parameter length */ connection_options_length)) {
- QUIC_BUG(quic_bug_10743_16)
- << "Failed to write google_connection_options of length "
- << connection_options_length << " for " << in;
- return false;
- }
- for (const QuicTag& connection_option :
- in.google_connection_options.value()) {
- if (!writer.WriteTag(connection_option)) {
- QUIC_BUG(quic_bug_10743_17)
- << "Failed to write google_connection_option "
- << QuicTagToString(connection_option) << " for " << in;
- return false;
- }
- }
- }
-
- // Google-specific user agent identifier.
- if (in.user_agent_id.has_value()) {
- if (!writer.WriteVarInt62(TransportParameters::kGoogleUserAgentId) ||
- !writer.WriteStringPieceVarInt62(in.user_agent_id.value())) {
- QUIC_BUG(quic_bug_10743_18)
- << "Failed to write Google user agent ID \""
- << in.user_agent_id.value() << "\" for " << in;
- return false;
- }
- }
-
- // Google-specific indicator for key update not yet supported.
- if (in.key_update_not_yet_supported) {
- if (!writer.WriteVarInt62(
- TransportParameters::kGoogleKeyUpdateNotYetSupported) ||
- !writer.WriteVarInt62(/* transport parameter length */ 0)) {
- QUIC_BUG(quic_bug_10743_19)
- << "Failed to write key_update_not_yet_supported for " << in;
- return false;
- }
- }
-
- // Google-specific version extension.
- static_assert(sizeof(QuicVersionLabel) == sizeof(uint32_t), "bad length");
- uint64_t google_version_length = sizeof(in.version);
- if (in.perspective == Perspective::IS_SERVER) {
- google_version_length +=
- /* versions length */ sizeof(uint8_t) +
- sizeof(QuicVersionLabel) * in.supported_versions.size();
- }
- if (!writer.WriteVarInt62(TransportParameters::kGoogleQuicVersion) ||
- !writer.WriteVarInt62(
- /* transport parameter length */ google_version_length) ||
- !writer.WriteUInt32(in.version)) {
- QUIC_BUG(quic_bug_10743_20)
- << "Failed to write Google version extension for " << in;
- return false;
- }
- if (in.perspective == Perspective::IS_SERVER) {
- if (!writer.WriteUInt8(sizeof(QuicVersionLabel) *
- in.supported_versions.size())) {
- QUIC_BUG(quic_bug_10743_21)
- << "Failed to write versions length for " << in;
- return false;
- }
- for (QuicVersionLabel version_label : in.supported_versions) {
- if (!writer.WriteUInt32(version_label)) {
- QUIC_BUG(quic_bug_10743_22)
- << "Failed to write supported version for " << in;
- return false;
- }
- }
- }
-
- for (const auto& kv : in.custom_parameters) {
- const TransportParameters::TransportParameterId param_id = kv.first;
- if (param_id % 31 == 27) {
- // See the "Reserved Transport Parameters" section of
- // draft-ietf-quic-transport.
- QUIC_BUG(quic_bug_10743_23)
- << "Serializing custom_parameters with GREASE ID " << param_id
- << " is not allowed";
- return false;
- }
- if (!writer.WriteVarInt62(param_id) ||
- !writer.WriteStringPieceVarInt62(kv.second)) {
- QUIC_BUG(quic_bug_10743_24)
- << "Failed to write custom parameter " << param_id;
- return false;
- }
- }
-
- {
- // Add a random GREASE transport parameter, as defined in the
- // "Reserved Transport Parameters" section of draft-ietf-quic-transport.
- // https://quicwg.org/base-drafts/draft-ietf-quic-transport.html
- // This forces receivers to support unexpected input.
- QuicRandom* random = QuicRandom::GetInstance();
- // Transport parameter identifiers are 62 bits long so we need to ensure
- // that the output of the computation below fits in 62 bits.
- uint64_t grease_id64 = random->RandUint64() % ((1ULL << 62) - 31);
- // Make sure grease_id % 31 == 27. Note that this is not uniformely
- // distributed but is acceptable since no security depends on this
- // randomness.
- grease_id64 = (grease_id64 / 31) * 31 + 27;
- TransportParameters::TransportParameterId grease_id =
- static_cast<TransportParameters::TransportParameterId>(grease_id64);
- const size_t grease_length = random->RandUint64() % kMaxGreaseLength;
- QUICHE_DCHECK_GE(kMaxGreaseLength, grease_length);
- char grease_contents[kMaxGreaseLength];
- random->RandBytes(grease_contents, grease_length);
- if (!writer.WriteVarInt62(grease_id) ||
- !writer.WriteStringPieceVarInt62(
- absl::string_view(grease_contents, grease_length))) {
- QUIC_BUG(quic_bug_10743_25) << "Failed to write GREASE parameter "
- << TransportParameterIdToString(grease_id);
- return false;
+ for (TransportParameters::TransportParameterId parameter_id : parameter_ids) {
+ switch (parameter_id) {
+ // original_destination_connection_id
+ case TransportParameters::kOriginalDestinationConnectionId: {
+ if (in.original_destination_connection_id.has_value()) {
+ QUICHE_DCHECK_EQ(Perspective::IS_SERVER, in.perspective);
+ QuicConnectionId original_destination_connection_id =
+ in.original_destination_connection_id.value();
+ if (!writer.WriteVarInt62(
+ TransportParameters::kOriginalDestinationConnectionId) ||
+ !writer.WriteStringPieceVarInt62(absl::string_view(
+ original_destination_connection_id.data(),
+ original_destination_connection_id.length()))) {
+ QUIC_BUG(Failed to write original_destination_connection_id)
+ << "Failed to write original_destination_connection_id "
+ << original_destination_connection_id << " for " << in;
+ return false;
+ }
+ }
+ } break;
+ // max_idle_timeout
+ case TransportParameters::kMaxIdleTimeout: {
+ if (!in.max_idle_timeout_ms.Write(&writer)) {
+ QUIC_BUG(Failed to write idle_timeout)
+ << "Failed to write idle_timeout for " << in;
+ return false;
+ }
+ } break;
+ // stateless_reset_token
+ case TransportParameters::kStatelessResetToken: {
+ if (!in.stateless_reset_token.empty()) {
+ QUICHE_DCHECK_EQ(kStatelessResetTokenLength,
+ in.stateless_reset_token.size());
+ QUICHE_DCHECK_EQ(Perspective::IS_SERVER, in.perspective);
+ if (!writer.WriteVarInt62(
+ TransportParameters::kStatelessResetToken) ||
+ !writer.WriteStringPieceVarInt62(
+ absl::string_view(reinterpret_cast<const char*>(
+ in.stateless_reset_token.data()),
+ in.stateless_reset_token.size()))) {
+ QUIC_BUG(Failed to write stateless_reset_token)
+ << "Failed to write stateless_reset_token of length "
+ << in.stateless_reset_token.size() << " for " << in;
+ return false;
+ }
+ }
+ } break;
+ // max_udp_payload_size
+ case TransportParameters::kMaxPacketSize: {
+ if (!in.max_udp_payload_size.Write(&writer)) {
+ QUIC_BUG(Failed to write max_udp_payload_size)
+ << "Failed to write max_udp_payload_size for " << in;
+ return false;
+ }
+ } break;
+ // initial_max_data
+ case TransportParameters::kInitialMaxData: {
+ if (!in.initial_max_data.Write(&writer)) {
+ QUIC_BUG(Failed to write initial_max_data)
+ << "Failed to write initial_max_data for " << in;
+ return false;
+ }
+ } break;
+ // initial_max_stream_data_bidi_local
+ case TransportParameters::kInitialMaxStreamDataBidiLocal: {
+ if (!in.initial_max_stream_data_bidi_local.Write(&writer)) {
+ QUIC_BUG(Failed to write initial_max_stream_data_bidi_local)
+ << "Failed to write initial_max_stream_data_bidi_local for "
+ << in;
+ return false;
+ }
+ } break;
+ // initial_max_stream_data_bidi_remote
+ case TransportParameters::kInitialMaxStreamDataBidiRemote: {
+ if (!in.initial_max_stream_data_bidi_remote.Write(&writer)) {
+ QUIC_BUG(Failed to write initial_max_stream_data_bidi_remote)
+ << "Failed to write initial_max_stream_data_bidi_remote for "
+ << in;
+ return false;
+ }
+ } break;
+ // initial_max_stream_data_uni
+ case TransportParameters::kInitialMaxStreamDataUni: {
+ if (!in.initial_max_stream_data_uni.Write(&writer)) {
+ QUIC_BUG(Failed to write initial_max_stream_data_uni)
+ << "Failed to write initial_max_stream_data_uni for " << in;
+ return false;
+ }
+ } break;
+ // initial_max_streams_bidi
+ case TransportParameters::kInitialMaxStreamsBidi: {
+ if (!in.initial_max_streams_bidi.Write(&writer)) {
+ QUIC_BUG(Failed to write initial_max_streams_bidi)
+ << "Failed to write initial_max_streams_bidi for " << in;
+ return false;
+ }
+ } break;
+ // initial_max_streams_uni
+ case TransportParameters::kInitialMaxStreamsUni: {
+ if (!in.initial_max_streams_uni.Write(&writer)) {
+ QUIC_BUG(Failed to write initial_max_streams_uni)
+ << "Failed to write initial_max_streams_uni for " << in;
+ return false;
+ }
+ } break;
+ // ack_delay_exponent
+ case TransportParameters::kAckDelayExponent: {
+ if (!in.ack_delay_exponent.Write(&writer)) {
+ QUIC_BUG(Failed to write ack_delay_exponent)
+ << "Failed to write ack_delay_exponent for " << in;
+ return false;
+ }
+ } break;
+ // max_ack_delay
+ case TransportParameters::kMaxAckDelay: {
+ if (!in.max_ack_delay.Write(&writer)) {
+ QUIC_BUG(Failed to write max_ack_delay)
+ << "Failed to write max_ack_delay for " << in;
+ return false;
+ }
+ } break;
+ // min_ack_delay_us
+ case TransportParameters::kMinAckDelay: {
+ if (!in.min_ack_delay_us.Write(&writer)) {
+ QUIC_BUG(Failed to write min_ack_delay_us)
+ << "Failed to write min_ack_delay_us for " << in;
+ return false;
+ }
+ } break;
+ // active_connection_id_limit
+ case TransportParameters::kActiveConnectionIdLimit: {
+ if (!in.active_connection_id_limit.Write(&writer)) {
+ QUIC_BUG(Failed to write active_connection_id_limit)
+ << "Failed to write active_connection_id_limit for " << in;
+ return false;
+ }
+ } break;
+ // max_datagram_frame_size
+ case TransportParameters::kMaxDatagramFrameSize: {
+ if (!in.max_datagram_frame_size.Write(&writer)) {
+ QUIC_BUG(Failed to write max_datagram_frame_size)
+ << "Failed to write max_datagram_frame_size for " << in;
+ return false;
+ }
+ } break;
+ // initial_round_trip_time_us
+ case TransportParameters::kInitialRoundTripTime: {
+ if (!in.initial_round_trip_time_us.Write(&writer)) {
+ QUIC_BUG(Failed to write initial_round_trip_time_us)
+ << "Failed to write initial_round_trip_time_us for " << in;
+ return false;
+ }
+ } break;
+ // disable_active_migration
+ case TransportParameters::kDisableActiveMigration: {
+ if (in.disable_active_migration) {
+ if (!writer.WriteVarInt62(
+ TransportParameters::kDisableActiveMigration) ||
+ !writer.WriteVarInt62(/* transport parameter length */ 0)) {
+ QUIC_BUG(Failed to write disable_active_migration)
+ << "Failed to write disable_active_migration for " << in;
+ return false;
+ }
+ }
+ } break;
+ // preferred_address
+ case TransportParameters::kPreferredAddress: {
+ if (in.preferred_address) {
+ std::string v4_address_bytes =
+ in.preferred_address->ipv4_socket_address.host().ToPackedString();
+ std::string v6_address_bytes =
+ in.preferred_address->ipv6_socket_address.host().ToPackedString();
+ if (v4_address_bytes.length() != 4 ||
+ v6_address_bytes.length() != 16 ||
+ in.preferred_address->stateless_reset_token.size() !=
+ kStatelessResetTokenLength) {
+ QUIC_BUG(quic_bug_10743_12)
+ << "Bad lengths " << *in.preferred_address;
+ return false;
+ }
+ const uint64_t preferred_address_length =
+ v4_address_bytes.length() + /* IPv4 port */ sizeof(uint16_t) +
+ v6_address_bytes.length() + /* IPv6 port */ sizeof(uint16_t) +
+ /* connection ID length byte */ sizeof(uint8_t) +
+ in.preferred_address->connection_id.length() +
+ in.preferred_address->stateless_reset_token.size();
+ if (!writer.WriteVarInt62(TransportParameters::kPreferredAddress) ||
+ !writer.WriteVarInt62(
+ /* transport parameter length */ preferred_address_length) ||
+ !writer.WriteStringPiece(v4_address_bytes) ||
+ !writer.WriteUInt16(
+ in.preferred_address->ipv4_socket_address.port()) ||
+ !writer.WriteStringPiece(v6_address_bytes) ||
+ !writer.WriteUInt16(
+ in.preferred_address->ipv6_socket_address.port()) ||
+ !writer.WriteUInt8(
+ in.preferred_address->connection_id.length()) ||
+ !writer.WriteBytes(
+ in.preferred_address->connection_id.data(),
+ in.preferred_address->connection_id.length()) ||
+ !writer.WriteBytes(
+ in.preferred_address->stateless_reset_token.data(),
+ in.preferred_address->stateless_reset_token.size())) {
+ QUIC_BUG(Failed to write preferred_address)
+ << "Failed to write preferred_address for " << in;
+ return false;
+ }
+ }
+ } break;
+ // initial_source_connection_id
+ case TransportParameters::kInitialSourceConnectionId: {
+ if (in.initial_source_connection_id.has_value()) {
+ QuicConnectionId initial_source_connection_id =
+ in.initial_source_connection_id.value();
+ if (!writer.WriteVarInt62(
+ TransportParameters::kInitialSourceConnectionId) ||
+ !writer.WriteStringPieceVarInt62(
+ absl::string_view(initial_source_connection_id.data(),
+ initial_source_connection_id.length()))) {
+ QUIC_BUG(Failed to write initial_source_connection_id)
+ << "Failed to write initial_source_connection_id "
+ << initial_source_connection_id << " for " << in;
+ return false;
+ }
+ }
+ } break;
+ // retry_source_connection_id
+ case TransportParameters::kRetrySourceConnectionId: {
+ if (in.retry_source_connection_id.has_value()) {
+ QUICHE_DCHECK_EQ(Perspective::IS_SERVER, in.perspective);
+ QuicConnectionId retry_source_connection_id =
+ in.retry_source_connection_id.value();
+ if (!writer.WriteVarInt62(
+ TransportParameters::kRetrySourceConnectionId) ||
+ !writer.WriteStringPieceVarInt62(
+ absl::string_view(retry_source_connection_id.data(),
+ retry_source_connection_id.length()))) {
+ QUIC_BUG(Failed to write retry_source_connection_id)
+ << "Failed to write retry_source_connection_id "
+ << retry_source_connection_id << " for " << in;
+ return false;
+ }
+ }
+ } break;
+ // Google-specific connection options.
+ case TransportParameters::kGoogleConnectionOptions: {
+ if (in.google_connection_options.has_value()) {
+ static_assert(
+ sizeof(in.google_connection_options.value().front()) == 4,
+ "bad size");
+ uint64_t connection_options_length =
+ in.google_connection_options.value().size() * 4;
+ if (!writer.WriteVarInt62(
+ TransportParameters::kGoogleConnectionOptions) ||
+ !writer.WriteVarInt62(
+ /* transport parameter length */ connection_options_length)) {
+ QUIC_BUG(Failed to write google_connection_options)
+ << "Failed to write google_connection_options of length "
+ << connection_options_length << " for " << in;
+ return false;
+ }
+ for (const QuicTag& connection_option :
+ in.google_connection_options.value()) {
+ if (!writer.WriteTag(connection_option)) {
+ QUIC_BUG(Failed to write google_connection_option)
+ << "Failed to write google_connection_option "
+ << QuicTagToString(connection_option) << " for " << in;
+ return false;
+ }
+ }
+ }
+ } break;
+ // Google-specific user agent identifier.
+ case TransportParameters::kGoogleUserAgentId: {
+ if (in.user_agent_id.has_value()) {
+ if (!writer.WriteVarInt62(TransportParameters::kGoogleUserAgentId) ||
+ !writer.WriteStringPieceVarInt62(in.user_agent_id.value())) {
+ QUIC_BUG(Failed to write Google user agent ID)
+ << "Failed to write Google user agent ID \""
+ << in.user_agent_id.value() << "\" for " << in;
+ return false;
+ }
+ }
+ } break;
+ // Google-specific indicator for key update not yet supported.
+ case TransportParameters::kGoogleKeyUpdateNotYetSupported: {
+ if (in.key_update_not_yet_supported) {
+ if (!writer.WriteVarInt62(
+ TransportParameters::kGoogleKeyUpdateNotYetSupported) ||
+ !writer.WriteVarInt62(/* transport parameter length */ 0)) {
+ QUIC_BUG(Failed to write key_update_not_yet_supported)
+ << "Failed to write key_update_not_yet_supported for " << in;
+ return false;
+ }
+ }
+ } break;
+ // Google-specific version extension.
+ case TransportParameters::kGoogleQuicVersion: {
+ static_assert(sizeof(QuicVersionLabel) == sizeof(uint32_t),
+ "bad length");
+ uint64_t google_version_length = sizeof(in.version);
+ if (in.perspective == Perspective::IS_SERVER) {
+ google_version_length +=
+ /* versions length */ sizeof(uint8_t) +
+ sizeof(QuicVersionLabel) * in.supported_versions.size();
+ }
+ if (!writer.WriteVarInt62(TransportParameters::kGoogleQuicVersion) ||
+ !writer.WriteVarInt62(
+ /* transport parameter length */ google_version_length) ||
+ !writer.WriteUInt32(in.version)) {
+ QUIC_BUG(Failed to write Google version extension)
+ << "Failed to write Google version extension for " << in;
+ return false;
+ }
+ if (in.perspective == Perspective::IS_SERVER) {
+ if (!writer.WriteUInt8(sizeof(QuicVersionLabel) *
+ in.supported_versions.size())) {
+ QUIC_BUG(Failed to write versions length)
+ << "Failed to write versions length for " << in;
+ return false;
+ }
+ for (QuicVersionLabel version_label : in.supported_versions) {
+ if (!writer.WriteUInt32(version_label)) {
+ QUIC_BUG(Failed to write supported version)
+ << "Failed to write supported version for " << in;
+ return false;
+ }
+ }
+ }
+ } break;
+ // Custom parameters and GREASE.
+ default: {
+ auto it = custom_parameters.find(parameter_id);
+ if (it == custom_parameters.end()) {
+ QUIC_BUG(Unknown parameter) << "Unknown parameter " << parameter_id;
+ return false;
+ }
+ if (!writer.WriteVarInt62(parameter_id) ||
+ !writer.WriteStringPieceVarInt62(it->second)) {
+ QUIC_BUG(Failed to write custom parameter)
+ << "Failed to write custom parameter " << parameter_id;
+ return false;
+ }
+ } break;
}
}