gfe-relnote: In QUIC, do not use framer object in QuicDispatcher. Protected by gfe2_restart_flag_quic_no_framer_object_in_dispatcher. Also, dispatcher does not parse and validate packet number anymore. PiperOrigin-RevId: 247959538 Change-Id: Ia0f7901428537f392b05ffd6beb2984bffd00232
diff --git a/quic/core/quic_dispatcher.cc b/quic/core/quic_dispatcher.cc index a9c6c4a..fa6fda7 100644 --- a/quic/core/quic_dispatcher.cc +++ b/quic/core/quic_dispatcher.cc
@@ -10,6 +10,7 @@ #include "net/third_party/quiche/src/quic/core/chlo_extractor.h" #include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" #include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" #include "net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" @@ -303,8 +304,14 @@ last_error_(QUIC_NO_ERROR), new_sessions_allowed_per_event_loop_(0u), accept_new_connections_(true), - allow_short_initial_connection_ids_(false) { - framer_.set_visitor(this); + allow_short_initial_connection_ids_(false), + last_version_label_(0), + expected_connection_id_length_(expected_connection_id_length), + should_update_expected_connection_id_length_(false), + no_framer_(GetQuicRestartFlag(quic_no_framer_object_in_dispatcher)) { + if (!no_framer_) { + framer_.set_visitor(this); + } } QuicDispatcher::~QuicDispatcher() { @@ -326,21 +333,60 @@ // GetClientAddress must be called after current_peer_address_ is set. current_client_address_ = GetClientAddress(); current_packet_ = &packet; - // ProcessPacket will cause the packet to be dispatched in - // OnUnauthenticatedPublicHeader, or sent to the time wait list manager - // in OnUnauthenticatedHeader. - framer_.ProcessPacket(packet); - // TODO(rjshade): Return a status describing if/why a packet was dropped, - // and log somehow. Maybe expose as a varz. - // TODO(wub): Consider invalidate the current_* variables so processing of the - // next packet does not use them incorrectly. + if (!no_framer_) { + // ProcessPacket will cause the packet to be dispatched in + // OnUnauthenticatedPublicHeader, or sent to the time wait list manager + // in OnUnauthenticatedHeader. + framer_.ProcessPacket(packet); + // TODO(rjshade): Return a status describing if/why a packet was dropped, + // and log somehow. Maybe expose as a varz. + return; + } + QUIC_RESTART_FLAG_COUNT(quic_no_framer_object_in_dispatcher); + QuicPacketHeader header; + uint8_t destination_connection_id_length; + string detailed_error; + const QuicErrorCode error = QuicFramer::ProcessPacketDispatcher( + packet, expected_connection_id_length_, &header.form, + &header.version_flag, &last_version_label_, + &destination_connection_id_length, &header.destination_connection_id, + &detailed_error); + if (error != QUIC_NO_ERROR) { + // Packet has framing error. + SetLastError(error); + QUIC_DLOG(ERROR) << detailed_error; + return; + } + header.version = ParseQuicVersionLabel(last_version_label_); + if (destination_connection_id_length != expected_connection_id_length_ && + !should_update_expected_connection_id_length_ && + !QuicUtils::VariableLengthConnectionIdAllowedForVersion( + header.version.transport_version)) { + SetLastError(QUIC_INVALID_PACKET_HEADER); + QUIC_DLOG(ERROR) << "Invalid Connection Id Length"; + return; + } + if (should_update_expected_connection_id_length_) { + expected_connection_id_length_ = destination_connection_id_length; + } + // TODO(fayang): Instead of passing in QuicPacketHeader, pass format, + // version_flag, version and destination_connection_id. Combine + // OnUnauthenticatedPublicHeader and OnUnauthenticatedHeader to a single + // function when deprecating quic_no_framer_object_in_dispatcher. + if (!OnUnauthenticatedPublicHeader(header)) { + return; + } + OnUnauthenticatedHeader(header); + // TODO(wub): Consider invalidate the current_* variables so processing of + // the next packet does not use them incorrectly. } QuicConnectionId QuicDispatcher::MaybeReplaceConnectionId( QuicConnectionId connection_id, ParsedQuicVersion version) { const uint8_t expected_connection_id_length = - framer_.GetExpectedConnectionIdLength(); + no_framer_ ? expected_connection_id_length_ + : framer_.GetExpectedConnectionIdLength(); if (connection_id.length() == expected_connection_id_length) { return connection_id; } @@ -383,16 +429,18 @@ // connection ID, the dispatcher picks a new one of its expected length. // Therefore we should never receive a connection ID that is smaller // than 64 bits and smaller than what we expect. + const uint8_t expected_connection_id_length = + no_framer_ ? expected_connection_id_length_ + : framer_.GetExpectedConnectionIdLength(); if (connection_id.length() < kQuicMinimumInitialConnectionIdLength && - connection_id.length() < framer_.GetExpectedConnectionIdLength() && + connection_id.length() < expected_connection_id_length && !allow_short_initial_connection_ids_) { DCHECK(header.version_flag); DCHECK(QuicUtils::VariableLengthConnectionIdAllowedForVersion( header.version.transport_version)); QUIC_DLOG(INFO) << "Packet with short destination connection ID " << connection_id << " expected " - << static_cast<int>( - framer_.GetExpectedConnectionIdLength()); + << static_cast<int>(expected_connection_id_length); ProcessUnauthenticatedHeaderFate(kFateTimeWait, connection_id, header.form, header.version_flag, header.version); return false; @@ -449,12 +497,14 @@ ParsedQuicVersion version = GetSupportedVersions().front(); if (header.version_flag) { ParsedQuicVersion packet_version = header.version; - if (framer_.supported_versions() != GetSupportedVersions()) { + if (!no_framer_ && framer_.supported_versions() != GetSupportedVersions()) { // Reset framer's version if version flags change in flight. framer_.SetSupportedVersions(GetSupportedVersions()); } - if (!framer_.IsSupportedVersion(packet_version)) { - if (ShouldCreateSessionForUnknownVersion(framer_.last_version_label())) { + if (!IsSupportedVersion(packet_version)) { + if (ShouldCreateSessionForUnknownVersion( + no_framer_ ? last_version_label_ + : framer_.last_version_label())) { return true; } if (!crypto_config()->validate_chlo_size() || @@ -470,8 +520,10 @@ } version = packet_version; } - // Set the framer's version and continue processing. - framer_.set_version(version); + if (!no_framer_) { + // Set the framer's version and continue processing. + framer_.set_version(version); + } if (version.HasHeaderProtection()) { ProcessHeader(header); @@ -573,6 +625,11 @@ return kFateTimeWait; } + if (no_framer_) { + // Let the connection parse and validate packet number. + return kFateProcess; + } + // initial packet number of 0 is always invalid. if (!framer_.version().HasHeaderProtection()) { if (!header.packet_number.IsInitialized()) { @@ -797,7 +854,7 @@ } // If the version is known and supported by framer, send a connection close. - if (framer_.IsSupportedVersion(version)) { + if (IsSupportedVersion(version)) { QUIC_DVLOG(1) << "Statelessly terminating " << connection_id << " based on an ietf-long packet, which has a supported version:" @@ -852,6 +909,7 @@ bool QuicDispatcher::OnProtocolVersionMismatch( ParsedQuicVersion /*received_version*/, PacketHeaderFormat /*form*/) { + DCHECK(!no_framer_); QUIC_BUG_IF( !time_wait_list_manager_->IsConnectionIdInTimeWait( current_connection_id_) && @@ -1146,7 +1204,7 @@ EnqueuePacketResult rs = buffered_packets_.EnqueuePacket( current_connection_id_, form != GOOGLE_QUIC_PACKET, *current_packet_, current_self_address_, current_peer_address_, - /*is_chlo=*/true, current_alpn_, framer_.version()); + /*is_chlo=*/true, current_alpn_, version); if (rs != EnqueuePacketResult::SUCCESS) { OnBufferPacketFailure(rs, current_connection_id_); } @@ -1155,11 +1213,10 @@ QuicConnectionId original_connection_id = current_connection_id_; current_connection_id_ = - MaybeReplaceConnectionId(current_connection_id_, framer_.version()); + MaybeReplaceConnectionId(current_connection_id_, version); // Creates a new session and process all buffered packets for this connection. - QuicSession* session = - CreateQuicSession(current_connection_id_, current_peer_address_, - current_alpn_, framer_.version()); + QuicSession* session = CreateQuicSession( + current_connection_id_, current_peer_address_, current_alpn_, version); if (original_connection_id != current_connection_id_) { session->connection()->AddIncomingConnectionId(original_connection_id); } @@ -1338,7 +1395,9 @@ current_self_address_ = current_self_address; current_packet_ = current_packet.get(); current_connection_id_ = rejector->connection_id(); - framer_.set_version(first_version); + if (!no_framer_) { + framer_.set_version(first_version); + } // Stop buffering packets on this connection const auto num_erased = @@ -1391,7 +1450,7 @@ break; case StatelessRejector::REJECTED: { - QUIC_BUG_IF(first_version != framer_.version()) + QUIC_BUG_IF(!no_framer_ && first_version != framer_.version()) << "SREJ: Client's version: " << QuicVersionToString(first_version.transport_version) << " is different from current dispatcher framer's version: " @@ -1438,7 +1497,22 @@ } void QuicDispatcher::DisableFlagValidation() { - framer_.set_validate_flags(false); + if (!no_framer_) { + framer_.set_validate_flags(false); + } +} + +bool QuicDispatcher::IsSupportedVersion(const ParsedQuicVersion version) { + if (!no_framer_) { + return framer_.IsSupportedVersion(version); + } + for (const ParsedQuicVersion& supported_version : + version_manager_->GetSupportedVersions()) { + if (version == supported_version) { + return true; + } + } + return false; } } // namespace quic
diff --git a/quic/core/quic_dispatcher.h b/quic/core/quic_dispatcher.h index 0c951ed..6adf5f3 100644 --- a/quic/core/quic_dispatcher.h +++ b/quic/core/quic_dispatcher.h
@@ -131,6 +131,9 @@ // QuicFramerVisitorInterface implementation. Not expected to be called // outside of this class. + // TODO(fayang): Make QuicDispatcher no longer implement + // QuicFramerVisitorInterface when deprecating + // quic_no_framer_object_in_dispatcher. void OnPacket() override; // Called when the public header has been parsed. Returns false when just the // public header is enough to dispatch the packet; true if the framer needs to @@ -361,8 +364,13 @@ // to the received destination connection ID length of all IETF long headers. void SetShouldUpdateExpectedConnectionIdLength( bool should_update_expected_connection_id_length) { - framer_.SetShouldUpdateExpectedConnectionIdLength( - should_update_expected_connection_id_length); + if (!no_framer_) { + framer_.SetShouldUpdateExpectedConnectionIdLength( + should_update_expected_connection_id_length); + return; + } + should_update_expected_connection_id_length_ = + should_update_expected_connection_id_length; } // If true, the dispatcher will allow incoming initial packets that have @@ -435,6 +443,9 @@ QuicConnectionId MaybeReplaceConnectionId(QuicConnectionId connection_id, ParsedQuicVersion version); + // Returns true if |version| is a supported protocol version. + bool IsSupportedVersion(const ParsedQuicVersion version); + void set_new_sessions_allowed_per_event_loop( int16_t new_sessions_allowed_per_event_loop) { new_sessions_allowed_per_event_loop_ = new_sessions_allowed_per_event_loop; @@ -515,6 +526,28 @@ // If false, the dispatcher follows the IETF spec and rejects packets with // invalid connection IDs lengths below 64 bits. If true they are allowed. bool allow_short_initial_connection_ids_; + + // The last QUIC version label received. Used when no_framer_ is true. + // TODO(fayang): remove this member variable, instead, add an argument to + // OnUnauthenticatedPublicHeader when deprecating + // quic_no_framer_object_in_dispatcher. + QuicVersionLabel last_version_label_; + + // IETF short headers contain a destination connection ID but do not + // encode its length. This variable contains the length we expect to read. + // This is also used to signal an error when a long header packet with + // different destination connection ID length is received when + // should_update_expected_connection_id_length_ is false and packet's version + // does not allow variable length connection ID. Used when no_framer_ is true. + uint8_t expected_connection_id_length_; + + // If true, change expected_connection_id_length_ to be the received + // destination connection ID length of all IETF long headers. Used when + // no_framer_ is true. + bool should_update_expected_connection_id_length_; + + // Latched value of quic_no_framer_object_in_dispatcher. + const bool no_framer_; }; } // namespace quic
diff --git a/quic/core/quic_dispatcher_test.cc b/quic/core/quic_dispatcher_test.cc index 9c7cd1e..03b281b 100644 --- a/quic/core/quic_dispatcher_test.cc +++ b/quic/core/quic_dispatcher_test.cc
@@ -815,7 +815,8 @@ } TEST_F(QuicDispatcherTest, TooBigSeqNoPacketToTimeWaitListManager) { - if (CurrentSupportedVersions().front().HasHeaderProtection()) { + if (CurrentSupportedVersions().front().HasHeaderProtection() || + GetQuicRestartFlag(quic_no_framer_object_in_dispatcher)) { // When header protection is in use, we don't put packets in the time wait // list manager based on packet number. return;
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc index 243529d..8d0dec2 100644 --- a/quic/core/quic_framer.cc +++ b/quic/core/quic_framer.cc
@@ -449,6 +449,11 @@ "each time such a packet is dropped"); } +PacketHeaderFormat GetIetfPacketHeaderFormat(uint8_t type_byte) { + return type_byte & FLAGS_LONG_HEADER ? IETF_QUIC_LONG_HEADER_PACKET + : IETF_QUIC_SHORT_HEADER_PACKET; +} + } // namespace QuicFramer::QuicFramer(const ParsedQuicVersionVector& supported_versions, @@ -2511,8 +2516,7 @@ } header->type_byte = type; // Determine whether this is a long or short header. - header->form = type & FLAGS_LONG_HEADER ? IETF_QUIC_LONG_HEADER_PACKET - : IETF_QUIC_SHORT_HEADER_PACKET; + header->form = GetIetfPacketHeaderFormat(type); if (header->form == IETF_QUIC_LONG_HEADER_PACKET) { // Version is always present in long headers. header->version_flag = true; @@ -2610,13 +2614,19 @@ } // static -bool QuicFramer::ValidateIetfConnectionIdLength( - uint8_t connection_id_lengths_byte, +bool QuicFramer::ProcessAndValidateIetfConnectionIdLength( + QuicDataReader* reader, ParsedQuicVersion version, bool should_update_expected_connection_id_length, uint8_t* expected_connection_id_length, uint8_t* destination_connection_id_length, - uint8_t* source_connection_id_length) { + uint8_t* source_connection_id_length, + std::string* detailed_error) { + uint8_t connection_id_lengths_byte; + if (!reader->ReadBytes(&connection_id_lengths_byte, 1)) { + *detailed_error = "Unable to read ConnectionId length."; + return false; + } uint8_t dcil = (connection_id_lengths_byte & kDestinationConnectionIdLengthMask) >> 4; if (dcil != 0) { @@ -2642,6 +2652,7 @@ // OnProtocolVersionMismatch call is moved to before this is run. QUIC_DVLOG(1) << "dcil: " << static_cast<uint32_t>(dcil) << ", scil: " << static_cast<uint32_t>(scil); + *detailed_error = "Invalid ConnectionId length."; return false; } *destination_connection_id_length = dcil; @@ -2664,18 +2675,11 @@ ? expected_connection_id_length_ : 0; if (header->form == IETF_QUIC_LONG_HEADER_PACKET) { - // Read and validate connection ID length. - uint8_t connection_id_lengths_byte; - if (!reader->ReadBytes(&connection_id_lengths_byte, 1)) { - set_detailed_error("Unable to read ConnectionId length."); - return false; - } - if (!ValidateIetfConnectionIdLength( - connection_id_lengths_byte, header->version, + if (!ProcessAndValidateIetfConnectionIdLength( + reader, header->version, should_update_expected_connection_id_length_, &expected_connection_id_length_, &destination_connection_id_length, - &source_connection_id_length)) { - set_detailed_error("Invalid ConnectionId length."); + &source_connection_id_length, &detailed_error_)) { return false; } } @@ -6054,5 +6058,75 @@ supports_multiple_packet_number_spaces_ = true; } +// static +QuicErrorCode QuicFramer::ProcessPacketDispatcher( + const QuicEncryptedPacket& packet, + uint8_t expected_connection_id_length, + PacketHeaderFormat* format, + bool* version_flag, + QuicVersionLabel* version_label, + uint8_t* destination_connection_id_length, + QuicConnectionId* destination_connection_id, + std::string* detailed_error) { + QuicDataReader reader(packet.data(), packet.length()); + + uint8_t first_byte; + if (!reader.ReadBytes(&first_byte, 1)) { + *detailed_error = "Unable to read first byte."; + return QUIC_INVALID_PACKET_HEADER; + } + if (!QuicUtils::IsIetfPacketHeader(first_byte)) { + *format = GOOGLE_QUIC_PACKET; + *version_flag = (first_byte & PACKET_PUBLIC_FLAGS_VERSION) != 0; + *destination_connection_id_length = + first_byte & PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID; + if (*destination_connection_id_length == 0 || + !reader.ReadConnectionId(destination_connection_id, + *destination_connection_id_length)) { + *detailed_error = "Unable to read ConnectionId."; + return QUIC_INVALID_PACKET_HEADER; + } + if (*version_flag && !ProcessVersionLabel(&reader, version_label)) { + *detailed_error = "Unable to read protocol version."; + return QUIC_INVALID_PACKET_HEADER; + } + return QUIC_NO_ERROR; + } + + *format = GetIetfPacketHeaderFormat(first_byte); + QUIC_DVLOG(1) << "Dispatcher: Processing IETF QUIC packet, format: " + << *format; + *version_flag = *format == IETF_QUIC_LONG_HEADER_PACKET; + if (*format == IETF_QUIC_LONG_HEADER_PACKET) { + if (!ProcessVersionLabel(&reader, version_label)) { + *detailed_error = "Unable to read protocol version."; + return QUIC_INVALID_PACKET_HEADER; + } + // Set should_update_expected_connection_id_length to true to bypass + // connection ID lengths validation. + uint8_t unused_source_connection_id_length = 0; + uint8_t unused_expected_connection_id_length = 0; + if (!ProcessAndValidateIetfConnectionIdLength( + &reader, ParseQuicVersionLabel(*version_label), + /*should_update_expected_connection_id_length=*/true, + &unused_expected_connection_id_length, + destination_connection_id_length, + &unused_source_connection_id_length, detailed_error)) { + return QUIC_INVALID_PACKET_HEADER; + } + } else { + // For short header packets, expected_connection_id_length is used to + // determine the destination_connection_id_length. + *destination_connection_id_length = expected_connection_id_length; + } + // Read destination connection ID. + if (!reader.ReadConnectionId(destination_connection_id, + *destination_connection_id_length)) { + *detailed_error = "Unable to read Destination ConnectionId."; + return QUIC_INVALID_PACKET_HEADER; + } + return QUIC_NO_ERROR; +} + #undef ENDPOINT // undef for jumbo builds } // namespace quic
diff --git a/quic/core/quic_framer.h b/quic/core/quic_framer.h index e2d7b4d..59e300c 100644 --- a/quic/core/quic_framer.h +++ b/quic/core/quic_framer.h
@@ -373,6 +373,21 @@ uint64_t retry_token_length, QuicVariableLengthIntegerLength length_length); + // Lightweight parsing of |packet| and populates |format|, |version_flag|, + // |version_label|, |destination_connection_id_length|, + // |destination_connection_id| and |detailed_error|. Please note, + // |expected_connection_id_length| is only used to determine IETF short header + // packet's destination connection ID length. + static QuicErrorCode ProcessPacketDispatcher( + const QuicEncryptedPacket& packet, + uint8_t expected_connection_id_length, + PacketHeaderFormat* format, + bool* version_flag, + QuicVersionLabel* version_label, + uint8_t* destination_connection_id_length, + QuicConnectionId* destination_connection_id, + std::string* detailed_error); + // Serializes a packet containing |frames| into |buffer|. // Returns the length of the packet, which must not be longer than // |packet_length|. Returns 0 if it fails to serialize. @@ -665,13 +680,14 @@ // Validates and updates |destination_connection_id_length| and // |source_connection_id_length|. - static bool ValidateIetfConnectionIdLength( - uint8_t connection_id_lengths_byte, + static bool ProcessAndValidateIetfConnectionIdLength( + QuicDataReader* reader, ParsedQuicVersion version, bool should_update_expected_connection_id_length, uint8_t* expected_connection_id_length, uint8_t* destination_connection_id_length, - uint8_t* source_connection_id_length); + uint8_t* source_connection_id_length, + std::string* detailed_error); bool ProcessIetfHeaderTypeByte(QuicDataReader* reader, QuicPacketHeader* header); @@ -921,6 +937,8 @@ // Updated by WritePacketHeader. QuicConnectionId last_serialized_connection_id_; // The last QUIC version label received. + // TODO(fayang): Remove this when deprecating + // quic_no_framer_object_in_dispatcher. QuicVersionLabel last_version_label_; // Version of the protocol being used. ParsedQuicVersion version_; @@ -975,10 +993,14 @@ // encode its length. This variable contains the length we expect to read. // This is also used to validate the long header connection ID lengths in // older versions of QUIC. + // TODO(fayang): Remove this when deprecating + // quic_no_framer_object_in_dispatcher. uint8_t expected_connection_id_length_; // When this is true, QuicFramer will change expected_connection_id_length_ // to the received destination connection ID length of all IETF long headers. + // TODO(fayang): Remove this when deprecating + // quic_no_framer_object_in_dispatcher. bool should_update_expected_connection_id_length_; // Indicates whether this framer supports multiple packet number spaces.
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc index 4af5b3a..96f335c 100644 --- a/quic/core/quic_framer_test.cc +++ b/quic/core/quic_framer_test.cc
@@ -913,6 +913,22 @@ EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number); CheckFramingBoundaries(fragments, QUIC_INVALID_PACKET_HEADER); + + PacketHeaderFormat format; + bool version_flag; + uint8_t destination_connection_id_length; + QuicConnectionId destination_connection_id; + QuicVersionLabel version_label; + std::string detailed_error; + EXPECT_EQ(QUIC_NO_ERROR, QuicFramer::ProcessPacketDispatcher( + *encrypted, kQuicDefaultConnectionIdLength, + &format, &version_flag, &version_label, + &destination_connection_id_length, + &destination_connection_id, &detailed_error)); + EXPECT_EQ(GOOGLE_QUIC_PACKET, format); + EXPECT_FALSE(version_flag); + EXPECT_EQ(kQuicDefaultConnectionIdLength, destination_connection_id_length); + EXPECT_EQ(FramerTestConnectionId(), destination_connection_id); } TEST_P(QuicFramerTest, LongPacketHeader) { @@ -975,6 +991,22 @@ CheckFramingBoundaries( framer_.transport_version() > QUIC_VERSION_44 ? packet46 : packet44, QUIC_INVALID_PACKET_HEADER); + + PacketHeaderFormat format; + bool version_flag; + uint8_t destination_connection_id_length; + QuicConnectionId destination_connection_id; + QuicVersionLabel version_label; + std::string detailed_error; + EXPECT_EQ(QUIC_NO_ERROR, QuicFramer::ProcessPacketDispatcher( + *encrypted, kQuicDefaultConnectionIdLength, + &format, &version_flag, &version_label, + &destination_connection_id_length, + &destination_connection_id, &detailed_error)); + EXPECT_EQ(IETF_QUIC_LONG_HEADER_PACKET, format); + EXPECT_TRUE(version_flag); + EXPECT_EQ(kQuicDefaultConnectionIdLength, destination_connection_id_length); + EXPECT_EQ(FramerTestConnectionId(), destination_connection_id); } TEST_P(QuicFramerTest, PacketHeaderWith0ByteConnectionId) { @@ -13251,6 +13283,34 @@ FramerTestConnectionIdNineBytes()); EXPECT_EQ(visitor_.header_.get()->packet_number, QuicPacketNumber(UINT64_C(0x13374233))); + + PacketHeaderFormat format; + bool version_flag; + uint8_t destination_connection_id_length; + QuicConnectionId destination_connection_id; + QuicVersionLabel version_label; + std::string detailed_error; + EXPECT_EQ(QUIC_NO_ERROR, + QuicFramer::ProcessPacketDispatcher( + QuicEncryptedPacket(AsChars(long_header_packet), + QUIC_ARRAYSIZE(long_header_packet)), + kQuicDefaultConnectionIdLength, &format, &version_flag, + &version_label, &destination_connection_id_length, + &destination_connection_id, &detailed_error)); + EXPECT_EQ(IETF_QUIC_LONG_HEADER_PACKET, format); + EXPECT_TRUE(version_flag); + EXPECT_EQ(9, destination_connection_id_length); + EXPECT_EQ(FramerTestConnectionIdNineBytes(), destination_connection_id); + + EXPECT_EQ(QUIC_NO_ERROR, + QuicFramer::ProcessPacketDispatcher( + short_header_encrypted, 9, &format, &version_flag, + &version_label, &destination_connection_id_length, + &destination_connection_id, &detailed_error)); + EXPECT_EQ(IETF_QUIC_SHORT_HEADER_PACKET, format); + EXPECT_FALSE(version_flag); + EXPECT_EQ(9, destination_connection_id_length); + EXPECT_EQ(FramerTestConnectionIdNineBytes(), destination_connection_id); } TEST_P(QuicFramerTest, MultiplePacketNumberSpaces) {