Update QUIC transport parameters to draft 20 This CL updates from draft-18 to draft-19/20. The only changes are idle_timeout being in milliseconds instead of seconds, and the removal of the versions before the parameters. We've kept the versions in a custom Google-specific extension for now. This extension will eventually evolve into the IETF compatible version negotiation extension. gfe-relnote: protected by disabled flag quic_supports_tls_handshake PiperOrigin-RevId: 246043673 Change-Id: I35929d0f381f506e2275e639af41e840ab16b8ca
diff --git a/quic/core/crypto/transport_parameters.cc b/quic/core/crypto/transport_parameters.cc index 84022fe..8982ae0 100644 --- a/quic/core/crypto/transport_parameters.cc +++ b/quic/core/crypto/transport_parameters.cc
@@ -4,6 +4,7 @@ #include "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h" +#include <cstdint> #include <cstring> #include "third_party/boringssl/src/include/openssl/bytestring.h" @@ -41,6 +42,8 @@ kPreferredAddress = 0xd, kGoogleQuicParam = 18257, // Used for non-standard Google-specific params. + kGoogleQuicVersion = + 18258, // Used to transmit version and supported_versions. }; namespace { @@ -89,6 +92,8 @@ return "preferred_address"; case kGoogleQuicParam: return "google"; + case kGoogleQuicVersion: + return "google-version"; } return "Unknown(" + QuicTextUtils::Uint64ToString(param_id) + ")"; } @@ -245,7 +250,7 @@ rv += " " + TransportParameterIdToString(kOriginalConnectionId) + " " + original_connection_id.ToString(); } - rv += idle_timeout_seconds.ToString(/*for_use_in_list=*/true); + rv += idle_timeout_milliseconds.ToString(/*for_use_in_list=*/true); if (!stateless_reset_token.empty()) { rv += " " + TransportParameterIdToString(kStatelessResetToken) + " " + QuicTextUtils::HexEncode( @@ -278,7 +283,7 @@ TransportParameters::TransportParameters() : version(0), original_connection_id(EmptyQuicConnectionId()), - idle_timeout_seconds(kIdleTimeout), + idle_timeout_milliseconds(kIdleTimeout), max_packet_size(kMaxPacketSize, kDefaultMaxPacketSizeTransportParam, kMinMaxPacketSizeTransportParam, @@ -338,8 +343,8 @@ QUIC_BUG << "Preferred address family failure"; return false; } - const bool ok = idle_timeout_seconds.IsValid() && max_packet_size.IsValid() && - initial_max_data.IsValid() && + const bool ok = idle_timeout_milliseconds.IsValid() && + max_packet_size.IsValid() && initial_max_data.IsValid() && initial_max_stream_data_bidi_local.IsValid() && initial_max_stream_data_bidi_remote.IsValid() && initial_max_stream_data_uni.IsValid() && @@ -360,26 +365,19 @@ QUIC_DLOG(ERROR) << "Not serializing invalid transport parameters " << in; return false; } + if (in.version == 0 || (in.perspective == Perspective::IS_SERVER && + in.supported_versions.empty())) { + QUIC_DLOG(ERROR) << "Refusing to serialize without versions"; + return false; + } + bssl::ScopedCBB cbb; // Empirically transport parameters generally fit within 128 bytes. // The CBB will grow to fit larger serializations if required. - if (!CBB_init(cbb.get(), 128) || !CBB_add_u32(cbb.get(), in.version)) { - QUIC_BUG << "Failed to write version for " << in; + if (!CBB_init(cbb.get(), 128)) { + QUIC_BUG << "Failed to initialize CBB for " << in; return false; } - CBB versions; - if (in.perspective == Perspective::IS_SERVER) { - if (!CBB_add_u8_length_prefixed(cbb.get(), &versions)) { - QUIC_BUG << "Failed to write versions length for " << in; - return false; - } - for (QuicVersionLabel version : in.supported_versions) { - if (!CBB_add_u32(&versions, version)) { - QUIC_BUG << "Failed to write supported version for " << in; - return false; - } - } - } CBB params; // Add length of the transport parameters list. @@ -404,7 +402,7 @@ } } - if (!in.idle_timeout_seconds.WriteToCbb(¶ms)) { + if (!in.idle_timeout_milliseconds.WriteToCbb(¶ms)) { QUIC_BUG << "Failed to write idle_timeout for " << in; return false; } @@ -504,6 +502,29 @@ return false; } } + + // Google-specific version extension. + CBB google_version_params; + if (!CBB_add_u16(¶ms, kGoogleQuicVersion) || + !CBB_add_u16_length_prefixed(¶ms, &google_version_params) || + !CBB_add_u32(&google_version_params, in.version)) { + QUIC_BUG << "Failed to write Google version extension for " << in; + return false; + } + CBB versions; + if (in.perspective == Perspective::IS_SERVER) { + if (!CBB_add_u8_length_prefixed(&google_version_params, &versions)) { + QUIC_BUG << "Failed to write versions length for " << in; + return false; + } + for (QuicVersionLabel version : in.supported_versions) { + if (!CBB_add_u32(&versions, version)) { + QUIC_BUG << "Failed to write supported version for " << in; + return false; + } + } + } + if (!CBB_flush(cbb.get())) { QUIC_BUG << "Failed to flush CBB for " << in; return false; @@ -519,31 +540,9 @@ size_t in_len, Perspective perspective, TransportParameters* out) { + out->perspective = perspective; CBS cbs; CBS_init(&cbs, in, in_len); - if (!CBS_get_u32(&cbs, &out->version)) { - QUIC_DLOG(ERROR) << "Failed to parse transport parameter version"; - return false; - } - if (perspective == Perspective::IS_SERVER) { - CBS versions; - if (!CBS_get_u8_length_prefixed(&cbs, &versions) || - CBS_len(&versions) % 4 != 0) { - QUIC_DLOG(ERROR) - << "Failed to parse transport parameter supported versions"; - return false; - } - while (CBS_len(&versions) > 0) { - QuicVersionLabel version; - if (!CBS_get_u32(&versions, &version)) { - QUIC_DLOG(ERROR) - << "Failed to parse transport parameter supported version"; - return false; - } - out->supported_versions.push_back(version); - } - } - out->perspective = perspective; CBS params; if (!CBS_get_u16_length_prefixed(&cbs, ¶ms)) { @@ -552,19 +551,20 @@ } while (CBS_len(¶ms) > 0) { - uint16_t param_id; + TransportParameters::TransportParameterId param_id; CBS value; - if (!CBS_get_u16(¶ms, ¶m_id)) { + static_assert(sizeof(param_id) == sizeof(uint16_t), "bad size"); + if (!CBS_get_u16(¶ms, reinterpret_cast<uint16_t*>(¶m_id))) { QUIC_DLOG(ERROR) << "Failed to parse transport parameter ID"; return false; } if (!CBS_get_u16_length_prefixed(¶ms, &value)) { QUIC_DLOG(ERROR) << "Failed to parse length of transport parameter " - << param_id; + << TransportParameterIdToString(param_id); return false; } bool parse_success = true; - switch (static_cast<TransportParameters::TransportParameterId>(param_id)) { + switch (param_id) { case kOriginalConnectionId: if (!out->original_connection_id.IsEmpty()) { QUIC_DLOG(ERROR) << "Received a second original connection ID"; @@ -582,7 +582,7 @@ } break; case kIdleTimeout: - parse_success = out->idle_timeout_seconds.ReadFromCbs(&value); + parse_success = out->idle_timeout_milliseconds.ReadFromCbs(&value); break; case kStatelessResetToken: if (!out->stateless_reset_token.empty()) { @@ -692,7 +692,7 @@ QuicMakeUnique<TransportParameters::PreferredAddress>( preferred_address); } break; - case kGoogleQuicParam: + case kGoogleQuicParam: { if (out->google_quic_params) { QUIC_DLOG(ERROR) << "Received a second Google parameter"; return false; @@ -700,6 +700,30 @@ QuicStringPiece serialized_params( reinterpret_cast<const char*>(CBS_data(&value)), CBS_len(&value)); out->google_quic_params = CryptoFramer::ParseMessage(serialized_params); + } break; + case kGoogleQuicVersion: { + if (!CBS_get_u32(&value, &out->version)) { + QUIC_DLOG(ERROR) << "Failed to parse Google version extension"; + return false; + } + if (perspective == Perspective::IS_SERVER) { + CBS versions; + if (!CBS_get_u8_length_prefixed(&value, &versions) || + CBS_len(&versions) % 4 != 0) { + QUIC_DLOG(ERROR) + << "Failed to parse Google supported versions length"; + return false; + } + while (CBS_len(&versions) > 0) { + QuicVersionLabel version; + if (!CBS_get_u32(&versions, &version)) { + QUIC_DLOG(ERROR) << "Failed to parse Google supported version"; + return false; + } + out->supported_versions.push_back(version); + } + } + } break; } if (!parse_success) { return false;
diff --git a/quic/core/crypto/transport_parameters.h b/quic/core/crypto/transport_parameters.h index c52b3c8..27b5fe7 100644 --- a/quic/core/crypto/transport_parameters.h +++ b/quic/core/crypto/transport_parameters.h
@@ -20,7 +20,7 @@ // TransportParameters contains parameters for QUIC's transport layer that are // exchanged during the TLS handshake. This struct is a mirror of the struct in // the "Transport Parameter Encoding" section of draft-ietf-quic-transport. -// This struct currently uses the values from draft 18. +// This struct currently uses the values from draft 20. struct QUIC_EXPORT_PRIVATE TransportParameters { // The identifier used to differentiate transport parameters. enum TransportParameterId : uint16_t; @@ -119,8 +119,8 @@ // Initial packet sent by the client. QuicConnectionId original_connection_id; - // Idle timeout expressed in seconds. - IntegerParameter idle_timeout_seconds; + // Idle timeout expressed in milliseconds. + IntegerParameter idle_timeout_milliseconds; // Stateless reset token used in verifying stateless resets. std::vector<uint8_t> stateless_reset_token;
diff --git a/quic/core/crypto/transport_parameters_test.cc b/quic/core/crypto/transport_parameters_test.cc index 8deaeb3..bf8ac51 100644 --- a/quic/core/crypto/transport_parameters_test.cc +++ b/quic/core/crypto/transport_parameters_test.cc
@@ -19,7 +19,7 @@ const QuicVersionLabel kFakeVersionLabel = 0x01234567; const QuicVersionLabel kFakeVersionLabel2 = 0x89ABCDEF; const QuicConnectionId kFakeOriginalConnectionId = TestConnectionId(0x1337); -const uint64_t kFakeIdleTimeout = 12; +const uint64_t kFakeIdleTimeoutMilliseconds = 12012; const uint8_t kFakeStatelessResetTokenData[16] = { 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F}; @@ -82,7 +82,7 @@ TransportParameters orig_params; orig_params.perspective = Perspective::IS_CLIENT; orig_params.version = kFakeVersionLabel; - orig_params.idle_timeout_seconds.set_value(kFakeIdleTimeout); + orig_params.idle_timeout_milliseconds.set_value(kFakeIdleTimeoutMilliseconds); orig_params.max_packet_size.set_value(kFakeMaxPacketSize); orig_params.initial_max_data.set_value(kFakeInitialMaxData); orig_params.initial_max_stream_data_bidi_local.set_value( @@ -108,7 +108,8 @@ EXPECT_EQ(kFakeVersionLabel, new_params.version); EXPECT_TRUE(new_params.supported_versions.empty()); EXPECT_EQ(EmptyQuicConnectionId(), new_params.original_connection_id); - EXPECT_EQ(kFakeIdleTimeout, new_params.idle_timeout_seconds.value()); + EXPECT_EQ(kFakeIdleTimeoutMilliseconds, + new_params.idle_timeout_milliseconds.value()); EXPECT_TRUE(new_params.stateless_reset_token.empty()); EXPECT_EQ(kFakeMaxPacketSize, new_params.max_packet_size.value()); EXPECT_EQ(kFakeInitialMaxData, new_params.initial_max_data.value()); @@ -134,7 +135,7 @@ orig_params.supported_versions.push_back(kFakeVersionLabel); orig_params.supported_versions.push_back(kFakeVersionLabel2); orig_params.original_connection_id = kFakeOriginalConnectionId; - orig_params.idle_timeout_seconds.set_value(kFakeIdleTimeout); + orig_params.idle_timeout_milliseconds.set_value(kFakeIdleTimeoutMilliseconds); orig_params.stateless_reset_token = kFakeStatelessResetToken; orig_params.max_packet_size.set_value(kFakeMaxPacketSize); orig_params.initial_max_data.set_value(kFakeInitialMaxData); @@ -164,7 +165,8 @@ EXPECT_EQ(kFakeVersionLabel, new_params.supported_versions[0]); EXPECT_EQ(kFakeVersionLabel2, new_params.supported_versions[1]); EXPECT_EQ(kFakeOriginalConnectionId, new_params.original_connection_id); - EXPECT_EQ(kFakeIdleTimeout, new_params.idle_timeout_seconds.value()); + EXPECT_EQ(kFakeIdleTimeoutMilliseconds, + new_params.idle_timeout_milliseconds.value()); EXPECT_EQ(kFakeStatelessResetToken, new_params.stateless_reset_token); EXPECT_EQ(kFakeMaxPacketSize, new_params.max_packet_size.value()); EXPECT_EQ(kFakeInitialMaxData, new_params.initial_max_data.value()); @@ -202,7 +204,9 @@ TransportParameters params; params.perspective = Perspective::IS_CLIENT; EXPECT_TRUE(params.AreValid()); - params.idle_timeout_seconds.set_value(601); + params.idle_timeout_milliseconds.set_value(kFakeIdleTimeoutMilliseconds); + EXPECT_TRUE(params.AreValid()); + params.idle_timeout_milliseconds.set_value(601000); EXPECT_TRUE(params.AreValid()); } { @@ -237,7 +241,7 @@ TransportParameters orig_params; orig_params.perspective = Perspective::IS_CLIENT; orig_params.version = kFakeVersionLabel; - orig_params.idle_timeout_seconds.set_value(kFakeIdleTimeout); + orig_params.idle_timeout_milliseconds.set_value(kFakeIdleTimeoutMilliseconds); orig_params.stateless_reset_token = kFakeStatelessResetToken; orig_params.max_packet_size.set_value(kFakeMaxPacketSize); @@ -248,12 +252,11 @@ TEST_F(TransportParametersTest, ParseClientParams) { // clang-format off const uint8_t kClientParams[] = { - 0x01, 0x23, 0x45, 0x67, // initial version - 0x00, 0x3B, // length of the parameters array that follows - // idle_timeout_seconds + 0x00, 0x44, // length of the parameters array that follows + // idle_timeout 0x00, 0x01, // parameter id - 0x00, 0x01, // length - 0x0c, // value + 0x00, 0x02, // length + 0x6e, 0xec, // value // max_packet_size 0x00, 0x03, // parameter id 0x00, 0x02, // length @@ -293,6 +296,10 @@ // disable_migration 0x00, 0x0c, // parameter id 0x00, 0x00, // length + // Google version extension + 0x47, 0x52, // parameter id + 0x00, 0x04, // length + 0x01, 0x23, 0x45, 0x67, // initial version }; // clang-format on @@ -305,7 +312,8 @@ EXPECT_EQ(kFakeVersionLabel, new_params.version); EXPECT_TRUE(new_params.supported_versions.empty()); EXPECT_EQ(EmptyQuicConnectionId(), new_params.original_connection_id); - EXPECT_EQ(kFakeIdleTimeout, new_params.idle_timeout_seconds.value()); + EXPECT_EQ(kFakeIdleTimeoutMilliseconds, + new_params.idle_timeout_milliseconds.value()); EXPECT_TRUE(new_params.stateless_reset_token.empty()); EXPECT_EQ(kFakeMaxPacketSize, new_params.max_packet_size.value()); EXPECT_EQ(kFakeInitialMaxData, new_params.initial_max_data.value()); @@ -329,12 +337,11 @@ // clang-format off const uint8_t kClientParamsWithFullToken[] = { - 0x01, 0x23, 0x45, 0x67, // initial version - 0x00, 0x25, // length parameters array that follows - // idle_timeout_seconds + 0x00, 0x26, // length parameters array that follows + // idle_timeout 0x00, 0x01, // parameter id - 0x00, 0x01, // length - 0x0c, // value + 0x00, 0x02, // length + 0x6e, 0xec, // value // stateless_reset_token 0x00, 0x02, 0x00, 0x10, @@ -357,12 +364,11 @@ // clang-format off const uint8_t kClientParamsWithEmptyToken[] = { - 0x01, 0x23, 0x45, 0x67, // initial version - 0x00, 0x15, // length parameters array that follows - // idle_timeout_seconds + 0x00, 0x16, // length parameters array that follows + // idle_timeout 0x00, 0x01, // parameter id - 0x00, 0x01, // length - 0x0c, // value + 0x00, 0x02, // length + 0x6e, 0xec, // value // stateless_reset_token 0x00, 0x02, 0x00, 0x00, @@ -385,12 +391,11 @@ TEST_F(TransportParametersTest, ParseClientParametersRepeated) { // clang-format off const uint8_t kClientParamsRepeated[] = { - 0x01, 0x23, 0x45, 0x67, // initial version - 0x00, 0x14, // length parameters array that follows - // idle_timeout_seconds + 0x00, 0x16, // length parameters array that follows + // idle_timeout 0x00, 0x01, // parameter id - 0x00, 0x01, // length - 0x0c, // value + 0x00, 0x02, // length + 0x6e, 0xec, // value // stateless_reset_token 0x00, 0x02, 0x00, 0x00, @@ -398,10 +403,10 @@ 0x00, 0x03, // parameter id 0x00, 0x02, // length 0x63, 0x29, // value - // idle_timeout_seconds (repeated) + // idle_timeout (repeated) 0x00, 0x01, // parameter id - 0x00, 0x01, // length - 0x0c, // value + 0x00, 0x02, // length + 0x6e, 0xec, // value }; // clang-format on TransportParameters out_params; @@ -413,19 +418,15 @@ TEST_F(TransportParametersTest, ParseServerParams) { // clang-format off const uint8_t kServerParams[] = { - 0x01, 0x23, 0x45, 0x67, // negotiated_version - 0x08, // length of supported versions array - 0x01, 0x23, 0x45, 0x67, - 0x89, 0xab, 0xcd, 0xef, - 0x00, 0x91, // length of parameters array that follows + 0x00, 0xa3, // length of parameters array that follows // original_connection_id 0x00, 0x00, // parameter id 0x00, 0x08, // length 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x37, - // idle_timeout_seconds + // idle_timeout 0x00, 0x01, // parameter id - 0x00, 0x01, // length - 0x0c, // value + 0x00, 0x02, // length + 0x6e, 0xec, // value // stateless_reset_token 0x00, 0x02, 0x00, 0x10, @@ -482,6 +483,13 @@ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xEF, // connection ID 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, // stateless reset token 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, + // Google version extension + 0x47, 0x52, // parameter id + 0x00, 0x0d, // length + 0x01, 0x23, 0x45, 0x67, // negotiated_version + 0x08, // length of supported versions array + 0x01, 0x23, 0x45, 0x67, + 0x89, 0xab, 0xcd, 0xef, }; // clang-format on @@ -496,7 +504,8 @@ EXPECT_EQ(kFakeVersionLabel, new_params.supported_versions[0]); EXPECT_EQ(kFakeVersionLabel2, new_params.supported_versions[1]); EXPECT_EQ(kFakeOriginalConnectionId, new_params.original_connection_id); - EXPECT_EQ(kFakeIdleTimeout, new_params.idle_timeout_seconds.value()); + EXPECT_EQ(kFakeIdleTimeoutMilliseconds, + new_params.idle_timeout_milliseconds.value()); EXPECT_EQ(kFakeStatelessResetToken, new_params.stateless_reset_token); EXPECT_EQ(kFakeMaxPacketSize, new_params.max_packet_size.value()); EXPECT_EQ(kFakeInitialMaxData, new_params.initial_max_data.value()); @@ -527,28 +536,24 @@ TEST_F(TransportParametersTest, ParseServerParametersRepeated) { // clang-format off const uint8_t kServerParamsRepeated[] = { - 0x01, 0x23, 0x45, 0x67, // negotiated_version - 0x08, // length of supported versions array - 0x01, 0x23, 0x45, 0x67, - 0x89, 0xab, 0xcd, 0xef, - 0x00, 0x2A, // length of parameters array that follows + 0x00, 0x2c, // length of parameters array that follows // original_connection_id 0x00, 0x00, // parameter id 0x00, 0x08, // length 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x37, - // idle_timeout_seconds + // idle_timeout 0x00, 0x01, // parameter id - 0x00, 0x01, // length - 0x0c, // value + 0x00, 0x02, // length + 0x6e, 0xec, // value // stateless_reset_token 0x00, 0x02, 0x00, 0x10, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, - // idle_timeout_seconds (repeated) + // idle_timeout (repeated) 0x00, 0x01, // parameter id - 0x00, 0x01, // length - 0x0c, // value + 0x00, 0x02, // length + 0x6e, 0xec, // value }; // clang-format on @@ -561,6 +566,7 @@ TEST_F(TransportParametersTest, CryptoHandshakeMessageRoundtrip) { TransportParameters orig_params; orig_params.perspective = Perspective::IS_CLIENT; + orig_params.version = kFakeVersionLabel; orig_params.max_packet_size.set_value(kFakeMaxPacketSize); orig_params.google_quic_params = QuicMakeUnique<CryptoHandshakeMessage>(); @@ -586,6 +592,7 @@ EXPECT_EQ(new_params.google_quic_params->GetUint32(1337, &test_value), QUIC_NO_ERROR); EXPECT_EQ(test_value, kTestValue); + EXPECT_EQ(kFakeVersionLabel, new_params.version); EXPECT_EQ(kFakeMaxPacketSize, new_params.max_packet_size.value()); }
diff --git a/quic/core/quic_config.cc b/quic/core/quic_config.cc index cad98db..b106f41 100644 --- a/quic/core/quic_config.cc +++ b/quic/core/quic_config.cc
@@ -750,8 +750,8 @@ } bool QuicConfig::FillTransportParameters(TransportParameters* params) const { - params->idle_timeout_seconds.set_value( - idle_network_timeout_seconds_.GetUint32()); + params->idle_timeout_milliseconds.set_value( + idle_network_timeout_seconds_.GetUint32() * kNumMillisPerSecond); if (stateless_reset_token_.HasSendValue()) { QuicUint128 stateless_reset_token = stateless_reset_token_.GetSendValue(); @@ -807,7 +807,9 @@ const TransportParameters& params, HelloType hello_type, std::string* error_details) { - uint64_t idle_timeout_seconds = params.idle_timeout_seconds.value(); + // Intentionally round down to probe too often rather than not often enough. + uint64_t idle_timeout_seconds = + params.idle_timeout_milliseconds.value() / kNumMillisPerSecond; if (idle_timeout_seconds > kMaximumIdleTimeoutSecs) { idle_timeout_seconds = kMaximumIdleTimeoutSecs; }
diff --git a/quic/core/quic_constants.h b/quic/core/quic_constants.h index 81bed70..939f746 100644 --- a/quic/core/quic_constants.h +++ b/quic/core/quic_constants.h
@@ -21,8 +21,9 @@ const uint64_t kNumSecondsPerMinute = 60; const uint64_t kNumSecondsPerHour = kNumSecondsPerMinute * 60; const uint64_t kNumSecondsPerWeek = kNumSecondsPerHour * 24 * 7; +const uint64_t kNumMillisPerSecond = 1000; const uint64_t kNumMicrosPerMilli = 1000; -const uint64_t kNumMicrosPerSecond = 1000 * 1000; +const uint64_t kNumMicrosPerSecond = kNumMicrosPerMilli * kNumMillisPerSecond; // Default number of connections for N-connection emulation. const uint32_t kDefaultNumConnections = 2;
diff --git a/quic/core/tls_client_handshaker.cc b/quic/core/tls_client_handshaker.cc index 932d537..3f2a725 100644 --- a/quic/core/tls_client_handshaker.cc +++ b/quic/core/tls_client_handshaker.cc
@@ -152,6 +152,15 @@ return false; } + // When interoperating with non-Google implementations that do not send + // the version extension, set it to what we expect. + if (params.version == 0) { + params.version = CreateQuicVersionLabel(session()->connection()->version()); + } + if (params.supported_versions.empty()) { + params.supported_versions.push_back(params.version); + } + if (params.version != CreateQuicVersionLabel(session()->connection()->version())) { *error_details = "Version mismatch detected";
diff --git a/quic/core/tls_server_handshaker.cc b/quic/core/tls_server_handshaker.cc index ec254b5..ca56e4d 100644 --- a/quic/core/tls_server_handshaker.cc +++ b/quic/core/tls_server_handshaker.cc
@@ -211,6 +211,14 @@ *error_details = "Unable to parse Transport Parameters"; return false; } + + // When interoperating with non-Google implementations that do not send + // the version extension, set it to what we expect. + if (client_params.version == 0) { + client_params.version = + CreateQuicVersionLabel(session()->connection()->version()); + } + if (CryptoUtils::ValidateClientHelloVersion( client_params.version, session()->connection()->version(), session()->supported_versions(), error_details) != QUIC_NO_ERROR ||