Allow parsing PROX packets with length prefix The PROX version is special in that it has custom data after the first 9 bytes of the connection IDs. That means that trying to parse it with length prefix as an unknown QUIC version means that a random payload byte can be interpreted as the source connection ID length. This CL makes parsing succeed in that case, ignoring the source connection ID. This allows a previously existing test to pass. gfe-relnote: allow parsing PROX packets with length prefix, protected by gfe2_reloadable_flag_quic_parse_prox_source_connection_id Startblock: after 2019-09-24 08:00 in US/Pacific PiperOrigin-RevId: 271678159 Change-Id: Ib079697029d7378cd7297e2400cefe8be33fad43
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc index 84dd00a..a6a663b 100644 --- a/quic/core/quic_framer.cc +++ b/quic/core/quic_framer.cc
@@ -6366,6 +6366,8 @@ namespace { +const QuicVersionLabel kProxVersionLabel = 0x50524F58; // "PROX" + inline bool PacketHasLengthPrefixedConnectionIds( const QuicDataReader& reader, ParsedQuicVersion parsed_version, @@ -6396,7 +6398,7 @@ // Check for munged packets with version tag PROX. if ((connection_id_length_byte & 0x0f) == 0 && - connection_id_length_byte >= 0x20 && version_label == 0x50524F58) { + connection_id_length_byte >= 0x20 && version_label == kProxVersionLabel) { return false; } @@ -6406,6 +6408,7 @@ inline bool ParseLongHeaderConnectionIds( QuicDataReader* reader, bool has_length_prefix, + QuicVersionLabel version_label, QuicConnectionId* destination_connection_id, QuicConnectionId* source_connection_id, std::string* detailed_error) { @@ -6415,6 +6418,16 @@ return false; } if (!reader->ReadLengthPrefixedConnectionId(source_connection_id)) { + if (GetQuicReloadableFlag(quic_parse_prox_source_connection_id) && + version_label == kProxVersionLabel) { + QUIC_RELOADABLE_FLAG_COUNT(quic_parse_prox_source_connection_id); + // The "PROX" version does not follow the length-prefixed invariants, + // and can therefore attempt to read a payload byte and interpret it + // as the source connection ID length, which could fail to parse. + // In that scenario we keep the source connection ID empty but mark + // parsing as successful. + return true; + } *detailed_error = "Unable to read source connection ID."; return false; } @@ -6525,7 +6538,7 @@ *reader, *parsed_version, *version_label, *first_byte); // Parse connection IDs. - if (!ParseLongHeaderConnectionIds(reader, *has_length_prefix, + if (!ParseLongHeaderConnectionIds(reader, *has_length_prefix, *version_label, destination_connection_id, source_connection_id, detailed_error)) { return QUIC_INVALID_PACKET_HEADER;
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc index d343c0d..9bc4076 100644 --- a/quic/core/quic_framer_test.cc +++ b/quic/core/quic_framer_test.cc
@@ -1283,6 +1283,69 @@ } } +TEST_P(QuicFramerTest, ParsePublicHeaderProxBadSourceConnectionIdLength) { + if (!framer_.version().HasLengthPrefixedConnectionIds()) { + return; + } + SetQuicReloadableFlag(quic_parse_prox_source_connection_id, true); + // clang-format off + unsigned char packet[] = { + // public flags (long header with packet type HANDSHAKE and + // 4-byte packet number) + 0xE3, + // version + 'P', 'R', 'O', 'X', + // destination connection ID length + 0x08, + // destination connection ID + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // source connection ID length (bogus) + 0xEE, + // long header packet length + 0x05, + // packet number + 0x12, 0x34, 0x56, 0x78, + // padding frame + 0x00, + }; + // clang-format on + unsigned char* p = packet; + size_t p_length = QUIC_ARRAYSIZE(packet); + + uint8_t first_byte = 0x33; + PacketHeaderFormat format = GOOGLE_QUIC_PACKET; + bool version_present = false, has_length_prefix = false; + QuicVersionLabel version_label = 0; + ParsedQuicVersion parsed_version = UnsupportedQuicVersion(); + QuicConnectionId destination_connection_id = EmptyQuicConnectionId(), + source_connection_id = EmptyQuicConnectionId(); + QuicLongHeaderType long_packet_type = INVALID_PACKET_TYPE; + QuicVariableLengthIntegerLength retry_token_length_length = + VARIABLE_LENGTH_INTEGER_LENGTH_4; + QuicStringPiece retry_token; + std::string detailed_error = "foobar"; + + QuicDataReader reader(AsChars(p), p_length); + const QuicErrorCode parse_error = QuicFramer::ParsePublicHeader( + &reader, kQuicDefaultConnectionIdLength, + /*ietf_format=*/true, &first_byte, &format, &version_present, + &has_length_prefix, &version_label, &parsed_version, + &destination_connection_id, &source_connection_id, &long_packet_type, + &retry_token_length_length, &retry_token, &detailed_error); + EXPECT_EQ(QUIC_NO_ERROR, parse_error); + EXPECT_EQ("", detailed_error); + EXPECT_EQ(p[0], first_byte); + EXPECT_TRUE(version_present); + EXPECT_TRUE(has_length_prefix); + EXPECT_EQ(0x50524F58u, version_label); // "PROX" + EXPECT_EQ(UnsupportedQuicVersion(), parsed_version); + EXPECT_EQ(FramerTestConnectionId(), destination_connection_id); + EXPECT_EQ(EmptyQuicConnectionId(), source_connection_id); + EXPECT_EQ(VARIABLE_LENGTH_INTEGER_LENGTH_0, retry_token_length_length); + EXPECT_EQ(QuicStringPiece(), retry_token); + EXPECT_EQ(IETF_QUIC_LONG_HEADER_PACKET, format); +} + TEST_P(QuicFramerTest, ClientConnectionIdFromShortHeaderToClient) { if (!framer_.version().SupportsClientConnectionIds()) { return;