Client-side workaround for probes receiving unexpected length prefixes This CL is a client-side workaround for b/139330014 where an old client expects no length prefix but receives a length-prefixed response. This workaround will be rolled back once cl/263172621 is deployed on production servers. gfe-relnote: n/a, client-only change PiperOrigin-RevId: 263188480 Change-Id: Ib41e848c21ecef840c11a9c33d77c6abb1f7fae3
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc index 13669d9..93b8575 100644 --- a/quic/core/quic_framer.cc +++ b/quic/core/quic_framer.cc
@@ -6795,6 +6795,27 @@ return false; } + if (!use_length_prefix && source_connection_id.length() == 0) { + // We received a bad response due to b/139330014. + // Reparse the packet assuming length prefixes. + // This is a temporary client-side workaround until cl/263172621 is + // deployed on production servers. + // TODO(dschinazi): remove this client-side workaround once the server-side + // fix is deployed. + QuicDataReader reader2(packet_bytes, packet_length); + uint8_t type_byte2 = 0; + uint32_t version2 = 0; + QuicConnectionId destination_connection_id2, source_connection_id2; + if (reader2.ReadUInt8(&type_byte2) && reader2.ReadUInt32(&version2) && + reader2.ReadLengthPrefixedConnectionId(&destination_connection_id2) && + reader2.ReadLengthPrefixedConnectionId(&source_connection_id2) && + (type_byte2 & 0x80) != 0 && version2 == 0 && + destination_connection_id2.length() == 0 && + source_connection_id2.length() != 0) { + source_connection_id = source_connection_id2; + } + } + memcpy(source_connection_id_bytes, source_connection_id.data(), source_connection_id.length()); *source_connection_id_length_out = source_connection_id.length();
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc index 2c04be8..2965e45 100644 --- a/quic/core/quic_framer_test.cc +++ b/quic/core/quic_framer_test.cc
@@ -14684,6 +14684,39 @@ probe_payload_bytes, sizeof(probe_payload_bytes)); } +// Test the client-side workaround for b/139330014 where an old client expects +// no length prefix but receives a length-prefixed response. +TEST_P(QuicFramerTest, ParseBadServerVersionNegotiationProbeResponse) { + SetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids, false); + // clang-format off + const char packet[] = { + // IETF long header with fixed bit set, type initial, all-0 encrypted bits. + 0xc0, + // Version of 0, indicating version negotiation. + 0x00, 0x00, 0x00, 0x00, + // Destination connection ID length 0, source connection ID length 8. + 0x00, 0x08, + // 8-byte source connection ID. + 0x56, 0x4e, 0x20, 0x70, 0x6c, 0x7a, 0x20, 0x21, + // A few supported versions. + 0xaa, 0xaa, 0xaa, 0xaa, + QUIC_VERSION_BYTES, + }; + // clang-format on + char probe_payload_bytes[] = {0x56, 0x4e, 0x20, 0x70, 0x6c, 0x7a, 0x20, 0x21}; + char parsed_probe_payload_bytes[255] = {}; + uint8_t parsed_probe_payload_length = 0; + std::string parse_detailed_error = ""; + EXPECT_TRUE(QuicFramer::ParseServerVersionNegotiationProbeResponse( + reinterpret_cast<const char*>(packet), sizeof(packet), + reinterpret_cast<char*>(parsed_probe_payload_bytes), + &parsed_probe_payload_length, &parse_detailed_error)); + EXPECT_EQ("", parse_detailed_error); + test::CompareCharArraysWithHexError( + "parsed probe", parsed_probe_payload_bytes, parsed_probe_payload_length, + probe_payload_bytes, sizeof(probe_payload_bytes)); +} + TEST_P(QuicFramerTest, ClientConnectionIdFromLongHeaderToClient) { if (framer_.transport_version() <= QUIC_VERSION_43) { // This test requires an IETF long header.