relnote: On server side, do not process the first initial QUIC packet received from a client if the UDP datagram is < 1200 bytes. Instead, send a connection close with PROTOCOL_VIOLATION if the version is supported. Protected by quic_reloadable_flag_quic_donot_process_small_initial_packets.

PiperOrigin-RevId: 270157435
Change-Id: Id0faa750300b2e75c7e87cc05dd8635c580f01ba
diff --git a/quic/core/http/quic_spdy_client_session_test.cc b/quic/core/http/quic_spdy_client_session_test.cc
index 3e951b3..306f6f5 100644
--- a/quic/core/http/quic_spdy_client_session_test.cc
+++ b/quic/core/http/quic_spdy_client_session_test.cc
@@ -517,7 +517,7 @@
   QuicConnectionId source_connection_id = connection_id;
   std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket(
       destination_connection_id, source_connection_id, false, false, 100,
-      "data", CONNECTION_ID_ABSENT, CONNECTION_ID_ABSENT,
+      "data", true, CONNECTION_ID_ABSENT, CONNECTION_ID_ABSENT,
       PACKET_4BYTE_PACKET_NUMBER, &versions, Perspective::IS_SERVER));
   std::unique_ptr<QuicReceivedPacket> received(
       ConstructReceivedPacket(*packet, QuicTime::Zero()));
diff --git a/quic/core/quic_dispatcher.cc b/quic/core/quic_dispatcher.cc
index beb70ca..3187c78 100644
--- a/quic/core/quic_dispatcher.cc
+++ b/quic/core/quic_dispatcher.cc
@@ -32,6 +32,9 @@
 
 namespace {
 
+// Minimal INITIAL packet length sent by clients is 1200.
+const QuicPacketLength kMinClientInitialPacketLength = 1200;
+
 // An alarm that informs the QuicDispatcher to delete old sessions.
 class DeleteSessionsAlarm : public QuicAlarm::Delegate {
  public:
@@ -142,11 +145,35 @@
     framer_.set_data_producer(nullptr);
   }
 
+  // Serializes a packet containing CONNECTION_CLOSE frame and send it (without
+  // adding connection to the time wait).
+  void StatelesslyCloseConnection(const QuicSocketAddress& self_address,
+                                  const QuicSocketAddress& peer_address,
+                                  QuicErrorCode error_code,
+                                  const std::string& error_details) {
+    SerializeConnectionClosePacket(error_code, error_details);
+
+    for (const auto& packet : *collector_.packets()) {
+      time_wait_list_manager_->SendPacket(self_address, peer_address, *packet);
+    }
+  }
+
   // Generates a packet containing a CONNECTION_CLOSE frame specifying
   // |error_code| and |error_details| and add the connection to time wait.
   void CloseConnection(QuicErrorCode error_code,
                        const std::string& error_details,
                        bool ietf_quic) {
+    SerializeConnectionClosePacket(error_code, error_details);
+
+    time_wait_list_manager_->AddConnectionIdToTimeWait(
+        server_connection_id_, ietf_quic,
+        QuicTimeWaitListManager::SEND_TERMINATION_PACKETS,
+        quic::ENCRYPTION_INITIAL, collector_.packets());
+  }
+
+ private:
+  void SerializeConnectionClosePacket(QuicErrorCode error_code,
+                                      const std::string& error_details) {
     QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame(
         framer_.transport_version(), error_code, error_details,
         /*transport_close_frame_type=*/0);
@@ -158,13 +185,8 @@
     }
     creator_.FlushCurrentPacket();
     DCHECK_EQ(1u, collector_.packets()->size());
-    time_wait_list_manager_->AddConnectionIdToTimeWait(
-        server_connection_id_, ietf_quic,
-        QuicTimeWaitListManager::SEND_TERMINATION_PACKETS,
-        quic::ENCRYPTION_INITIAL, collector_.packets());
   }
 
- private:
   QuicConnectionId server_connection_id_;
   QuicFramer framer_;
   // Set as the visitor of |creator_| to collect any generated packets.
@@ -256,9 +278,9 @@
     QuicStringPiece retry_token;
     error = QuicFramer::ParsePublicHeaderDispatcher(
         packet, expected_server_connection_id_length_, &packet_info.form,
-        &packet_info.version_flag, &packet_info.use_length_prefix,
-        &packet_info.version_label, &packet_info.version,
-        &packet_info.destination_connection_id,
+        &packet_info.long_packet_type, &packet_info.version_flag,
+        &packet_info.use_length_prefix, &packet_info.version_label,
+        &packet_info.version, &packet_info.destination_connection_id,
         &packet_info.source_connection_id, &retry_token_present, &retry_token,
         &detailed_error);
   }
@@ -447,6 +469,24 @@
       }
       return true;
     }
+
+    if (GetQuicReloadableFlag(quic_use_parse_public_header) &&
+        GetQuicReloadableFlag(quic_donot_process_small_initial_packets) &&
+        crypto_config()->validate_chlo_size() &&
+        packet_info.form == IETF_QUIC_LONG_HEADER_PACKET &&
+        packet_info.long_packet_type == INITIAL &&
+        packet_info.packet.length() < kMinClientInitialPacketLength) {
+      QUIC_RELOADABLE_FLAG_COUNT(quic_donot_process_small_initial_packets);
+      StatelessConnectionTerminator terminator(
+          packet_info.destination_connection_id, packet_info.version,
+          helper_.get(), time_wait_list_manager_.get());
+      QUIC_DVLOG(1) << "Initial packet too small: "
+                    << packet_info.packet.length();
+      terminator.StatelesslyCloseConnection(
+          packet_info.self_address, packet_info.peer_address,
+          IETF_QUIC_PROTOCOL_VIOLATION, "Initial packet too small");
+      return true;
+    }
   }
 
   return false;
diff --git a/quic/core/quic_dispatcher_test.cc b/quic/core/quic_dispatcher_test.cc
index 34408e0..f2114c2 100644
--- a/quic/core/quic_dispatcher_test.cc
+++ b/quic/core/quic_dispatcher_test.cc
@@ -262,7 +262,7 @@
                      QuicPacketNumberLength packet_number_length,
                      uint64_t packet_number) {
     ProcessPacket(peer_address, server_connection_id, has_version_flag,
-                  CurrentSupportedVersions().front(), data,
+                  CurrentSupportedVersions().front(), data, true,
                   server_connection_id_included, packet_number_length,
                   packet_number);
   }
@@ -273,11 +273,12 @@
                      bool has_version_flag,
                      ParsedQuicVersion version,
                      const std::string& data,
+                     bool full_padding,
                      QuicConnectionIdIncluded server_connection_id_included,
                      QuicPacketNumberLength packet_number_length,
                      uint64_t packet_number) {
     ProcessPacket(peer_address, server_connection_id, EmptyQuicConnectionId(),
-                  has_version_flag, version, data,
+                  has_version_flag, version, data, full_padding,
                   server_connection_id_included, CONNECTION_ID_ABSENT,
                   packet_number_length, packet_number);
   }
@@ -289,6 +290,7 @@
                      bool has_version_flag,
                      ParsedQuicVersion version,
                      const std::string& data,
+                     bool full_padding,
                      QuicConnectionIdIncluded server_connection_id_included,
                      QuicConnectionIdIncluded client_connection_id_included,
                      QuicPacketNumberLength packet_number_length,
@@ -296,7 +298,7 @@
     ParsedQuicVersionVector versions(SupportedVersions(version));
     std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket(
         server_connection_id, client_connection_id, has_version_flag, false,
-        packet_number, data, server_connection_id_included,
+        packet_number, data, full_padding, server_connection_id_included,
         client_connection_id_included, packet_number_length, &versions));
     std::unique_ptr<QuicReceivedPacket> received_packet(
         ConstructReceivedPacket(*packet, mock_helper_.GetClock()->Now()));
@@ -383,7 +385,7 @@
                 ShouldCreateOrBufferPacketForConnection(
                     ReceivedPacketInfoConnectionIdEquals(connection_id)));
     ProcessPacket(client_address, connection_id, true, version, SerializeCHLO(),
-                  CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
+                  true, CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
   }
 
   void VerifyVersionNotSupported(ParsedQuicVersion version) {
@@ -393,7 +395,7 @@
                                                 QuicStringPiece("hq"), _))
         .Times(0);
     ProcessPacket(client_address, connection_id, true, version, SerializeCHLO(),
-                  CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
+                  true, CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
   }
 
   MockQuicConnectionHelper mock_helper_;
@@ -439,7 +441,8 @@
       client_address, TestConnectionId(1), true,
       ParsedQuicVersion(PROTOCOL_TLS1_3,
                         CurrentSupportedVersions().front().transport_version),
-      SerializeCHLO(), CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
+      SerializeCHLO(), true, CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER,
+      1);
 }
 
 TEST_F(QuicDispatcherTest, ProcessPackets) {
@@ -514,14 +517,15 @@
       client_address, TestConnectionId(1), true,
       ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO,
                         CurrentSupportedVersions().front().transport_version),
-      SerializeCHLO(), CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
+      SerializeCHLO(), true, CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER,
+      1);
   // Packet number 256 with packet number length 1 would be considered as 0 in
   // dispatcher.
   ProcessPacket(
       client_address, TestConnectionId(1), false,
       ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO,
                         CurrentSupportedVersions().front().transport_version),
-      "", CONNECTION_ID_PRESENT, PACKET_1BYTE_PACKET_NUMBER, 256);
+      "", true, CONNECTION_ID_PRESENT, PACKET_1BYTE_PACKET_NUMBER, 256);
 }
 
 TEST_F(QuicDispatcherTest, StatelessVersionNegotiation) {
@@ -539,7 +543,7 @@
   std::string chlo = SerializeCHLO() + std::string(1200, 'a');
   DCHECK_LE(1200u, chlo.length());
   ProcessPacket(client_address, TestConnectionId(1), true,
-                QuicVersionReservedForNegotiation(), chlo,
+                QuicVersionReservedForNegotiation(), chlo, true,
                 CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
 }
 
@@ -558,7 +562,7 @@
   std::string chlo = SerializeCHLO() + std::string(1200, 'a');
   DCHECK_LE(1200u, chlo.length());
   ProcessPacket(client_address, TestConnectionId(1), TestConnectionId(2), true,
-                QuicVersionReservedForNegotiation(), chlo,
+                QuicVersionReservedForNegotiation(), chlo, true,
                 CONNECTION_ID_PRESENT, CONNECTION_ID_PRESENT,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
 }
@@ -578,7 +582,7 @@
   std::string truncated_chlo = chlo.substr(0, 1100);
   DCHECK_EQ(1100u, truncated_chlo.length());
   ProcessPacket(client_address, TestConnectionId(1), true,
-                QuicVersionReservedForNegotiation(), truncated_chlo,
+                QuicVersionReservedForNegotiation(), truncated_chlo, false,
                 CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
 }
 
@@ -602,7 +606,7 @@
   std::string truncated_chlo = chlo.substr(0, 1100);
   DCHECK_EQ(1100u, truncated_chlo.length());
   ProcessPacket(client_address, TestConnectionId(1), true,
-                QuicVersionReservedForNegotiation(), truncated_chlo,
+                QuicVersionReservedForNegotiation(), truncated_chlo, true,
                 CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
 }
 
@@ -1327,6 +1331,68 @@
       sizeof(connection_id_bytes));
 }
 
+TEST_F(QuicDispatcherTest, DoNotProcessSmallPacket) {
+  SetQuicReloadableFlag(quic_donot_process_small_initial_packets, true);
+  SetQuicReloadableFlag(quic_use_parse_public_header, true);
+  CreateTimeWaitListManager();
+  QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+
+  EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0);
+  EXPECT_CALL(*time_wait_list_manager_, SendPacket(_, _, _)).Times(1);
+  ProcessPacket(client_address, TestConnectionId(1), true,
+                CurrentSupportedVersions()[0], SerializeCHLO(), false,
+                CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
+}
+
+TEST_F(QuicDispatcherTest, ProcessSmallCoalescedPacket) {
+  SetQuicReloadableFlag(quic_enable_version_99, true);
+  CreateTimeWaitListManager();
+  QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+
+  EXPECT_CALL(*time_wait_list_manager_, SendPacket(_, _, _)).Times(0);
+
+  // clang-format off
+  char coalesced_packet[1200] = {
+    // first coalesced packet
+      // public flags (long header with packet type INITIAL and
+      // 4-byte packet number)
+      0xC3,
+      // version
+      'Q', '0', '9', '9',
+      // destination connection ID length
+      0x08,
+      // destination connection ID
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+      // source connection ID length
+      0x00,
+      // long header packet length
+      0x05,
+      // packet number
+      0x12, 0x34, 0x56, 0x78,
+      // Padding
+      0x00,
+    // second coalesced packet
+      // public flags (long header with packet type ZERO_RTT_PROTECTED and
+      // 4-byte packet number)
+      0xC3,
+      // version
+      'Q', '0', '9', '9',
+      // destination connection ID length
+      0x08,
+      // destination connection ID
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+      // source connection ID length
+      0x00,
+      // long header packet length
+      0x1E,
+      // packet number
+      0x12, 0x34, 0x56, 0x79,
+  };
+  // clang-format on
+  QuicReceivedPacket packet(coalesced_packet, 1200, QuicTime::Zero());
+  dispatcher_->ProcessPacket(server_address_, client_address, packet);
+}
+
 // Verify the stopgap test: Packets with truncated connection IDs should be
 // dropped.
 class QuicDispatcherTestStrayPacketConnectionId : public QuicDispatcherTest {};
@@ -2118,7 +2184,7 @@
               })));
     }
     ProcessPacket(client_addr_, TestConnectionId(conn_id), true, version,
-                  SerializeFullCHLO(), CONNECTION_ID_PRESENT,
+                  SerializeFullCHLO(), true, CONNECTION_ID_PRESENT,
                   PACKET_4BYTE_PACKET_NUMBER, 1);
   }
 
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc
index e33dc00..84dd00a 100644
--- a/quic/core/quic_framer.cc
+++ b/quic/core/quic_framer.cc
@@ -6303,6 +6303,7 @@
     const QuicEncryptedPacket& packet,
     uint8_t expected_destination_connection_id_length,
     PacketHeaderFormat* format,
+    QuicLongHeaderType* long_packet_type,
     bool* version_present,
     bool* has_length_prefix,
     QuicVersionLabel* version_label,
@@ -6321,12 +6322,11 @@
   const bool ietf_format = QuicUtils::IsIetfPacketHeader(first_byte);
   uint8_t unused_first_byte;
   QuicVariableLengthIntegerLength retry_token_length_length;
-  QuicLongHeaderType unused_log_packet_type;
-  const QuicErrorCode error_code = ParsePublicHeader(
+  QuicErrorCode error_code = ParsePublicHeader(
       &reader, expected_destination_connection_id_length, ietf_format,
       &unused_first_byte, format, version_present, has_length_prefix,
       version_label, parsed_version, destination_connection_id,
-      source_connection_id, &unused_log_packet_type, &retry_token_length_length,
+      source_connection_id, long_packet_type, &retry_token_length_length,
       retry_token, detailed_error);
   *retry_token_present =
       retry_token_length_length != VARIABLE_LENGTH_INTEGER_LENGTH_0;
diff --git a/quic/core/quic_framer.h b/quic/core/quic_framer.h
index ffeeab1..3d4c063 100644
--- a/quic/core/quic_framer.h
+++ b/quic/core/quic_framer.h
@@ -422,6 +422,7 @@
       const QuicEncryptedPacket& packet,
       uint8_t expected_destination_connection_id_length,
       PacketHeaderFormat* format,
+      QuicLongHeaderType* long_packet_type,
       bool* version_present,
       bool* has_length_prefix,
       QuicVersionLabel* version_label,
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc
index 9211d68..deb66d3 100644
--- a/quic/core/quic_framer_test.cc
+++ b/quic/core/quic_framer_test.cc
@@ -1015,6 +1015,7 @@
   CheckFramingBoundaries(fragments, QUIC_INVALID_PACKET_HEADER);
 
   PacketHeaderFormat format;
+  QuicLongHeaderType long_packet_type = INVALID_PACKET_TYPE;
   bool version_flag;
   QuicConnectionId destination_connection_id, source_connection_id;
   QuicVersionLabel version_label;
@@ -1030,8 +1031,8 @@
     QuicStringPiece retry_token;
     ParsedQuicVersion parsed_version = UnsupportedQuicVersion();
     error_code = QuicFramer::ParsePublicHeaderDispatcher(
-        *encrypted, kQuicDefaultConnectionIdLength, &format, &version_flag,
-        &use_length_prefix, &version_label, &parsed_version,
+        *encrypted, kQuicDefaultConnectionIdLength, &format, &long_packet_type,
+        &version_flag, &use_length_prefix, &version_label, &parsed_version,
         &destination_connection_id, &source_connection_id, &retry_token_present,
         &retry_token, &detailed_error);
     EXPECT_FALSE(retry_token_present);
@@ -1086,6 +1087,7 @@
   CheckFramingBoundaries(packet46, QUIC_INVALID_PACKET_HEADER);
 
   PacketHeaderFormat format;
+  QuicLongHeaderType long_packet_type = INVALID_PACKET_TYPE;
   bool version_flag;
   QuicConnectionId destination_connection_id, source_connection_id;
   QuicVersionLabel version_label;
@@ -1101,8 +1103,8 @@
     QuicStringPiece retry_token;
     ParsedQuicVersion parsed_version = UnsupportedQuicVersion();
     error_code = QuicFramer::ParsePublicHeaderDispatcher(
-        *encrypted, kQuicDefaultConnectionIdLength, &format, &version_flag,
-        &use_length_prefix, &version_label, &parsed_version,
+        *encrypted, kQuicDefaultConnectionIdLength, &format, &long_packet_type,
+        &version_flag, &use_length_prefix, &version_label, &parsed_version,
         &destination_connection_id, &source_connection_id, &retry_token_present,
         &retry_token, &detailed_error);
     EXPECT_EQ(retry_token_present, framer_.version().SupportsRetry());
@@ -1146,6 +1148,7 @@
 
   QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false);
   PacketHeaderFormat format = GOOGLE_QUIC_PACKET;
+  QuicLongHeaderType long_packet_type = INVALID_PACKET_TYPE;
   bool version_flag = false;
   QuicConnectionId destination_connection_id, source_connection_id;
   QuicVersionLabel version_label = 0;
@@ -1161,8 +1164,8 @@
     QuicStringPiece retry_token;
     ParsedQuicVersion parsed_version = UnsupportedQuicVersion();
     error_code = QuicFramer::ParsePublicHeaderDispatcher(
-        encrypted, kQuicDefaultConnectionIdLength, &format, &version_flag,
-        &use_length_prefix, &version_label, &parsed_version,
+        encrypted, kQuicDefaultConnectionIdLength, &format, &long_packet_type,
+        &version_flag, &use_length_prefix, &version_label, &parsed_version,
         &destination_connection_id, &source_connection_id, &retry_token_present,
         &retry_token, &detailed_error);
     EXPECT_FALSE(retry_token_present);
@@ -13152,6 +13155,7 @@
             visitor_.header_.get()->destination_connection_id);
 
   PacketHeaderFormat format = GOOGLE_QUIC_PACKET;
+  QuicLongHeaderType long_packet_type = INVALID_PACKET_TYPE;
   bool version_present = false, has_length_prefix = false;
   QuicVersionLabel version_label = 0;
   ParsedQuicVersion parsed_version = QuicVersionReservedForNegotiation();
@@ -13162,8 +13166,8 @@
   std::string detailed_error = "foobar";
 
   QuicErrorCode parse_result = QuicFramer::ParsePublicHeaderDispatcher(
-      encrypted, kQuicDefaultConnectionIdLength, &format, &version_present,
-      &has_length_prefix, &version_label, &parsed_version,
+      encrypted, kQuicDefaultConnectionIdLength, &format, &long_packet_type,
+      &version_present, &has_length_prefix, &version_label, &parsed_version,
       &destination_connection_id, &source_connection_id, &retry_token_present,
       &retry_token, &detailed_error);
   EXPECT_EQ(QUIC_NO_ERROR, parse_result);
@@ -13302,6 +13306,7 @@
   QuicEncryptedPacket encrypted(reinterpret_cast<const char*>(packet),
                                 sizeof(packet));
   PacketHeaderFormat format = GOOGLE_QUIC_PACKET;
+  QuicLongHeaderType long_packet_type = INVALID_PACKET_TYPE;
   bool version_present = false, has_length_prefix = true;
   QuicVersionLabel version_label = 33;
   ParsedQuicVersion parsed_version = UnsupportedQuicVersion();
@@ -13311,8 +13316,8 @@
   QuicStringPiece retry_token;
   std::string detailed_error = "foobar";
   QuicErrorCode header_parse_result = QuicFramer::ParsePublicHeaderDispatcher(
-      encrypted, kQuicDefaultConnectionIdLength, &format, &version_present,
-      &has_length_prefix, &version_label, &parsed_version,
+      encrypted, kQuicDefaultConnectionIdLength, &format, &long_packet_type,
+      &version_present, &has_length_prefix, &version_label, &parsed_version,
       &destination_connection_id, &source_connection_id, &retry_token_present,
       &retry_token, &detailed_error);
   EXPECT_EQ(QUIC_NO_ERROR, header_parse_result);
@@ -13380,6 +13385,7 @@
   QuicEncryptedPacket encrypted(reinterpret_cast<const char*>(packet),
                                 sizeof(packet));
   PacketHeaderFormat format = GOOGLE_QUIC_PACKET;
+  QuicLongHeaderType long_packet_type = INVALID_PACKET_TYPE;
   bool version_present = false, has_length_prefix = false;
   QuicVersionLabel version_label = 33;
   ParsedQuicVersion parsed_version = UnsupportedQuicVersion();
@@ -13389,8 +13395,8 @@
   QuicStringPiece retry_token;
   std::string detailed_error = "foobar";
   QuicErrorCode header_parse_result = QuicFramer::ParsePublicHeaderDispatcher(
-      encrypted, kQuicDefaultConnectionIdLength, &format, &version_present,
-      &has_length_prefix, &version_label, &parsed_version,
+      encrypted, kQuicDefaultConnectionIdLength, &format, &long_packet_type,
+      &version_present, &has_length_prefix, &version_label, &parsed_version,
       &destination_connection_id, &source_connection_id, &retry_token_present,
       &retry_token, &detailed_error);
   EXPECT_EQ(QUIC_NO_ERROR, header_parse_result);
diff --git a/quic/core/quic_packets.cc b/quic/core/quic_packets.cc
index 0352056..139fcda 100644
--- a/quic/core/quic_packets.cc
+++ b/quic/core/quic_packets.cc
@@ -502,6 +502,7 @@
       peer_address(peer_address),
       packet(packet),
       form(GOOGLE_QUIC_PACKET),
+      long_packet_type(INVALID_PACKET_TYPE),
       version_flag(false),
       use_length_prefix(false),
       version_label(0),
diff --git a/quic/core/quic_packets.h b/quic/core/quic_packets.h
index 5c34b64..7faa741 100644
--- a/quic/core/quic_packets.h
+++ b/quic/core/quic_packets.h
@@ -437,6 +437,8 @@
 
   // Fields below are populated by QuicFramer::ProcessPacketDispatcher.
   PacketHeaderFormat form;
+  // This is only used if the form is IETF_QUIC_LONG_HEADER_PACKET.
+  QuicLongHeaderType long_packet_type;
   bool version_flag;
   bool use_length_prefix;
   QuicVersionLabel version_label;
diff --git a/quic/core/quic_time_wait_list_manager.cc b/quic/core/quic_time_wait_list_manager.cc
index b163185..b8becf6 100644
--- a/quic/core/quic_time_wait_list_manager.cc
+++ b/quic/core/quic_time_wait_list_manager.cc
@@ -271,6 +271,14 @@
                     packet_context.get());
 }
 
+void QuicTimeWaitListManager::SendPacket(const QuicSocketAddress& self_address,
+                                         const QuicSocketAddress& peer_address,
+                                         const QuicEncryptedPacket& packet) {
+  SendOrQueuePacket(std::make_unique<QueuedPacket>(self_address, peer_address,
+                                                   packet.Clone()),
+                    nullptr);
+}
+
 std::unique_ptr<QuicEncryptedPacket> QuicTimeWaitListManager::BuildPublicReset(
     const QuicPublicResetPacket& packet) {
   return QuicFramer::BuildPublicResetPacket(packet);
diff --git a/quic/core/quic_time_wait_list_manager.h b/quic/core/quic_time_wait_list_manager.h
index 9a807c0..e2a4594 100644
--- a/quic/core/quic_time_wait_list_manager.h
+++ b/quic/core/quic_time_wait_list_manager.h
@@ -139,6 +139,11 @@
       bool ietf_quic,
       std::unique_ptr<QuicPerPacketContext> packet_context);
 
+  // Called to send |packet|.
+  virtual void SendPacket(const QuicSocketAddress& self_address,
+                          const QuicSocketAddress& peer_address,
+                          const QuicEncryptedPacket& packet);
+
   // Return a non-owning pointer to the packet writer.
   QuicPacketWriter* writer() { return writer_; }
 
diff --git a/quic/test_tools/mock_quic_time_wait_list_manager.h b/quic/test_tools/mock_quic_time_wait_list_manager.h
index 6b2b6d0..b8a9776 100644
--- a/quic/test_tools/mock_quic_time_wait_list_manager.h
+++ b/quic/test_tools/mock_quic_time_wait_list_manager.h
@@ -61,6 +61,11 @@
                     QuicConnectionId,
                     bool,
                     std::unique_ptr<QuicPerPacketContext>));
+
+  MOCK_METHOD3(SendPacket,
+               void(const QuicSocketAddress&,
+                    const QuicSocketAddress&,
+                    const QuicEncryptedPacket&));
 };
 
 }  // namespace test
diff --git a/quic/test_tools/quic_test_utils.cc b/quic/test_tools/quic_test_utils.cc
index 387dbe1..2637702 100644
--- a/quic/test_tools/quic_test_utils.cc
+++ b/quic/test_tools/quic_test_utils.cc
@@ -867,10 +867,11 @@
     ParsedQuicVersionVector* versions) {
   return ConstructEncryptedPacket(
       destination_connection_id, source_connection_id, version_flag, reset_flag,
-      packet_number, data, destination_connection_id_included,
+      packet_number, data, false, destination_connection_id_included,
       source_connection_id_included, packet_number_length, versions,
       Perspective::IS_CLIENT);
 }
+
 QuicEncryptedPacket* ConstructEncryptedPacket(
     QuicConnectionId destination_connection_id,
     QuicConnectionId source_connection_id,
@@ -878,6 +879,26 @@
     bool reset_flag,
     uint64_t packet_number,
     const std::string& data,
+    bool full_padding,
+    QuicConnectionIdIncluded destination_connection_id_included,
+    QuicConnectionIdIncluded source_connection_id_included,
+    QuicPacketNumberLength packet_number_length,
+    ParsedQuicVersionVector* versions) {
+  return ConstructEncryptedPacket(
+      destination_connection_id, source_connection_id, version_flag, reset_flag,
+      packet_number, data, full_padding, destination_connection_id_included,
+      source_connection_id_included, packet_number_length, versions,
+      Perspective::IS_CLIENT);
+}
+
+QuicEncryptedPacket* ConstructEncryptedPacket(
+    QuicConnectionId destination_connection_id,
+    QuicConnectionId source_connection_id,
+    bool version_flag,
+    bool reset_flag,
+    uint64_t packet_number,
+    const std::string& data,
+    bool full_padding,
     QuicConnectionIdIncluded destination_connection_id_included,
     QuicConnectionIdIncluded source_connection_id_included,
     QuicPacketNumberLength packet_number_length,
@@ -922,14 +943,18 @@
     QuicFrame frame(new QuicCryptoFrame(level, 0, data));
     frames.push_back(frame);
   }
-  // We need a minimum number of bytes of encrypted payload. This will
-  // guarantee that we have at least that much. (It ignores the overhead of the
-  // stream/crypto framing, so it overpads slightly.)
-  size_t min_plaintext_size =
-      QuicPacketCreator::MinPlaintextPacketSize(version);
-  if (data.length() < min_plaintext_size) {
-    size_t padding_length = min_plaintext_size - data.length();
-    frames.push_back(QuicFrame(QuicPaddingFrame(padding_length)));
+  if (full_padding) {
+    frames.push_back(QuicFrame(QuicPaddingFrame(-1)));
+  } else {
+    // We need a minimum number of bytes of encrypted payload. This will
+    // guarantee that we have at least that much. (It ignores the overhead of
+    // the stream/crypto framing, so it overpads slightly.)
+    size_t min_plaintext_size =
+        QuicPacketCreator::MinPlaintextPacketSize(version);
+    if (data.length() < min_plaintext_size) {
+      size_t padding_length = min_plaintext_size - data.length();
+      frames.push_back(QuicFrame(QuicPaddingFrame(padding_length)));
+    }
   }
 
   std::unique_ptr<QuicPacket> packet(
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h
index ba5e1cd..94f25fc 100644
--- a/quic/test_tools/quic_test_utils.h
+++ b/quic/test_tools/quic_test_utils.h
@@ -82,12 +82,26 @@
     bool reset_flag,
     uint64_t packet_number,
     const std::string& data,
+    bool full_padding,
     QuicConnectionIdIncluded destination_connection_id_included,
     QuicConnectionIdIncluded source_connection_id_included,
     QuicPacketNumberLength packet_number_length,
     ParsedQuicVersionVector* versions,
     Perspective perspective);
 
+QuicEncryptedPacket* ConstructEncryptedPacket(
+    QuicConnectionId destination_connection_id,
+    QuicConnectionId source_connection_id,
+    bool version_flag,
+    bool reset_flag,
+    uint64_t packet_number,
+    const std::string& data,
+    bool full_padding,
+    QuicConnectionIdIncluded destination_connection_id_included,
+    QuicConnectionIdIncluded source_connection_id_included,
+    QuicPacketNumberLength packet_number_length,
+    ParsedQuicVersionVector* versions);
+
 // Create an encrypted packet for testing.
 // If versions == nullptr, uses &AllSupportedVersions().
 // Note that the packet is encrypted with NullEncrypter, so to decrypt the