blob: 1a2160b0e9d45bcf7c5ef9198070d9bdb4c17cbb [file] [log] [blame]
// 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 "quic/quic_transport/quic_transport_client_session.h"
#include <cstdint>
#include <limits>
#include <memory>
#include <string>
#include <utility>
#include "absl/strings/string_view.h"
#include "url/gurl.h"
#include "quic/core/quic_constants.h"
#include "quic/core/quic_crypto_client_stream.h"
#include "quic/core/quic_data_writer.h"
#include "quic/core/quic_error_codes.h"
#include "quic/core/quic_session.h"
#include "quic/core/quic_types.h"
#include "quic/core/quic_versions.h"
#include "quic/platform/api/quic_bug_tracker.h"
#include "quic/platform/api/quic_logging.h"
#include "quic/quic_transport/quic_transport_protocol.h"
#include "quic/quic_transport/quic_transport_stream.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,
WebTransportVisitor* visitor,
std::unique_ptr<QuicDatagramQueue::Observer> datagram_observer)
: QuicSession(connection,
owner,
config,
supported_versions,
/*num_expected_unidirectional_static_streams*/ 0,
std::move(datagram_observer)),
url_(url),
origin_(origin),
visitor_(visitor) {
for (const ParsedQuicVersion& version : supported_versions) {
QUIC_BUG_IF(quic_bug_12035_1, 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(absl::string_view alpn) {
// Defense in-depth: ensure the ALPN selected is the desired one.
if (alpn != QuicTransportAlpn()) {
QUIC_BUG(quic_bug_10881_1)
<< "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::OnTlsHandshakeComplete() {
QuicSession::OnTlsHandshakeComplete();
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(quic_bug_10881_2)
<< "Attempted to open a stream in violation of flow control";
return nullptr;
}
return CreateStream(GetNextOutgoingBidirectionalStreamId());
}
QuicTransportStream*
QuicTransportClientSession::OpenOutgoingUnidirectionalStream() {
if (!CanOpenNextOutgoingUnidirectionalStream()) {
QUIC_BUG(quic_bug_10881_3)
<< "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(quic_bug_10881_4) << "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(quic_bug_10881_5, !success)
<< "Failed to serialize client indication";
QUIC_BUG_IF(quic_bug_12035_2, writer.length() != buffer.length())
<< "Serialized client indication has length different from expected";
return buffer;
}
void QuicTransportClientSession::SendClientIndication() {
if (!crypto_stream_->encryption_established()) {
QUIC_BUG(quic_bug_10881_6)
<< "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(quic_bug_10881_7) << "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(quic_bug_12035_3,
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(quic_bug_10881_8)
<< "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(spdy::SpdyHeaderBlock());
}
void QuicTransportClientSession::OnMessageReceived(absl::string_view 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