Support IETF RETRY from client

This CL parses the retry token, and updates our connection ID and crypters. Server-side support will come in a subsequent CL which will also add end to end tests.

gfe-relnote: Support IETF Retry packets from client in v99, protected by disabled v99 flag
PiperOrigin-RevId: 246911895
Change-Id: Icd5ecd22190fd18ad42882a66c3aa470640ce223
diff --git a/quic/core/chlo_extractor.cc b/quic/core/chlo_extractor.cc
index 5c46910..602940c 100644
--- a/quic/core/chlo_extractor.cc
+++ b/quic/core/chlo_extractor.cc
@@ -35,6 +35,9 @@
   void OnPublicResetPacket(const QuicPublicResetPacket& packet) override {}
   void OnVersionNegotiationPacket(
       const QuicVersionNegotiationPacket& packet) override {}
+  void OnRetryPacket(QuicConnectionId original_connection_id,
+                     QuicConnectionId new_connection_id,
+                     QuicStringPiece retry_token) override {}
   bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) override;
   bool OnUnauthenticatedHeader(const QuicPacketHeader& header) override;
   void OnDecryptedPacket(EncryptionLevel level) override {}
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index ac5101e..23f386b 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -16,6 +16,7 @@
 #include <utility>
 
 #include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h"
 #include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
 #include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
 #include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h"
@@ -348,6 +349,7 @@
       fix_termination_packets_(
           GetQuicReloadableFlag(quic_fix_termination_packets)),
       send_ack_when_on_can_write_(false),
+      retry_has_been_parsed_(false),
       validate_packet_number_post_decryption_(
           GetQuicReloadableFlag(quic_validate_packet_number_post_decryption)),
       use_uber_received_packet_manager_(
@@ -746,6 +748,39 @@
   RetransmitUnackedPackets(ALL_UNACKED_RETRANSMISSION);
 }
 
+// Handles retry for client connection.
+void QuicConnection::OnRetryPacket(QuicConnectionId original_connection_id,
+                                   QuicConnectionId new_connection_id,
+                                   QuicStringPiece retry_token) {
+  if (original_connection_id != connection_id_) {
+    QUIC_DLOG(ERROR) << "Ignoring RETRY with original connection ID "
+                     << original_connection_id << " not matching expected "
+                     << connection_id_ << " token "
+                     << QuicTextUtils::HexEncode(retry_token);
+    return;
+  }
+  if (retry_has_been_parsed_) {
+    QUIC_DLOG(ERROR) << "Ignoring non-first RETRY with token "
+                     << QuicTextUtils::HexEncode(retry_token);
+    return;
+  }
+  retry_has_been_parsed_ = true;
+  QUIC_DLOG(INFO) << "Received RETRY, replacing connection ID "
+                  << connection_id_ << " with " << new_connection_id
+                  << ", received token "
+                  << QuicTextUtils::HexEncode(retry_token);
+  connection_id_ = new_connection_id;
+  packet_generator_.SetConnectionId(connection_id_);
+  packet_generator_.SetRetryToken(retry_token);
+
+  // Reinstall initial crypters because the connection ID changed.
+  CrypterPair crypters;
+  CryptoUtils::CreateTlsInitialCrypters(
+      Perspective::IS_CLIENT, transport_version(), connection_id_, &crypters);
+  SetEncrypter(ENCRYPTION_INITIAL, std::move(crypters.encrypter));
+  InstallDecrypter(ENCRYPTION_INITIAL, std::move(crypters.decrypter));
+}
+
 bool QuicConnection::HasIncomingConnectionId(QuicConnectionId connection_id) {
   for (QuicConnectionId const& incoming_connection_id :
        incoming_connection_ids_) {
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index b28ece5..ea12d71 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -481,6 +481,9 @@
   void OnPublicResetPacket(const QuicPublicResetPacket& packet) override;
   void OnVersionNegotiationPacket(
       const QuicVersionNegotiationPacket& packet) override;
+  void OnRetryPacket(QuicConnectionId original_connection_id,
+                     QuicConnectionId new_connection_id,
+                     QuicStringPiece retry_token) override;
   bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) override;
   bool OnUnauthenticatedHeader(const QuicPacketHeader& header) override;
   void OnDecryptedPacket(EncryptionLevel level) override;
@@ -1508,6 +1511,9 @@
   // needs to be sent.
   bool send_ack_when_on_can_write_;
 
+  // Indicates whether a RETRY packet has been parsed.
+  bool retry_has_been_parsed_;
+
   // Latched value of quic_validate_packet_number_post_decryption.
   const bool validate_packet_number_post_decryption_;
 
diff --git a/quic/core/quic_dispatcher.cc b/quic/core/quic_dispatcher.cc
index a254585..094fc7b 100644
--- a/quic/core/quic_dispatcher.cc
+++ b/quic/core/quic_dispatcher.cc
@@ -869,6 +869,12 @@
   DCHECK(false);
 }
 
+void QuicDispatcher::OnRetryPacket(QuicConnectionId /*original_connection_id*/,
+                                   QuicConnectionId /*new_connection_id*/,
+                                   QuicStringPiece /*retry_token*/) {
+  DCHECK(false);
+}
+
 void QuicDispatcher::OnDecryptedPacket(EncryptionLevel level) {
   DCHECK(false);
 }
@@ -1066,7 +1072,7 @@
 bool QuicDispatcher::ShouldCreateOrBufferPacketForConnection(
     QuicConnectionId connection_id,
     bool ietf_quic) {
-  VLOG(1) << "Received packet from new connection " << connection_id;
+  QUIC_VLOG(1) << "Received packet from new connection " << connection_id;
   return true;
 }
 
diff --git a/quic/core/quic_dispatcher.h b/quic/core/quic_dispatcher.h
index 4503fc0..c4fe223 100644
--- a/quic/core/quic_dispatcher.h
+++ b/quic/core/quic_dispatcher.h
@@ -149,6 +149,9 @@
   void OnPublicResetPacket(const QuicPublicResetPacket& packet) override;
   void OnVersionNegotiationPacket(
       const QuicVersionNegotiationPacket& packet) override;
+  void OnRetryPacket(QuicConnectionId original_connection_id,
+                     QuicConnectionId new_connection_id,
+                     QuicStringPiece retry_token) override;
   void OnDecryptedPacket(EncryptionLevel level) override;
   bool OnPacketHeader(const QuicPacketHeader& header) override;
   void OnCoalescedPacket(const QuicEncryptedPacket& packet) override;
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc
index 303dca2..55208e9 100644
--- a/quic/core/quic_framer.cc
+++ b/quic/core/quic_framer.cc
@@ -1510,6 +1510,8 @@
   if (IsVersionNegotiation(header, packet_has_ietf_packet_header)) {
     QUIC_DVLOG(1) << ENDPOINT << "Received version negotiation packet";
     rv = ProcessVersionNegotiationPacket(&reader, header);
+  } else if (header.long_packet_type == RETRY) {
+    rv = ProcessRetryPacket(&reader, header);
   } else if (header.reset_flag) {
     rv = ProcessPublicResetPacket(&reader, header);
   } else if (packet.length() <= kMaxIncomingPacketSize) {
@@ -1563,6 +1565,29 @@
   return true;
 }
 
+bool QuicFramer::ProcessRetryPacket(QuicDataReader* reader,
+                                    const QuicPacketHeader& header) {
+  DCHECK_EQ(Perspective::IS_CLIENT, perspective_);
+
+  // Parse Original Destination Connection ID Length.
+  uint8_t odcil = header.type_byte & 0xf;
+  if (odcil != 0) {
+    odcil += kConnectionIdLengthAdjustment;
+  }
+
+  // Parse Original Destination Connection ID.
+  QuicConnectionId original_destination_connection_id;
+  if (!reader->ReadConnectionId(&original_destination_connection_id, odcil)) {
+    set_detailed_error("Unable to read Original Destination ConnectionId.");
+    return false;
+  }
+
+  QuicStringPiece retry_token = reader->ReadRemainingPayload();
+  visitor_->OnRetryPacket(original_destination_connection_id,
+                          header.source_connection_id, retry_token);
+  return true;
+}
+
 bool QuicFramer::MaybeProcessIetfInitialRetryToken(
     QuicDataReader* encrypted_reader,
     QuicPacketHeader* header) {
@@ -2430,6 +2455,7 @@
     set_detailed_error("Unable to read type.");
     return false;
   }
+  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;
@@ -2469,14 +2495,19 @@
           set_detailed_error("Illegal long header type value.");
           return false;
         }
-        if (header->long_packet_type == RETRY &&
-            (version().KnowsWhichDecrypterToUse() ||
-             supports_multiple_packet_number_spaces_)) {
-          set_detailed_error("Not yet supported IETF RETRY packet received.");
-          return RaiseError(QUIC_INVALID_PACKET_HEADER);
+        if (header->long_packet_type == RETRY) {
+          if (!version().SupportsRetry()) {
+            set_detailed_error("RETRY not supported in this version.");
+            return false;
+          }
+          if (perspective_ == Perspective::IS_SERVER) {
+            set_detailed_error("Client-initiated RETRY is invalid.");
+            return false;
+          }
+        } else {
+          header->packet_number_length = GetLongHeaderPacketNumberLength(
+              header->version.transport_version, type);
         }
-        header->packet_number_length = GetLongHeaderPacketNumberLength(
-            header->version.transport_version, type);
       }
     }
     if (header->long_packet_type != VERSION_NEGOTIATION) {
diff --git a/quic/core/quic_framer.h b/quic/core/quic_framer.h
index b20ac92..decb456 100644
--- a/quic/core/quic_framer.h
+++ b/quic/core/quic_framer.h
@@ -13,6 +13,7 @@
 #include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
 #include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
 #include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
 #include "net/third_party/quiche/src/quic/core/quic_packets.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
@@ -92,6 +93,12 @@
   virtual void OnVersionNegotiationPacket(
       const QuicVersionNegotiationPacket& packet) = 0;
 
+  // Called only when |perspective_| is IS_CLIENT and a retry packet has been
+  // parsed.
+  virtual void OnRetryPacket(QuicConnectionId original_connection_id,
+                             QuicConnectionId new_connection_id,
+                             QuicStringPiece retry_token) = 0;
+
   // Called when all fields except packet number has been parsed, but has not
   // been authenticated. If it returns false, framing for this packet will
   // cease.
@@ -263,7 +270,7 @@
   }
 
   // Pass a UDP packet into the framer for parsing.
-  // Return true if the packet was processed succesfully. |packet| must be a
+  // Return true if the packet was processed successfully. |packet| must be a
   // single, complete UDP packet (not a frame of a packet).  This packet
   // might be null padded past the end of the payload, which will be correctly
   // ignored.
@@ -602,6 +609,9 @@
   bool ProcessVersionNegotiationPacket(QuicDataReader* reader,
                                        const QuicPacketHeader& header);
 
+  bool ProcessRetryPacket(QuicDataReader* reader,
+                          const QuicPacketHeader& header);
+
   bool MaybeProcessIetfInitialRetryToken(QuicDataReader* encrypted_reader,
                                          QuicPacketHeader* header);
 
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc
index 5edb288..56665df 100644
--- a/quic/core/quic_framer_test.cc
+++ b/quic/core/quic_framer_test.cc
@@ -198,6 +198,16 @@
         QuicMakeUnique<QuicVersionNegotiationPacket>((packet));
   }
 
+  void OnRetryPacket(QuicConnectionId original_connection_id,
+                     QuicConnectionId new_connection_id,
+                     QuicStringPiece retry_token) override {
+    retry_original_connection_id_ =
+        QuicMakeUnique<QuicConnectionId>(original_connection_id);
+    retry_new_connection_id_ =
+        QuicMakeUnique<QuicConnectionId>(new_connection_id);
+    retry_token_ = QuicMakeUnique<std::string>(std::string(retry_token));
+  }
+
   bool OnProtocolVersionMismatch(ParsedQuicVersion received_version,
                                  PacketHeaderFormat /*form*/) override {
     QUIC_DLOG(INFO) << "QuicFramer Version Mismatch, version: "
@@ -394,6 +404,9 @@
   std::unique_ptr<QuicPublicResetPacket> public_reset_packet_;
   std::unique_ptr<QuicIetfStatelessResetPacket> stateless_reset_packet_;
   std::unique_ptr<QuicVersionNegotiationPacket> version_negotiation_packet_;
+  std::unique_ptr<QuicConnectionId> retry_original_connection_id_;
+  std::unique_ptr<QuicConnectionId> retry_new_connection_id_;
+  std::unique_ptr<std::string> retry_token_;
   std::vector<std::unique_ptr<QuicStreamFrame>> stream_frames_;
   std::vector<std::unique_ptr<QuicCryptoFrame>> crypto_frames_;
   std::vector<std::unique_ptr<QuicAckFrame>> ack_frames_;
@@ -5479,6 +5492,78 @@
   CheckFramingBoundaries(packet, QUIC_INVALID_VERSION_NEGOTIATION_PACKET);
 }
 
+TEST_P(QuicFramerTest, ParseIetfRetryPacket) {
+  if (!framer_.version().SupportsRetry()) {
+    return;
+  }
+  // IETF RETRY is only sent from client to server.
+  QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+  // clang-format off
+  unsigned char packet[] = {
+      // public flags (long header with packet type RETRY and ODCIL=8)
+      0xF5,
+      // version
+      QUIC_VERSION_BYTES,
+      // connection ID lengths
+      0x05,
+      // source connection ID
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11,
+      // original destination connection ID
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+      // retry token
+      'H', 'e', 'l', 'l', 'o', ' ', 't', 'h', 'i', 's',
+      ' ', 'i', 's', ' ', 'R', 'E', 'T', 'R', 'Y', '!',
+  };
+  // clang-format on
+
+  QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false);
+  EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+
+  ASSERT_TRUE(visitor_.retry_original_connection_id_.get());
+  ASSERT_TRUE(visitor_.retry_new_connection_id_.get());
+  ASSERT_TRUE(visitor_.retry_token_.get());
+
+  EXPECT_EQ(FramerTestConnectionId(),
+            *visitor_.retry_original_connection_id_.get());
+  EXPECT_EQ(FramerTestConnectionIdPlusOne(),
+            *visitor_.retry_new_connection_id_.get());
+  EXPECT_EQ("Hello this is RETRY!", *visitor_.retry_token_.get());
+}
+
+TEST_P(QuicFramerTest, RejectIetfRetryPacketAsServer) {
+  if (!framer_.version().SupportsRetry()) {
+    return;
+  }
+  // IETF RETRY is only sent from client to server.
+  QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER);
+  // clang-format off
+  unsigned char packet[] = {
+      // public flags (long header with packet type RETRY and ODCIL=8)
+      0xF5,
+      // version
+      QUIC_VERSION_BYTES,
+      // connection ID lengths
+      0x05,
+      // source connection ID
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11,
+      // original destination connection ID
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+      // retry token
+      'H', 'e', 'l', 'l', 'o', ' ', 't', 'h', 'i', 's',
+      ' ', 'i', 's', ' ', 'R', 'E', 'T', 'R', 'Y', '!',
+  };
+  // clang-format on
+
+  QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false);
+  EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+
+  EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, framer_.error());
+  EXPECT_EQ("Client-initiated RETRY is invalid.", framer_.detailed_error());
+}
+
 TEST_P(QuicFramerTest, BuildPaddingFramePacket) {
   QuicPacketHeader header;
   header.destination_connection_id = FramerTestConnectionId();
@@ -13085,7 +13170,8 @@
 }
 
 TEST_P(QuicFramerTest, IetfRetryPacketRejected) {
-  if (!framer_.version().KnowsWhichDecrypterToUse()) {
+  if (!framer_.version().KnowsWhichDecrypterToUse() ||
+      framer_.version().SupportsRetry()) {
     return;
   }
 
@@ -13112,7 +13198,7 @@
     {"Unable to read protocol version.",
      {QUIC_VERSION_BYTES}},
     // connection_id length
-    {"Not yet supported IETF RETRY packet received.",
+    {"RETRY not supported in this version.",
      {0x00}},
   };
   // clang-format on
@@ -13128,7 +13214,8 @@
 }
 
 TEST_P(QuicFramerTest, RetryPacketRejectedWithMultiplePacketNumberSpaces) {
-  if (framer_.transport_version() < QUIC_VERSION_46) {
+  if (framer_.transport_version() < QUIC_VERSION_46 ||
+      framer_.version().SupportsRetry()) {
     return;
   }
   framer_.EnableMultiplePacketNumberSpacesSupport();
@@ -13142,7 +13229,7 @@
     {"Unable to read protocol version.",
      {QUIC_VERSION_BYTES}},
     // connection_id length
-    {"Not yet supported IETF RETRY packet received.",
+    {"RETRY not supported in this version.",
      {0x00}},
   };
   // clang-format on
diff --git a/quic/core/quic_ietf_framer_test.cc b/quic/core/quic_ietf_framer_test.cc
index fa3aca4..f30fc99 100644
--- a/quic/core/quic_ietf_framer_test.cc
+++ b/quic/core/quic_ietf_framer_test.cc
@@ -96,6 +96,10 @@
   void OnVersionNegotiationPacket(
       const QuicVersionNegotiationPacket& packet) override {}
 
+  void OnRetryPacket(QuicConnectionId original_connection_id,
+                     QuicConnectionId new_connection_id,
+                     QuicStringPiece retry_token) override {}
+
   bool OnProtocolVersionMismatch(ParsedQuicVersion received_version,
                                  PacketHeaderFormat form) override {
     return true;
diff --git a/quic/core/quic_packet_creator.h b/quic/core/quic_packet_creator.h
index 99e8cc3..7ec8739 100644
--- a/quic/core/quic_packet_creator.h
+++ b/quic/core/quic_packet_creator.h
@@ -255,7 +255,7 @@
   // Sets transmission type of next constructed packets.
   void SetTransmissionType(TransmissionType type);
 
-  // Sets the retry token to be sent over the wire in v99 IETF Initial packets.
+  // Sets the retry token to be sent over the wire in IETF Initial packets.
   void SetRetryToken(QuicStringPiece retry_token);
 
   // Returns the largest payload that will fit into a single MESSAGE frame.
diff --git a/quic/core/quic_packet_generator.cc b/quic/core/quic_packet_generator.cc
index 7e98274..b90880e 100644
--- a/quic/core/quic_packet_generator.cc
+++ b/quic/core/quic_packet_generator.cc
@@ -485,6 +485,10 @@
   }
 }
 
+void QuicPacketGenerator::SetRetryToken(QuicStringPiece retry_token) {
+  packet_creator_.SetRetryToken(retry_token);
+}
+
 void QuicPacketGenerator::SetCanSetTransmissionType(
     bool can_set_transmission_type) {
   packet_creator_.set_can_set_transmission_type(can_set_transmission_type);
diff --git a/quic/core/quic_packet_generator.h b/quic/core/quic_packet_generator.h
index 047295d..417fda6 100644
--- a/quic/core/quic_packet_generator.h
+++ b/quic/core/quic_packet_generator.h
@@ -217,6 +217,9 @@
   // Set transmission type of next constructed packets.
   void SetTransmissionType(TransmissionType type);
 
+  // Sets the retry token to be sent over the wire in IETF Initial packets.
+  void SetRetryToken(QuicStringPiece retry_token);
+
   // Allow/Disallow setting transmission type of next constructed packets.
   void SetCanSetTransmissionType(bool can_set_transmission_type);
 
diff --git a/quic/core/quic_packets.h b/quic/core/quic_packets.h
index e745e07..3ced31f 100644
--- a/quic/core/quic_packets.h
+++ b/quic/core/quic_packets.h
@@ -102,6 +102,7 @@
   // parsed from the packet buffer. IETF QUIC only, always false for GQUIC.
   bool has_possible_stateless_reset_token;
   QuicPacketNumberLength packet_number_length;
+  uint8_t type_byte;
   ParsedQuicVersion version;
   // nonce contains an optional, 32-byte nonce value. If not included in the
   // packet, |nonce| will be empty.
diff --git a/quic/core/quic_versions.cc b/quic/core/quic_versions.cc
index 3fb817b..99f90fe 100644
--- a/quic/core/quic_versions.cc
+++ b/quic/core/quic_versions.cc
@@ -60,6 +60,10 @@
   return false;
 }
 
+bool ParsedQuicVersion::SupportsRetry() const {
+  return transport_version == QUIC_VERSION_99;
+}
+
 std::ostream& operator<<(std::ostream& os, const ParsedQuicVersion& version) {
   os << ParsedQuicVersionToString(version);
   return os;
diff --git a/quic/core/quic_versions.h b/quic/core/quic_versions.h
index 00bbb32..7264810 100644
--- a/quic/core/quic_versions.h
+++ b/quic/core/quic_versions.h
@@ -154,6 +154,9 @@
 
   // Returns whether header protection is used in this version of QUIC.
   bool HasHeaderProtection() const;
+
+  // Returns whether this version supports IETF RETRY packets.
+  bool SupportsRetry() const;
 };
 
 QUIC_EXPORT_PRIVATE ParsedQuicVersion UnsupportedQuicVersion();