Eliminate unexpected calls to ConnectionIdLength() and MaybeReplaceConnectionId(). Fixes several incorrect edge cases in QuicDispatcher and QuicFramer that do not have current observable behaviors in production Protected by FLAGS_quic_reloadable_flag_quic_ask_for_short_header_connection_id_length2. PiperOrigin-RevId: 482228126
diff --git a/quiche/quic/core/quic_buffered_packet_store.cc b/quiche/quic/core/quic_buffered_packet_store.cc index 4c729b6..a962499 100644 --- a/quiche/quic/core/quic_buffered_packet_store.cc +++ b/quiche/quic/core/quic_buffered_packet_store.cc
@@ -199,7 +199,7 @@ // connection ID. QuicErrorCode error_code = QuicFramer::ParsePublicHeaderDispatcher( *packet.packet, - GetQuicReloadableFlag(quic_ask_for_short_header_connection_id_length) + GetQuicReloadableFlag(quic_ask_for_short_header_connection_id_length2) ? connection_id.length() : kQuicDefaultConnectionIdLength, &unused_format, &long_packet_type, &unused_version_flag,
diff --git a/quiche/quic/core/quic_dispatcher.cc b/quiche/quic/core/quic_dispatcher.cc index 7e6de46..208359d 100644 --- a/quiche/quic/core/quic_dispatcher.cc +++ b/quiche/quic/core/quic_dispatcher.cc
@@ -306,7 +306,8 @@ ReceivedPacketInfo packet_info(self_address, peer_address, packet); std::string detailed_error; QuicErrorCode error; - if (GetQuicReloadableFlag(quic_ask_for_short_header_connection_id_length)) { + if (GetQuicReloadableFlag(quic_ask_for_short_header_connection_id_length2)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_ask_for_short_header_connection_id_length2); error = QuicFramer::ParsePublicHeaderDispatcherShortHeaderLengthUnknown( packet, &packet_info.form, &packet_info.long_packet_type, &packet_info.version_flag, &packet_info.use_length_prefix, @@ -362,7 +363,7 @@ // update |expected_server_connection_id_length_|. QUIC_BUG_IF( quic_bug_480483284_01, - !GetQuicReloadableFlag(quic_ask_for_short_header_connection_id_length) && + !GetQuicReloadableFlag(quic_ask_for_short_header_connection_id_length2) && !packet_info.version_flag && packet_info.destination_connection_id.length() != expected_server_connection_id_length_); @@ -384,7 +385,7 @@ // where NEW_CONNECTION_IDs are not using the generator, and the dispatcher // is, due to flag misconfiguration. if (!packet_info.version_flag && - GetQuicReloadableFlag(quic_ask_for_short_header_connection_id_length) && + GetQuicReloadableFlag(quic_ask_for_short_header_connection_id_length2) && (IsSupportedVersion(ParsedQuicVersion::Q046()) || IsSupportedVersion(ParsedQuicVersion::Q050()))) { ReceivedPacketInfo gquic_packet_info(self_address, peer_address, packet); @@ -466,20 +467,13 @@ // than 64 bits and smaller than what we expect. Unless the version is // unknown, in which case we allow short connection IDs for version // negotiation because that version could allow those. - uint8_t expected_connection_id_length = - GetQuicReloadableFlag(quic_ask_for_short_header_connection_id_length) - ? connection_id_generator_.ConnectionIdLength( - static_cast<uint8_t>(*server_connection_id.data())) - : expected_server_connection_id_length_; if (packet_info.version_flag && packet_info.version.IsKnown() && - server_connection_id.length() < kQuicMinimumInitialConnectionIdLength && - server_connection_id.length() < expected_connection_id_length && - !allow_short_initial_server_connection_ids_) { + IsServerConnectionIdTooShort(server_connection_id)) { QUICHE_DCHECK(packet_info.version_flag); QUICHE_DCHECK(packet_info.version.AllowsVariableLengthConnectionIds()); QUIC_DLOG(INFO) << "Packet with short destination connection ID " << server_connection_id << " expected " - << static_cast<int>(expected_connection_id_length); + << static_cast<int>(expected_server_connection_id_length_); // Drop the packet silently. QUIC_CODE_COUNT(quic_dropped_invalid_small_initial_connection_id); return true; @@ -1262,6 +1256,24 @@ return false; } +bool QuicDispatcher::IsServerConnectionIdTooShort( + QuicConnectionId connection_id) const { + if (connection_id.length() >= kQuicMinimumInitialConnectionIdLength || + connection_id.length() >= expected_server_connection_id_length_ || + allow_short_initial_server_connection_ids_) { + return false; + } + if (!GetQuicReloadableFlag(quic_ask_for_short_header_connection_id_length2)) { + return true; + } + uint8_t generator_output = + connection_id.IsEmpty() + ? connection_id_generator_.ConnectionIdLength(0x00) + : connection_id_generator_.ConnectionIdLength( + static_cast<uint8_t>(*connection_id.data())); + return connection_id.length() < generator_output; +} + std::shared_ptr<QuicSession> QuicDispatcher::CreateSessionFromChlo( const QuicConnectionId original_connection_id, const ParsedClientHello& parsed_chlo, const ParsedQuicVersion version, @@ -1349,13 +1361,8 @@ return; } } else { - uint8_t min_connection_id_length = - GetQuicReloadableFlag(quic_ask_for_short_header_connection_id_length) - ? connection_id_generator_.ConnectionIdLength(static_cast<uint8_t>( - *packet_info.destination_connection_id.data())) - : expected_server_connection_id_length_; const size_t MinValidPacketLength = - kPacketHeaderTypeSize + min_connection_id_length + + kPacketHeaderTypeSize + expected_server_connection_id_length_ + PACKET_1BYTE_PACKET_NUMBER + /*payload size=*/1 + /*tag size=*/12; if (packet_info.packet.length() < MinValidPacketLength) { // The packet size is too small.
diff --git a/quiche/quic/core/quic_dispatcher.h b/quiche/quic/core/quic_dispatcher.h index a8e6e72..e74a8a7 100644 --- a/quiche/quic/core/quic_dispatcher.h +++ b/quiche/quic/core/quic_dispatcher.h
@@ -363,6 +363,10 @@ // Returns true if |version| is a supported protocol version. bool IsSupportedVersion(const ParsedQuicVersion version); + // Returns true if a server connection ID length is below all the minima + // required by various parameters. + bool IsServerConnectionIdTooShort(QuicConnectionId connection_id) const; + // Core CHLO processing logic. std::shared_ptr<QuicSession> CreateSessionFromChlo( const QuicConnectionId original_connection_id,
diff --git a/quiche/quic/core/quic_dispatcher_test.cc b/quiche/quic/core/quic_dispatcher_test.cc index d702e62..9341cde 100644 --- a/quiche/quic/core/quic_dispatcher_test.cc +++ b/quiche/quic/core/quic_dispatcher_test.cc
@@ -324,6 +324,21 @@ client_connection_id_included, packet_number_length, &versions)); std::unique_ptr<QuicReceivedPacket> received_packet( ConstructReceivedPacket(*packet, mock_helper_.GetClock()->Now())); + // Call ConnectionIdLength if the packet clears the Long Header bit, or + // if the test involves sending a connection ID that is too short + if ((!has_version_flag || !version.AllowsVariableLengthConnectionIds() || + server_connection_id.length() == 0 || + server_connection_id_included == CONNECTION_ID_ABSENT) && + GetQuicReloadableFlag( + quic_ask_for_short_header_connection_id_length2)) { + // Short headers will ask for the length + EXPECT_CALL(connection_id_generator_, ConnectionIdLength(_)) + .WillRepeatedly(Return(generated_connection_id_.has_value() + ? generated_connection_id_->length() + : kQuicDefaultConnectionIdLength)); + } else { + EXPECT_CALL(connection_id_generator_, ConnectionIdLength(_)).Times(0); + } ProcessReceivedPacket(std::move(received_packet), peer_address, version, server_connection_id); } @@ -436,12 +451,15 @@ const QuicConnectionId& client_connection_id, std::unique_ptr<QuicCryptoClientConfig> client_crypto_config) { if (expect_generator_is_called_) { - // Can't replace the connection ID in early gQUIC versions! - ASSERT_TRUE(version.AllowsVariableLengthConnectionIds() || - generated_connection_id_ == absl::nullopt); - EXPECT_CALL(connection_id_generator_, - MaybeReplaceConnectionId(server_connection_id, version)) - .WillOnce(Return(generated_connection_id_)); + if (version.AllowsVariableLengthConnectionIds()) { + EXPECT_CALL(connection_id_generator_, + MaybeReplaceConnectionId(server_connection_id, version)) + .WillOnce(Return(generated_connection_id_)); + } else { + EXPECT_CALL(connection_id_generator_, + MaybeReplaceConnectionId(server_connection_id, version)) + .WillOnce(Return(absl::nullopt)); + } } std::vector<std::unique_ptr<QuicReceivedPacket>> packets = GetFirstFlightOfPackets(version, DefaultQuicConfig(), @@ -485,6 +503,7 @@ void MarkSession1Deleted() { session1_ = nullptr; } void VerifyVersionSupported(ParsedQuicVersion version) { + expect_generator_is_called_ = true; QuicConnectionId connection_id = TestConnectionId(++connection_id_); QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); EXPECT_CALL(*dispatcher_, @@ -532,9 +551,11 @@ QuicVersionManager version_manager_; QuicCryptoServerConfig crypto_config_; QuicSocketAddress server_address_; + // Set to false if the dispatcher won't create a session. bool expect_generator_is_called_ = true; + // Set in conditions where the generator should return a different connection + // ID. absl::optional<QuicConnectionId> generated_connection_id_; - // Constant to set generated_connection_id to when needed. MockConnectionIdGenerator connection_id_generator_; std::unique_ptr<NiceMock<TestDispatcher>> dispatcher_; MockTimeWaitListManager* time_wait_list_manager_; @@ -588,19 +609,13 @@ QuicConnectionId old_id = TestConnectionId(1); // Return a connection ID that is not expected_server_connection_id_length_ // bytes long. - generated_connection_id_ = QuicConnectionId( - {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b}); - expect_generator_is_called_ = version_.HasIetfQuicFrames(); - QuicConnectionId new_id = - expect_generator_is_called_ ? *generated_connection_id_ : old_id; - // This will not return the correct value for long headers that might use a - // first byte other than 0x00, but long header replies don't matter. - if (GetQuicReloadableFlag(quic_ask_for_short_header_connection_id_length)) { - EXPECT_CALL(connection_id_generator_, ConnectionIdLength(0x00)) - .WillRepeatedly(Return(generated_connection_id_->length())); - } else { - EXPECT_CALL(connection_id_generator_, ConnectionIdLength(_)).Times(0); + if (version_.HasIetfQuicFrames()) { + generated_connection_id_ = + QuicConnectionId({0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b}); } + QuicConnectionId new_id = + generated_connection_id_.has_value() ? *generated_connection_id_ : old_id; QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); EXPECT_CALL(*dispatcher_, CreateQuicSession(new_id, _, client_address, Eq(ExpectedAlpn()), @@ -621,7 +636,7 @@ // fail (this is the bugfix!). gQUIC never gets a new connection ID, so it's // not affected by asking. if (version_.HasIetfQuicFrames() && - !GetQuicReloadableFlag(quic_ask_for_short_header_connection_id_length)) { + !GetQuicReloadableFlag(quic_ask_for_short_header_connection_id_length2)) { // Dispatcher issued a longer connection ID if IETF QUIC, but won't ask for // that length when processing a short header. Thus dispatch will fail. EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), @@ -960,21 +975,43 @@ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); CreateTimeWaitListManager(); - uint8_t short_packet[21] = {0x70, 0xa7, 0x02, 0x6b}; - QuicReceivedPacket packet(reinterpret_cast<char*>(short_packet), 21, - QuicTime::Zero()); + uint8_t short_packet[22] = {0x70, 0xa7, 0x02, 0x6b}; uint8_t valid_size_packet[23] = {0x70, 0xa7, 0x02, 0x6c}; - QuicReceivedPacket packet2(reinterpret_cast<char*>(valid_size_packet), 23, - QuicTime::Zero()); + size_t short_packet_len; + if (version_.HasIetfInvariantHeader()) { + short_packet_len = 21; + } else { + short_packet_len = 22; + short_packet[0] = 0x0a; + valid_size_packet[0] = 0x0a; + } + QuicReceivedPacket packet(reinterpret_cast<char*>(short_packet), + short_packet_len, QuicTime::Zero()); + QuicReceivedPacket packet2(reinterpret_cast<char*>(valid_size_packet), + short_packet_len + 1, QuicTime::Zero()); EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _, _)) .Times(0); EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _)) .Times(0); // Verify small packet is silently dropped. + if (GetQuicReloadableFlag(quic_ask_for_short_header_connection_id_length2) && + version_.HasIetfInvariantHeader()) { + EXPECT_CALL(connection_id_generator_, ConnectionIdLength(0xa7)) + .WillOnce(Return(kQuicDefaultConnectionIdLength)); + } else { + EXPECT_CALL(connection_id_generator_, ConnectionIdLength(_)).Times(0); + } EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _)) .Times(0); dispatcher_->ProcessPacket(server_address_, client_address, packet); + if (GetQuicReloadableFlag(quic_ask_for_short_header_connection_id_length2) && + version_.HasIetfInvariantHeader()) { + EXPECT_CALL(connection_id_generator_, ConnectionIdLength(0xa7)) + .WillOnce(Return(kQuicDefaultConnectionIdLength)); + } else { + EXPECT_CALL(connection_id_generator_, ConnectionIdLength(_)).Times(0); + } EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _)) .Times(1); dispatcher_->ProcessPacket(server_address_, client_address, packet2); @@ -993,6 +1030,12 @@ .Times(0); EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _)) .Times(0); + if (GetQuicReloadableFlag(quic_ask_for_short_header_connection_id_length2)) { + EXPECT_CALL(connection_id_generator_, ConnectionIdLength(_)) + .WillOnce(Return(kQuicDefaultConnectionIdLength)); + } else { + EXPECT_CALL(connection_id_generator_, ConnectionIdLength(_)).Times(0); + } dispatcher_->ProcessPacket(server_address_, client_address, packet); } @@ -1115,7 +1158,6 @@ dispatcher_->SetAllowShortInitialServerConnectionIds(true); // Note that StrayPacketTruncatedConnectionId covers the case where the // validation is still enabled. - EXPECT_CALL(*dispatcher_, CreateQuicSession(*generated_connection_id_, _, client_address, Eq(ExpectedAlpn()), _, _)) @@ -1244,9 +1286,11 @@ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); // dispatcher_ should drop this packet. - if (GetQuicReloadableFlag(quic_ask_for_short_header_connection_id_length)) { - EXPECT_CALL(connection_id_generator_, ConnectionIdLength(_)) - .WillOnce(Return(15)); + if (GetQuicReloadableFlag(quic_ask_for_short_header_connection_id_length2)) { + EXPECT_CALL(connection_id_generator_, ConnectionIdLength(0x00)) + .WillOnce(Return(10)); + } else { + EXPECT_CALL(connection_id_generator_, ConnectionIdLength(_)).Times(0); } EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _, _)) @@ -1297,6 +1341,7 @@ /*use_length_prefix=*/true, _, _, client_address, _)) .Times(1); expect_generator_is_called_ = false; + EXPECT_CALL(connection_id_generator_, ConnectionIdLength(_)).Times(0); ProcessFirstFlight(ParsedQuicVersion::ReservedForNegotiation(), client_address, server_connection_id, client_connection_id); @@ -1817,6 +1862,7 @@ dispatcher_->StartAcceptingNewConnections(); EXPECT_TRUE(dispatcher_->accept_new_connections()); + expect_generator_is_called_ = true; EXPECT_CALL(*dispatcher_, CreateQuicSession(TestConnectionId(1), _, client_address, Eq(ExpectedAlpn()), _, _)) @@ -2571,6 +2617,9 @@ // create session. QuicDispatcherPeer::set_new_sessions_allowed_per_event_loop(dispatcher_.get(), kNumConnections); + // Deactivate the EXPECT_CALL in ProcessFirstFlight() because we have to be + // in sequence, so the EXPECT_CALL has to explicitly be in order here. + expect_generator_is_called_ = false; // Process CHLOs to create session for these connections. for (size_t i = 1; i <= kNumConnections; ++i) { QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 20000 + i); @@ -2596,7 +2645,6 @@ ValidatePacket(conn_id, packet); } }))); - expect_generator_is_called_ = false; ProcessFirstFlight(client_address, conn_id); } }
diff --git a/quiche/quic/core/quic_flags_list.h b/quiche/quic/core/quic_flags_list.h index 627532a..6ac495e 100644 --- a/quiche/quic/core/quic_flags_list.h +++ b/quiche/quic/core/quic_flags_list.h
@@ -86,7 +86,7 @@ // If true, uses conservative cwnd gain and pacing gain when cwnd gets bootstrapped. QUIC_FLAG(quic_reloadable_flag_quic_conservative_cwnd_and_pacing_gains, false) // Instead of assuming an incoming connection ID length for short headers, ask each time, if both quic_abstract_connection_id_generator and quic_connection_uses_abstract_connection_id_generator are true. -QUIC_FLAG(quic_reloadable_flag_quic_ask_for_short_header_connection_id_length, false) +QUIC_FLAG(quic_reloadable_flag_quic_ask_for_short_header_connection_id_length2, false) // When true, defaults to BBR congestion control instead of Cubic. QUIC_FLAG(quic_reloadable_flag_quic_default_to_bbr, false) // When true, support draft-ietf-quic-v2-01
diff --git a/quiche/quic/core/quic_framer.cc b/quiche/quic/core/quic_framer.cc index 29a0789..919deb6 100644 --- a/quiche/quic/core/quic_framer.cc +++ b/quiche/quic/core/quic_framer.cc
@@ -1811,9 +1811,9 @@ !IsValidFullPacketNumber(full_packet_number, version())) { if (IsIetfStatelessResetPacket(*header)) { // This is a stateless reset packet. - QuicIetfStatelessResetPacket packet( + QuicIetfStatelessResetPacket reset_packet( *header, header->possible_stateless_reset_token); - visitor_->OnAuthenticatedIetfStatelessResetPacket(packet); + visitor_->OnAuthenticatedIetfStatelessResetPacket(reset_packet); return true; } if (hp_removal_failed) { @@ -1880,9 +1880,9 @@ &decrypted_level)) { if (IsIetfStatelessResetPacket(*header)) { // This is a stateless reset packet. - QuicIetfStatelessResetPacket packet( + QuicIetfStatelessResetPacket reset_packet( *header, header->possible_stateless_reset_token); - visitor_->OnAuthenticatedIetfStatelessResetPacket(packet); + visitor_->OnAuthenticatedIetfStatelessResetPacket(reset_packet); return true; } const EncryptionLevel decryption_level = GetEncryptionLevel(*header); @@ -4664,10 +4664,10 @@ // Apply the rest of the mask to the packet number. for (size_t i = 0; i < last_written_packet_number_length_; ++i) { uint8_t buffer_byte; - uint8_t mask_byte; - if (!mask_reader.ReadUInt8(&mask_byte) || + uint8_t pn_mask_byte; + if (!mask_reader.ReadUInt8(&pn_mask_byte) || !buffer_reader.ReadUInt8(&buffer_byte) || - !buffer_writer.WriteUInt8(buffer_byte ^ mask_byte)) { + !buffer_writer.WriteUInt8(buffer_byte ^ pn_mask_byte)) { return false; } } @@ -4743,10 +4743,10 @@ // Read the (protected) packet number from the reader and unmask the packet // number. for (size_t i = 0; i < header->packet_number_length; ++i) { - uint8_t protected_pn_byte, mask_byte; - if (!mask_reader.ReadUInt8(&mask_byte) || + uint8_t protected_pn_byte, pn_mask_byte; + if (!mask_reader.ReadUInt8(&pn_mask_byte) || !reader->ReadUInt8(&protected_pn_byte) || - !pn_writer.WriteUInt8(protected_pn_byte ^ mask_byte)) { + !pn_writer.WriteUInt8(protected_pn_byte ^ pn_mask_byte)) { QUIC_DVLOG(1) << "Failed to unmask packet number"; return false; } @@ -5055,9 +5055,9 @@ alternative_decrypter_level_ = NUM_ENCRYPTION_LEVELS; } else { // Switch the alternative decrypter so that we use it first next time. - EncryptionLevel level = alternative_decrypter_level_; + EncryptionLevel alt_level = alternative_decrypter_level_; alternative_decrypter_level_ = decrypter_level_; - decrypter_level_ = level; + decrypter_level_ = alt_level; } } } @@ -6883,7 +6883,8 @@ uint8_t two_bytes[2]; reader.ReadBytes(two_bytes, 2); uint8_t expected_destination_connection_id_length = - (two_bytes[0] & FLAGS_LONG_HEADER) + (!QuicUtils::IsIetfPacketHeader(two_bytes[0]) || + two_bytes[0] & FLAGS_LONG_HEADER) ? 0 : generator.ConnectionIdLength(two_bytes[1]); return ParsePublicHeaderDispatcher(
diff --git a/quiche/quic/core/quic_framer_test.cc b/quiche/quic/core/quic_framer_test.cc index 1293834..63c6adf 100644 --- a/quiche/quic/core/quic_framer_test.cc +++ b/quiche/quic/core/quic_framer_test.cc
@@ -16431,6 +16431,13 @@ 0x00, 0x00, 0x00, 0x00, 0x00 }; + MockConnectionIdGenerator generator; + if (version_.HasIetfInvariantHeader()) { + EXPECT_CALL(generator, ConnectionIdLength(0x28)).WillOnce(Return(9)); + } else { + packet[0] = 0x0a; + EXPECT_CALL(generator, ConnectionIdLength(_)).Times(0); + } unsigned char* p = packet; size_t p_size = ABSL_ARRAYSIZE(packet); @@ -16445,7 +16452,6 @@ memset(p + header_size, 0, kMaxIncomingPacketSize - header_size); QuicEncryptedPacket encrypted(AsChars(p), p_size, false); - MockConnectionIdGenerator generator; PacketHeaderFormat format; QuicLongHeaderType long_packet_type = INVALID_PACKET_TYPE; bool version_flag; @@ -16455,21 +16461,24 @@ bool use_length_prefix; absl::optional<absl::string_view> retry_token; ParsedQuicVersion parsed_version = UnsupportedQuicVersion(); - EXPECT_CALL(generator, ConnectionIdLength(0x28)) - .WillOnce(Return(9)); EXPECT_EQ(QUIC_NO_ERROR, QuicFramer::ParsePublicHeaderDispatcherShortHeaderLengthUnknown( encrypted, &format, &long_packet_type, &version_flag, &use_length_prefix, &version_label, &parsed_version, &destination_connection_id, &source_connection_id, &retry_token, &detailed_error, generator)); - EXPECT_EQ(format, IETF_QUIC_SHORT_HEADER_PACKET); + if (version_.HasIetfInvariantHeader()) { + EXPECT_EQ(format, IETF_QUIC_SHORT_HEADER_PACKET); + EXPECT_EQ(destination_connection_id.length(), 9); + } else { + EXPECT_EQ(format, GOOGLE_QUIC_PACKET); + EXPECT_EQ(destination_connection_id.length(), 8); + } EXPECT_EQ(long_packet_type, INVALID_PACKET_TYPE); EXPECT_FALSE(version_flag); EXPECT_FALSE(use_length_prefix); EXPECT_EQ(version_label, 0); EXPECT_EQ(parsed_version, UnsupportedQuicVersion()); - EXPECT_EQ(destination_connection_id.length(), 9); EXPECT_EQ(source_connection_id.length(), 0); EXPECT_FALSE(retry_token.has_value()); EXPECT_EQ(detailed_error, "");