| // Copyright (c) 2019 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.h" | |
| #include <cstdint> | |
| #include <limits> | |
| #include <memory> | |
| #include <string> | |
| #include <utility> | |
| #include "url/gurl.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_constants.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_data_writer.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_error_codes.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_session.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_types.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_versions.h" | |
| #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" | |
| #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" | |
| #include "net/third_party/quiche/src/quic/quic_transport/quic_transport_protocol.h" | |
| #include "net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h" | |
| #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" | |
| namespace quic { | |
| QuicTransportClientSession::QuicTransportClientSession( | |
| QuicConnection* connection, | |
| Visitor* owner, | |
| const QuicConfig& config, | |
| const ParsedQuicVersionVector& supported_versions, | |
| const GURL& url, | |
| QuicCryptoClientConfig* crypto_config, | |
| url::Origin origin, | |
| ClientVisitor* visitor) | |
| : QuicSession(connection, | |
| owner, | |
| config, | |
| supported_versions, | |
| /*num_expected_unidirectional_static_streams*/ 0), | |
| url_(url), | |
| origin_(origin), | |
| visitor_(visitor) { | |
| for (const ParsedQuicVersion& version : supported_versions) { | |
| QUIC_BUG_IF(version.handshake_protocol != PROTOCOL_TLS1_3) | |
| << "QuicTransport requires TLS 1.3 handshake"; | |
| } | |
| crypto_stream_ = std::make_unique<QuicCryptoClientStream>( | |
| QuicServerId(url.host(), url.EffectiveIntPort()), this, | |
| crypto_config->proof_verifier()->CreateDefaultContext(), crypto_config, | |
| /*proof_handler=*/this, /*has_application_state = */ true); | |
| } | |
| void QuicTransportClientSession::OnAlpnSelected( | |
| quiche::QuicheStringPiece alpn) { | |
| // Defense in-depth: ensure the ALPN selected is the desired one. | |
| if (alpn != QuicTransportAlpn()) { | |
| QUIC_BUG << "QuicTransport negotiated non-QuicTransport ALPN: " << alpn; | |
| connection()->CloseConnection( | |
| QUIC_INTERNAL_ERROR, "QuicTransport negotiated non-QuicTransport ALPN", | |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
| return; | |
| } | |
| alpn_received_ = true; | |
| } | |
| QuicStream* QuicTransportClientSession::CreateIncomingStream(QuicStreamId id) { | |
| QUIC_DVLOG(1) << "Creating incoming QuicTransport stream " << id; | |
| QuicTransportStream* stream = CreateStream(id); | |
| if (stream->type() == BIDIRECTIONAL) { | |
| incoming_bidirectional_streams_.push_back(stream); | |
| visitor_->OnIncomingBidirectionalStreamAvailable(); | |
| } else { | |
| incoming_unidirectional_streams_.push_back(stream); | |
| visitor_->OnIncomingUnidirectionalStreamAvailable(); | |
| } | |
| return stream; | |
| } | |
| void QuicTransportClientSession::SetDefaultEncryptionLevel( | |
| EncryptionLevel level) { | |
| QuicSession::SetDefaultEncryptionLevel(level); | |
| if (level == ENCRYPTION_FORWARD_SECURE) { | |
| SendClientIndication(); | |
| } | |
| } | |
| void QuicTransportClientSession::OnOneRttKeysAvailable() { | |
| QuicSession::OnOneRttKeysAvailable(); | |
| SendClientIndication(); | |
| } | |
| QuicTransportStream* | |
| QuicTransportClientSession::AcceptIncomingBidirectionalStream() { | |
| if (incoming_bidirectional_streams_.empty()) { | |
| return nullptr; | |
| } | |
| QuicTransportStream* stream = incoming_bidirectional_streams_.front(); | |
| incoming_bidirectional_streams_.pop_front(); | |
| return stream; | |
| } | |
| QuicTransportStream* | |
| QuicTransportClientSession::AcceptIncomingUnidirectionalStream() { | |
| if (incoming_unidirectional_streams_.empty()) { | |
| return nullptr; | |
| } | |
| QuicTransportStream* stream = incoming_unidirectional_streams_.front(); | |
| incoming_unidirectional_streams_.pop_front(); | |
| return stream; | |
| } | |
| QuicTransportStream* | |
| QuicTransportClientSession::OpenOutgoingBidirectionalStream() { | |
| if (!CanOpenNextOutgoingBidirectionalStream()) { | |
| QUIC_BUG << "Attempted to open a stream in violation of flow control"; | |
| return nullptr; | |
| } | |
| return CreateStream(GetNextOutgoingBidirectionalStreamId()); | |
| } | |
| QuicTransportStream* | |
| QuicTransportClientSession::OpenOutgoingUnidirectionalStream() { | |
| if (!CanOpenNextOutgoingUnidirectionalStream()) { | |
| QUIC_BUG << "Attempted to open a stream in violation of flow control"; | |
| return nullptr; | |
| } | |
| return CreateStream(GetNextOutgoingUnidirectionalStreamId()); | |
| } | |
| QuicTransportStream* QuicTransportClientSession::CreateStream(QuicStreamId id) { | |
| auto stream = std::make_unique<QuicTransportStream>(id, this, this); | |
| QuicTransportStream* stream_ptr = stream.get(); | |
| ActivateStream(std::move(stream)); | |
| return stream_ptr; | |
| } | |
| std::string QuicTransportClientSession::SerializeClientIndication() { | |
| std::string serialized_origin = origin_.Serialize(); | |
| if (serialized_origin.size() > std::numeric_limits<uint16_t>::max()) { | |
| QUIC_BUG << "Client origin too long"; | |
| connection()->CloseConnection( | |
| QUIC_INTERNAL_ERROR, "Client origin too long", | |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
| return ""; | |
| } | |
| QUIC_DLOG(INFO) << "Sending client indication with origin " | |
| << serialized_origin; | |
| std::string path = url_.PathForRequest(); | |
| if (path.size() > std::numeric_limits<uint16_t>::max()) { | |
| connection()->CloseConnection( | |
| QUIC_TRANSPORT_INVALID_CLIENT_INDICATION, "Requested URL path too long", | |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
| return ""; | |
| } | |
| constexpr size_t kPrefixSize = | |
| sizeof(QuicTransportClientIndicationKeys) + sizeof(uint16_t); | |
| const size_t buffer_size = | |
| 2 * kPrefixSize + serialized_origin.size() + path.size(); | |
| if (buffer_size > std::numeric_limits<uint16_t>::max()) { | |
| connection()->CloseConnection( | |
| QUIC_TRANSPORT_INVALID_CLIENT_INDICATION, | |
| "Client indication size limit exceeded", | |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
| return ""; | |
| } | |
| std::string buffer; | |
| buffer.resize(buffer_size); | |
| QuicDataWriter writer(buffer.size(), &buffer[0]); | |
| bool success = | |
| writer.WriteUInt16( | |
| static_cast<uint16_t>(QuicTransportClientIndicationKeys::kOrigin)) && | |
| writer.WriteUInt16(serialized_origin.size()) && | |
| writer.WriteStringPiece(serialized_origin) && | |
| writer.WriteUInt16( | |
| static_cast<uint16_t>(QuicTransportClientIndicationKeys::kPath)) && | |
| writer.WriteUInt16(path.size()) && writer.WriteStringPiece(path); | |
| QUIC_BUG_IF(!success) << "Failed to serialize client indication"; | |
| QUIC_BUG_IF(writer.length() != buffer.length()) | |
| << "Serialized client indication has length different from expected"; | |
| return buffer; | |
| } | |
| void QuicTransportClientSession::SendClientIndication() { | |
| if (!crypto_stream_->encryption_established()) { | |
| QUIC_BUG << "Client indication may only be sent once the encryption is " | |
| "established."; | |
| connection()->CloseConnection( | |
| QUIC_INTERNAL_ERROR, "Attempted to send client indication unencrypted", | |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
| return; | |
| } | |
| if (ready_) { | |
| QUIC_BUG << "Client indication may only be sent once."; | |
| connection()->CloseConnection( | |
| QUIC_INTERNAL_ERROR, "Attempted to send client indication twice", | |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
| return; | |
| } | |
| auto client_indication_owned = std::make_unique<ClientIndication>( | |
| /*stream_id=*/GetNextOutgoingUnidirectionalStreamId(), this, | |
| /*is_static=*/false, WRITE_UNIDIRECTIONAL); | |
| QUIC_BUG_IF(client_indication_owned->id() != ClientIndicationStream()) | |
| << "Client indication stream is " << client_indication_owned->id() | |
| << " instead of expected " << ClientIndicationStream(); | |
| ClientIndication* client_indication = client_indication_owned.get(); | |
| ActivateStream(std::move(client_indication_owned)); | |
| client_indication->WriteOrBufferData(SerializeClientIndication(), | |
| /*fin=*/true, nullptr); | |
| client_indication_sent_ = true; | |
| // Defense in depth: never set the ready bit unless ALPN has been confirmed. | |
| if (!alpn_received_) { | |
| QUIC_BUG << "ALPN confirmation missing after handshake complete"; | |
| connection()->CloseConnection( | |
| QUIC_INTERNAL_ERROR, | |
| "ALPN confirmation missing after handshake complete", | |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
| return; | |
| } | |
| // Don't set the ready bit if we closed the connection due to any error | |
| // beforehand. | |
| if (!connection()->connected()) { | |
| return; | |
| } | |
| ready_ = true; | |
| visitor_->OnSessionReady(); | |
| } | |
| void QuicTransportClientSession::OnMessageReceived( | |
| quiche::QuicheStringPiece message) { | |
| visitor_->OnDatagramReceived(message); | |
| } | |
| void QuicTransportClientSession::OnCanCreateNewOutgoingStream( | |
| bool unidirectional) { | |
| if (unidirectional) { | |
| visitor_->OnCanCreateNewOutgoingUnidirectionalStream(); | |
| } else { | |
| visitor_->OnCanCreateNewOutgoingBidirectionalStream(); | |
| } | |
| } | |
| void QuicTransportClientSession::OnProofValid( | |
| const QuicCryptoClientConfig::CachedState& /*cached*/) {} | |
| void QuicTransportClientSession::OnProofVerifyDetailsAvailable( | |
| const ProofVerifyDetails& /*verify_details*/) {} | |
| } // namespace quic |