| // 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 "absl/strings/string_view.h" |
| #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" |
| |
| namespace quic { |
| |
| namespace { |
| class QuicTransportServerCryptoHelper |
| : public QuicCryptoServerStreamBase::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_ = CreateCryptoServerStream( |
| 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; |
| } |
| |
| absl::string_view 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( |
| absl::string_view 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( |
| absl::string_view error_message) { |
| Error(quiche::QuicheStrCat("Failed to parse the client indication stream: ", |
| error_message, reader_.DebugString())); |
| } |
| |
| void QuicTransportServerSession::ProcessClientIndication( |
| absl::string_view 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 |