Send correct QUIC CRYPTO_ERROR connection error for TLS errors
Protected by FLAGS_quic_reloadable_flag_quic_send_tls_crypto_error_code.
PiperOrigin-RevId: 351885444
Change-Id: I5388eaa8632342f17a0212c68ab587ea0d04ee79
diff --git a/quic/core/frames/quic_connection_close_frame.cc b/quic/core/frames/quic_connection_close_frame.cc
index ccc352a..0eee910 100644
--- a/quic/core/frames/quic_connection_close_frame.cc
+++ b/quic/core/frames/quic_connection_close_frame.cc
@@ -14,6 +14,7 @@
QuicConnectionCloseFrame::QuicConnectionCloseFrame(
QuicTransportVersion transport_version,
QuicErrorCode error_code,
+ QuicIetfTransportErrorCodes ietf_error,
std::string error_phrase,
uint64_t frame_type)
: quic_error_code(error_code), error_details(error_phrase) {
@@ -25,7 +26,11 @@
}
QuicErrorCodeToIetfMapping mapping =
QuicErrorCodeToTransportErrorCode(error_code);
- wire_error_code = mapping.error_code;
+ if (ietf_error != NO_IETF_QUIC_ERROR) {
+ wire_error_code = ietf_error;
+ } else {
+ wire_error_code = mapping.error_code;
+ }
if (mapping.is_transport_close) {
// Maps to a transport close
close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE;
diff --git a/quic/core/frames/quic_connection_close_frame.h b/quic/core/frames/quic_connection_close_frame.h
index 3d5df24..66a90ad 100644
--- a/quic/core/frames/quic_connection_close_frame.h
+++ b/quic/core/frames/quic_connection_close_frame.h
@@ -22,8 +22,12 @@
// and the mapping of error_code. THIS IS THE PREFERRED C'TOR
// TO USE IF YOU NEED TO CREATE A CONNECTION-CLOSE-FRAME AND
// HAVE IT BE CORRECT FOR THE VERSION AND CODE MAPPINGS.
+ // |ietf_error| may optionally be be used to directly specify the wire
+ // error code. Otherwise if |ietf_error| is NO_IETF_QUIC_ERROR, the
+ // QuicErrorCodeToTransportErrorCode mapping of |error_code| will be used.
QuicConnectionCloseFrame(QuicTransportVersion transport_version,
QuicErrorCode error_code,
+ QuicIetfTransportErrorCodes ietf_error,
std::string error_phrase,
uint64_t transport_close_frame_type);
diff --git a/quic/core/http/quic_receive_control_stream_test.cc b/quic/core/http/quic_receive_control_stream_test.cc
index 0d1fd6f..fdd74b0 100644
--- a/quic/core/http/quic_receive_control_stream_test.cc
+++ b/quic/core/http/quic_receive_control_stream_test.cc
@@ -209,7 +209,7 @@
"Settings frames are received twice.", _))
.WillOnce(
Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
- EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _));
+ EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _));
EXPECT_CALL(session_, OnConnectionClosed(_, _));
// Receive second SETTINGS frame.
@@ -264,7 +264,7 @@
"PRIORITY_UPDATE frame received before SETTINGS.", _))
.WillOnce(
Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
- EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _));
+ EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _));
EXPECT_CALL(session_, OnConnectionClosed(_, _));
receive_control_stream_->OnStreamFrame(data);
@@ -315,7 +315,7 @@
CloseConnection(QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM, _, _))
.WillOnce(
Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
- EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _));
+ EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _));
EXPECT_CALL(session_, OnConnectionClosed(_, _));
receive_control_stream_->OnStreamFrame(frame);
}
@@ -388,7 +388,7 @@
"CANCEL_PUSH frame received before SETTINGS.", _))
.WillOnce(
Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
- EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _));
+ EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _));
EXPECT_CALL(session_, OnConnectionClosed(_, _));
receive_control_stream_->OnStreamFrame(
@@ -409,7 +409,7 @@
_))
.WillOnce(
Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
- EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _));
+ EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _));
EXPECT_CALL(session_, OnConnectionClosed(_, _));
receive_control_stream_->OnStreamFrame(
@@ -447,7 +447,7 @@
"ACCEPT_CH frame received on control stream", _))
.WillOnce(
Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
- EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _));
+ EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _));
EXPECT_CALL(session_, OnConnectionClosed(_, _));
}
} else {
@@ -471,7 +471,7 @@
"Unknown frame received before SETTINGS.", _))
.WillOnce(
Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
- EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _));
+ EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _));
EXPECT_CALL(session_, OnConnectionClosed(_, _));
receive_control_stream_->OnStreamFrame(
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index 568c891..767a881 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -1915,7 +1915,7 @@
EXPECT_CALL(*connection_, CloseConnection(_, _, _))
.WillOnce(
Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
- EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _));
+ EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _));
}
session_.OnStreamFrame(data1);
}
@@ -3194,7 +3194,7 @@
EXPECT_CALL(*connection_, CloseConnection(QUIC_NO_ERROR, _, _))
.WillOnce(
Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
- EXPECT_CALL(*connection_, SendConnectionClosePacket(QUIC_NO_ERROR, _))
+ EXPECT_CALL(*connection_, SendConnectionClosePacket(QUIC_NO_ERROR, _, _))
.WillOnce(Invoke(connection_,
&MockQuicConnection::ReallySendConnectionClosePacket));
connection_->CloseConnection(
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc
index 0aeba50..d7aa6e7 100644
--- a/quic/core/http/quic_spdy_stream_test.cc
+++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -679,7 +679,7 @@
connection_->ReallyCloseConnection(error, error_details,
connection_close_behavior);
})));
- EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _));
+ EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _));
EXPECT_CALL(*session_, OnConnectionClosed(_, _))
.WillOnce(Invoke([this](const QuicConnectionCloseFrame& frame,
ConnectionCloseSource source) {
@@ -2110,7 +2110,7 @@
connection_->ReallyCloseConnection(error, error_details,
connection_close_behavior);
})));
- EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _));
+ EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _));
EXPECT_CALL(*session_, OnConnectionClosed(_, _))
.WillOnce(Invoke([this](const QuicConnectionCloseFrame& frame,
ConnectionCloseSource source) {
@@ -2151,7 +2151,7 @@
connection_->ReallyCloseConnection(error, error_details,
connection_close_behavior);
})));
- EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _));
+ EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _));
EXPECT_CALL(*session_, OnConnectionClosed(_, _))
.WillOnce(Invoke([this](const QuicConnectionCloseFrame& frame,
ConnectionCloseSource source) {
@@ -2959,7 +2959,7 @@
CloseConnection(QUIC_HTTP_FRAME_UNEXPECTED_ON_SPDY_STREAM, _, _))
.WillOnce(
Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
- EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _));
+ EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _));
EXPECT_CALL(*session_, OnConnectionClosed(_, _));
stream_->OnStreamFrame(QuicStreamFrame(stream_->id(), /* fin = */ false,
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index d4a884e..ac8b22d 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -848,8 +848,8 @@
}
QUIC_DLOG(INFO) << ENDPOINT << error_details;
QUIC_CODE_COUNT(quic_tear_down_local_connection_on_public_reset);
- TearDownLocalConnectionState(QUIC_PUBLIC_RESET, error_details,
- ConnectionCloseSource::FROM_PEER);
+ TearDownLocalConnectionState(QUIC_PUBLIC_RESET, NO_IETF_QUIC_ERROR,
+ error_details, ConnectionCloseSource::FROM_PEER);
}
bool QuicConnection::OnProtocolVersionMismatch(
@@ -1982,8 +1982,8 @@
const std::string error_details = "Received stateless reset.";
QUIC_CODE_COUNT(quic_tear_down_local_connection_on_stateless_reset);
- TearDownLocalConnectionState(QUIC_PUBLIC_RESET, error_details,
- ConnectionCloseSource::FROM_PEER);
+ TearDownLocalConnectionState(QUIC_PUBLIC_RESET, NO_IETF_QUIC_ERROR,
+ error_details, ConnectionCloseSource::FROM_PEER);
}
void QuicConnection::OnKeyUpdate(KeyUpdateReason reason) {
@@ -3909,6 +3909,15 @@
void QuicConnection::CloseConnection(
QuicErrorCode error,
+ const std::string& details,
+ ConnectionCloseBehavior connection_close_behavior) {
+ CloseConnection(error, NO_IETF_QUIC_ERROR, details,
+ connection_close_behavior);
+}
+
+void QuicConnection::CloseConnection(
+ QuicErrorCode error,
+ QuicIetfTransportErrorCodes ietf_error,
const std::string& error_details,
ConnectionCloseBehavior connection_close_behavior) {
DCHECK(!error_details.empty());
@@ -3917,20 +3926,29 @@
return;
}
- QUIC_DLOG(INFO) << ENDPOINT << "Closing connection: " << connection_id()
- << ", with error: " << QuicErrorCodeToString(error) << " ("
- << error << "), and details: " << error_details;
-
- if (connection_close_behavior != ConnectionCloseBehavior::SILENT_CLOSE) {
- SendConnectionClosePacket(error, error_details);
+ if (ietf_error != NO_IETF_QUIC_ERROR) {
+ QUIC_DLOG(INFO) << ENDPOINT << "Closing connection: " << connection_id()
+ << ", with wire error: " << ietf_error
+ << ", error: " << QuicErrorCodeToString(error)
+ << ", and details: " << error_details;
+ } else {
+ QUIC_DLOG(INFO) << ENDPOINT << "Closing connection: " << connection_id()
+ << ", with error: " << QuicErrorCodeToString(error) << " ("
+ << error << "), and details: " << error_details;
}
- TearDownLocalConnectionState(error, error_details,
+ if (connection_close_behavior != ConnectionCloseBehavior::SILENT_CLOSE) {
+ SendConnectionClosePacket(error, ietf_error, error_details);
+ }
+
+ TearDownLocalConnectionState(error, ietf_error, error_details,
ConnectionCloseSource::FROM_SELF);
}
-void QuicConnection::SendConnectionClosePacket(QuicErrorCode error,
- const std::string& details) {
+void QuicConnection::SendConnectionClosePacket(
+ QuicErrorCode error,
+ QuicIetfTransportErrorCodes ietf_error,
+ const std::string& details) {
// Always use the current path to send CONNECTION_CLOSE.
QuicPacketCreator::ScopedPeerAddressContext context(&packet_creator_,
peer_address());
@@ -3961,7 +3979,8 @@
}
QuicConnectionCloseFrame* frame;
- frame = new QuicConnectionCloseFrame(transport_version(), error, details,
+ frame = new QuicConnectionCloseFrame(transport_version(), error, ietf_error,
+ details,
framer_.current_received_frame_type());
packet_creator_.ConsumeRetransmittableControlFrame(QuicFrame(frame));
packet_creator_.FlushCurrentPacket();
@@ -4014,9 +4033,9 @@
visitor_->BeforeConnectionCloseSent();
}
- auto* frame =
- new QuicConnectionCloseFrame(transport_version(), error, details,
- framer_.current_received_frame_type());
+ auto* frame = new QuicConnectionCloseFrame(
+ transport_version(), error, ietf_error, details,
+ framer_.current_received_frame_type());
packet_creator_.ConsumeRetransmittableControlFrame(QuicFrame(frame));
packet_creator_.FlushCurrentPacket();
}
@@ -4033,9 +4052,11 @@
void QuicConnection::TearDownLocalConnectionState(
QuicErrorCode error,
+ QuicIetfTransportErrorCodes ietf_error,
const std::string& error_details,
ConnectionCloseSource source) {
- QuicConnectionCloseFrame frame(transport_version(), error, error_details,
+ QuicConnectionCloseFrame frame(transport_version(), error, ietf_error,
+ error_details,
framer_.current_received_frame_type());
return TearDownLocalConnectionState(frame, source);
}
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index 600e882..b96952e 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -529,6 +529,13 @@
QuicErrorCode error,
const std::string& details,
ConnectionCloseBehavior connection_close_behavior);
+ // Closes the connection, specifying the wire error code |ietf_error|
+ // explicitly.
+ virtual void CloseConnection(
+ QuicErrorCode error,
+ QuicIetfTransportErrorCodes ietf_error,
+ const std::string& details,
+ ConnectionCloseBehavior connection_close_behavior);
QuicConnectionStats& mutable_stats() { return stats_; }
@@ -1229,7 +1236,11 @@
// Sends a connection close packet to the peer and includes an ACK if the ACK
// is not empty, the |error| is not PACKET_WRITE_ERROR, and it fits.
+ // |ietf_error| may optionally be be used to directly specify the wire
+ // error code. Otherwise if |ietf_error| is NO_IETF_QUIC_ERROR, the
+ // QuicErrorCodeToTransportErrorCode mapping of |error| will be used.
virtual void SendConnectionClosePacket(QuicErrorCode error,
+ QuicIetfTransportErrorCodes ietf_error,
const std::string& details);
// Returns true if the packet should be discarded and not sent.
@@ -1331,7 +1342,11 @@
// Does not send a connection close frame to the peer. It should only be
// called by CloseConnection or OnConnectionCloseFrame, OnPublicResetPacket,
// and OnAuthenticatedIetfStatelessResetPacket.
+ // |ietf_error| may optionally be be used to directly specify the wire
+ // error code. Otherwise if |ietf_error| is NO_IETF_QUIC_ERROR, the
+ // QuicErrorCodeToTransportErrorCode mapping of |error| will be used.
void TearDownLocalConnectionState(QuicErrorCode error,
+ QuicIetfTransportErrorCodes ietf_error,
const std::string& details,
ConnectionCloseSource source);
void TearDownLocalConnectionState(const QuicConnectionCloseFrame& frame,
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 32cbf8d..8ad4214 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -1211,7 +1211,7 @@
QuicErrorCode kQuicErrorCode = QUIC_PEER_GOING_AWAY;
QuicConnectionCloseFrame qccf(peer_framer_.transport_version(),
- kQuicErrorCode, "",
+ kQuicErrorCode, NO_IETF_QUIC_ERROR, "",
/*transport_close_frame_type=*/0);
QuicFrames frames;
frames.push_back(QuicFrame(&qccf));
@@ -6776,7 +6776,7 @@
// close. If doing IETF QUIC then set fields appropriately for CC/T or CC/A,
// depending on the mapping.
QuicConnectionCloseFrame qccf(peer_framer_.transport_version(),
- kQuicErrorCode, "",
+ kQuicErrorCode, NO_IETF_QUIC_ERROR, "",
/*transport_close_frame_type=*/0);
QuicFrames frames;
frames.push_back(QuicFrame(frame1_));
@@ -9028,7 +9028,7 @@
const QuicErrorCode kErrorCode = QUIC_INTERNAL_ERROR;
std::unique_ptr<QuicConnectionCloseFrame> connection_close_frame(
new QuicConnectionCloseFrame(connection_.transport_version(), kErrorCode,
- "",
+ NO_IETF_QUIC_ERROR, "",
/*transport_close_frame_type=*/0));
// Received 2 packets.
diff --git a/quic/core/quic_dispatcher.cc b/quic/core/quic_dispatcher.cc
index a4b4099..8a1d854 100644
--- a/quic/core/quic_dispatcher.cc
+++ b/quic/core/quic_dispatcher.cc
@@ -170,9 +170,10 @@
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);
+ QuicConnectionCloseFrame* frame =
+ new QuicConnectionCloseFrame(framer_.transport_version(), error_code,
+ NO_IETF_QUIC_ERROR, error_details,
+ /*transport_close_frame_type=*/0);
if (!creator_.AddFrame(QuicFrame(frame), NOT_RETRANSMISSION)) {
QUIC_BUG << "Unable to add frame to an empty packet";
diff --git a/quic/core/quic_error_codes.cc b/quic/core/quic_error_codes.cc
index cbc6590..8363dc1 100644
--- a/quic/core/quic_error_codes.cc
+++ b/quic/core/quic_error_codes.cc
@@ -259,6 +259,14 @@
RETURN_STRING_LITERAL(QUIC_AEAD_LIMIT_REACHED);
RETURN_STRING_LITERAL(QUIC_MAX_AGE_TIMEOUT);
RETURN_STRING_LITERAL(QUIC_INVALID_PRIORITY_UPDATE);
+ RETURN_STRING_LITERAL(QUIC_TLS_BAD_CERTIFICATE);
+ RETURN_STRING_LITERAL(QUIC_TLS_UNSUPPORTED_CERTIFICATE);
+ RETURN_STRING_LITERAL(QUIC_TLS_CERTIFICATE_REVOKED);
+ RETURN_STRING_LITERAL(QUIC_TLS_CERTIFICATE_EXPIRED);
+ RETURN_STRING_LITERAL(QUIC_TLS_CERTIFICATE_UNKNOWN);
+ RETURN_STRING_LITERAL(QUIC_TLS_INTERNAL_ERROR);
+ RETURN_STRING_LITERAL(QUIC_TLS_UNRECOGNIZED_NAME);
+ RETURN_STRING_LITERAL(QUIC_TLS_CERTIFICATE_REQUIRED);
RETURN_STRING_LITERAL(QUIC_LAST_ERROR);
// Intentionally have no default case, so we'll break the build
@@ -726,6 +734,31 @@
case QUIC_INVALID_PRIORITY_UPDATE:
return {false, static_cast<uint64_t>(
QuicHttp3ErrorCode::GENERAL_PROTOCOL_ERROR)};
+ case QUIC_TLS_BAD_CERTIFICATE:
+ return {true, static_cast<uint64_t>(CRYPTO_ERROR_FIRST +
+ SSL_AD_BAD_CERTIFICATE)};
+ case QUIC_TLS_UNSUPPORTED_CERTIFICATE:
+ return {true, static_cast<uint64_t>(CRYPTO_ERROR_FIRST +
+ SSL_AD_UNSUPPORTED_CERTIFICATE)};
+ case QUIC_TLS_CERTIFICATE_REVOKED:
+ return {true, static_cast<uint64_t>(CRYPTO_ERROR_FIRST +
+ SSL_AD_CERTIFICATE_REVOKED)};
+ case QUIC_TLS_CERTIFICATE_EXPIRED:
+ return {true, static_cast<uint64_t>(CRYPTO_ERROR_FIRST +
+ SSL_AD_CERTIFICATE_EXPIRED)};
+ case QUIC_TLS_CERTIFICATE_UNKNOWN:
+ return {true, static_cast<uint64_t>(CRYPTO_ERROR_FIRST +
+ SSL_AD_CERTIFICATE_UNKNOWN)};
+ case QUIC_TLS_INTERNAL_ERROR:
+ return {true, static_cast<uint64_t>(CRYPTO_ERROR_FIRST +
+ SSL_AD_INTERNAL_ERROR)};
+ case QUIC_TLS_UNRECOGNIZED_NAME:
+ return {true, static_cast<uint64_t>(CRYPTO_ERROR_FIRST +
+ SSL_AD_UNRECOGNIZED_NAME)};
+ case QUIC_TLS_CERTIFICATE_REQUIRED:
+ return {true, static_cast<uint64_t>(CRYPTO_ERROR_FIRST +
+ SSL_AD_CERTIFICATE_REQUIRED)};
+
case QUIC_LAST_ERROR:
return {false, static_cast<uint64_t>(QUIC_LAST_ERROR)};
}
@@ -733,6 +766,29 @@
return {true, static_cast<uint64_t>(INTERNAL_ERROR)};
}
+QuicErrorCode TlsAlertToQuicErrorCode(uint8_t desc) {
+ switch (desc) {
+ case SSL_AD_BAD_CERTIFICATE:
+ return QUIC_TLS_BAD_CERTIFICATE;
+ case SSL_AD_UNSUPPORTED_CERTIFICATE:
+ return QUIC_TLS_UNSUPPORTED_CERTIFICATE;
+ case SSL_AD_CERTIFICATE_REVOKED:
+ return QUIC_TLS_CERTIFICATE_REVOKED;
+ case SSL_AD_CERTIFICATE_EXPIRED:
+ return QUIC_TLS_CERTIFICATE_EXPIRED;
+ case SSL_AD_CERTIFICATE_UNKNOWN:
+ return QUIC_TLS_CERTIFICATE_UNKNOWN;
+ case SSL_AD_INTERNAL_ERROR:
+ return QUIC_TLS_INTERNAL_ERROR;
+ case SSL_AD_UNRECOGNIZED_NAME:
+ return QUIC_TLS_UNRECOGNIZED_NAME;
+ case SSL_AD_CERTIFICATE_REQUIRED:
+ return QUIC_TLS_CERTIFICATE_REQUIRED;
+ default:
+ return QUIC_HANDSHAKE_FAILED;
+ }
+}
+
// Convert a QuicRstStreamErrorCode to an application error code to be used in
// an IETF QUIC RESET_STREAM frame
uint64_t RstStreamErrorCodeToIetfResetStreamErrorCode(
diff --git a/quic/core/quic_error_codes.h b/quic/core/quic_error_codes.h
index b8e4748..885e088 100644
--- a/quic/core/quic_error_codes.h
+++ b/quic/core/quic_error_codes.h
@@ -570,8 +570,21 @@
// Received PRIORITY_UPDATE frame with invalid payload.
QUIC_INVALID_PRIORITY_UPDATE = 193,
+ // Maps to specific errors from the CRYPTO_ERROR range from
+ // https://quicwg.org/base-drafts/draft-ietf-quic-transport.html#name-transport-error-codes
+ // This attempts to choose a subset of the most interesting errors rather
+ // than mapping every possible CRYPTO_ERROR code.
+ QUIC_TLS_BAD_CERTIFICATE = 195,
+ QUIC_TLS_UNSUPPORTED_CERTIFICATE = 196,
+ QUIC_TLS_CERTIFICATE_REVOKED = 197,
+ QUIC_TLS_CERTIFICATE_EXPIRED = 198,
+ QUIC_TLS_CERTIFICATE_UNKNOWN = 199,
+ QUIC_TLS_INTERNAL_ERROR = 200,
+ QUIC_TLS_UNRECOGNIZED_NAME = 201,
+ QUIC_TLS_CERTIFICATE_REQUIRED = 202,
+
// No error. Used as bound while iterating.
- QUIC_LAST_ERROR = 195,
+ QUIC_LAST_ERROR = 203,
};
// QuicErrorCodes is encoded as four octets on-the-wire when doing Google QUIC,
// or a varint62 when doing IETF QUIC. Ensure that its value does not exceed
@@ -580,6 +593,9 @@
static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()),
"QuicErrorCode exceeds four octets");
+// Convert TLS alert code to QuicErrorCode.
+QUIC_EXPORT_PRIVATE QuicErrorCode TlsAlertToQuicErrorCode(uint8_t desc);
+
// Returns the name of the QuicRstStreamErrorCode as a char*
QUIC_EXPORT_PRIVATE const char* QuicRstStreamErrorCodeToString(
QuicRstStreamErrorCode error);
diff --git a/quic/core/quic_error_codes_test.cc b/quic/core/quic_error_codes_test.cc
index 6bf63d0..f72c150 100644
--- a/quic/core/quic_error_codes_test.cc
+++ b/quic/core/quic_error_codes_test.cc
@@ -78,7 +78,17 @@
if (ietf_error_code.is_transport_close) {
QuicIetfTransportErrorCodes transport_error_code =
static_cast<QuicIetfTransportErrorCodes>(ietf_error_code.error_code);
- bool is_valid_transport_error_code = transport_error_code <= 0x0f;
+ bool is_transport_crypto_error_code =
+ transport_error_code >= 0x100 && transport_error_code <= 0x1ff;
+ if (is_transport_crypto_error_code) {
+ // Ensure that every QuicErrorCode that maps to a CRYPTO_ERROR code has
+ // a corresponding reverse mapping in TlsAlertToQuicErrorCode:
+ EXPECT_EQ(
+ internal_error_code,
+ TlsAlertToQuicErrorCode(transport_error_code - CRYPTO_ERROR_FIRST));
+ }
+ bool is_valid_transport_error_code =
+ transport_error_code <= 0x0f || is_transport_crypto_error_code;
EXPECT_TRUE(is_valid_transport_error_code) << internal_error_code_string;
} else {
// Non-transport errors are application errors, either HTTP/3 or QPACK.
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 8461aa7..b74e936 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -56,6 +56,7 @@
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_goaway_with_connection_close, true)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_path_response, false)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_timestamps, false)
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_tls_crypto_error_code, false)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_version_negotiation_for_short_connection_ids, true)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_single_ack_in_packet2, false)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_split_up_send_rst_2, true)
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc
index 803a06b..218d9a9 100644
--- a/quic/core/quic_framer_test.cc
+++ b/quic/core/quic_framer_test.cc
@@ -7820,8 +7820,9 @@
header.version_flag = false;
header.packet_number = kPacketNumber;
- QuicConnectionCloseFrame close_frame(
- framer_.transport_version(), QUIC_INTERNAL_ERROR, "because I can", 0x05);
+ QuicConnectionCloseFrame close_frame(framer_.transport_version(),
+ QUIC_INTERNAL_ERROR, NO_IETF_QUIC_ERROR,
+ "because I can", 0x05);
QuicFrames frames = {QuicFrame(&close_frame)};
// clang-format off
@@ -7921,7 +7922,7 @@
static_cast<QuicErrorCode>(
VersionHasIetfQuicFrames(framer_.transport_version()) ? 0x01
: 0x05060708),
- "because I can", 0x05);
+ NO_IETF_QUIC_ERROR, "because I can", 0x05);
// Set this so that it is "there" for both Google QUIC and IETF QUIC
// framing. It better not show up for Google QUIC!
close_frame.quic_error_code = static_cast<QuicErrorCode>(0x4567);
@@ -8023,7 +8024,7 @@
header.packet_number = kPacketNumber;
QuicConnectionCloseFrame close_frame(framer_.transport_version(),
- QUIC_INTERNAL_ERROR,
+ QUIC_INTERNAL_ERROR, NO_IETF_QUIC_ERROR,
std::string(2048, 'A'), 0x05);
QuicFrames frames = {QuicFrame(&close_frame)};
@@ -11463,9 +11464,10 @@
framer_.transport_version(), QuicFrame(&rst_stream)));
std::string error_detail(2048, 'e');
- QuicConnectionCloseFrame connection_close(
- framer_.transport_version(), QUIC_NETWORK_IDLE_TIMEOUT, error_detail,
- /*transport_close_frame_type=*/0);
+ QuicConnectionCloseFrame connection_close(framer_.transport_version(),
+ QUIC_NETWORK_IDLE_TIMEOUT,
+ NO_IETF_QUIC_ERROR, error_detail,
+ /*transport_close_frame_type=*/0);
EXPECT_EQ(QuicFramer::GetConnectionCloseFrameSize(framer_.transport_version(),
connection_close),
diff --git a/quic/core/quic_packet_creator_test.cc b/quic/core/quic_packet_creator_test.cc
index 287a41d..da0c6b2 100644
--- a/quic/core/quic_packet_creator_test.cc
+++ b/quic/core/quic_packet_creator_test.cc
@@ -340,7 +340,7 @@
TEST_P(QuicPacketCreatorTest, SerializeConnectionClose) {
QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame(
- creator_.transport_version(), QUIC_NO_ERROR, "error",
+ creator_.transport_version(), QUIC_NO_ERROR, NO_IETF_QUIC_ERROR, "error",
/*transport_close_frame_type=*/0);
QuicFrames frames;
@@ -3554,7 +3554,8 @@
const QuicErrorCode kQuicErrorCode = QUIC_PACKET_WRITE_ERROR;
QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame(
- framer_.transport_version(), kQuicErrorCode, std::string(error_details),
+ framer_.transport_version(), kQuicErrorCode, NO_IETF_QUIC_ERROR,
+ std::string(error_details),
/*transport_close_frame_type=*/0);
creator_.ConsumeRetransmittableControlFrame(QuicFrame(frame),
/*bundle_ack=*/false);
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index 0c17d6c..26c6ea7 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -976,6 +976,14 @@
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
}
+void QuicSession::OnStreamError(QuicErrorCode error_code,
+ QuicIetfTransportErrorCodes ietf_error,
+ std::string error_details) {
+ connection_->CloseConnection(
+ error_code, ietf_error, error_details,
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+}
+
void QuicSession::SendMaxStreams(QuicStreamCount stream_count,
bool unidirectional) {
if (!is_configured_) {
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index 3eb2867..f3662f0 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -303,6 +303,9 @@
// Implement StreamDelegateInterface.
void OnStreamError(QuicErrorCode error_code,
std::string error_details) override;
+ void OnStreamError(QuicErrorCode error_code,
+ QuicIetfTransportErrorCodes ietf_error,
+ std::string error_details) override;
// Sets priority in the write blocked list.
void RegisterStreamPriority(
QuicStreamId id,
diff --git a/quic/core/quic_session_test.cc b/quic/core/quic_session_test.cc
index dbe9624..3288047 100644
--- a/quic/core/quic_session_test.cc
+++ b/quic/core/quic_session_test.cc
@@ -2174,7 +2174,7 @@
EXPECT_CALL(*connection_, CloseConnection(_, _, _))
.WillOnce(
Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
- EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _));
+ EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _));
connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
session_.OnConfigNegotiated();
diff --git a/quic/core/quic_stream.cc b/quic/core/quic_stream.cc
index 1ce96a5..5aa7150 100644
--- a/quic/core/quic_stream.cc
+++ b/quic/core/quic_stream.cc
@@ -154,6 +154,12 @@
stream_delegate_->OnStreamError(error, details);
}
+void PendingStream::OnUnrecoverableError(QuicErrorCode error,
+ QuicIetfTransportErrorCodes ietf_error,
+ const std::string& details) {
+ stream_delegate_->OnStreamError(error, ietf_error, details);
+}
+
QuicStreamId PendingStream::id() const {
return id_;
}
@@ -621,6 +627,12 @@
stream_delegate_->OnStreamError(error, details);
}
+void QuicStream::OnUnrecoverableError(QuicErrorCode error,
+ QuicIetfTransportErrorCodes ietf_error,
+ const std::string& details) {
+ stream_delegate_->OnStreamError(error, ietf_error, details);
+}
+
const spdy::SpdyStreamPrecedence& QuicStream::precedence() const {
return precedence_;
}
diff --git a/quic/core/quic_stream.h b/quic/core/quic_stream.h
index d13ea7e..83dd375 100644
--- a/quic/core/quic_stream.h
+++ b/quic/core/quic_stream.h
@@ -61,6 +61,9 @@
void Reset(QuicRstStreamErrorCode error) override;
void OnUnrecoverableError(QuicErrorCode error,
const std::string& details) override;
+ void OnUnrecoverableError(QuicErrorCode error,
+ QuicIetfTransportErrorCodes ietf_error,
+ const std::string& details) override;
QuicStreamId id() const override;
ParsedQuicVersion version() const override;
@@ -163,6 +166,9 @@
// this end.
void OnUnrecoverableError(QuicErrorCode error,
const std::string& details) override;
+ void OnUnrecoverableError(QuicErrorCode error,
+ QuicIetfTransportErrorCodes ietf_error,
+ const std::string& details) override;
// Called by the session when a (potentially duplicate) stream frame has been
// received for this stream.
diff --git a/quic/core/quic_stream_sequencer.h b/quic/core/quic_stream_sequencer.h
index e5530a6..b7ea49d 100644
--- a/quic/core/quic_stream_sequencer.h
+++ b/quic/core/quic_stream_sequencer.h
@@ -42,6 +42,11 @@
// being closed.
virtual void OnUnrecoverableError(QuicErrorCode error,
const std::string& details) = 0;
+ // Called when an error has occurred which should result in the connection
+ // being closed, specifying the wire error code |ietf_error| explicitly.
+ virtual void OnUnrecoverableError(QuicErrorCode error,
+ QuicIetfTransportErrorCodes ietf_error,
+ const std::string& details) = 0;
// Returns the stream id of this stream.
virtual QuicStreamId id() const = 0;
diff --git a/quic/core/quic_stream_sequencer_test.cc b/quic/core/quic_stream_sequencer_test.cc
index e30cee0..e8fe6f3 100644
--- a/quic/core/quic_stream_sequencer_test.cc
+++ b/quic/core/quic_stream_sequencer_test.cc
@@ -37,6 +37,12 @@
OnUnrecoverableError,
(QuicErrorCode error, const std::string& details),
(override));
+ MOCK_METHOD(void,
+ OnUnrecoverableError,
+ (QuicErrorCode error,
+ QuicIetfTransportErrorCodes ietf_error,
+ const std::string& details),
+ (override));
MOCK_METHOD(void, Reset, (QuicRstStreamErrorCode error), (override));
MOCK_METHOD(void, AddBytesConsumed, (QuicByteCount bytes), (override));
diff --git a/quic/core/stream_delegate_interface.h b/quic/core/stream_delegate_interface.h
index f29e15c..38b8e53 100644
--- a/quic/core/stream_delegate_interface.h
+++ b/quic/core/stream_delegate_interface.h
@@ -23,6 +23,11 @@
// Called when the stream has encountered errors that it can't handle.
virtual void OnStreamError(QuicErrorCode error_code,
std::string error_details) = 0;
+ // Called when the stream has encountered errors that it can't handle,
+ // specifying the wire error code |ietf_error| explicitly.
+ virtual void OnStreamError(QuicErrorCode error_code,
+ QuicIetfTransportErrorCodes ietf_error,
+ std::string error_details) = 0;
// Called when the stream needs to write data. If |level| is present, the data
// will be written at the specified |level|. The data will be written
// at specified transmission |type|.
diff --git a/quic/core/tls_chlo_extractor.cc b/quic/core/tls_chlo_extractor.cc
index 505ad28..89aaea8 100644
--- a/quic/core/tls_chlo_extractor.cc
+++ b/quic/core/tls_chlo_extractor.cc
@@ -142,6 +142,15 @@
"Crypto stream error ", QuicErrorCodeToString(error), ": ", details));
}
+void TlsChloExtractor::OnUnrecoverableError(
+ QuicErrorCode error,
+ QuicIetfTransportErrorCodes ietf_error,
+ const std::string& details) {
+ HandleUnrecoverableError(absl::StrCat(
+ "Crypto stream error ", QuicErrorCodeToString(error), "(",
+ QuicIetfTransportErrorCodeString(ietf_error), "): ", details));
+}
+
// This is called by the framer if it sees a CRYPTO frame during parsing.
bool TlsChloExtractor::OnCryptoFrame(const QuicCryptoFrame& frame) {
if (frame.level != ENCRYPTION_INITIAL) {
diff --git a/quic/core/tls_chlo_extractor.h b/quic/core/tls_chlo_extractor.h
index a95f52e..f7392a5 100644
--- a/quic/core/tls_chlo_extractor.h
+++ b/quic/core/tls_chlo_extractor.h
@@ -178,6 +178,9 @@
void Reset(QuicRstStreamErrorCode /*error*/) override {}
void OnUnrecoverableError(QuicErrorCode error,
const std::string& details) override;
+ void OnUnrecoverableError(QuicErrorCode error,
+ QuicIetfTransportErrorCodes ietf_error,
+ const std::string& details) override;
QuicStreamId id() const override { return 0; }
ParsedQuicVersion version() const override { return framer_->version(); }
diff --git a/quic/core/tls_client_handshaker_test.cc b/quic/core/tls_client_handshaker_test.cc
index 2c1a14a..19fdab5 100644
--- a/quic/core/tls_client_handshaker_test.cc
+++ b/quic/core/tls_client_handshaker_test.cc
@@ -280,7 +280,11 @@
TEST_P(TlsClientHandshakerTest, ConnectionClosedOnTlsError) {
// Have client send ClientHello.
stream()->CryptoConnect();
- EXPECT_CALL(*connection_, CloseConnection(QUIC_HANDSHAKE_FAILED, _, _));
+ if (GetQuicReloadableFlag(quic_send_tls_crypto_error_code)) {
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_HANDSHAKE_FAILED, _, _, _));
+ } else {
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_HANDSHAKE_FAILED, _, _));
+ }
// Send a zero-length ServerHello from server to client.
char bogus_handshake_message[] = {
@@ -562,11 +566,23 @@
.WillOnce([kTestAlpn](const std::vector<absl::string_view>& alpns) {
return std::find(alpns.cbegin(), alpns.cend(), kTestAlpn);
});
- EXPECT_CALL(*server_connection_,
- CloseConnection(QUIC_HANDSHAKE_FAILED,
- "TLS handshake failure (ENCRYPTION_INITIAL) 120: "
- "no application protocol",
- _));
+ if (GetQuicReloadableFlag(quic_send_tls_crypto_error_code)) {
+ EXPECT_CALL(
+ *server_connection_,
+ CloseConnection(
+ QUIC_HANDSHAKE_FAILED,
+ static_cast<QuicIetfTransportErrorCodes>(CRYPTO_ERROR_FIRST + 120),
+ "TLS handshake failure (ENCRYPTION_INITIAL) 120: "
+ "no application protocol",
+ _));
+ } else {
+ EXPECT_CALL(
+ *server_connection_,
+ CloseConnection(QUIC_HANDSHAKE_FAILED,
+ "TLS handshake failure (ENCRYPTION_INITIAL) 120: "
+ "no application protocol",
+ _));
+ }
stream()->CryptoConnect();
crypto_test_utils::AdvanceHandshake(connection_, stream(), 0,
diff --git a/quic/core/tls_handshaker.cc b/quic/core/tls_handshaker.cc
index 9d787e6..d0079c5 100644
--- a/quic/core/tls_handshaker.cc
+++ b/quic/core/tls_handshaker.cc
@@ -119,6 +119,14 @@
is_connection_closed_ = true;
}
+void TlsHandshaker::CloseConnection(QuicErrorCode error,
+ QuicIetfTransportErrorCodes ietf_error,
+ const std::string& reason_phrase) {
+ DCHECK(!reason_phrase.empty());
+ stream()->OnUnrecoverableError(error, ietf_error, reason_phrase);
+ is_connection_closed_ = true;
+}
+
void TlsHandshaker::OnConnectionClosed(QuicErrorCode /*error*/,
ConnectionCloseSource /*source*/) {
is_connection_closed_ = true;
@@ -290,17 +298,19 @@
void TlsHandshaker::FlushFlight() {}
void TlsHandshaker::SendAlert(EncryptionLevel level, uint8_t desc) {
- // TODO(b/151676147): Alerts should be sent on the wire as a varint QUIC error
- // code computed to be 0x100 | desc (draft-ietf-quic-tls-27, section 4.9).
- // This puts it in the range reserved for CRYPTO_ERROR
- // (draft-ietf-quic-transport-27, section 20). However, according to
- // quic_error_codes.h, this QUIC implementation only sends 1-byte error codes
- // right now.
std::string error_details = absl::StrCat(
"TLS handshake failure (", EncryptionLevelToString(level), ") ",
static_cast<int>(desc), ": ", SSL_alert_desc_string_long(desc));
QUIC_DLOG(ERROR) << error_details;
- CloseConnection(QUIC_HANDSHAKE_FAILED, error_details);
+ if (GetQuicReloadableFlag(quic_send_tls_crypto_error_code)) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_send_tls_crypto_error_code);
+ CloseConnection(
+ TlsAlertToQuicErrorCode(desc),
+ static_cast<QuicIetfTransportErrorCodes>(CRYPTO_ERROR_FIRST + desc),
+ error_details);
+ } else {
+ CloseConnection(QUIC_HANDSHAKE_FAILED, error_details);
+ }
}
} // namespace quic
diff --git a/quic/core/tls_handshaker.h b/quic/core/tls_handshaker.h
index af25ed5..4f8bf47 100644
--- a/quic/core/tls_handshaker.h
+++ b/quic/core/tls_handshaker.h
@@ -59,6 +59,11 @@
void AdvanceHandshake();
void CloseConnection(QuicErrorCode error, const std::string& reason_phrase);
+ // Closes the connection, specifying the wire error code |ietf_error|
+ // explicitly.
+ void CloseConnection(QuicErrorCode error,
+ QuicIetfTransportErrorCodes ietf_error,
+ const std::string& reason_phrase);
void OnConnectionClosed(QuicErrorCode error, ConnectionCloseSource source);
diff --git a/quic/core/tls_server_handshaker_test.cc b/quic/core/tls_server_handshaker_test.cc
index c0b1337..e2cb745 100644
--- a/quic/core/tls_server_handshaker_test.cc
+++ b/quic/core/tls_server_handshaker_test.cc
@@ -537,8 +537,13 @@
}
TEST_P(TlsServerHandshakerTest, ConnectionClosedOnTlsError) {
- EXPECT_CALL(*server_connection_,
- CloseConnection(QUIC_HANDSHAKE_FAILED, _, _));
+ if (GetQuicReloadableFlag(quic_send_tls_crypto_error_code)) {
+ EXPECT_CALL(*server_connection_,
+ CloseConnection(QUIC_HANDSHAKE_FAILED, _, _, _));
+ } else {
+ EXPECT_CALL(*server_connection_,
+ CloseConnection(QUIC_HANDSHAKE_FAILED, _, _));
+ }
// Send a zero-length ClientHello from client to server.
char bogus_handshake_message[] = {
@@ -558,11 +563,23 @@
const std::string kTestBadClientAlpn = "bad-client-alpn";
EXPECT_CALL(*client_session_, GetAlpnsToOffer())
.WillOnce(Return(std::vector<std::string>({kTestBadClientAlpn})));
- EXPECT_CALL(*server_connection_,
- CloseConnection(QUIC_HANDSHAKE_FAILED,
- "TLS handshake failure (ENCRYPTION_INITIAL) 120: "
- "no application protocol",
- _));
+ if (GetQuicReloadableFlag(quic_send_tls_crypto_error_code)) {
+ EXPECT_CALL(
+ *server_connection_,
+ CloseConnection(
+ QUIC_HANDSHAKE_FAILED,
+ static_cast<QuicIetfTransportErrorCodes>(CRYPTO_ERROR_FIRST + 120),
+ "TLS handshake failure (ENCRYPTION_INITIAL) 120: "
+ "no application protocol",
+ _));
+ } else {
+ EXPECT_CALL(
+ *server_connection_,
+ CloseConnection(QUIC_HANDSHAKE_FAILED,
+ "TLS handshake failure (ENCRYPTION_INITIAL) 120: "
+ "no application protocol",
+ _));
+ }
AdvanceHandshakeWithFakeClient();