Allow QUIC server to replace connection IDs

This CL changes the QuicDispatcher to have it replace the connection ID provided by the client if its length differs from what the dispatcher was configured with. It also changes QuicConnection on the client side to accept connection ID changes coming from the server, and replace its own connection ID to match what the server expects on outgoing packets. This checks VariableLengthConnectionIdAllowedForVersion() so it only impacts v99.

gfe-relnote: v99-only change, not flag protected
PiperOrigin-RevId: 239328650
Change-Id: I21ee0c0ca74c7624823c38a72f323ae6491e21e6
diff --git a/quic/core/quic_dispatcher.cc b/quic/core/quic_dispatcher.cc
index 5fddab6..7ef2869 100644
--- a/quic/core/quic_dispatcher.cc
+++ b/quic/core/quic_dispatcher.cc
@@ -329,6 +329,30 @@
   //            next packet does not use them incorrectly.
 }
 
+QuicConnectionId QuicDispatcher::MaybeReplaceConnectionId(
+    QuicConnectionId connection_id,
+    ParsedQuicVersion version) {
+  const uint8_t expected_connection_id_length =
+      framer_.GetExpectedConnectionIdLength();
+  if (connection_id.length() == expected_connection_id_length) {
+    return connection_id;
+  }
+  DCHECK(QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+      version.transport_version));
+  auto it = connection_id_map_.find(connection_id);
+  if (it != connection_id_map_.end()) {
+    return it->second;
+  }
+  QuicConnectionId new_connection_id =
+      session_helper_->GenerateConnectionIdForReject(version.transport_version,
+                                                     connection_id);
+  DCHECK_EQ(expected_connection_id_length, new_connection_id.length());
+  connection_id_map_.insert(std::make_pair(connection_id, new_connection_id));
+  QUIC_DLOG(INFO) << "Replacing incoming connection ID " << connection_id
+                  << " with " << new_connection_id;
+  return new_connection_id;
+}
+
 bool QuicDispatcher::OnUnauthenticatedPublicHeader(
     const QuicPacketHeader& header) {
   current_connection_id_ = header.destination_connection_id;
@@ -345,28 +369,10 @@
   if (header.destination_connection_id_included != CONNECTION_ID_PRESENT) {
     return false;
   }
-
-  // We currently do not support having the server change its connection ID
-  // length during the handshake. Until then, fast-fail connections.
-  // TODO(dschinazi): actually support changing connection IDs from the server.
-  if (header.destination_connection_id.length() !=
-      framer_.GetExpectedConnectionIdLength()) {
-    DCHECK(QuicUtils::VariableLengthConnectionIdAllowedForVersion(
-        header.version.transport_version));
-    QUIC_DLOG(INFO)
-        << "Packet with unexpected connection ID lengths: destination "
-        << header.destination_connection_id << " source "
-        << header.source_connection_id << " expected "
-        << static_cast<int>(framer_.GetExpectedConnectionIdLength());
-    ProcessUnauthenticatedHeaderFate(kFateTimeWait,
-                                     header.destination_connection_id,
-                                     header.form, header.version);
-    return false;
-  }
+  QuicConnectionId connection_id = header.destination_connection_id;
 
   // Packets with connection IDs for active connections are processed
   // immediately.
-  QuicConnectionId connection_id = header.destination_connection_id;
   auto it = session_map_.find(connection_id);
   if (it != session_map_.end()) {
     DCHECK(!buffered_packets_.HasBufferedPackets(connection_id));
@@ -998,9 +1004,15 @@
     if (packets.empty()) {
       return;
     }
+    QuicConnectionId original_connection_id = connection_id;
+    connection_id =
+        MaybeReplaceConnectionId(connection_id, packet_list.version);
     QuicSession* session =
         CreateQuicSession(connection_id, packets.front().peer_address,
                           packet_list.alpn, packet_list.version);
+    if (original_connection_id != connection_id) {
+      session->connection()->AddIncomingConnectionId(original_connection_id);
+    }
     QUIC_DLOG(INFO) << "Created new session for " << connection_id;
     session_map_.insert(std::make_pair(connection_id, QuicWrapUnique(session)));
     DeliverPacketsToSession(packets, session);
@@ -1093,10 +1105,17 @@
     }
     return;
   }
+
+  QuicConnectionId original_connection_id = current_connection_id_;
+  current_connection_id_ =
+      MaybeReplaceConnectionId(current_connection_id_, framer_.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());
+  if (original_connection_id != current_connection_id_) {
+    session->connection()->AddIncomingConnectionId(original_connection_id);
+  }
   QUIC_DLOG(INFO) << "Created new session for " << current_connection_id_;
   session_map_.insert(
       std::make_pair(current_connection_id_, QuicWrapUnique(session)));