No public description
PiperOrigin-RevId: 749080794
diff --git a/quiche/common/quiche_feature_flags_list.h b/quiche/common/quiche_feature_flags_list.h
index 9a01830..1ae5624 100755
--- a/quiche/common/quiche_feature_flags_list.h
+++ b/quiche/common/quiche_feature_flags_list.h
@@ -35,6 +35,7 @@
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_enable_version_rfcv2, false, false, "When true, support RFC9369.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_fin_before_completed_http_headers, false, true, "If true, close the connection with error if FIN is received before finish receiving the whole HTTP headers.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_fix_timeouts, true, true, "If true, postpone setting handshake timeout to infinite to handshake complete.")
+QUICHE_FLAG(bool, quiche_reloadable_flag_quic_heapless_static_parser, false, false, "If true, stops parsing immediately on unknown version, to avoid a potential malloc when parsing the connection ID")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_ignore_gquic_probing, true, true, "If true, QUIC server will not respond to gQUIC probing packet(PING + PADDING) but treat it as a regular packet.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_limit_new_streams_per_loop_2, true, true, "If true, when the peer sends connection options \\\'SLP1\\\', \\\'SLP2\\\' and \\\'SLPF\\\', internet facing GFEs will only allow a limited number of new requests to be processed per event loop, and postpone the rest to the following event loops. Also guard QuicConnection to iterate through all decrypters at each encryption level to get cipher id for a request.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_no_path_degrading_before_handshake_confirmed, true, true, "If true, an endpoint does not detect path degrading or blackholing until handshake gets confirmed.")
diff --git a/quiche/quic/core/http/end_to_end_test.cc b/quiche/quic/core/http/end_to_end_test.cc
index 0b4d55e..639276c 100644
--- a/quiche/quic/core/http/end_to_end_test.cc
+++ b/quiche/quic/core/http/end_to_end_test.cc
@@ -33,6 +33,7 @@
#include "quiche/quic/core/io/quic_event_loop.h"
#include "quiche/quic/core/qpack/value_splitting_header_list.h"
#include "quiche/quic/core/quic_connection.h"
+#include "quiche/quic/core/quic_connection_id.h"
#include "quiche/quic/core/quic_constants.h"
#include "quiche/quic/core/quic_data_writer.h"
#include "quiche/quic/core/quic_default_clock.h"
@@ -4519,7 +4520,7 @@
bool version_present, has_length_prefix;
QuicVersionLabel version_label;
ParsedQuicVersion parsed_version = ParsedQuicVersion::Unsupported();
- QuicConnectionId destination_connection_id, source_connection_id;
+ absl::string_view destination_connection_id, source_connection_id;
std::optional<absl::string_view> retry_token;
std::string detailed_error;
if (QuicFramer::ParsePublicHeaderDispatcher(
@@ -4543,7 +4544,8 @@
// Send a version negotiation packet.
std::unique_ptr<QuicEncryptedPacket> packet(
QuicFramer::BuildVersionNegotiationPacket(
- destination_connection_id, source_connection_id, /*ietf_quic=*/true,
+ QuicConnectionId(destination_connection_id),
+ QuicConnectionId(source_connection_id), /*ietf_quic=*/true,
has_length_prefix, supported_versions_));
QuicPacketWriterParams default_params;
server_writer_->WritePacket(
diff --git a/quiche/quic/core/quic_buffered_packet_store_test.cc b/quiche/quic/core/quic_buffered_packet_store_test.cc
index 392f79f..3244ab2 100644
--- a/quiche/quic/core/quic_buffered_packet_store_test.cc
+++ b/quiche/quic/core/quic_buffered_packet_store_test.cc
@@ -165,15 +165,19 @@
packet_info(self_address, peer_address, *packet) {
std::string detailed_error;
MockConnectionIdGenerator unused_generator;
+ absl::string_view destination_connection_id, source_connection_id;
if (QuicFramer::ParsePublicHeaderDispatcherShortHeaderLengthUnknown(
*packet, &packet_info.form, &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, &packet_info.retry_token,
- &detailed_error, unused_generator) != QUIC_NO_ERROR) {
+ &destination_connection_id, &source_connection_id,
+ &packet_info.retry_token, &detailed_error,
+ unused_generator) != QUIC_NO_ERROR) {
ADD_FAILURE() << "Failed to parse packet header: " << detailed_error;
}
+ packet_info.destination_connection_id =
+ QuicConnectionId(destination_connection_id);
+ packet_info.source_connection_id = QuicConnectionId(source_connection_id);
}
const QuicSocketAddress self_address;
@@ -223,15 +227,19 @@
ReceivedPacketInfo packet_info(self_address_, peer_address_,
received_client_initial);
std::string detailed_error;
+ absl::string_view destination_connection_id, source_connection_id;
ASSERT_EQ(QuicFramer::ParsePublicHeaderDispatcherShortHeaderLengthUnknown(
received_client_initial, &packet_info.form,
&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, &packet_info.retry_token,
+ &packet_info.version, &destination_connection_id,
+ &source_connection_id, &packet_info.retry_token,
&detailed_error, connection_id_generator_),
QUIC_NO_ERROR)
<< detailed_error;
+ packet_info.destination_connection_id =
+ QuicConnectionId(destination_connection_id);
+ packet_info.source_connection_id = QuicConnectionId(source_connection_id);
store_.EnqueuePacket(packet_info, kNoParsedChlo, connection_id_generator_);
const BufferedPacketList* buffered_list = store_.GetPacketList(kDCID);
@@ -774,8 +782,8 @@
bool unused_use_length_prefix;
QuicVersionLabel unused_version_label;
ParsedQuicVersion unused_parsed_version = UnsupportedQuicVersion();
- QuicConnectionId unused_destination_connection_id;
- QuicConnectionId unused_source_connection_id;
+ absl::string_view unused_destination_connection_id;
+ absl::string_view unused_source_connection_id;
std::optional<absl::string_view> unused_retry_token;
std::string unused_detailed_error;
QuicErrorCode error_code = QuicFramer::ParsePublicHeaderDispatcher(
@@ -793,8 +801,8 @@
bool unused_use_length_prefix;
QuicVersionLabel unused_version_label;
ParsedQuicVersion unused_parsed_version = UnsupportedQuicVersion();
- QuicConnectionId unused_destination_connection_id;
- QuicConnectionId unused_source_connection_id;
+ absl::string_view unused_destination_connection_id;
+ absl::string_view unused_source_connection_id;
std::optional<absl::string_view> unused_retry_token;
std::string unused_detailed_error;
QuicErrorCode error_code = QUIC_NO_ERROR;
@@ -877,15 +885,19 @@
ReceivedPacketInfo packet_info(self_address_, peer_address_,
received_client_initial);
std::string detailed_error;
+ absl::string_view destination_connection_id, source_connection_id;
ASSERT_EQ(QuicFramer::ParsePublicHeaderDispatcherShortHeaderLengthUnknown(
received_client_initial, &packet_info.form,
&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, &packet_info.retry_token,
+ &packet_info.version, &destination_connection_id,
+ &source_connection_id, &packet_info.retry_token,
&detailed_error, connection_id_generator_),
QUIC_NO_ERROR)
<< detailed_error;
+ packet_info.destination_connection_id =
+ QuicConnectionId(destination_connection_id);
+ packet_info.source_connection_id = QuicConnectionId(source_connection_id);
store_.EnqueuePacket(packet_info, kNoParsedChlo, connection_id_generator_);
ASSERT_EQ(client_received_packets_.size(), 1u);
diff --git a/quiche/quic/core/quic_connection_id.cc b/quiche/quic/core/quic_connection_id.cc
index dce13b2..5ef1dac 100644
--- a/quiche/quic/core/quic_connection_id.cc
+++ b/quiche/quic/core/quic_connection_id.cc
@@ -6,14 +6,12 @@
#include <cstddef>
#include <cstdint>
-#include <cstdlib>
#include <cstring>
#include <ostream>
#include <string>
#include "absl/strings/escaping.h"
#include "absl/strings/string_view.h"
-#include "absl/types/span.h"
#include "openssl/siphash.h"
#include "quiche/quic/core/crypto/quic_random.h"
#include "quiche/common/platform/api/quiche_logging.h"
@@ -73,6 +71,9 @@
: QuicConnectionId(reinterpret_cast<const char*>(data.data()),
data.length()) {}
+QuicConnectionId::QuicConnectionId(absl::string_view data)
+ : QuicConnectionId(data.data(), data.length()) {}
+
QuicConnectionId::~QuicConnectionId() {
if (length_ > sizeof(data_short_)) {
free(data_long_);
diff --git a/quiche/quic/core/quic_connection_id.h b/quiche/quic/core/quic_connection_id.h
index 84702db..c479816 100644
--- a/quiche/quic/core/quic_connection_id.h
+++ b/quiche/quic/core/quic_connection_id.h
@@ -5,11 +5,10 @@
#ifndef QUICHE_QUIC_CORE_QUIC_CONNECTION_ID_H_
#define QUICHE_QUIC_CORE_QUIC_CONNECTION_ID_H_
-#include <cstddef>
#include <cstdint>
-#include <ostream>
#include <string>
+#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "quiche/common/platform/api/quiche_export.h"
@@ -46,6 +45,7 @@
// Creates a connection ID from network order bytes.
QuicConnectionId(const char* data, uint8_t length);
QuicConnectionId(absl::Span<const uint8_t> data);
+ QuicConnectionId(absl::string_view data);
// Creates a connection ID from another connection ID.
QuicConnectionId(const QuicConnectionId& other);
@@ -89,8 +89,12 @@
}
// Generates an ASCII string that represents
- // the contents of the connection ID, or "0" if it is empty.
+ // the contents of the connection ID in hex, or "0" if it is empty.
std::string ToString() const;
+ // ToStringView() is not in hex. Returns "" if empty.
+ absl::string_view ToStringView() const {
+ return absl::string_view(data(), length());
+ }
// operator<< allows easily logging connection IDs.
friend QUICHE_EXPORT std::ostream& operator<<(std::ostream& os,
diff --git a/quiche/quic/core/quic_connection_id_test.cc b/quiche/quic/core/quic_connection_id_test.cc
index 9d663a4..aa7704d 100644
--- a/quiche/quic/core/quic_connection_id_test.cc
+++ b/quiche/quic/core/quic_connection_id_test.cc
@@ -85,6 +85,19 @@
EXPECT_EQ(connection_id2.length(), 16);
}
+TEST_F(QuicConnectionIdTest, StringData) {
+ QuicConnectionId connection_id = QuicConnectionId("foobar");
+ EXPECT_EQ(connection_id.length(), 6);
+ EXPECT_EQ(connection_id.ToString(), "666f6f626172");
+ EXPECT_EQ(connection_id.ToStringView(), "foobar");
+ absl::string_view null_sv(nullptr, 0);
+ QuicConnectionId null_connection_id(null_sv);
+ EXPECT_EQ(null_connection_id.length(), 0);
+ absl::string_view empty_sv = "";
+ QuicConnectionId empty_connection_id(empty_sv);
+ EXPECT_EQ(empty_connection_id.length(), 0);
+}
+
TEST_F(QuicConnectionIdTest, DoubleConvert) {
QuicConnectionId connection_id64_1 = test::TestConnectionId(1);
QuicConnectionId connection_id64_2 = test::TestConnectionId(42);
diff --git a/quiche/quic/core/quic_dispatcher.cc b/quiche/quic/core/quic_dispatcher.cc
index 64063f3..763e630 100644
--- a/quiche/quic/core/quic_dispatcher.cc
+++ b/quiche/quic/core/quic_dispatcher.cc
@@ -280,13 +280,27 @@
ReceivedPacketInfo packet_info(self_address, peer_address, packet);
std::string detailed_error;
QuicErrorCode error;
- error = QuicFramer::ParsePublicHeaderDispatcherShortHeaderLengthUnknown(
- packet, &packet_info.form, &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,
- &packet_info.retry_token, &detailed_error, connection_id_generator_);
-
+ if (GetQuicReloadableFlag(quic_heapless_static_parser)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_heapless_static_parser, 2, 3);
+ absl::string_view destination_connection_id, source_connection_id;
+ error = QuicFramer::ParsePublicHeaderDispatcherShortHeaderLengthUnknown(
+ packet, &packet_info.form, &packet_info.long_packet_type,
+ &packet_info.version_flag, &packet_info.use_length_prefix,
+ &packet_info.version_label, &packet_info.version,
+ &destination_connection_id, &source_connection_id,
+ &packet_info.retry_token, &detailed_error, connection_id_generator_);
+ packet_info.destination_connection_id =
+ QuicConnectionId(destination_connection_id);
+ packet_info.source_connection_id = QuicConnectionId(source_connection_id);
+ } else {
+ error = QuicFramer::ParsePublicHeaderDispatcherShortHeaderLengthUnknown(
+ packet, &packet_info.form, &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, &packet_info.retry_token,
+ &detailed_error, connection_id_generator_);
+ }
if (error != QUIC_NO_ERROR) {
// Packet has framing error.
SetLastError(error);
@@ -349,14 +363,33 @@
IsSupportedVersion(ParsedQuicVersion::Q046())) {
ReceivedPacketInfo gquic_packet_info(self_address, peer_address, packet);
// Try again without asking |connection_id_generator_| for the length.
- const QuicErrorCode gquic_error = QuicFramer::ParsePublicHeaderDispatcher(
- packet, expected_server_connection_id_length_, &gquic_packet_info.form,
- &gquic_packet_info.long_packet_type, &gquic_packet_info.version_flag,
- &gquic_packet_info.use_length_prefix, &gquic_packet_info.version_label,
- &gquic_packet_info.version,
- &gquic_packet_info.destination_connection_id,
- &gquic_packet_info.source_connection_id, &gquic_packet_info.retry_token,
- &detailed_error);
+ QuicErrorCode gquic_error;
+ if (GetQuicReloadableFlag(quic_heapless_static_parser)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_heapless_static_parser, 3, 3);
+ absl::string_view destination_connection_id, source_connection_id;
+ gquic_error = QuicFramer::ParsePublicHeaderDispatcher(
+ packet, expected_server_connection_id_length_,
+ &gquic_packet_info.form, &gquic_packet_info.long_packet_type,
+ &gquic_packet_info.version_flag, &gquic_packet_info.use_length_prefix,
+ &gquic_packet_info.version_label, &gquic_packet_info.version,
+ &destination_connection_id, &source_connection_id,
+ &gquic_packet_info.retry_token, &detailed_error);
+ if (gquic_error == QUIC_NO_ERROR) {
+ gquic_packet_info.destination_connection_id =
+ QuicConnectionId(destination_connection_id);
+ gquic_packet_info.source_connection_id =
+ QuicConnectionId(source_connection_id);
+ }
+ } else {
+ gquic_error = QuicFramer::ParsePublicHeaderDispatcher(
+ packet, expected_server_connection_id_length_,
+ &gquic_packet_info.form, &gquic_packet_info.long_packet_type,
+ &gquic_packet_info.version_flag, &gquic_packet_info.use_length_prefix,
+ &gquic_packet_info.version_label, &gquic_packet_info.version,
+ &gquic_packet_info.destination_connection_id,
+ &gquic_packet_info.source_connection_id,
+ &gquic_packet_info.retry_token, &detailed_error);
+ }
if (gquic_error == QUIC_NO_ERROR) {
if (MaybeDispatchPacket(gquic_packet_info)) {
return;
diff --git a/quiche/quic/core/quic_framer.cc b/quiche/quic/core/quic_framer.cc
index a727cad..a506624 100644
--- a/quiche/quic/core/quic_framer.cc
+++ b/quiche/quic/core/quic_framer.cc
@@ -385,6 +385,13 @@
QuicDataWriter::GetVarInt62Len(ack_frame.ecn_counters->ce));
}
+// A version of QuicFramer::set_detailed_error() for static methods.
+void set_detailed_error_static(std::string* out, const char* detailed_error) {
+ if (out != nullptr) {
+ *out = detailed_error;
+ }
+}
+
} // namespace
QuicFramer::QuicFramer(const ParsedQuicVersionVector& supported_versions,
@@ -2404,16 +2411,37 @@
QuicVersionLabel version_label;
bool has_length_prefix;
std::string detailed_error;
- QuicErrorCode parse_result = QuicFramer::ParsePublicHeader(
- reader, expected_destination_connection_id_length, /*ietf_format=*/true,
- &header->type_byte, &header->form, &header->version_flag,
- &has_length_prefix, &version_label, &header->version,
- &header->destination_connection_id, &header->source_connection_id,
- &header->long_packet_type, &header->retry_token_length_length,
- &header->retry_token, &detailed_error);
- if (parse_result != QUIC_NO_ERROR) {
- set_detailed_error(detailed_error);
- return false;
+ if (GetQuicReloadableFlag(quic_heapless_static_parser)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_heapless_static_parser, 1, 3);
+ absl::string_view destination_connection_id;
+ absl::string_view source_connection_id;
+ QuicErrorCode parse_result = QuicFramer::ParsePublicHeader(
+ reader, expected_destination_connection_id_length,
+ /*ietf_format=*/true, &header->type_byte, &header->form,
+ &header->version_flag, &has_length_prefix, &version_label,
+ &header->version, &destination_connection_id, &source_connection_id,
+ &header->long_packet_type, &header->retry_token_length_length,
+ &header->retry_token, &detailed_error);
+ if (parse_result != QUIC_NO_ERROR) {
+ set_detailed_error(detailed_error);
+ return false;
+ }
+ header->destination_connection_id =
+ QuicConnectionId(destination_connection_id);
+ header->source_connection_id = QuicConnectionId(source_connection_id);
+ } else {
+ QuicErrorCode parse_result = QuicFramer::ParsePublicHeader(
+ reader, expected_destination_connection_id_length,
+ /*ietf_format=*/true, &header->type_byte, &header->form,
+ &header->version_flag, &has_length_prefix, &version_label,
+ &header->version, &header->destination_connection_id,
+ &header->source_connection_id, &header->long_packet_type,
+ &header->retry_token_length_length, &header->retry_token,
+ &detailed_error);
+ if (parse_result != QUIC_NO_ERROR) {
+ set_detailed_error(detailed_error);
+ return false;
+ }
}
header->destination_connection_id_included = CONNECTION_ID_PRESENT;
header->source_connection_id_included =
@@ -6426,6 +6454,57 @@
PacketHeaderFormat* format, QuicLongHeaderType* long_packet_type,
bool* version_present, bool* has_length_prefix,
QuicVersionLabel* version_label, ParsedQuicVersion* parsed_version,
+ absl::string_view* destination_connection_id,
+ absl::string_view* source_connection_id,
+ std::optional<absl::string_view>* retry_token,
+ std::string* detailed_error) {
+ QuicDataReader reader(packet.data(), packet.length());
+ if (reader.IsDoneReading()) {
+ set_detailed_error_static(detailed_error, "Unable to read first byte.");
+ return QUIC_INVALID_PACKET_HEADER;
+ }
+ const uint8_t first_byte = reader.PeekByte();
+ if ((first_byte & FLAGS_LONG_HEADER) == 0 &&
+ (first_byte & FLAGS_FIXED_BIT) == 0 &&
+ (first_byte & FLAGS_DEMULTIPLEXING_BIT) == 0) {
+ // All versions of Google QUIC up to and including Q043 set
+ // FLAGS_DEMULTIPLEXING_BIT to one on all client-to-server packets. Q044
+ // and Q045 were never default-enabled in production. All subsequent
+ // versions of Google QUIC (starting with Q046) require FLAGS_FIXED_BIT to
+ // be set to one on all packets. All versions of IETF QUIC (since
+ // draft-ietf-quic-transport-17 which was earlier than the first IETF QUIC
+ // version that was deployed in production by any implementation) also
+ // require FLAGS_FIXED_BIT to be set to one on all packets. If a packet
+ // has the FLAGS_LONG_HEADER bit set to one, it could be a first flight
+ // from an unknown future version that allows the other two bits to be set
+ // to zero. Based on this, packets that have all three of those bits set
+ // to zero are known to be invalid.
+ set_detailed_error_static(detailed_error, "Invalid flags.");
+ return QUIC_INVALID_PACKET_HEADER;
+ }
+ const bool ietf_format = QuicUtils::IsIetfPacketHeader(first_byte);
+ uint8_t unused_first_byte;
+ quiche::QuicheVariableLengthIntegerLength retry_token_length_length;
+ absl::string_view maybe_retry_token;
+ 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, long_packet_type, &retry_token_length_length,
+ &maybe_retry_token, detailed_error);
+ if (retry_token_length_length != quiche::VARIABLE_LENGTH_INTEGER_LENGTH_0) {
+ *retry_token = maybe_retry_token;
+ } else {
+ retry_token->reset();
+ }
+ return error_code;
+}
+QuicErrorCode QuicFramer::ParsePublicHeaderDispatcher(
+ 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, ParsedQuicVersion* parsed_version,
QuicConnectionId* destination_connection_id,
QuicConnectionId* source_connection_id,
std::optional<absl::string_view>* retry_token,
@@ -6478,6 +6557,35 @@
QuicLongHeaderType* long_packet_type, bool* version_present,
bool* has_length_prefix, QuicVersionLabel* version_label,
ParsedQuicVersion* parsed_version,
+ absl::string_view* destination_connection_id,
+ absl::string_view* source_connection_id,
+ std::optional<absl::string_view>* retry_token, std::string* detailed_error,
+ ConnectionIdGeneratorInterface& generator) {
+ QuicDataReader reader(packet.data(), packet.length());
+ // Get the first two bytes.
+ if (reader.BytesRemaining() < 2) {
+ set_detailed_error_static(detailed_error,
+ "Unable to read first two bytes.");
+ return QUIC_INVALID_PACKET_HEADER;
+ }
+ uint8_t two_bytes[2];
+ reader.ReadBytes(two_bytes, 2);
+ uint8_t expected_destination_connection_id_length =
+ (!QuicUtils::IsIetfPacketHeader(two_bytes[0]) ||
+ two_bytes[0] & FLAGS_LONG_HEADER)
+ ? 0
+ : generator.ConnectionIdLength(two_bytes[1]);
+ return ParsePublicHeaderDispatcher(
+ packet, expected_destination_connection_id_length, format,
+ long_packet_type, version_present, has_length_prefix, version_label,
+ parsed_version, destination_connection_id, source_connection_id,
+ retry_token, detailed_error);
+}
+QuicErrorCode QuicFramer::ParsePublicHeaderDispatcherShortHeaderLengthUnknown(
+ const QuicEncryptedPacket& packet, PacketHeaderFormat* format,
+ QuicLongHeaderType* long_packet_type, bool* version_present,
+ bool* has_length_prefix, QuicVersionLabel* version_label,
+ ParsedQuicVersion* parsed_version,
QuicConnectionId* destination_connection_id,
QuicConnectionId* source_connection_id,
std::optional<absl::string_view>* retry_token, std::string* detailed_error,
@@ -6614,6 +6722,32 @@
QuicDataReader* reader, uint8_t* first_byte, PacketHeaderFormat* format,
bool* version_present, QuicVersionLabel* version_label,
ParsedQuicVersion* parsed_version,
+ absl::string_view* destination_connection_id, std::string* detailed_error) {
+ *format = GOOGLE_QUIC_PACKET;
+ *version_present = (*first_byte & PACKET_PUBLIC_FLAGS_VERSION) != 0;
+ uint8_t destination_connection_id_length = 0;
+ if ((*first_byte & PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID) != 0) {
+ destination_connection_id_length = kQuicDefaultConnectionIdLength;
+ }
+ if (!reader->ReadStringPiece(destination_connection_id,
+ destination_connection_id_length)) {
+ set_detailed_error_static(detailed_error, "Unable to read ConnectionId.");
+ return QUIC_INVALID_PACKET_HEADER;
+ }
+ if (*version_present) {
+ if (!ProcessVersionLabel(reader, version_label)) {
+ set_detailed_error_static(detailed_error,
+ "Unable to read protocol version.");
+ return QUIC_INVALID_PACKET_HEADER;
+ }
+ *parsed_version = ParseQuicVersionLabel(*version_label);
+ }
+ return QUIC_NO_ERROR;
+}
+QuicErrorCode QuicFramer::ParsePublicHeaderGoogleQuic(
+ QuicDataReader* reader, uint8_t* first_byte, PacketHeaderFormat* format,
+ bool* version_present, QuicVersionLabel* version_label,
+ ParsedQuicVersion* parsed_version,
QuicConnectionId* destination_connection_id, std::string* detailed_error) {
*format = GOOGLE_QUIC_PACKET;
*version_present = (*first_byte & PACKET_PUBLIC_FLAGS_VERSION) != 0;
@@ -6677,6 +6811,68 @@
inline bool ParseLongHeaderConnectionIds(
QuicDataReader& reader, bool has_length_prefix,
+ QuicVersionLabel version_label,
+ absl::string_view* destination_connection_id,
+ absl::string_view* source_connection_id, std::string* detailed_error) {
+ if (has_length_prefix) {
+ if (!reader.ReadStringPiece8(destination_connection_id)) {
+ set_detailed_error_static(detailed_error,
+ "Unable to read destination connection ID.");
+ return false;
+ }
+ if (!reader.ReadStringPiece8(source_connection_id)) {
+ if (version_label == kProxVersionLabel) {
+ // The "PROX" version does not follow the length-prefixed invariants,
+ // and can therefore attempt to read a payload byte and interpret it
+ // as the source connection ID length, which could fail to parse.
+ // In that scenario we keep the source connection ID empty but mark
+ // parsing as successful.
+ return true;
+ }
+ set_detailed_error_static(detailed_error,
+ "Unable to read source connection ID.");
+ return false;
+ }
+ } else {
+ // Parse connection ID lengths.
+ uint8_t connection_id_lengths_byte;
+ if (!reader.ReadUInt8(&connection_id_lengths_byte)) {
+ set_detailed_error_static(detailed_error,
+ "Unable to read connection ID lengths.");
+ return false;
+ }
+ uint8_t destination_connection_id_length =
+ (connection_id_lengths_byte & kDestinationConnectionIdLengthMask) >> 4;
+ if (destination_connection_id_length != 0) {
+ destination_connection_id_length += kConnectionIdLengthAdjustment;
+ }
+ uint8_t source_connection_id_length =
+ connection_id_lengths_byte & kSourceConnectionIdLengthMask;
+ if (source_connection_id_length != 0) {
+ source_connection_id_length += kConnectionIdLengthAdjustment;
+ }
+
+ // Read destination connection ID.
+ if (!reader.ReadStringPiece(destination_connection_id,
+ destination_connection_id_length)) {
+ set_detailed_error_static(detailed_error,
+ "Unable to read destination connection ID.");
+ return false;
+ }
+
+ // Read source connection ID.
+ if (!reader.ReadStringPiece(source_connection_id,
+ source_connection_id_length)) {
+ set_detailed_error_static(detailed_error,
+ "Unable to read source connection ID.");
+ return false;
+ }
+ }
+ return true;
+}
+// Deprecated version that uses QuicConnectionId instead of string_view.
+inline bool ParseLongHeaderConnectionIds(
+ QuicDataReader& reader, bool has_length_prefix,
QuicVersionLabel version_label, QuicConnectionId& destination_connection_id,
QuicConnectionId& source_connection_id, std::string& detailed_error) {
if (has_length_prefix) {
@@ -6739,6 +6935,114 @@
bool ietf_format, uint8_t* first_byte, PacketHeaderFormat* format,
bool* version_present, bool* has_length_prefix,
QuicVersionLabel* version_label, ParsedQuicVersion* parsed_version,
+ absl::string_view* destination_connection_id,
+ absl::string_view* source_connection_id,
+ QuicLongHeaderType* long_packet_type,
+ quiche::QuicheVariableLengthIntegerLength* retry_token_length_length,
+ absl::string_view* retry_token, std::string* detailed_error) {
+ *version_present = false;
+ *has_length_prefix = false;
+ *version_label = 0;
+ *parsed_version = UnsupportedQuicVersion();
+ *source_connection_id = absl::string_view();
+ *long_packet_type = INVALID_PACKET_TYPE;
+ *retry_token_length_length = quiche::VARIABLE_LENGTH_INTEGER_LENGTH_0;
+ *retry_token = absl::string_view();
+ set_detailed_error_static(detailed_error, "");
+
+ if (!reader->ReadUInt8(first_byte)) {
+ set_detailed_error_static(detailed_error, "Unable to read first byte.");
+ return QUIC_INVALID_PACKET_HEADER;
+ }
+
+ if (!ietf_format) {
+ return ParsePublicHeaderGoogleQuic(
+ reader, first_byte, format, version_present, version_label,
+ parsed_version, destination_connection_id, detailed_error);
+ }
+
+ *format = GetIetfPacketHeaderFormat(*first_byte);
+
+ if (*format == IETF_QUIC_SHORT_HEADER_PACKET) {
+ if (!reader->ReadStringPiece(destination_connection_id,
+ expected_destination_connection_id_length)) {
+ set_detailed_error_static(detailed_error,
+ "Unable to read destination connection ID.");
+ return QUIC_INVALID_PACKET_HEADER;
+ }
+ return QUIC_NO_ERROR;
+ }
+
+ QUICHE_DCHECK_EQ(IETF_QUIC_LONG_HEADER_PACKET, *format);
+ *version_present = true;
+ if (!ProcessVersionLabel(reader, version_label)) {
+ set_detailed_error_static(detailed_error,
+ "Unable to read protocol version.");
+ return QUIC_INVALID_PACKET_HEADER;
+ }
+
+ if (*version_label == 0) {
+ *long_packet_type = VERSION_NEGOTIATION;
+ }
+
+ // Parse version.
+ *parsed_version = ParseQuicVersionLabel(*version_label);
+
+ // Figure out which IETF QUIC invariants this packet follows.
+ *has_length_prefix = PacketHasLengthPrefixedConnectionIds(
+ *reader, *parsed_version, *version_label, *first_byte);
+
+ // Parse connection IDs.
+ if (!ParseLongHeaderConnectionIds(*reader, *has_length_prefix, *version_label,
+ destination_connection_id,
+ source_connection_id, detailed_error)) {
+ return QUIC_INVALID_PACKET_HEADER;
+ }
+
+ if (!parsed_version->IsKnown()) {
+ // Skip parsing of long packet type and retry token for unknown versions.
+ return QUIC_NO_ERROR;
+ }
+
+ // Parse long packet type.
+ *long_packet_type = GetLongHeaderType(*first_byte, *parsed_version);
+
+ switch (*long_packet_type) {
+ case INVALID_PACKET_TYPE:
+ set_detailed_error_static(detailed_error,
+ "Unable to parse long packet type.");
+ return QUIC_INVALID_PACKET_HEADER;
+ case INITIAL:
+ if (!parsed_version->SupportsRetry()) {
+ // Retry token is only present on initial packets for some versions.
+ return QUIC_NO_ERROR;
+ }
+ break;
+ default:
+ return QUIC_NO_ERROR;
+ }
+
+ *retry_token_length_length = reader->PeekVarInt62Length();
+ uint64_t retry_token_length;
+ if (!reader->ReadVarInt62(&retry_token_length)) {
+ *retry_token_length_length = quiche::VARIABLE_LENGTH_INTEGER_LENGTH_0;
+ set_detailed_error_static(detailed_error,
+ "Unable to read retry token length.");
+ return QUIC_INVALID_PACKET_HEADER;
+ }
+
+ if (!reader->ReadStringPiece(retry_token, retry_token_length)) {
+ set_detailed_error_static(detailed_error, "Unable to read retry token.");
+ return QUIC_INVALID_PACKET_HEADER;
+ }
+
+ return QUIC_NO_ERROR;
+}
+QuicErrorCode QuicFramer::ParsePublicHeader(
+ QuicDataReader* reader, uint8_t expected_destination_connection_id_length,
+ bool ietf_format, uint8_t* first_byte, PacketHeaderFormat* format,
+ bool* version_present, bool* has_length_prefix,
+ QuicVersionLabel* version_label, ParsedQuicVersion* parsed_version,
QuicConnectionId* destination_connection_id,
QuicConnectionId* source_connection_id,
QuicLongHeaderType* long_packet_type,
diff --git a/quiche/quic/core/quic_framer.h b/quiche/quic/core/quic_framer.h
index a941e02..4b925aa 100644
--- a/quiche/quic/core/quic_framer.h
+++ b/quiche/quic/core/quic_framer.h
@@ -8,6 +8,7 @@
#include <cstddef>
#include <cstdint>
#include <memory>
+#include <optional>
#include <string>
#include "absl/strings/string_view.h"
@@ -18,8 +19,10 @@
#include "quiche/quic/core/frames/quic_immediate_ack_frame.h"
#include "quiche/quic/core/frames/quic_reset_stream_at_frame.h"
#include "quiche/quic/core/quic_connection_id.h"
+#include "quiche/quic/core/quic_error_codes.h"
#include "quiche/quic/core/quic_packets.h"
#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/core/quic_versions.h"
namespace quic {
@@ -449,6 +452,18 @@
// ConnectionIdGeneartor interface, and callers need an accurate
// Destination Connection ID for short header packets, call
// ParsePublicHeaderDispatcherShortHeaderLengthUnknown() instead.
+ // |detailed_error| can be nullptr.
+ static QuicErrorCode ParsePublicHeader(
+ QuicDataReader* reader, uint8_t expected_destination_connection_id_length,
+ bool ietf_format, uint8_t* first_byte, PacketHeaderFormat* format,
+ bool* version_present, bool* has_length_prefix,
+ QuicVersionLabel* version_label, ParsedQuicVersion* parsed_version,
+ absl::string_view* destination_connection_id,
+ absl::string_view* source_connection_id,
+ QuicLongHeaderType* long_packet_type,
+ quiche::QuicheVariableLengthIntegerLength* retry_token_length_length,
+ absl::string_view* retry_token, std::string* detailed_error);
+ // Deprecated version that uses QuicConnectionId instead of string_view.
static QuicErrorCode ParsePublicHeader(
QuicDataReader* reader, uint8_t expected_destination_connection_id_length,
bool ietf_format, uint8_t* first_byte, PacketHeaderFormat* format,
@@ -466,6 +481,18 @@
// for short headers. When callers need an accurate Destination Connection ID
// specifically for short header packets, call
// ParsePublicHeaderDispatcherShortHeaderLengthUnknown() instead.
+ // |detailed_error| can be nullptr.
+ static QuicErrorCode ParsePublicHeaderDispatcher(
+ 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, ParsedQuicVersion* parsed_version,
+ absl::string_view* destination_connection_id,
+ absl::string_view* source_connection_id,
+ std::optional<absl::string_view>* retry_token,
+ std::string* detailed_error);
+ // Deprecated version that uses QuicConnectionId instead of string_view.
static QuicErrorCode ParsePublicHeaderDispatcher(
const QuicEncryptedPacket& packet,
uint8_t expected_destination_connection_id_length,
@@ -490,6 +517,16 @@
QuicLongHeaderType* long_packet_type, bool* version_present,
bool* has_length_prefix, QuicVersionLabel* version_label,
ParsedQuicVersion* parsed_version,
+ absl::string_view* destination_connection_id,
+ absl::string_view* source_connection_id,
+ std::optional<absl::string_view>* retry_token,
+ std::string* detailed_error, ConnectionIdGeneratorInterface& generator);
+ // Deprecated version that uses QuicConnectionId instead of string_view.
+ static QuicErrorCode ParsePublicHeaderDispatcherShortHeaderLengthUnknown(
+ const QuicEncryptedPacket& packet, PacketHeaderFormat* format,
+ QuicLongHeaderType* long_packet_type, bool* version_present,
+ bool* has_length_prefix, QuicVersionLabel* version_label,
+ ParsedQuicVersion* parsed_version,
QuicConnectionId* destination_connection_id,
QuicConnectionId* source_connection_id,
std::optional<absl::string_view>* retry_token,
@@ -954,6 +991,14 @@
static AckFrameInfo GetAckFrameInfo(const QuicAckFrame& frame);
+ // |detailed_error| can be nullptr.
+ static QuicErrorCode ParsePublicHeaderGoogleQuic(
+ QuicDataReader* reader, uint8_t* first_byte, PacketHeaderFormat* format,
+ bool* version_present, QuicVersionLabel* version_label,
+ ParsedQuicVersion* parsed_version,
+ absl::string_view* destination_connection_id,
+ std::string* detailed_error);
+ // Deprecated version that uses QuicConnectionId instead of string_view.
static QuicErrorCode ParsePublicHeaderGoogleQuic(
QuicDataReader* reader, uint8_t* first_byte, PacketHeaderFormat* format,
bool* version_present, QuicVersionLabel* version_label,
diff --git a/quiche/quic/core/quic_framer_test.cc b/quiche/quic/core/quic_framer_test.cc
index 44fd4f9..cced010 100644
--- a/quiche/quic/core/quic_framer_test.cc
+++ b/quiche/quic/core/quic_framer_test.cc
@@ -4,11 +4,9 @@
#include "quiche/quic/core/quic_framer.h"
-#include <algorithm>
#include <cstdint>
#include <cstring>
#include <limits>
-#include <map>
#include <memory>
#include <optional>
#include <string>
@@ -21,11 +19,11 @@
#include "absl/strings/match.h"
#include "absl/strings/string_view.h"
#include "quiche/quic/core/crypto/null_decrypter.h"
-#include "quiche/quic/core/crypto/null_encrypter.h"
#include "quiche/quic/core/crypto/quic_decrypter.h"
#include "quiche/quic/core/crypto/quic_encrypter.h"
#include "quiche/quic/core/frames/quic_reset_stream_at_frame.h"
#include "quiche/quic/core/quic_connection_id.h"
+#include "quiche/quic/core/quic_constants.h"
#include "quiche/quic/core/quic_error_codes.h"
#include "quiche/quic/core/quic_packets.h"
#include "quiche/quic/core/quic_types.h"
@@ -34,7 +32,6 @@
#include "quiche/quic/platform/api/quic_expect_bug.h"
#include "quiche/quic/platform/api/quic_flags.h"
#include "quiche/quic/platform/api/quic_ip_address.h"
-#include "quiche/quic/platform/api/quic_ip_address_family.h"
#include "quiche/quic/platform/api/quic_logging.h"
#include "quiche/quic/platform/api/quic_test.h"
#include "quiche/quic/test_tools/quic_framer_peer.h"
@@ -958,6 +955,9 @@
GetQuicVersionByte(0), GetQuicVersionByte(1), GetQuicVersionByte(2), \
GetQuicVersionByte(3)
+#define QUIC_V1_BYTES 0x00, 0x00, 0x00, 0x01
+#define QUIC_V2_BYTES 0x6b, 0x33, 0x43, 0xcf
+
// Run all framer tests with all supported versions of QUIC.
INSTANTIATE_TEST_SUITE_P(QuicFramerTests, QuicFramerTest,
::testing::ValuesIn(AllSupportedVersions()),
@@ -1154,7 +1154,7 @@
PacketHeaderFormat format;
QuicLongHeaderType long_packet_type = INVALID_PACKET_TYPE;
bool version_flag;
- QuicConnectionId destination_connection_id, source_connection_id;
+ absl::string_view destination_connection_id, source_connection_id;
QuicVersionLabel version_label;
std::string detailed_error;
bool use_length_prefix;
@@ -1172,8 +1172,8 @@
EXPECT_EQ(IETF_QUIC_LONG_HEADER_PACKET, format);
EXPECT_TRUE(version_flag);
EXPECT_EQ(kQuicDefaultConnectionIdLength, destination_connection_id.length());
- EXPECT_EQ(FramerTestConnectionId(), destination_connection_id);
- EXPECT_EQ(EmptyQuicConnectionId(), source_connection_id);
+ EXPECT_EQ(FramerTestConnectionId().ToStringView(), destination_connection_id);
+ EXPECT_TRUE(source_connection_id.empty());
}
TEST_P(QuicFramerTest, LongPacketHeaderWithBothConnectionIds) {
@@ -1231,7 +1231,7 @@
PacketHeaderFormat format = GOOGLE_QUIC_PACKET;
QuicLongHeaderType long_packet_type = INVALID_PACKET_TYPE;
bool version_flag = false;
- QuicConnectionId destination_connection_id, source_connection_id;
+ absl::string_view destination_connection_id, source_connection_id;
QuicVersionLabel version_label = 0;
std::string detailed_error = "";
bool use_length_prefix;
@@ -1249,8 +1249,9 @@
EXPECT_EQ("", detailed_error);
EXPECT_EQ(IETF_QUIC_LONG_HEADER_PACKET, format);
EXPECT_TRUE(version_flag);
- EXPECT_EQ(FramerTestConnectionId(), destination_connection_id);
- EXPECT_EQ(FramerTestConnectionIdPlusOne(), source_connection_id);
+ EXPECT_EQ(FramerTestConnectionId().ToStringView(), destination_connection_id);
+ EXPECT_EQ(FramerTestConnectionIdPlusOne().ToStringView(),
+ source_connection_id);
}
TEST_P(QuicFramerTest, AllZeroPacketParsingFails) {
@@ -1259,7 +1260,7 @@
PacketHeaderFormat format = GOOGLE_QUIC_PACKET;
QuicLongHeaderType long_packet_type = INVALID_PACKET_TYPE;
bool version_flag = false;
- QuicConnectionId destination_connection_id, source_connection_id;
+ absl::string_view destination_connection_id, source_connection_id;
QuicVersionLabel version_label = 0;
std::string detailed_error = "";
bool use_length_prefix;
@@ -1326,8 +1327,7 @@
bool version_present = false, has_length_prefix = false;
QuicVersionLabel version_label = 0;
ParsedQuicVersion parsed_version = UnsupportedQuicVersion();
- QuicConnectionId destination_connection_id = EmptyQuicConnectionId(),
- source_connection_id = EmptyQuicConnectionId();
+ absl::string_view destination_connection_id, source_connection_id;
QuicLongHeaderType long_packet_type = INVALID_PACKET_TYPE;
quiche::QuicheVariableLengthIntegerLength retry_token_length_length =
quiche::VARIABLE_LENGTH_INTEGER_LENGTH_4;
@@ -1349,8 +1349,8 @@
has_length_prefix);
EXPECT_EQ(CreateQuicVersionLabel(framer_.version()), version_label);
EXPECT_EQ(framer_.version(), parsed_version);
- EXPECT_EQ(FramerTestConnectionId(), destination_connection_id);
- EXPECT_EQ(EmptyQuicConnectionId(), source_connection_id);
+ EXPECT_EQ(FramerTestConnectionId().ToStringView(), destination_connection_id);
+ EXPECT_TRUE(source_connection_id.empty());
EXPECT_EQ(quiche::VARIABLE_LENGTH_INTEGER_LENGTH_0,
retry_token_length_length);
EXPECT_EQ(absl::string_view(), retry_token);
@@ -1391,8 +1391,7 @@
bool version_present = false, has_length_prefix = false;
QuicVersionLabel version_label = 0;
ParsedQuicVersion parsed_version = UnsupportedQuicVersion();
- QuicConnectionId destination_connection_id = EmptyQuicConnectionId(),
- source_connection_id = EmptyQuicConnectionId();
+ absl::string_view destination_connection_id, source_connection_id;
QuicLongHeaderType long_packet_type = INVALID_PACKET_TYPE;
quiche::QuicheVariableLengthIntegerLength retry_token_length_length =
quiche::VARIABLE_LENGTH_INTEGER_LENGTH_4;
@@ -1413,8 +1412,8 @@
EXPECT_TRUE(has_length_prefix);
EXPECT_EQ(0x50524F58u, version_label); // "PROX"
EXPECT_EQ(UnsupportedQuicVersion(), parsed_version);
- EXPECT_EQ(FramerTestConnectionId(), destination_connection_id);
- EXPECT_EQ(EmptyQuicConnectionId(), source_connection_id);
+ EXPECT_EQ(FramerTestConnectionId().ToStringView(), destination_connection_id);
+ EXPECT_TRUE(source_connection_id.empty());
EXPECT_EQ(quiche::VARIABLE_LENGTH_INTEGER_LENGTH_0,
retry_token_length_length);
EXPECT_EQ(absl::string_view(), retry_token);
@@ -1888,9 +1887,14 @@
EXPECT_EQ(5, visitor_.padding_frames_[0]->num_padding_bytes);
}
+// A packet arrives with a valid version field that is not correct for the
+// framer's version.
TEST_P(QuicFramerTest, LargePublicFlagWithMismatchedVersions) {
+ constexpr size_t kPayloadSize = 5;
+ constexpr size_t kAadSize = 20;
+
// clang-format off
- unsigned char packet[] = {
+ unsigned char old_packet[] = {
// type (long header, ZERO_RTT_PROTECTED, 4-byte packet number)
0xD3,
// version tag
@@ -1906,18 +1910,40 @@
0x00,
0x00, 0x00, 0x00, 0x00
};
-
- unsigned char packet49[] = {
+ unsigned char packetv1[100] = {
// type (long header, ZERO_RTT_PROTECTED, 4-byte packet number)
0xD3,
// version tag
- 'Q', '0', '0', '0',
+ 0x00, 0x00, 0x00, 0x01,
+ // connection_id length
+ 0x08,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // source connection ID length
+ 0x00,
+ // packet length
+ static_cast<uint8_t>(kPayloadSize + 20), // Key = 16 + packet number = 4
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ unsigned char packetv2[100] = {
+ // type (long header, ZERO_RTT_PROTECTED, 4-byte packet number)
+ 0xE3,
+ // version tag = QUICv2
+ 0x6b, 0x33, 0x43, 0xcf,
// destination connection ID length
0x08,
// destination connection ID
0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
// source connection ID length
0x00,
+ // packet length
+ static_cast<uint8_t>(kPayloadSize + 20),
// packet number
0x12, 0x34, 0x56, 0x78,
@@ -1927,13 +1953,47 @@
};
// clang-format on
- unsigned char* p = packet;
- size_t p_size = ABSL_ARRAYSIZE(packet);
- if (framer_.version().HasLongHeaderLengths()) {
- p = packet49;
- p_size = ABSL_ARRAYSIZE(packet49);
+ unsigned char* p;
+ size_t p_size;
+ switch (framer_.transport_version()) {
+ case QUIC_VERSION_46:
+ p = old_packet;
+ p_size = ABSL_ARRAYSIZE(old_packet);
+ break;
+ case QUIC_VERSION_IETF_DRAFT_29:
+ case QUIC_VERSION_IETF_RFC_V1:
+ p = packetv2;
+ p_size = ABSL_ARRAYSIZE(packetv2);
+ break;
+ case QUIC_VERSION_IETF_RFC_V2:
+ p = packetv1;
+ p_size = ABSL_ARRAYSIZE(packetv1);
+ break;
+ default:
+ p = packetv2; // To silence warnings.
+ QUICHE_NOTREACHED();
+ break;
}
- QuicEncryptedPacket encrypted(AsChars(p), p_size, false);
+ size_t final_size;
+ if (framer_.version() != ParsedQuicVersion::Q046()) {
+ std::unique_ptr<TaggingEncrypter> encrypter =
+ std::make_unique<TaggingEncrypter>(0x70);
+ framer_.SetEncrypter(ENCRYPTION_ZERO_RTT, std::move(encrypter));
+ std::unique_ptr<TaggingDecrypter> decrypter =
+ std::make_unique<TaggingDecrypter>();
+ if (version_.KnowsWhichDecrypterToUse()) {
+ framer_.InstallDecrypter(ENCRYPTION_ZERO_RTT, std::move(decrypter));
+ } else {
+ framer_.SetDecrypter(ENCRYPTION_ZERO_RTT, std::move(decrypter));
+ }
+ final_size = framer_.EncryptInPlace(
+ ENCRYPTION_ZERO_RTT, QuicPacketNumber(0x12345678), kAadSize,
+ kPayloadSize + kAadSize, p_size, AsChars(p));
+ } else {
+ final_size = p_size;
+ }
+ QuicEncryptedPacket encrypted(AsChars(p), final_size, false);
+
EXPECT_TRUE(framer_.ProcessPacket(encrypted));
EXPECT_THAT(framer_.error(), IsQuicNoError());
ASSERT_TRUE(visitor_.header_.get());
@@ -4146,7 +4206,7 @@
// As above, but checks that for Google-QUIC, if there happens
// to be an ErrorCode string at the start of the details, it is
// NOT extracted/parsed/folded/spindled/and/mutilated.
-TEST_P(QuicFramerTest, ConnectionCloseFrameWithExtractedInfoIgnoreGCuic) {
+TEST_P(QuicFramerTest, ConnectionCloseFrameWithExtractedInfoIgnoreGQuic) {
SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
// clang-format off
@@ -12107,6 +12167,246 @@
'O', '_', 'W', 'O',
'R', 'L', 'D', '?',
};
+ unsigned char packet_v1[] = {
+ // first coalesced packet
+ // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // 4-byte packet number)
+ 0xD3,
+ // version
+ QUIC_VERSION_BYTES,
+ // 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, 0x78,
+ // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set)
+ 0x08 | 0x01 | 0x02 | 0x04,
+ // stream id
+ kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04,
+ // offset
+ kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ kVarInt62OneByte + 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ // second coalesced packet
+ // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // 4-byte packet number)
+ 0xE3,
+ // garbage version
+ QUIC_V2_BYTES,
+ // 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,
+ // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set)
+ 0x08 | 0x01 | 0x02 | 0x04,
+ // stream id
+ kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04,
+ // offset
+ kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ kVarInt62OneByte + 0x0c,
+ // data
+ 'H', 'E', 'L', 'L',
+ 'O', '_', 'W', 'O',
+ 'R', 'L', 'D', '?',
+ };
+ unsigned char packet_v2[] = {
+ // first coalesced packet
+ // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // 4-byte packet number)
+ 0xE3,
+ // version
+ QUIC_VERSION_BYTES,
+ // 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, 0x78,
+ // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set)
+ 0x08 | 0x01 | 0x02 | 0x04,
+ // stream id
+ kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04,
+ // offset
+ kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ kVarInt62OneByte + 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ // second coalesced packet
+ // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // 4-byte packet number)
+ 0xD3,
+ // garbage version
+ QUIC_V1_BYTES,
+ // 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,
+ // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set)
+ 0x08 | 0x01 | 0x02 | 0x04,
+ // stream id
+ kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04,
+ // offset
+ kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ kVarInt62OneByte + 0x0c,
+ // data
+ 'H', 'E', 'L', 'L',
+ 'O', '_', 'W', 'O',
+ 'R', 'L', 'D', '?',
+ };
+ // clang-format on
+ const size_t first_packet_ietf_size = 46;
+ // If the first packet changes, the attempt to fix the first byte of the
+ // second packet will fail.
+ EXPECT_EQ(packet_v1[first_packet_ietf_size], 0xE3);
+ EXPECT_EQ(packet_v2[first_packet_ietf_size], 0xD3);
+
+ unsigned char* p;
+ size_t p_length;
+ switch (framer_.version().transport_version) {
+ case QUIC_VERSION_46:
+ p = packet;
+ p_length = ABSL_ARRAYSIZE(packet);
+ break;
+ case QUIC_VERSION_IETF_DRAFT_29:
+ case QUIC_VERSION_IETF_RFC_V1:
+ p = packet_v1;
+ p_length = ABSL_ARRAYSIZE(packet_v1);
+ break;
+ case QUIC_VERSION_IETF_RFC_V2:
+ p = packet_v2;
+ p_length = ABSL_ARRAYSIZE(packet_v2);
+ break;
+ default:
+ p = packet_v2; // To silence warnings.
+ p_length = ABSL_ARRAYSIZE(packet_v2);
+ QUICHE_NOTREACHED();
+ break;
+ }
+
+ QuicEncryptedPacket encrypted(AsChars(p), p_length, false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_THAT(framer_.error(), IsQuicNoError());
+ ASSERT_TRUE(visitor_.header_.get());
+
+ ASSERT_EQ(1u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+
+ // Stream ID should be the last 3 bytes of kStreamId.
+ EXPECT_EQ(0x00FFFFFF & kStreamId, visitor_.stream_frames_[0]->stream_id);
+ EXPECT_TRUE(visitor_.stream_frames_[0]->fin);
+ EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset);
+ CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get());
+
+ ASSERT_EQ(visitor_.coalesced_packets_.size(), 1u);
+ EXPECT_TRUE(framer_.ProcessPacket(*visitor_.coalesced_packets_[0].get()));
+
+ EXPECT_THAT(framer_.error(), IsQuicNoError());
+ ASSERT_TRUE(visitor_.header_.get());
+
+ ASSERT_EQ(1u, visitor_.stream_frames_.size());
+ // Verify version mismatch gets reported.
+ EXPECT_EQ(1, visitor_.version_mismatch_);
+}
+
+TEST_P(QuicFramerTest, CoalescedPacketWithUnknownVersion) {
+ if (!QuicVersionHasLongHeaderLengths(framer_.transport_version())) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_ZERO_RTT);
+ // clang-format off
+ unsigned char packet[] = {
+ // first coalesced packet
+ // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // 4-byte packet number)
+ 0xD3,
+ // version
+ QUIC_VERSION_BYTES,
+ // 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, 0x78,
+ // frame type (stream frame with fin)
+ 0xFE,
+ // stream id
+ 0x02, 0x03, 0x04,
+ // offset
+ 0x3A, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54,
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ // second coalesced packet
+ // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // 4-byte packet number)
+ 0xD3,
+ // garbage version
+ 'G', 'A', 'B', 'G',
+ // 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,
+ // frame type (stream frame with fin)
+ 0xFE,
+ // stream id
+ 0x02, 0x03, 0x04,
+ // offset
+ 0x3A, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54,
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'H', 'E', 'L', 'L',
+ 'O', '_', 'W', 'O',
+ 'R', 'L', 'D', '?',
+ };
unsigned char packet_ietf[] = {
// first coalesced packet
// public flags (long header with packet type ZERO_RTT_PROTECTED and
@@ -12892,6 +13192,73 @@
// clang-format off
unsigned char packet[] = {
+ // type (long header, ZERO_RTT_PROTECTED, 4-byte packet number)
+ 0xD3,
+ // version tag
+ 0x00, 0x00, 0x00, 0x01,
+ // connection_id length
+ 0x08,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // source connection ID length
+ 0x00,
+ // packet length
+ 0x09, // packet_number + payload = 9
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ };
+
+ unsigned char packetv2[] = {
+ // type (long header, ZERO_RTT_PROTECTED, 4-byte packet number)
+ 0xE3,
+ // version tag = QUICv2
+ 0x6b, 0x33, 0x43, 0xcf,
+ // destination connection ID length
+ 0x08,
+ // destination connection ID
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // source connection ID length
+ 0x00,
+ // packet length
+ 0x09, // packet_number + payload = 9
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ // clang-format on
+
+ unsigned char* p = packet;
+ size_t p_length = ABSL_ARRAYSIZE(packet);
+ if (framer_.version() == ParsedQuicVersion::RFCv1()) {
+ p = packetv2;
+ p_length = ABSL_ARRAYSIZE(packetv2);
+ }
+
+ QuicEncryptedPacket encrypted(AsChars(p), p_length, false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ if (!framer_.version().HasLengthPrefixedConnectionIds()) {
+ // Q046 will mis-parse the connection ID length.
+ EXPECT_THAT(framer_.error(), IsError(QUIC_INVALID_PACKET_HEADER));
+ EXPECT_EQ("Received server connection ID with invalid length.",
+ framer_.detailed_error());
+ return;
+ }
+ EXPECT_THAT(framer_.error(), IsError(QUIC_PACKET_WRONG_VERSION));
+ EXPECT_EQ("Client received unexpected version.", framer_.detailed_error());
+}
+
+TEST_P(QuicFramerTest, ClientReceivesUnknownVersion) {
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+
+ // clang-format off
+ unsigned char packet[] = {
// public flags (long header with packet type INITIAL)
0xC3,
// version that is different from the framer's version
@@ -13264,8 +13631,8 @@
bool version_present = false, has_length_prefix = true;
QuicVersionLabel version_label = 33;
ParsedQuicVersion parsed_version = UnsupportedQuicVersion();
- QuicConnectionId destination_connection_id = TestConnectionId(1);
- QuicConnectionId source_connection_id = TestConnectionId(2);
+ absl::string_view destination_connection_id;
+ absl::string_view source_connection_id;
std::optional<absl::string_view> retry_token;
std::string detailed_error = "foobar";
QuicErrorCode header_parse_result = QuicFramer::ParsePublicHeaderDispatcher(
@@ -13278,10 +13645,11 @@
EXPECT_TRUE(version_present);
EXPECT_FALSE(has_length_prefix);
EXPECT_EQ(0xcabadaba, version_label);
- EXPECT_EQ(expected_destination_connection_id, destination_connection_id);
- EXPECT_EQ(EmptyQuicConnectionId(), source_connection_id);
- EXPECT_FALSE(retry_token.has_value());
+ EXPECT_EQ(expected_destination_connection_id.ToStringView(),
+ destination_connection_id);
+ EXPECT_TRUE(source_connection_id.empty());
EXPECT_EQ("", detailed_error);
+ EXPECT_FALSE(retry_token.has_value());
}
TEST_P(QuicFramerTest, DispatcherParseClientVersionNegotiationProbePacket) {
@@ -13342,8 +13710,8 @@
bool version_present = false, has_length_prefix = false;
QuicVersionLabel version_label = 33;
ParsedQuicVersion parsed_version = UnsupportedQuicVersion();
- QuicConnectionId destination_connection_id = TestConnectionId(1);
- QuicConnectionId source_connection_id = TestConnectionId(2);
+ absl::string_view destination_connection_id;
+ absl::string_view source_connection_id;
std::optional<absl::string_view> retry_token;
std::string detailed_error = "foobar";
QuicErrorCode header_parse_result = QuicFramer::ParsePublicHeaderDispatcher(
@@ -13352,13 +13720,14 @@
&destination_connection_id, &source_connection_id, &retry_token,
&detailed_error);
EXPECT_THAT(header_parse_result, IsQuicNoError());
+ EXPECT_EQ(expected_destination_connection_id.ToStringView(),
+ destination_connection_id);
+ EXPECT_TRUE(source_connection_id.empty());
+ EXPECT_EQ("", detailed_error);
EXPECT_EQ(IETF_QUIC_LONG_HEADER_PACKET, format);
EXPECT_TRUE(version_present);
EXPECT_TRUE(has_length_prefix);
EXPECT_EQ(0xcabadaba, version_label);
- EXPECT_EQ(expected_destination_connection_id, destination_connection_id);
- EXPECT_EQ(EmptyQuicConnectionId(), source_connection_id);
- EXPECT_EQ("", detailed_error);
}
TEST_P(QuicFramerTest, DispatcherParseClientInitialPacketNumber) {
@@ -13406,7 +13775,7 @@
QuicVersionLabel version_label;
std::optional<absl::string_view> retry_token;
ParsedQuicVersion parsed_version = UnsupportedQuicVersion();
- QuicConnectionId destination_connection_id, source_connection_id;
+ absl::string_view destination_connection_id, source_connection_id;
std::string detailed_error;
MockConnectionIdGenerator generator;
EXPECT_CALL(generator, ConnectionIdLength(_)).Times(0);
@@ -13432,7 +13801,8 @@
EXPECT_EQ(QUIC_NO_ERROR,
QuicFramer::TryDecryptInitialPacketDispatcher(
*encrypted, parsed_version, format, long_packet_type,
- destination_connection_id, source_connection_id, retry_token,
+ QuicConnectionId(destination_connection_id),
+ QuicConnectionId(source_connection_id), retry_token,
/*largest_decrypted_inital_packet_number=*/QuicPacketNumber(),
*decrypter_, &packet_number));
EXPECT_THAT(packet_number, Optional(2));
@@ -13523,7 +13893,7 @@
QuicVersionLabel version_label;
std::optional<absl::string_view> retry_token;
ParsedQuicVersion parsed_version = UnsupportedQuicVersion();
- QuicConnectionId destination_connection_id, source_connection_id;
+ absl::string_view destination_connection_id, source_connection_id;
std::string detailed_error;
MockConnectionIdGenerator generator;
EXPECT_CALL(generator, ConnectionIdLength(_)).Times(0);
@@ -13549,7 +13919,8 @@
EXPECT_EQ(QUIC_NO_ERROR,
QuicFramer::TryDecryptInitialPacketDispatcher(
encrypted, parsed_version, format, long_packet_type,
- destination_connection_id, source_connection_id, retry_token,
+ QuicConnectionId(destination_connection_id),
+ QuicConnectionId(source_connection_id), retry_token,
/*largest_decrypted_inital_packet_number=*/QuicPacketNumber(),
*decrypter_, &packet_number));
EXPECT_THAT(packet_number, Optional(0x12345678));
@@ -14601,7 +14972,7 @@
0x00, 0x00, 0x00, 0x00
};
MockConnectionIdGenerator generator;
- EXPECT_CALL(generator, ConnectionIdLength(0x28)).WillOnce(Return(9));
+ ON_CALL(generator, ConnectionIdLength(0x28)).WillByDefault(Return(9));
unsigned char* p = packet;
size_t p_size = ABSL_ARRAYSIZE(packet);
@@ -14619,7 +14990,7 @@
PacketHeaderFormat format;
QuicLongHeaderType long_packet_type = INVALID_PACKET_TYPE;
bool version_flag;
- QuicConnectionId destination_connection_id, source_connection_id;
+ absl::string_view destination_connection_id, source_connection_id;
QuicVersionLabel version_label;
std::string detailed_error;
bool use_length_prefix;
diff --git a/quiche/quic/core/quic_versions.cc b/quiche/quic/core/quic_versions.cc
index f40cc33..69069ac 100644
--- a/quiche/quic/core/quic_versions.cc
+++ b/quiche/quic/core/quic_versions.cc
@@ -332,14 +332,22 @@
}
ParsedQuicVersion ParseQuicVersionLabel(QuicVersionLabel version_label) {
- for (const ParsedQuicVersion& version : AllSupportedVersions()) {
- if (version_label == CreateQuicVersionLabel(version)) {
- return version;
+ if (GetQuicReloadableFlag(quic_heapless_static_parser)) {
+ for (const ParsedQuicVersion& version : SupportedVersions()) {
+ if (version_label == CreateQuicVersionLabel(version)) {
+ return version;
+ }
}
+ } else {
+ for (const ParsedQuicVersion& version : AllSupportedVersions()) {
+ if (version_label == CreateQuicVersionLabel(version)) {
+ return version;
+ }
+ }
+ // Reading from the client so this should not be considered an ERROR.
+ QUIC_DLOG(INFO) << "Unsupported QuicVersionLabel version: "
+ << QuicVersionLabelToString(version_label);
}
- // Reading from the client so this should not be considered an ERROR.
- QUIC_DLOG(INFO) << "Unsupported QuicVersionLabel version: "
- << QuicVersionLabelToString(version_label);
return UnsupportedQuicVersion();
}
diff --git a/quiche/quic/core/tls_chlo_extractor_test.cc b/quiche/quic/core/tls_chlo_extractor_test.cc
index 22dea1b..6cfa9c3 100644
--- a/quiche/quic/core/tls_chlo_extractor_test.cc
+++ b/quiche/quic/core/tls_chlo_extractor_test.cc
@@ -123,13 +123,14 @@
QuicSocketAddress(TestPeerIPAddress(), kTestPort), *packet);
std::string detailed_error;
std::optional<absl::string_view> retry_token;
+ absl::string_view destination_connection_id, source_connection_id;
const QuicErrorCode error = QuicFramer::ParsePublicHeaderDispatcher(
*packet, /*expected_destination_connection_id_length=*/0,
&packet_info.form, &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, &detailed_error);
+ &destination_connection_id, &source_connection_id, &retry_token,
+ &detailed_error);
ASSERT_THAT(error, IsQuicNoError()) << detailed_error;
tls_chlo_extractor_->IngestPacket(packet_info.version,
packet_info.packet);
@@ -359,13 +360,14 @@
QuicSocketAddress(TestPeerIPAddress(), kTestPort), *packets_[0]);
std::string detailed_error;
std::optional<absl::string_view> retry_token;
+ absl::string_view destination_connection_id, source_connection_id;
const QuicErrorCode error = QuicFramer::ParsePublicHeaderDispatcher(
*packets_[0], /*expected_destination_connection_id_length=*/0,
&packet_info.form, &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, &detailed_error);
+ &destination_connection_id, &source_connection_id, &retry_token,
+ &detailed_error);
ASSERT_THAT(error, IsQuicNoError()) << detailed_error;
other_extractor.IngestPacket(packet_info.version, packet_info.packet);
// Remove the first packet from the list.
diff --git a/quiche/quic/test_tools/quic_test_utils.cc b/quiche/quic/test_tools/quic_test_utils.cc
index 2b49ce2..2235653 100644
--- a/quiche/quic/test_tools/quic_test_utils.cc
+++ b/quiche/quic/test_tools/quic_test_utils.cc
@@ -1497,7 +1497,7 @@
bool version_present, has_length_prefix;
QuicVersionLabel version_label;
ParsedQuicVersion parsed_version = ParsedQuicVersion::Unsupported();
- QuicConnectionId destination_connection_id, source_connection_id;
+ absl::string_view destination_connection_id, source_connection_id;
std::optional<absl::string_view> retry_token;
std::string detailed_error;
QuicErrorCode error = QuicFramer::ParsePublicHeaderDispatcher(