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.