Change the return type of QuicDispatcher::TryExtractChloOrBufferEarlyPacket from absl::optional<ParsedClientHello> to ExtractChloResult. The ExtractChloResult could contain a parsed CHLO, a TLS alert, or nothing.
There is no behavior change because the caller of TryExtractChloOrBufferEarlyPacket does not use the TLS alert yet. cl/455683374 will use it and send a connection close in response to a TLS alert.
PiperOrigin-RevId: 456862334
diff --git a/quiche/quic/core/quic_buffered_packet_store.cc b/quiche/quic/core/quic_buffered_packet_store.cc
index 0269cdf..fae7ad1 100644
--- a/quiche/quic/core/quic_buffered_packet_store.cc
+++ b/quiche/quic/core/quic_buffered_packet_store.cc
@@ -294,9 +294,10 @@
const QuicConnectionId& connection_id, const ParsedQuicVersion& version,
const QuicReceivedPacket& packet, std::vector<std::string>* out_alpns,
std::string* out_sni, bool* out_resumption_attempted,
- bool* out_early_data_attempted) {
+ bool* out_early_data_attempted, absl::optional<uint8_t>* tls_alert) {
QUICHE_DCHECK_NE(out_alpns, nullptr);
QUICHE_DCHECK_NE(out_sni, nullptr);
+ QUICHE_DCHECK_NE(tls_alert, nullptr);
QUICHE_DCHECK_EQ(version.handshake_protocol, PROTOCOL_TLS1_3);
auto it = undecryptable_packets_.find(connection_id);
if (it == undecryptable_packets_.end()) {
@@ -306,6 +307,7 @@
}
it->second.tls_chlo_extractor.IngestPacket(version, packet);
if (!it->second.tls_chlo_extractor.HasParsedFullChlo()) {
+ *tls_alert = it->second.tls_chlo_extractor.tls_alert();
return false;
}
const TlsChloExtractor& tls_chlo_extractor = it->second.tls_chlo_extractor;
diff --git a/quiche/quic/core/quic_buffered_packet_store.h b/quiche/quic/core/quic_buffered_packet_store.h
index f3310b4..95bb737 100644
--- a/quiche/quic/core/quic_buffered_packet_store.h
+++ b/quiche/quic/core/quic_buffered_packet_store.h
@@ -119,13 +119,13 @@
// |out_resumption_attempted| is populated if the CHLO has the
// 'pre_shared_key' TLS extension. |out_early_data_attempted| is populated if
// the CHLO has the 'early_data' TLS extension.
- bool IngestPacketForTlsChloExtraction(const QuicConnectionId& connection_id,
- const ParsedQuicVersion& version,
- const QuicReceivedPacket& packet,
- std::vector<std::string>* out_alpns,
- std::string* out_sni,
- bool* out_resumption_attempted,
- bool* out_early_data_attempted);
+ // When this returns false, and an unrecoverable error happened due to a TLS
+ // alert, |*tls_alert| will be set to the alert value.
+ bool IngestPacketForTlsChloExtraction(
+ const QuicConnectionId& connection_id, const ParsedQuicVersion& version,
+ const QuicReceivedPacket& packet, std::vector<std::string>* out_alpns,
+ std::string* out_sni, bool* out_resumption_attempted,
+ bool* out_early_data_attempted, absl::optional<uint8_t>* tls_alert);
// Returns the list of buffered packets for |connection_id| and removes them
// from the store. Returns an empty list if no early arrived packets for this
diff --git a/quiche/quic/core/quic_buffered_packet_store_test.cc b/quiche/quic/core/quic_buffered_packet_store_test.cc
index 012ae6e..4944db7 100644
--- a/quiche/quic/core/quic_buffered_packet_store_test.cc
+++ b/quiche/quic/core/quic_buffered_packet_store_test.cc
@@ -468,6 +468,7 @@
bool resumption_attempted = false;
bool early_data_attempted = false;
QuicConfig config;
+ absl::optional<uint8_t> tls_alert;
EXPECT_FALSE(store_.HasBufferedPackets(connection_id));
store_.EnqueuePacket(connection_id, false, packet_, self_address_,
@@ -477,7 +478,7 @@
// The packet in 'packet_' is not a TLS CHLO packet.
EXPECT_FALSE(store_.IngestPacketForTlsChloExtraction(
connection_id, valid_version_, packet_, &alpns, &sni,
- &resumption_attempted, &early_data_attempted));
+ &resumption_attempted, &early_data_attempted, &tls_alert));
store_.DiscardPackets(connection_id);
@@ -498,10 +499,10 @@
EXPECT_TRUE(store_.HasBufferedPackets(connection_id));
EXPECT_FALSE(store_.IngestPacketForTlsChloExtraction(
connection_id, valid_version_, *packets[0], &alpns, &sni,
- &resumption_attempted, &early_data_attempted));
+ &resumption_attempted, &early_data_attempted, &tls_alert));
EXPECT_TRUE(store_.IngestPacketForTlsChloExtraction(
connection_id, valid_version_, *packets[1], &alpns, &sni,
- &resumption_attempted, &early_data_attempted));
+ &resumption_attempted, &early_data_attempted, &tls_alert));
EXPECT_THAT(alpns, ElementsAre(AlpnForVersion(valid_version_)));
EXPECT_EQ(sni, TestHostname());
diff --git a/quiche/quic/core/quic_dispatcher.cc b/quiche/quic/core/quic_dispatcher.cc
index 883a59d..81226f6 100644
--- a/quiche/quic/core/quic_dispatcher.cc
+++ b/quiche/quic/core/quic_dispatcher.cc
@@ -717,8 +717,9 @@
QuicPacketFate fate = ValidityChecks(*packet_info);
if (fate == kFateProcess) {
- absl::optional<ParsedClientHello> parsed_chlo =
+ ExtractChloResult extract_chlo_result =
TryExtractChloOrBufferEarlyPacket(*packet_info);
+ auto& parsed_chlo = extract_chlo_result.parsed_chlo;
if (!parsed_chlo.has_value()) {
// Client Hello incomplete. Packet has been buffered or (rarely) dropped.
return;
@@ -780,9 +781,10 @@
}
}
-absl::optional<ParsedClientHello>
+QuicDispatcher::ExtractChloResult
QuicDispatcher::TryExtractChloOrBufferEarlyPacket(
const ReceivedPacketInfo& packet_info) {
+ ExtractChloResult result;
if (packet_info.version.UsesTls()) {
bool has_full_tls_chlo = false;
std::string sni;
@@ -795,7 +797,7 @@
has_full_tls_chlo = buffered_packets_.IngestPacketForTlsChloExtraction(
packet_info.destination_connection_id, packet_info.version,
packet_info.packet, &alpns, &sni, &resumption_attempted,
- &early_data_attempted);
+ &early_data_attempted, &result.tls_alert);
} else {
// If we do not have a BufferedPacketList for this connection ID,
// create a single-use one to check whether this packet contains a
@@ -809,6 +811,8 @@
sni = tls_chlo_extractor.server_name();
resumption_attempted = tls_chlo_extractor.resumption_attempted();
early_data_attempted = tls_chlo_extractor.early_data_attempted();
+ } else {
+ result.tls_alert = tls_chlo_extractor.tls_alert();
}
}
if (!has_full_tls_chlo) {
@@ -816,10 +820,10 @@
// packet that arrived before the CHLO (due to loss or reordering),
// or it could be a fragment of a multi-packet CHLO.
BufferEarlyPacket(packet_info);
- return absl::nullopt;
+ return result;
}
- ParsedClientHello parsed_chlo;
+ ParsedClientHello& parsed_chlo = result.parsed_chlo.emplace();
parsed_chlo.sni = std::move(sni);
parsed_chlo.alpns = std::move(alpns);
if (packet_info.retry_token.has_value()) {
@@ -827,7 +831,7 @@
}
parsed_chlo.resumption_attempted = resumption_attempted;
parsed_chlo.early_data_attempted = early_data_attempted;
- return parsed_chlo;
+ return result;
}
ChloAlpnSniExtractor alpn_extractor;
@@ -838,7 +842,7 @@
packet_info.destination_connection_id.length())) {
// Buffer non-CHLO packets.
BufferEarlyPacket(packet_info);
- return absl::nullopt;
+ return result;
}
// We only apply this check for versions that do not use the IETF
@@ -851,16 +855,16 @@
QUIC_DVLOG(1) << "Dropping CHLO packet which is too short, length: "
<< packet_info.packet.length();
QUIC_CODE_COUNT(quic_drop_small_chlo_packets);
- return absl::nullopt;
+ return result;
}
- ParsedClientHello parsed_chlo;
+ ParsedClientHello& parsed_chlo = result.parsed_chlo.emplace();
parsed_chlo.legacy_version_encapsulation_inner_packet =
alpn_extractor.ConsumeLegacyVersionEncapsulationInnerPacket();
parsed_chlo.sni = alpn_extractor.ConsumeSni();
parsed_chlo.uaid = alpn_extractor.ConsumeUaid();
parsed_chlo.alpns = {alpn_extractor.ConsumeAlpn()};
- return parsed_chlo;
+ return result;
}
std::string QuicDispatcher::SelectAlpn(const std::vector<std::string>& alpns) {
diff --git a/quiche/quic/core/quic_dispatcher.h b/quiche/quic/core/quic_dispatcher.h
index 878f6ac..e230739 100644
--- a/quiche/quic/core/quic_dispatcher.h
+++ b/quiche/quic/core/quic_dispatcher.h
@@ -367,15 +367,26 @@
// ProcessValidatedPacketWithUnknownConnectionId.
void ProcessHeader(ReceivedPacketInfo* packet_info);
+ struct ExtractChloResult {
+ // If set, a full client hello has been successfully parsed.
+ absl::optional<ParsedClientHello> parsed_chlo;
+ // If set, the TLS alert that will cause a connection close.
+ // Always empty for Google QUIC.
+ absl::optional<uint8_t> tls_alert;
+ };
+
// Try to extract information(sni, alpns, ...) if the full Client Hello has
// been parsed.
//
- // Return the parsed client hello if the full Client Hello has been
- // successfully parsed.
+ // Returns the parsed client hello in ExtractChloResult.parsed_chlo, if the
+ // full Client Hello has been successfully parsed.
//
- // Otherwise return absl::nullopt and either buffer or (rarely) drop the
- // packet.
- absl::optional<ParsedClientHello> TryExtractChloOrBufferEarlyPacket(
+ // Returns the TLS alert in ExtractChloResult.tls_alert, if the extraction of
+ // Client Hello failed due to that alert.
+ //
+ // Otherwise returns a default-constructed ExtractChloResult and either buffer
+ // or (rarely) drop the packet.
+ ExtractChloResult TryExtractChloOrBufferEarlyPacket(
const ReceivedPacketInfo& packet_info);
// Deliver |packets| to |session| for further processing.
diff --git a/quiche/quic/core/tls_chlo_extractor.cc b/quiche/quic/core/tls_chlo_extractor.cc
index 046f83d..09057f0 100644
--- a/quiche/quic/core/tls_chlo_extractor.cc
+++ b/quiche/quic/core/tls_chlo_extractor.cc
@@ -267,6 +267,9 @@
HandleUnrecoverableError(absl::StrCat(
"BoringSSL attempted to send alert ", static_cast<int>(tls_alert_value),
" ", SSL_alert_desc_string_long(tls_alert_value)));
+ if (state_ == State::kUnrecoverableFailure) {
+ tls_alert_ = tls_alert_value;
+ }
}
// static
diff --git a/quiche/quic/core/tls_chlo_extractor.h b/quiche/quic/core/tls_chlo_extractor.h
index b2cf50c..d08f56f 100644
--- a/quiche/quic/core/tls_chlo_extractor.h
+++ b/quiche/quic/core/tls_chlo_extractor.h
@@ -62,6 +62,13 @@
state_ == State::kParsedFullMultiPacketChlo;
}
+ // Returns the TLS alert that caused the unrecoverable error, if any.
+ absl::optional<uint8_t> tls_alert() const {
+ QUICHE_DCHECK(!tls_alert_.has_value() ||
+ state_ == State::kUnrecoverableFailure);
+ return tls_alert_;
+ }
+
// Methods from QuicFramerVisitorInterface.
void OnError(QuicFramer* /*framer*/) override {}
bool OnProtocolVersionMismatch(ParsedQuicVersion version) override;
@@ -249,6 +256,9 @@
// Whether early data is attempted from the CHLO, indicated by the
// 'early_data' TLS extension.
bool early_data_attempted_ = false;
+ // If set, contains the TLS alert that caused an unrecoverable error, which is
+ // an AlertDescription value defined in go/rfc/8446#appendix-B.2.
+ absl::optional<uint8_t> tls_alert_;
};
// Convenience method to facilitate logging TlsChloExtractor::State.