| // 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_server_session.h" | |
| #include <algorithm> | |
| #include <memory> | |
| #include <string> | |
| #include "url/gurl.h" | |
| #include "url/url_constants.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_error_codes.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_stream.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_types.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_str_cat.h" | |
| #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" | |
| namespace quic { | |
| namespace { | |
| class QuicTransportServerCryptoHelper : public QuicCryptoServerStream::Helper { | |
| public: | |
| bool CanAcceptClientHello(const CryptoHandshakeMessage& /*message*/, | |
| const QuicSocketAddress& /*client_address*/, | |
| const QuicSocketAddress& /*peer_address*/, | |
| const QuicSocketAddress& /*self_address*/, | |
| std::string* /*error_details*/) const override { | |
| return true; | |
| } | |
| }; | |
| } // namespace | |
| QuicTransportServerSession::QuicTransportServerSession( | |
| QuicConnection* connection, | |
| Visitor* owner, | |
| const QuicConfig& config, | |
| const ParsedQuicVersionVector& supported_versions, | |
| const QuicCryptoServerConfig* crypto_config, | |
| QuicCompressedCertsCache* compressed_certs_cache, | |
| ServerVisitor* visitor) | |
| : QuicSession(connection, | |
| owner, | |
| config, | |
| supported_versions, | |
| /*num_expected_unidirectional_static_streams*/ 0), | |
| visitor_(visitor) { | |
| for (const ParsedQuicVersion& version : supported_versions) { | |
| QUIC_BUG_IF(version.handshake_protocol != PROTOCOL_TLS1_3) | |
| << "QuicTransport requires TLS 1.3 handshake"; | |
| } | |
| static QuicTransportServerCryptoHelper* helper = | |
| new QuicTransportServerCryptoHelper(); | |
| crypto_stream_ = std::make_unique<QuicCryptoServerStream>( | |
| crypto_config, compressed_certs_cache, this, helper); | |
| } | |
| QuicStream* QuicTransportServerSession::CreateIncomingStream(QuicStreamId id) { | |
| if (id == ClientIndicationStream()) { | |
| auto indication = std::make_unique<ClientIndication>(this); | |
| ClientIndication* indication_ptr = indication.get(); | |
| ActivateStream(std::move(indication)); | |
| return indication_ptr; | |
| } | |
| auto stream = std::make_unique<QuicTransportStream>(id, this, this); | |
| QuicTransportStream* stream_ptr = stream.get(); | |
| ActivateStream(std::move(stream)); | |
| OnIncomingDataStream(stream_ptr); | |
| return stream_ptr; | |
| } | |
| QuicTransportServerSession::ClientIndication::ClientIndication( | |
| QuicTransportServerSession* session) | |
| : QuicStream(ClientIndicationStream(), | |
| session, | |
| /* is_static= */ false, | |
| StreamType::READ_UNIDIRECTIONAL), | |
| session_(session) {} | |
| void QuicTransportServerSession::ClientIndication::OnDataAvailable() { | |
| sequencer()->Read(&buffer_); | |
| if (buffer_.size() > ClientIndicationMaxSize()) { | |
| session_->connection()->CloseConnection( | |
| QUIC_TRANSPORT_INVALID_CLIENT_INDICATION, | |
| quiche::QuicheStrCat("Client indication size exceeds ", | |
| ClientIndicationMaxSize(), " bytes"), | |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
| return; | |
| } | |
| if (sequencer()->IsClosed()) { | |
| session_->ProcessClientIndication(buffer_); | |
| OnFinRead(); | |
| } | |
| } | |
| bool QuicTransportServerSession::ClientIndicationParser::Parse() { | |
| bool origin_received = false; | |
| bool path_received = false; | |
| while (!reader_.IsDoneReading()) { | |
| uint16_t key; | |
| if (!reader_.ReadUInt16(&key)) { | |
| ParseError("Expected 16-bit key"); | |
| return false; | |
| } | |
| quiche::QuicheStringPiece value; | |
| if (!reader_.ReadStringPiece16(&value)) { | |
| ParseError(quiche::QuicheStrCat("Failed to read value for key ", key)); | |
| return false; | |
| } | |
| switch (static_cast<QuicTransportClientIndicationKeys>(key)) { | |
| case QuicTransportClientIndicationKeys::kOrigin: { | |
| GURL origin_url{std::string(value)}; | |
| if (!origin_url.is_valid()) { | |
| Error("Unable to parse the specified origin"); | |
| return false; | |
| } | |
| url::Origin origin = url::Origin::Create(origin_url); | |
| QUIC_DLOG(INFO) << "QuicTransport server received origin " << origin; | |
| if (!session_->visitor_->CheckOrigin(origin)) { | |
| Error("Origin check failed"); | |
| return false; | |
| } | |
| origin_received = true; | |
| break; | |
| } | |
| case QuicTransportClientIndicationKeys::kPath: { | |
| if (!ProcessPath(value)) { | |
| return false; | |
| } | |
| path_received = true; | |
| break; | |
| } | |
| default: | |
| QUIC_DLOG(INFO) << "Unknown client indication key: " << key; | |
| break; | |
| } | |
| } | |
| if (!origin_received) { | |
| Error("No origin received"); | |
| return false; | |
| } | |
| if (!path_received) { | |
| Error("No path received"); | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool QuicTransportServerSession::ClientIndicationParser::ProcessPath( | |
| quiche::QuicheStringPiece path) { | |
| if (path.empty() || path[0] != '/') { | |
| // https://tools.ietf.org/html/draft-vvv-webtransport-quic-01#section-3.2.2 | |
| Error("Path must begin with a '/'"); | |
| return false; | |
| } | |
| // TODO(b/145674008): use the SNI value from the handshake instead of the IP | |
| // address. | |
| std::string url_text = quiche::QuicheStrCat( | |
| url::kQuicTransportScheme, url::kStandardSchemeSeparator, | |
| session_->self_address().ToString(), path); | |
| GURL url{url_text}; | |
| if (!url.is_valid()) { | |
| Error("Invalid path specified"); | |
| return false; | |
| } | |
| if (!session_->visitor_->ProcessPath(url)) { | |
| Error("Specified path rejected"); | |
| return false; | |
| } | |
| return true; | |
| } | |
| void QuicTransportServerSession::ClientIndicationParser::Error( | |
| const std::string& error_message) { | |
| session_->connection()->CloseConnection( | |
| QUIC_TRANSPORT_INVALID_CLIENT_INDICATION, error_message, | |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
| } | |
| void QuicTransportServerSession::ClientIndicationParser::ParseError( | |
| quiche::QuicheStringPiece error_message) { | |
| Error(quiche::QuicheStrCat("Failed to parse the client indication stream: ", | |
| error_message, reader_.DebugString())); | |
| } | |
| void QuicTransportServerSession::ProcessClientIndication( | |
| quiche::QuicheStringPiece indication) { | |
| ClientIndicationParser parser(this, indication); | |
| if (!parser.Parse()) { | |
| return; | |
| } | |
| // Don't set the ready bit if we closed the connection due to any error | |
| // beforehand. | |
| if (!connection()->connected()) { | |
| return; | |
| } | |
| ready_ = true; | |
| } | |
| } // namespace quic |