MASQUE initial drop
diff --git a/quic/masque/README.md b/quic/masque/README.md new file mode 100644 index 0000000..6bfc08e --- /dev/null +++ b/quic/masque/README.md
@@ -0,0 +1,4 @@ +# MASQUE + +The files in this directory implement MASQUE as described in +<https://tools.ietf.org/html/draft-schinazi-masque>.
diff --git a/quic/masque/masque_client_bin.cc b/quic/masque/masque_client_bin.cc new file mode 100644 index 0000000..abc4995 --- /dev/null +++ b/quic/masque/masque_client_bin.cc
@@ -0,0 +1,175 @@ +// Copyright 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 <memory> +#include "net/third_party/quiche/src/quic/core/quic_server_id.h" +#include "net/third_party/quiche/src/quic/masque/masque_encapsulated_epoll_client.h" +#include "net/third_party/quiche/src/quic/masque/masque_epoll_client.h" +#include "net/third_party/quiche/src/quic/masque/masque_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_default_proof_providers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_system_event_loop.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/tools/fake_proof_verifier.h" +#include "net/third_party/quiche/src/quic/tools/quic_url.h" + +DEFINE_QUIC_COMMAND_LINE_FLAG(bool, + disable_certificate_verification, + false, + "If true, don't verify the server certificate."); + +namespace quic { + +namespace { + +bool SendRequest(MasqueEpollClient* masque_client, + QuicEpollServer* epoll_server, + std::string url_string, + bool disable_certificate_verification) { + QuicUrl url(url_string, "https"); + std::unique_ptr<ProofVerifier> proof_verifier; + if (disable_certificate_verification) { + proof_verifier = std::make_unique<FakeProofVerifier>(); + } else { + proof_verifier = CreateDefaultProofVerifier(url.host()); + } + + // Build the client, and try to connect. + QuicSocketAddress addr = + tools::LookupAddress(url.host(), QuicStrCat(url.port())); + if (!addr.IsInitialized()) { + QUIC_LOG(ERROR) << "Unable to resolve address: " << url.host(); + return false; + } + QuicServerId server_id(url.host(), url.port(), + /*privacy_mode_enabled=*/false); + auto client = std::make_unique<MasqueEncapsulatedEpollClient>( + addr, server_id, epoll_server, std::move(proof_verifier), masque_client); + + if (client == nullptr) { + QUIC_LOG(ERROR) << "Failed to create MasqueEncapsulatedEpollClient for " + << url_string; + return false; + } + + client->set_initial_max_packet_length(kMasqueMaxEncapsulatedPacketSize); + client->set_drop_response_body(false); + if (!client->Initialize()) { + QUIC_LOG(ERROR) << "Failed to initialize MasqueEncapsulatedEpollClient for " + << url_string; + return false; + } + + if (!client->Connect()) { + QuicErrorCode error = client->session()->error(); + QUIC_LOG(ERROR) << "Failed to connect with client " + << client->session()->connection()->client_connection_id() + << " server " << client->session()->connection_id() + << " to " << url.HostPort() + << ". Error: " << QuicErrorCodeToString(error); + return false; + } + + std::cerr << "Connected client " + << client->session()->connection()->client_connection_id() + << " server " << client->session()->connection_id() << " for " + << url_string << std::endl; + + // Construct the string body from flags, if provided. + std::string body = "foo"; + + // Construct a GET or POST request for supplied URL. + spdy::SpdyHeaderBlock header_block; + header_block[":method"] = "GET"; + header_block[":scheme"] = url.scheme(); + header_block[":authority"] = url.HostPort(); + header_block[":path"] = url.PathParamsQuery(); + + // Make sure to store the response, for later output. + client->set_store_response(true); + + // Send the MASQUE init request. + client->SendRequestAndWaitForResponse(header_block, body, + /*fin=*/true); + + if (!client->connected()) { + QUIC_LOG(ERROR) << "Request for " << url_string + << " caused connection failure. Error: " + << QuicErrorCodeToString(client->session()->error()); + return false; + } + + const int response_code = client->latest_response_code(); + if (response_code < 200 || response_code >= 300) { + QUIC_LOG(ERROR) << "Request for " << url_string + << " failed with HTTP response code " << response_code; + return false; + } + + std::string response_body = client->latest_response_body(); + std::cerr << "Request succeeded for " << url_string << std::endl + << response_body << std::endl; + + return true; +} + +int RunMasqueClient(int argc, char* argv[]) { + QuicSystemEventLoop event_loop("masque_client"); + const char* usage = "Usage: masque_client [options] <url>"; + + // All non-flag arguments should be interpreted as URLs to fetch. + std::vector<std::string> urls = QuicParseCommandLineFlags(usage, argc, argv); + if (urls.empty()) { + QuicPrintCommandLineFlagHelp(usage); + return 1; + } + + const bool disable_certificate_verification = + GetQuicFlag(FLAGS_disable_certificate_verification); + QuicEpollServer epoll_server; + + QuicUrl masque_url(urls[0], "https"); + if (masque_url.host().empty()) { + masque_url = QuicUrl(QuicStrCat("https://", urls[0]), "https"); + } + if (masque_url.host().empty()) { + QUIC_LOG(ERROR) << "Failed to parse MASQUE server address " << urls[0]; + return 1; + } + std::unique_ptr<ProofVerifier> proof_verifier; + if (disable_certificate_verification) { + proof_verifier = std::make_unique<FakeProofVerifier>(); + } else { + proof_verifier = CreateDefaultProofVerifier(masque_url.host()); + } + std::unique_ptr<MasqueEpollClient> masque_client = + MasqueEpollClient::Create(masque_url.host(), masque_url.port(), + &epoll_server, std::move(proof_verifier)); + if (masque_client == nullptr) { + return 1; + } + + std::cerr << "MASQUE is connected " << masque_client->connection_id() + << std::endl; + + for (size_t i = 1; i < urls.size(); ++i) { + if (!SendRequest(masque_client.get(), &epoll_server, urls[i], + disable_certificate_verification)) { + return 1; + } + } + + return 0; +} + +} // namespace + +} // namespace quic + +int main(int argc, char* argv[]) { + return quic::RunMasqueClient(argc, argv); +}
diff --git a/quic/masque/masque_client_session.cc b/quic/masque/masque_client_session.cc new file mode 100644 index 0000000..a2ebe46 --- /dev/null +++ b/quic/masque/masque_client_session.cc
@@ -0,0 +1,96 @@ +// Copyright 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/masque/masque_client_session.h" + +namespace quic { + +MasqueClientSession::MasqueClientSession( + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection, + const QuicServerId& server_id, + QuicCryptoClientConfig* crypto_config, + QuicClientPushPromiseIndex* push_promise_index, + Visitor* owner) + : QuicSpdyClientSession(config, + supported_versions, + connection, + server_id, + crypto_config, + push_promise_index), + owner_(owner), + compression_engine_(this) {} + +void MasqueClientSession::OnMessageReceived(QuicStringPiece message) { + QUIC_DVLOG(1) << "Received message of length " << message.length(); + + QuicConnectionId client_connection_id, server_connection_id; + QuicSocketAddress server_socket_address; + std::string packet; + bool version_present; + if (!compression_engine_.DecompressMessage( + message, &client_connection_id, &server_connection_id, + &server_socket_address, &packet, &version_present)) { + return; + } + + auto connection_id_registration = + client_connection_id_registrations_.find(client_connection_id); + if (connection_id_registration == client_connection_id_registrations_.end()) { + QUIC_DLOG(ERROR) << "MasqueClientSession failed to dispatch " + << client_connection_id; + return; + } + EncapsulatedClientSession* encapsulated_client_session = + connection_id_registration->second; + encapsulated_client_session->ProcessPacket(packet, server_socket_address); + + QUIC_DVLOG(1) << "Sent " << packet.length() << " bytes to connection for " + << client_connection_id; +} + +void MasqueClientSession::OnMessageAcked(QuicMessageId message_id, + QuicTime /*receive_timestamp*/) { + QUIC_DVLOG(1) << "Received ack for message " << message_id; +} + +void MasqueClientSession::OnMessageLost(QuicMessageId message_id) { + QUIC_DVLOG(1) << "We believe message " << message_id << " was lost"; +} + +void MasqueClientSession::SendPacket( + QuicConnectionId client_connection_id, + QuicConnectionId server_connection_id, + QuicStringPiece packet, + const QuicSocketAddress& server_socket_address) { + compression_engine_.CompressAndSendPacket(packet, client_connection_id, + server_connection_id, + server_socket_address); +} + +void MasqueClientSession::RegisterConnectionId( + QuicConnectionId client_connection_id, + EncapsulatedClientSession* encapsulated_client_session) { + QUIC_DLOG(INFO) << "Registering " << client_connection_id + << " to encapsulated client"; + DCHECK(client_connection_id_registrations_.find(client_connection_id) == + client_connection_id_registrations_.end() || + client_connection_id_registrations_[client_connection_id] == + encapsulated_client_session); + client_connection_id_registrations_[client_connection_id] = + encapsulated_client_session; +} + +void MasqueClientSession::UnregisterConnectionId( + QuicConnectionId client_connection_id) { + QUIC_DLOG(INFO) << "Unregistering " << client_connection_id; + DCHECK(client_connection_id_registrations_.find(client_connection_id) != + client_connection_id_registrations_.end()); + client_connection_id_registrations_.erase(client_connection_id); + owner_->SendClientConnectionIdUnregister(client_connection_id); + compression_engine_.UnregisterClientConnectionId(client_connection_id); +} + +} // namespace quic
diff --git a/quic/masque/masque_client_session.h b/quic/masque/masque_client_session.h new file mode 100644 index 0000000..9db3862 --- /dev/null +++ b/quic/masque/masque_client_session.h
@@ -0,0 +1,85 @@ +// Copyright 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. + +#ifndef QUICHE_QUIC_MASQUE_MASQUE_CLIENT_SESSION_H_ +#define QUICHE_QUIC_MASQUE_MASQUE_CLIENT_SESSION_H_ + +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h" +#include "net/third_party/quiche/src/quic/masque/masque_compression_engine.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// QUIC client session for connection to MASQUE proxy. +class QUIC_EXPORT_PRIVATE MasqueClientSession : public QuicSpdyClientSession { + public: + // Visitor interface meant to be implemented by the owner of the + // MasqueClientSession instance. + class QUIC_EXPORT_PRIVATE Visitor { + public: + virtual ~Visitor() {} + + // Notifies the visitor that the client connection ID is no longer in use. + virtual void SendClientConnectionIdUnregister( + QuicConnectionId client_connection_id) = 0; + }; + // Interface meant to be implemented by encapsulated client sessions. + class QUIC_EXPORT_PRIVATE EncapsulatedClientSession { + public: + virtual ~EncapsulatedClientSession() {} + + // Process packet that was just decapsulated. + virtual void ProcessPacket(QuicStringPiece packet, + QuicSocketAddress server_socket_address) = 0; + }; + + MasqueClientSession(const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection, + const QuicServerId& server_id, + QuicCryptoClientConfig* crypto_config, + QuicClientPushPromiseIndex* push_promise_index, + Visitor* owner); + + // From QuicSession. + void OnMessageReceived(QuicStringPiece message) override; + + void OnMessageAcked(QuicMessageId message_id, + QuicTime receive_timestamp) override; + + void OnMessageLost(QuicMessageId message_id) override; + + // Send encapsulated packet. + void SendPacket(QuicConnectionId client_connection_id, + QuicConnectionId server_connection_id, + QuicStringPiece packet, + const QuicSocketAddress& server_socket_address); + + // Register encapsulated client. + void RegisterConnectionId( + QuicConnectionId client_connection_id, + EncapsulatedClientSession* encapsulated_client_session); + + // Unregister encapsulated client. + void UnregisterConnectionId(QuicConnectionId client_connection_id); + + // Disallow default constructor, copy, and assign. + MasqueClientSession() = delete; + MasqueClientSession(const MasqueClientSession&) = delete; + MasqueClientSession& operator=(const MasqueClientSession&) = delete; + + private: + QuicUnorderedMap<QuicConnectionId, + EncapsulatedClientSession*, + QuicConnectionIdHash> + client_connection_id_registrations_; + Visitor* owner_; // Unowned; + MasqueCompressionEngine compression_engine_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_MASQUE_MASQUE_CLIENT_SESSION_H_
diff --git a/quic/masque/masque_compression_engine.cc b/quic/masque/masque_compression_engine.cc new file mode 100644 index 0000000..99def66 --- /dev/null +++ b/quic/masque/masque_compression_engine.cc
@@ -0,0 +1,476 @@ +// Copyright 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/masque/masque_compression_engine.h" + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/core/quic_data_reader.h" +#include "net/third_party/quiche/src/quic/core/quic_data_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_framer.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_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" + +namespace quic { + +namespace { +const QuicDatagramFlowId kFlowId0 = 0; + +enum MasqueAddressFamily : uint8_t { + MasqueAddressFamilyIPv4 = 4, + MasqueAddressFamilyIPv6 = 6, +}; + +} // namespace + +MasqueCompressionEngine::MasqueCompressionEngine(QuicSession* masque_session) + : masque_session_(masque_session) { + if (masque_session_->perspective() == Perspective::IS_SERVER) { + next_flow_id_ = 1; + } else { + next_flow_id_ = 2; + } +} + +void MasqueCompressionEngine::CompressAndSendPacket( + QuicStringPiece packet, + QuicConnectionId client_connection_id, + QuicConnectionId server_connection_id, + const QuicSocketAddress& server_socket_address) { + QUIC_DVLOG(2) << "Compressing client " << client_connection_id << " server " + << server_connection_id << "\n" + << QuicTextUtils::HexDump(packet); + DCHECK(server_socket_address.IsInitialized()); + if (packet.empty()) { + QUIC_BUG << "Tried to send empty packet"; + return; + } + QuicDataReader reader(packet.data(), packet.length()); + uint8_t first_byte; + if (!reader.ReadUInt8(&first_byte)) { + QUIC_BUG << "Failed to read first_byte"; + return; + } + const bool long_header = (first_byte & FLAGS_LONG_HEADER) != 0; + bool client_connection_id_present = true, server_connection_id_present = true; + QuicConnectionId destination_connection_id, source_connection_id; + if (masque_session_->perspective() == Perspective::IS_SERVER) { + destination_connection_id = client_connection_id; + source_connection_id = server_connection_id; + if (!long_header) { + server_connection_id_present = false; + } + } else { + destination_connection_id = server_connection_id; + source_connection_id = client_connection_id; + if (!long_header) { + client_connection_id_present = false; + } + } + + QuicDatagramFlowId flow_id = 0; + bool validated = false; + for (auto kv : contexts_) { + const MasqueCompressionContext& context = kv.second; + if (context.server_socket_address != server_socket_address) { + continue; + } + if (client_connection_id_present && + context.client_connection_id != client_connection_id) { + continue; + } + if (server_connection_id_present && + context.server_connection_id != server_connection_id) { + continue; + } + flow_id = kv.first; + DCHECK_NE(flow_id, kFlowId0); + validated = context.validated; + QUIC_DVLOG(1) << "Compressing using " << (validated ? "" : "un") + << "validated flow_id " << flow_id << " to " + << context.server_socket_address << " client " + << context.client_connection_id << " server " + << context.server_connection_id; + break; + } + if (flow_id == 0) { + flow_id = GetNextFlowId(); + QUIC_DVLOG(1) << "Compression assigning new flow_id " << flow_id << " to " + << server_socket_address << " client " << client_connection_id + << " server " << server_connection_id; + MasqueCompressionContext context; + context.client_connection_id = client_connection_id; + context.server_connection_id = server_connection_id; + context.server_socket_address = server_socket_address; + contexts_[flow_id] = context; + } + size_t slice_length = packet.length() - destination_connection_id.length(); + if (long_header) { + slice_length -= sizeof(uint8_t) * 2 + source_connection_id.length(); + } + if (validated) { + slice_length += QuicDataWriter::GetVarInt62Len(flow_id); + } else { + slice_length += QuicDataWriter::GetVarInt62Len(kFlowId0) + + QuicDataWriter::GetVarInt62Len(flow_id) + sizeof(uint8_t) + + client_connection_id.length() + sizeof(uint8_t) + + server_connection_id.length() + + sizeof(server_socket_address.port()) + sizeof(uint8_t) + + server_socket_address.host().ToPackedString().length(); + } + QuicMemSlice slice( + masque_session_->connection()->helper()->GetStreamSendBufferAllocator(), + slice_length); + QuicDataWriter writer(slice_length, const_cast<char*>(slice.data())); + if (validated) { + QUIC_DVLOG(1) << "Compressing using validated flow_id " << flow_id; + if (!writer.WriteVarInt62(flow_id)) { + QUIC_BUG << "Failed to write flow_id"; + return; + } + } else { + QUIC_DVLOG(1) << "Compressing using unvalidated flow_id " << flow_id; + if (!writer.WriteVarInt62(kFlowId0)) { + QUIC_BUG << "Failed to write kFlowId0"; + return; + } + if (!writer.WriteVarInt62(flow_id)) { + QUIC_BUG << "Failed to write flow_id"; + return; + } + if (!writer.WriteLengthPrefixedConnectionId(client_connection_id)) { + QUIC_BUG << "Failed to write client_connection_id"; + return; + } + if (!writer.WriteLengthPrefixedConnectionId(server_connection_id)) { + QUIC_BUG << "Failed to write server_connection_id"; + return; + } + if (!writer.WriteUInt16(server_socket_address.port())) { + QUIC_BUG << "Failed to write port"; + return; + } + QuicIpAddress peer_ip = server_socket_address.host(); + DCHECK(peer_ip.IsInitialized()); + std::string peer_ip_bytes = peer_ip.ToPackedString(); + DCHECK(!peer_ip_bytes.empty()); + uint8_t address_id; + if (peer_ip.address_family() == IpAddressFamily::IP_V6) { + address_id = MasqueAddressFamilyIPv6; + if (peer_ip_bytes.length() != QuicIpAddress::kIPv6AddressSize) { + QUIC_BUG << "Bad IPv6 length " << server_socket_address; + return; + } + } else if (peer_ip.address_family() == IpAddressFamily::IP_V4) { + address_id = MasqueAddressFamilyIPv4; + if (peer_ip_bytes.length() != QuicIpAddress::kIPv4AddressSize) { + QUIC_BUG << "Bad IPv4 length " << server_socket_address; + return; + } + } else { + QUIC_BUG << "Unexpected server_socket_address " << server_socket_address; + return; + } + if (!writer.WriteUInt8(address_id)) { + QUIC_BUG << "Failed to write address_id"; + return; + } + if (!writer.WriteStringPiece(peer_ip_bytes)) { + QUIC_BUG << "Failed to write IP address"; + return; + } + } + if (!writer.WriteUInt8(first_byte)) { + QUIC_BUG << "Failed to write first_byte"; + return; + } + if (long_header) { + QuicVersionLabel version_label; + if (!reader.ReadUInt32(&version_label)) { + QUIC_DLOG(ERROR) << "Failed to read version"; + return; + } + if (!writer.WriteUInt32(version_label)) { + QUIC_BUG << "Failed to write version"; + return; + } + QuicConnectionId packet_destination_connection_id, + packet_source_connection_id; + if (!reader.ReadLengthPrefixedConnectionId( + &packet_destination_connection_id) || + !reader.ReadLengthPrefixedConnectionId(&packet_source_connection_id)) { + QUIC_DLOG(ERROR) << "Failed to parse long header connection IDs"; + return; + } + if (packet_destination_connection_id != destination_connection_id) { + QUIC_DLOG(ERROR) << "Long header packet's destination_connection_id " + << packet_destination_connection_id + << " does not match expected " + << destination_connection_id; + return; + } + if (packet_source_connection_id != source_connection_id) { + QUIC_DLOG(ERROR) << "Long header packet's source_connection_id " + << packet_source_connection_id + << " does not match expected " << source_connection_id; + return; + } + } else { + QuicConnectionId packet_destination_connection_id; + if (!reader.ReadConnectionId(&packet_destination_connection_id, + destination_connection_id.length())) { + QUIC_DLOG(ERROR) + << "Failed to read short header packet's destination_connection_id"; + return; + } + if (packet_destination_connection_id != destination_connection_id) { + QUIC_DLOG(ERROR) << "Short header packet's destination_connection_id " + << packet_destination_connection_id + << " does not match expected " + << destination_connection_id; + return; + } + } + QuicStringPiece packet_payload = reader.ReadRemainingPayload(); + if (!writer.WriteStringPiece(packet_payload)) { + QUIC_BUG << "Failed to write packet_payload"; + return; + } + MessageResult message_result = + masque_session_->SendMessage(QuicMemSliceSpan(&slice)); + + QUIC_DVLOG(1) << "Message result " << message_result; +} + +bool MasqueCompressionEngine::DecompressMessage( + QuicStringPiece message, + QuicConnectionId* client_connection_id, + QuicConnectionId* server_connection_id, + QuicSocketAddress* server_socket_address, + std::string* packet, + bool* version_present) { + QUIC_DVLOG(1) << "Decompressing message of length " << message.length(); + QuicDataReader reader(message); + QuicDatagramFlowId flow_id; + if (!reader.ReadVarInt62(&flow_id)) { + QUIC_DLOG(ERROR) << "Could not read flow_id"; + return false; + } + MasqueCompressionContext context; + if (flow_id == kFlowId0) { + QuicDatagramFlowId new_flow_id; + if (!reader.ReadVarInt62(&new_flow_id)) { + QUIC_DLOG(ERROR) << "Could not read new_flow_id"; + return false; + } + QuicConnectionId new_client_connection_id; + if (!reader.ReadLengthPrefixedConnectionId(&new_client_connection_id)) { + QUIC_DLOG(ERROR) << "Could not read new_client_connection_id"; + return false; + } + QuicConnectionId new_server_connection_id; + if (!reader.ReadLengthPrefixedConnectionId(&new_server_connection_id)) { + QUIC_DLOG(ERROR) << "Could not read new_server_connection_id"; + return false; + } + uint16_t port; + if (!reader.ReadUInt16(&port)) { + QUIC_DLOG(ERROR) << "Could not read port"; + return false; + } + uint8_t address_id; + if (!reader.ReadUInt8(&address_id)) { + QUIC_DLOG(ERROR) << "Could not read address_id"; + return false; + } + size_t ip_bytes_length; + if (address_id == MasqueAddressFamilyIPv6) { + ip_bytes_length = QuicIpAddress::kIPv6AddressSize; + } else if (address_id == MasqueAddressFamilyIPv4) { + ip_bytes_length = QuicIpAddress::kIPv4AddressSize; + } else { + QUIC_DLOG(ERROR) << "Unknown address_id " << static_cast<int>(address_id); + return false; + } + char ip_bytes[QuicIpAddress::kMaxAddressSize]; + if (!reader.ReadBytes(ip_bytes, ip_bytes_length)) { + QUIC_DLOG(ERROR) << "Could not read IP address"; + return false; + } + QuicIpAddress ip_address; + ip_address.FromPackedString(ip_bytes, ip_bytes_length); + if (!ip_address.IsInitialized()) { + QUIC_BUG << "Failed to parse IP address"; + return false; + } + QuicSocketAddress new_server_socket_address = + QuicSocketAddress(ip_address, port); + auto context_pair = contexts_.find(new_flow_id); + if (context_pair == contexts_.end()) { + context.client_connection_id = new_client_connection_id; + context.server_connection_id = new_server_connection_id; + context.server_socket_address = new_server_socket_address; + context.validated = true; + contexts_[new_flow_id] = context; + QUIC_DVLOG(1) << "Registered new flow_id " << new_flow_id << " to " + << new_server_socket_address << " client " + << new_client_connection_id << " server " + << new_server_connection_id; + } else { + context = context_pair->second; + if (context.client_connection_id != new_client_connection_id) { + QUIC_LOG(ERROR) + << "Received incorrect context registration for existing flow_id " + << new_flow_id << " mismatched client " + << context.client_connection_id << " " << new_client_connection_id; + return false; + } + if (context.server_connection_id != new_server_connection_id) { + QUIC_LOG(ERROR) + << "Received incorrect context registration for existing flow_id " + << new_flow_id << " mismatched server " + << context.server_connection_id << " " << new_server_connection_id; + return false; + } + if (context.server_socket_address != new_server_socket_address) { + QUIC_LOG(ERROR) + << "Received incorrect context registration for existing flow_id " + << new_flow_id << " mismatched server " + << context.server_socket_address << " " + << new_server_socket_address; + return false; + } + if (!context.validated) { + context.validated = true; + contexts_[new_flow_id] = context; + QUIC_DLOG(INFO) + << "Successfully validated remotely-unvalidated flow_id " + << new_flow_id << " to " << new_server_socket_address << " client " + << new_client_connection_id << " server " + << new_server_connection_id; + } else { + QUIC_DVLOG(1) << "Decompressing using incoming locally-validated " + "remotely-unvalidated flow_id " + << new_flow_id << " to " << new_server_socket_address + << " client " << new_client_connection_id << " server " + << new_server_connection_id; + } + } + } else { + auto context_pair = contexts_.find(flow_id); + if (context_pair == contexts_.end()) { + QUIC_DLOG(ERROR) << "Received unknown flow_id " << flow_id; + return false; + } + context = context_pair->second; + + if (!context.validated) { + context.validated = true; + contexts_[flow_id] = context; + QUIC_DLOG(INFO) << "Successfully validated remotely-validated flow_id " + << flow_id << " to " << context.server_socket_address + << " client " << context.client_connection_id + << " server " << context.server_connection_id; + } else { + QUIC_DVLOG(1) << "Decompressing using incoming locally-validated " + "remotely-validated flow_id " + << flow_id << " to " << context.server_socket_address + << " client " << context.client_connection_id << " server " + << context.server_connection_id; + } + } + QuicConnectionId destination_connection_id, source_connection_id; + if (masque_session_->perspective() == Perspective::IS_SERVER) { + destination_connection_id = context.server_connection_id; + source_connection_id = context.client_connection_id; + } else { + destination_connection_id = context.client_connection_id; + source_connection_id = context.server_connection_id; + } + + size_t packet_length = + reader.BytesRemaining() + destination_connection_id.length(); + uint8_t first_byte; + if (!reader.ReadUInt8(&first_byte)) { + QUIC_DLOG(ERROR) << "Failed to read first_byte"; + return false; + } + const bool long_header = (first_byte & FLAGS_LONG_HEADER) != 0; + if (long_header) { + packet_length += sizeof(uint8_t) * 2 + source_connection_id.length(); + } + std::string new_packet(packet_length, 0); + QuicDataWriter writer(new_packet.length(), new_packet.data()); + if (!writer.WriteUInt8(first_byte)) { + QUIC_BUG << "Failed to write first_byte"; + return false; + } + if (long_header) { + QuicVersionLabel version_label; + if (!reader.ReadUInt32(&version_label)) { + QUIC_DLOG(ERROR) << "Failed to read version"; + return false; + } + if (!writer.WriteUInt32(version_label)) { + QUIC_BUG << "Failed to write version"; + return false; + } + if (!writer.WriteLengthPrefixedConnectionId(destination_connection_id)) { + QUIC_BUG << "Failed to write long header destination_connection_id"; + return false; + } + if (!writer.WriteLengthPrefixedConnectionId(source_connection_id)) { + QUIC_BUG << "Failed to write long header source_connection_id"; + return false; + } + } else { + if (!writer.WriteConnectionId(destination_connection_id)) { + QUIC_BUG << "Failed to write short header destination_connection_id"; + return false; + } + } + QuicStringPiece payload = reader.ReadRemainingPayload(); + if (!writer.WriteStringPiece(payload)) { + QUIC_BUG << "Failed to write payload"; + return false; + } + + *server_socket_address = context.server_socket_address; + *client_connection_id = context.client_connection_id; + *server_connection_id = context.server_connection_id; + *packet = new_packet; + *version_present = long_header; + + QUIC_DVLOG(2) << "Decompressed client " << context.client_connection_id + << " server " << context.server_connection_id << "\n" + << QuicTextUtils::HexDump(new_packet); + + return true; +} + +QuicDatagramFlowId MasqueCompressionEngine::GetNextFlowId() { + const QuicDatagramFlowId next_flow_id = next_flow_id_; + next_flow_id_ += 2; + return next_flow_id; +} + +void MasqueCompressionEngine::UnregisterClientConnectionId( + QuicConnectionId client_connection_id) { + std::vector<QuicDatagramFlowId> flow_ids_to_remove; + for (auto kv : contexts_) { + const MasqueCompressionContext& context = kv.second; + if (context.client_connection_id == client_connection_id) { + flow_ids_to_remove.push_back(kv.first); + } + } + for (QuicDatagramFlowId flow_id : flow_ids_to_remove) { + contexts_.erase(flow_id); + } +} + +} // namespace quic
diff --git a/quic/masque/masque_compression_engine.h b/quic/masque/masque_compression_engine.h new file mode 100644 index 0000000..a061456 --- /dev/null +++ b/quic/masque/masque_compression_engine.h
@@ -0,0 +1,68 @@ +// Copyright 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. + +#ifndef QUICHE_QUIC_MASQUE_MASQUE_PROTOCOL_H_ +#define QUICHE_QUIC_MASQUE_MASQUE_PROTOCOL_H_ + +#include "net/third_party/quiche/src/quic/core/quic_connection_id.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/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// MASQUE compression engine used by client and servers. +// This class allows converting QUIC packets into a compressed form suitable +// for sending over QUIC DATAGRAM frames. It leverages a flow identifier at the +// start of each datagram to indicate which compression context was used to +// compress this packet, or to create new compression contexts. +class QUIC_EXPORT_PRIVATE MasqueCompressionEngine { + public: + explicit MasqueCompressionEngine(QuicSession* masque_session); + + // Compress packet and send it over a MASQUE session. + void CompressAndSendPacket(QuicStringPiece packet, + QuicConnectionId client_connection_id, + QuicConnectionId server_connection_id, + const QuicSocketAddress& server_socket_address); + + // Decompress received message and place it in |packet|. + bool DecompressMessage(QuicStringPiece message, + QuicConnectionId* client_connection_id, + QuicConnectionId* server_connection_id, + QuicSocketAddress* server_socket_address, + std::string* packet, + bool* version_present); + + // Clear all entries referencing |client_connection_id| the from + // compression table. + void UnregisterClientConnectionId(QuicConnectionId client_connection_id); + + // Disallow default constructor, copy, and assign. + MasqueCompressionEngine() = delete; + MasqueCompressionEngine(const MasqueCompressionEngine&) = delete; + MasqueCompressionEngine& operator=(const MasqueCompressionEngine&) = delete; + + private: + struct QUIC_EXPORT_PRIVATE MasqueCompressionContext { + QuicConnectionId client_connection_id; + QuicConnectionId server_connection_id; + QuicSocketAddress server_socket_address; + bool validated = false; + }; + + // Generate a new datagram flow ID. + QuicDatagramFlowId GetNextFlowId(); + + QuicSession* masque_session_; // Unowned. + QuicUnorderedMap<QuicDatagramFlowId, MasqueCompressionContext> contexts_; + QuicDatagramFlowId next_flow_id_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_MASQUE_MASQUE_PROTOCOL_H_
diff --git a/quic/masque/masque_dispatcher.cc b/quic/masque/masque_dispatcher.cc new file mode 100644 index 0000000..7a2e74e --- /dev/null +++ b/quic/masque/masque_dispatcher.cc
@@ -0,0 +1,82 @@ +// Copyright 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/masque/masque_dispatcher.h" +#include "net/third_party/quiche/src/quic/masque/masque_server_session.h" + +namespace quic { + +MasqueDispatcher::MasqueDispatcher( + const QuicConfig* config, + const QuicCryptoServerConfig* crypto_config, + QuicVersionManager* version_manager, + std::unique_ptr<QuicConnectionHelperInterface> helper, + std::unique_ptr<QuicCryptoServerStream::Helper> session_helper, + std::unique_ptr<QuicAlarmFactory> alarm_factory, + MasqueServerBackend* masque_server_backend, + uint8_t expected_server_connection_id_length) + : QuicSimpleDispatcher(config, + crypto_config, + version_manager, + std::move(helper), + std::move(session_helper), + std::move(alarm_factory), + masque_server_backend, + expected_server_connection_id_length), + masque_server_backend_(masque_server_backend) {} + +QuicServerSessionBase* MasqueDispatcher::CreateQuicSession( + QuicConnectionId connection_id, + const QuicSocketAddress& client_address, + QuicStringPiece /*alpn*/, + const ParsedQuicVersion& version) { + // The MasqueServerSession takes ownership of |connection| below. + QuicConnection* connection = new QuicConnection( + connection_id, client_address, helper(), alarm_factory(), writer(), + /* owns_writer= */ false, Perspective::IS_SERVER, + ParsedQuicVersionVector{version}); + + MasqueServerSession* session = + new MasqueServerSession(config(), GetSupportedVersions(), connection, + this, this, session_helper(), crypto_config(), + compressed_certs_cache(), masque_server_backend_); + session->Initialize(); + return session; +} + +bool MasqueDispatcher::OnFailedToDispatchPacket( + const ReceivedPacketInfo& packet_info) { + auto connection_id_registration = client_connection_id_registrations_.find( + packet_info.destination_connection_id); + if (connection_id_registration == client_connection_id_registrations_.end()) { + QUIC_DLOG(INFO) << "MasqueDispatcher failed to dispatch " << packet_info; + return false; + } + MasqueServerSession* masque_server_session = + connection_id_registration->second; + masque_server_session->HandlePacketFromServer(packet_info); + return true; +} + +void MasqueDispatcher::RegisterClientConnectionId( + QuicConnectionId client_connection_id, + MasqueServerSession* masque_server_session) { + QUIC_DLOG(INFO) << "Registering encapsulated " << client_connection_id + << " to MASQUE session " + << masque_server_session->connection_id(); + DCHECK(client_connection_id_registrations_.find(client_connection_id) == + client_connection_id_registrations_.end() || + client_connection_id_registrations_[client_connection_id] == + masque_server_session); + client_connection_id_registrations_[client_connection_id] = + masque_server_session; +} + +void MasqueDispatcher::UnregisterClientConnectionId( + QuicConnectionId client_connection_id) { + QUIC_DLOG(INFO) << "Unregistering " << client_connection_id; + client_connection_id_registrations_.erase(client_connection_id); +} + +} // namespace quic
diff --git a/quic/masque/masque_dispatcher.h b/quic/masque/masque_dispatcher.h new file mode 100644 index 0000000..55562de --- /dev/null +++ b/quic/masque/masque_dispatcher.h
@@ -0,0 +1,60 @@ +// Copyright 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. + +#ifndef QUICHE_QUIC_MASQUE_MASQUE_DISPATCHER_H_ +#define QUICHE_QUIC_MASQUE_MASQUE_DISPATCHER_H_ + +#include "net/third_party/quiche/src/quic/masque/masque_server_backend.h" +#include "net/third_party/quiche/src/quic/masque/masque_server_session.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h" + +namespace quic { + +// QUIC dispatcher that handles MASQUE requests. +class QUIC_EXPORT_PRIVATE MasqueDispatcher + : public QuicSimpleDispatcher, + public MasqueServerSession::Visitor { + public: + MasqueDispatcher( + const QuicConfig* config, + const QuicCryptoServerConfig* crypto_config, + QuicVersionManager* version_manager, + std::unique_ptr<QuicConnectionHelperInterface> helper, + std::unique_ptr<QuicCryptoServerStream::Helper> session_helper, + std::unique_ptr<QuicAlarmFactory> alarm_factory, + MasqueServerBackend* masque_server_backend, + uint8_t expected_server_connection_id_length); + + // From QuicSimpleDispatcher. + QuicServerSessionBase* CreateQuicSession( + QuicConnectionId connection_id, + const QuicSocketAddress& client_address, + QuicStringPiece alpn, + const ParsedQuicVersion& version) override; + + bool OnFailedToDispatchPacket(const ReceivedPacketInfo& packet_info) override; + + // From MasqueServerSession::Visitor. + void RegisterClientConnectionId( + QuicConnectionId client_connection_id, + MasqueServerSession* masque_server_session) override; + + void UnregisterClientConnectionId( + QuicConnectionId client_connection_id) override; + + // Disallow default constructor, copy, and assign. + MasqueDispatcher() = delete; + MasqueDispatcher(const MasqueDispatcher&) = delete; + MasqueDispatcher& operator=(const MasqueDispatcher&) = delete; + + private: + MasqueServerBackend* masque_server_backend_; // Unowned. + QuicUnorderedMap<QuicConnectionId, MasqueServerSession*, QuicConnectionIdHash> + client_connection_id_registrations_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_MASQUE_MASQUE_DISPATCHER_H_
diff --git a/quic/masque/masque_encapsulated_client_session.cc b/quic/masque/masque_encapsulated_client_session.cc new file mode 100644 index 0000000..7f6d6ed --- /dev/null +++ b/quic/masque/masque_encapsulated_client_session.cc
@@ -0,0 +1,41 @@ +// Copyright 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/masque/masque_encapsulated_client_session.h" + +namespace quic { + +MasqueEncapsulatedClientSession::MasqueEncapsulatedClientSession( + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection, + const QuicServerId& server_id, + QuicCryptoClientConfig* crypto_config, + QuicClientPushPromiseIndex* push_promise_index, + MasqueClientSession* masque_client_session) + : QuicSpdyClientSession(config, + supported_versions, + connection, + server_id, + crypto_config, + push_promise_index), + masque_client_session_(masque_client_session) {} + +void MasqueEncapsulatedClientSession::ProcessPacket( + QuicStringPiece packet, + QuicSocketAddress server_socket_address) { + QuicTime now = connection()->clock()->ApproximateNow(); + QuicReceivedPacket received_packet(packet.data(), packet.length(), now); + connection()->ProcessUdpPacket(connection()->self_address(), + server_socket_address, received_packet); +} + +void MasqueEncapsulatedClientSession::OnConnectionClosed( + const QuicConnectionCloseFrame& /*frame*/, + ConnectionCloseSource /*source*/) { + masque_client_session_->UnregisterConnectionId( + connection()->client_connection_id()); +} + +} // namespace quic
diff --git a/quic/masque/masque_encapsulated_client_session.h b/quic/masque/masque_encapsulated_client_session.h new file mode 100644 index 0000000..e04d4a5 --- /dev/null +++ b/quic/masque/masque_encapsulated_client_session.h
@@ -0,0 +1,49 @@ +// Copyright 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. + +#ifndef QUICHE_QUIC_MASQUE_MASQUE_ENCAPSULATED_CLIENT_SESSION_H_ +#define QUICHE_QUIC_MASQUE_MASQUE_ENCAPSULATED_CLIENT_SESSION_H_ + +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h" +#include "net/third_party/quiche/src/quic/masque/masque_client_session.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// QUIC client session for QUIC encapsulated in MASQUE. +class QUIC_EXPORT_PRIVATE MasqueEncapsulatedClientSession + : public QuicSpdyClientSession, + public MasqueClientSession::EncapsulatedClientSession { + public: + MasqueEncapsulatedClientSession( + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection, + const QuicServerId& server_id, + QuicCryptoClientConfig* crypto_config, + QuicClientPushPromiseIndex* push_promise_index, + MasqueClientSession* masque_client_session); + + // From MasqueClientSession::EncapsulatedClientSession. + void ProcessPacket(QuicStringPiece packet, + QuicSocketAddress server_socket_address) override; + + // From QuicSession. + void OnConnectionClosed(const QuicConnectionCloseFrame& frame, + ConnectionCloseSource source) override; + + // Disallow default constructor, copy, and assign. + MasqueEncapsulatedClientSession() = delete; + MasqueEncapsulatedClientSession(const MasqueEncapsulatedClientSession&) = + delete; + MasqueEncapsulatedClientSession& operator=( + const MasqueEncapsulatedClientSession&) = delete; + + private: + MasqueClientSession* masque_client_session_; // Unowned. +}; + +} // namespace quic + +#endif // QUICHE_QUIC_MASQUE_MASQUE_ENCAPSULATED_CLIENT_SESSION_H_
diff --git a/quic/masque/masque_encapsulated_epoll_client.cc b/quic/masque/masque_encapsulated_epoll_client.cc new file mode 100644 index 0000000..07224d7 --- /dev/null +++ b/quic/masque/masque_encapsulated_epoll_client.cc
@@ -0,0 +1,126 @@ +// Copyright 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/masque/masque_encapsulated_epoll_client.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/masque/masque_client_session.h" +#include "net/third_party/quiche/src/quic/masque/masque_encapsulated_client_session.h" +#include "net/third_party/quiche/src/quic/masque/masque_epoll_client.h" +#include "net/third_party/quiche/src/quic/masque/masque_utils.h" + +namespace quic { + +namespace { + +// Custom packet writer that allows getting all of a connection's outgoing +// packets. +class MasquePacketWriter : public QuicPacketWriter { + public: + MasquePacketWriter() = delete; + explicit MasquePacketWriter(MasqueEncapsulatedEpollClient* client) + : client_(client) {} + WriteResult WritePacket(const char* buffer, + size_t buf_len, + const QuicIpAddress& /*self_address*/, + const QuicSocketAddress& peer_address, + PerPacketOptions* /*options*/) override { + DCHECK(peer_address.IsInitialized()); + QUIC_DVLOG(1) << "MasquePacketWriter trying to write " << buf_len + << " bytes to " << peer_address; + QuicStringPiece packet(buffer, buf_len); + client_->masque_client()->masque_client_session()->SendPacket( + client_->session()->connection()->client_connection_id(), + client_->session()->connection()->connection_id(), packet, + peer_address); + return WriteResult(WRITE_STATUS_OK, buf_len); + } + + bool IsWriteBlocked() const override { return false; } + + void SetWritable() override {} + + QuicByteCount GetMaxPacketSize( + const QuicSocketAddress& /*peer_address*/) const override { + return kMasqueMaxEncapsulatedPacketSize; + } + + bool SupportsReleaseTime() const override { return false; } + + bool IsBatchMode() const override { return false; } + char* GetNextWriteLocation( + const QuicIpAddress& /*self_address*/, + const QuicSocketAddress& /*peer_address*/) override { + return nullptr; + } + + WriteResult Flush() override { return WriteResult(WRITE_STATUS_OK, 0); } + + private: + MasqueEncapsulatedEpollClient* client_; // Unowned. +}; + +// Custom network helper that allows injecting a custom packet writer in order +// to get all of a connection's outgoing packets. +class MasqueClientEpollNetworkHelper : public QuicClientEpollNetworkHelper { + public: + MasqueClientEpollNetworkHelper(QuicEpollServer* epoll_server, + MasqueEncapsulatedEpollClient* client) + : QuicClientEpollNetworkHelper(epoll_server, client), client_(client) {} + QuicPacketWriter* CreateQuicPacketWriter() override { + return new MasquePacketWriter(client_); + } + + private: + MasqueEncapsulatedEpollClient* client_; // Unowned. +}; + +} // namespace + +MasqueEncapsulatedEpollClient::MasqueEncapsulatedEpollClient( + QuicSocketAddress server_address, + const QuicServerId& server_id, + QuicEpollServer* epoll_server, + std::unique_ptr<ProofVerifier> proof_verifier, + MasqueEpollClient* masque_client) + : QuicClient( + server_address, + server_id, + MasqueSupportedVersions(), + MasqueEncapsulatedConfig(), + epoll_server, + std::make_unique<MasqueClientEpollNetworkHelper>(epoll_server, this), + std::move(proof_verifier)), + masque_client_(masque_client) {} + +MasqueEncapsulatedEpollClient::~MasqueEncapsulatedEpollClient() { + masque_client_->masque_client_session()->UnregisterConnectionId( + client_connection_id_); +} + +std::unique_ptr<QuicSession> +MasqueEncapsulatedEpollClient::CreateQuicClientSession( + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection) { + QUIC_DLOG(INFO) << "Creating MASQUE encapsulated session for " + << connection->connection_id(); + return std::make_unique<MasqueEncapsulatedClientSession>( + *config(), supported_versions, connection, server_id(), crypto_config(), + push_promise_index(), masque_client_->masque_client_session()); +} + +MasqueEncapsulatedClientSession* +MasqueEncapsulatedEpollClient::masque_encapsulated_client_session() { + return static_cast<MasqueEncapsulatedClientSession*>(QuicClient::session()); +} + +QuicConnectionId MasqueEncapsulatedEpollClient::GetClientConnectionId() { + if (client_connection_id_.IsEmpty()) { + client_connection_id_ = QuicUtils::CreateRandomConnectionId(); + masque_client_->masque_client_session()->RegisterConnectionId( + client_connection_id_, masque_encapsulated_client_session()); + } + return client_connection_id_; +} + +} // namespace quic
diff --git a/quic/masque/masque_encapsulated_epoll_client.h b/quic/masque/masque_encapsulated_epoll_client.h new file mode 100644 index 0000000..fe1618a --- /dev/null +++ b/quic/masque/masque_encapsulated_epoll_client.h
@@ -0,0 +1,51 @@ +// Copyright 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. + +#ifndef QUICHE_QUIC_MASQUE_MASQUE_ENCAPSULATED_EPOLL_CLIENT_H_ +#define QUICHE_QUIC_MASQUE_MASQUE_ENCAPSULATED_EPOLL_CLIENT_H_ + +#include "net/third_party/quiche/src/quic/masque/masque_encapsulated_client_session.h" +#include "net/third_party/quiche/src/quic/masque/masque_epoll_client.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/tools/quic_client.h" + +namespace quic { + +// QUIC client for QUIC encapsulated in MASQUE. +class QUIC_EXPORT_PRIVATE MasqueEncapsulatedEpollClient : public QuicClient { + public: + MasqueEncapsulatedEpollClient(QuicSocketAddress server_address, + const QuicServerId& server_id, + QuicEpollServer* epoll_server, + std::unique_ptr<ProofVerifier> proof_verifier, + MasqueEpollClient* masque_client); + ~MasqueEncapsulatedEpollClient() override; + + // From QuicClient. + std::unique_ptr<QuicSession> CreateQuicClientSession( + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection) override; + + QuicConnectionId GetClientConnectionId() override; + + // MASQUE client that this client is encapsulated in. + MasqueEpollClient* masque_client() { return masque_client_; } + + // Client session for this client. + MasqueEncapsulatedClientSession* masque_encapsulated_client_session(); + + // Disallow default constructor, copy, and assign. + MasqueEncapsulatedEpollClient() = delete; + MasqueEncapsulatedEpollClient(const MasqueEncapsulatedEpollClient&) = delete; + MasqueEncapsulatedEpollClient& operator=( + const MasqueEncapsulatedEpollClient&) = delete; + + private: + MasqueEpollClient* masque_client_; // Unowned. + QuicConnectionId client_connection_id_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_MASQUE_MASQUE_ENCAPSULATED_EPOLL_CLIENT_H_
diff --git a/quic/masque/masque_epoll_client.cc b/quic/masque/masque_epoll_client.cc new file mode 100644 index 0000000..eaba97d --- /dev/null +++ b/quic/masque/masque_epoll_client.cc
@@ -0,0 +1,129 @@ +// Copyright 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/masque/masque_epoll_client.h" +#include "net/third_party/quiche/src/quic/masque/masque_client_session.h" +#include "net/third_party/quiche/src/quic/masque/masque_utils.h" + +namespace quic { + +MasqueEpollClient::MasqueEpollClient( + QuicSocketAddress server_address, + const QuicServerId& server_id, + QuicEpollServer* epoll_server, + std::unique_ptr<ProofVerifier> proof_verifier, + const std::string& authority) + : QuicClient(server_address, + server_id, + MasqueSupportedVersions(), + epoll_server, + std::move(proof_verifier)), + epoll_server_(epoll_server), + authority_(authority) {} + +std::unique_ptr<QuicSession> MasqueEpollClient::CreateQuicClientSession( + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection) { + QUIC_DLOG(INFO) << "Creating MASQUE session for " + << connection->connection_id(); + return std::make_unique<MasqueClientSession>( + *config(), supported_versions, connection, server_id(), crypto_config(), + push_promise_index(), this); +} + +MasqueClientSession* MasqueEpollClient::masque_client_session() { + return static_cast<MasqueClientSession*>(QuicClient::session()); +} + +QuicConnectionId MasqueEpollClient::connection_id() { + return masque_client_session()->connection_id(); +} + +// static +std::unique_ptr<MasqueEpollClient> MasqueEpollClient::Create( + const std::string& host, + int port, + QuicEpollServer* epoll_server, + std::unique_ptr<ProofVerifier> proof_verifier) { + // Build the masque_client, and try to connect. + QuicSocketAddress addr = tools::LookupAddress(host, QuicStrCat(port)); + if (!addr.IsInitialized()) { + QUIC_LOG(ERROR) << "Unable to resolve address: " << host; + return nullptr; + } + QuicServerId server_id(host, port, + /*privacy_mode_enabled=*/false); + auto masque_client = std::make_unique<MasqueEpollClient>( + addr, server_id, epoll_server, std::move(proof_verifier), + QuicStrCat(host, ":", port)); + + if (masque_client == nullptr) { + QUIC_LOG(ERROR) << "Failed to create masque_client"; + return nullptr; + } + + masque_client->set_initial_max_packet_length(kDefaultMaxPacketSize); + masque_client->set_drop_response_body(false); + if (!masque_client->Initialize()) { + QUIC_LOG(ERROR) << "Failed to initialize masque_client"; + return nullptr; + } + if (!masque_client->Connect()) { + QuicErrorCode error = masque_client->session()->error(); + QUIC_LOG(ERROR) << "Failed to connect to " << host << ":" << port + << ". Error: " << QuicErrorCodeToString(error); + return nullptr; + } + + std::string body = "foo"; + + // Construct a GET or POST request for supplied URL. + spdy::SpdyHeaderBlock header_block; + header_block[":method"] = "POST"; + header_block[":scheme"] = "https"; + header_block[":authority"] = masque_client->authority_; + header_block[":path"] = "/.well-known/masque/init"; + + // Make sure to store the response, for later output. + masque_client->set_store_response(true); + + // Send the MASQUE init request. + masque_client->SendRequestAndWaitForResponse(header_block, body, + /*fin=*/true); + + if (!masque_client->connected()) { + QUIC_LOG(ERROR) << "MASQUE init request caused connection failure. Error: " + << QuicErrorCodeToString(masque_client->session()->error()); + return nullptr; + } + + const int response_code = masque_client->latest_response_code(); + if (response_code != 200) { + QUIC_LOG(ERROR) << "MASQUE init request failed with HTTP response code " + << response_code; + return nullptr; + } + return masque_client; +} + +void MasqueEpollClient::SendClientConnectionIdUnregister( + QuicConnectionId client_connection_id) { + std::string body(client_connection_id.data(), client_connection_id.length()); + + // Construct a GET or POST request for supplied URL. + spdy::SpdyHeaderBlock header_block; + header_block[":method"] = "POST"; + header_block[":scheme"] = "https"; + header_block[":authority"] = authority_; + header_block[":path"] = "/.well-known/masque/unregister"; + + // Make sure to store the response, for later output. + set_store_response(true); + + // Send the MASQUE init request. + SendRequestAndWaitForResponse(header_block, body, + /*fin=*/true); +} + +} // namespace quic
diff --git a/quic/masque/masque_epoll_client.h b/quic/masque/masque_epoll_client.h new file mode 100644 index 0000000..eb1d3f4 --- /dev/null +++ b/quic/masque/masque_epoll_client.h
@@ -0,0 +1,60 @@ +// Copyright 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. + +#ifndef QUICHE_QUIC_MASQUE_MASQUE_EPOLL_CLIENT_H_ +#define QUICHE_QUIC_MASQUE_MASQUE_EPOLL_CLIENT_H_ + +#include "net/third_party/quiche/src/quic/masque/masque_client_session.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/tools/quic_client.h" + +namespace quic { + +// QUIC client that implements MASQUE. +class QUIC_EXPORT_PRIVATE MasqueEpollClient + : public QuicClient, + public MasqueClientSession::Visitor { + public: + MasqueEpollClient(QuicSocketAddress server_address, + const QuicServerId& server_id, + QuicEpollServer* epoll_server, + std::unique_ptr<ProofVerifier> proof_verifier, + const std::string& authority); + + // Convenience method to construct a MasqueEpollClient, performs a + // synchronous DNS lookup. + static std::unique_ptr<MasqueEpollClient> Create( + const std::string& host, + int port, + QuicEpollServer* epoll_server, + std::unique_ptr<ProofVerifier> proof_verifier); + + // From QuicClient. + std::unique_ptr<QuicSession> CreateQuicClientSession( + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection) override; + + // Client session for this client. + MasqueClientSession* masque_client_session(); + + // Convenience accessor for the underlying connection ID. + QuicConnectionId connection_id(); + + // Send a MASQUE client connection ID unregister message to the server. + void SendClientConnectionIdUnregister( + QuicConnectionId client_connection_id) override; + + // Disallow default constructor, copy, and assign. + MasqueEpollClient() = delete; + MasqueEpollClient(const MasqueEpollClient&) = delete; + MasqueEpollClient& operator=(const MasqueEpollClient&) = delete; + + private: + QuicEpollServer* epoll_server_; // Unowned. + std::string authority_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_MASQUE_MASQUE_EPOLL_CLIENT_H_
diff --git a/quic/masque/masque_epoll_server.cc b/quic/masque/masque_epoll_server.cc new file mode 100644 index 0000000..9b0b0a3 --- /dev/null +++ b/quic/masque/masque_epoll_server.cc
@@ -0,0 +1,33 @@ +// Copyright 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/masque/masque_epoll_server.h" +#include "net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h" +#include "net/third_party/quiche/src/quic/masque/masque_dispatcher.h" +#include "net/third_party/quiche/src/quic/masque/masque_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_default_proof_providers.h" +#include "net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h" + +namespace quic { + +MasqueEpollServer::MasqueEpollServer(MasqueServerBackend* masque_server_backend) + : QuicServer(CreateDefaultProofSource(), + masque_server_backend, + MasqueSupportedVersions()), + masque_server_backend_(masque_server_backend) {} + +QuicDispatcher* MasqueEpollServer::CreateQuicDispatcher() { + QuicEpollAlarmFactory alarm_factory(epoll_server()); + return new MasqueDispatcher( + &config(), &crypto_config(), version_manager(), + std::unique_ptr<QuicEpollConnectionHelper>(new QuicEpollConnectionHelper( + epoll_server(), QuicAllocator::BUFFER_POOL)), + std::unique_ptr<QuicCryptoServerStream::Helper>( + new QuicSimpleCryptoServerStreamHelper()), + std::unique_ptr<QuicEpollAlarmFactory>( + new QuicEpollAlarmFactory(epoll_server())), + masque_server_backend_, expected_server_connection_id_length()); +} + +} // namespace quic
diff --git a/quic/masque/masque_epoll_server.h b/quic/masque/masque_epoll_server.h new file mode 100644 index 0000000..d6fb090 --- /dev/null +++ b/quic/masque/masque_epoll_server.h
@@ -0,0 +1,33 @@ +// Copyright 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. + +#ifndef QUICHE_QUIC_MASQUE_MASQUE_EPOLL_SERVER_H_ +#define QUICHE_QUIC_MASQUE_MASQUE_EPOLL_SERVER_H_ + +#include "net/third_party/quiche/src/quic/masque/masque_server_backend.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/tools/quic_server.h" + +namespace quic { + +// QUIC server that implements MASQUE. +class QUIC_EXPORT_PRIVATE MasqueEpollServer : public QuicServer { + public: + explicit MasqueEpollServer(MasqueServerBackend* masque_server_backend); + + // From QuicServer. + QuicDispatcher* CreateQuicDispatcher() override; + + // Disallow default constructor, copy, and assign. + MasqueEpollServer() = delete; + MasqueEpollServer(const MasqueEpollServer&) = delete; + MasqueEpollServer& operator=(const MasqueEpollServer&) = delete; + + private: + MasqueServerBackend* masque_server_backend_; // Unowned. +}; + +} // namespace quic + +#endif // QUICHE_QUIC_MASQUE_MASQUE_EPOLL_SERVER_H_
diff --git a/quic/masque/masque_server_backend.cc b/quic/masque/masque_server_backend.cc new file mode 100644 index 0000000..7f0a8b0 --- /dev/null +++ b/quic/masque/masque_server_backend.cc
@@ -0,0 +1,138 @@ +// Copyright 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/masque/masque_server_backend.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" + +namespace quic { + +namespace { + +std::string GetRequestHandlerKey( + const QuicSimpleServerBackend::RequestHandler* request_handler) { + return QuicStrCat(request_handler->connection_id().ToString(), "_", + request_handler->stream_id(), "_", + request_handler->peer_host()); +} + +} // namespace + +MasqueServerBackend::MasqueServerBackend(const std::string& server_authority, + const std::string& cache_directory) + : server_authority_(server_authority) { + if (!cache_directory.empty()) { + QuicMemoryCacheBackend::InitializeBackend(cache_directory); + } +} + +bool MasqueServerBackend::MaybeHandleMasqueRequest( + const spdy::SpdyHeaderBlock& request_headers, + const std::string& request_body, + QuicSimpleServerBackend::RequestHandler* request_handler) { + auto path_pair = request_headers.find(":path"); + auto method_pair = request_headers.find(":method"); + auto scheme_pair = request_headers.find(":scheme"); + if (path_pair == request_headers.end() || + method_pair == request_headers.end() || + scheme_pair == request_headers.end()) { + // This request is missing required headers. + return false; + } + spdy::SpdyStringPiece path = path_pair->second; + spdy::SpdyStringPiece scheme = scheme_pair->second; + spdy::SpdyStringPiece method = method_pair->second; + if (scheme != "https" || method != "POST" || request_body.empty()) { + // MASQUE requests MUST be a non-empty https POST. + return false; + } + + if (path.rfind("/.well-known/masque/", 0) != 0) { + // This request is not a MASQUE path. + return false; + } + std::string masque_path(path.substr(sizeof("/.well-known/masque/") - 1)); + + if (!server_authority_.empty()) { + auto authority_pair = request_headers.find(":authority"); + if (authority_pair == request_headers.end()) { + // Cannot enforce missing authority. + return false; + } + spdy::SpdyStringPiece authority = authority_pair->second; + if (server_authority_ != authority) { + // This request does not match server_authority. + return false; + } + } + + auto backend_client_pair = + backend_clients_.find(request_handler->connection_id()); + if (backend_client_pair == backend_clients_.end()) { + QUIC_LOG(ERROR) << "Could not find backend client for " + << GetRequestHandlerKey(request_handler) << " " + << masque_path << request_headers.DebugString(); + return false; + } + + BackendClient* backend_client = backend_client_pair->second; + + std::unique_ptr<QuicBackendResponse> response = + backend_client->HandleMasqueRequest(masque_path, request_headers, + request_body, request_handler); + if (response == nullptr) { + QUIC_LOG(ERROR) << "Backend client did not process request for " + << GetRequestHandlerKey(request_handler) << " " + << masque_path << request_headers.DebugString(); + return false; + } + + QUIC_DLOG(INFO) << "Sending MASQUE response for " + << GetRequestHandlerKey(request_handler) << " " << masque_path + << request_headers.DebugString(); + + request_handler->OnResponseBackendComplete(response.get(), {}); + active_response_map_[GetRequestHandlerKey(request_handler)] = + std::move(response); + + return true; +} + +void MasqueServerBackend::FetchResponseFromBackend( + const spdy::SpdyHeaderBlock& request_headers, + const std::string& request_body, + QuicSimpleServerBackend::RequestHandler* request_handler) { + if (MaybeHandleMasqueRequest(request_headers, request_body, + request_handler)) { + // Request was handled as a MASQUE request. + return; + } + QUIC_DLOG(INFO) << "Fetching non-MASQUE response for " + << GetRequestHandlerKey(request_handler) + << request_headers.DebugString(); + QuicMemoryCacheBackend::FetchResponseFromBackend( + request_headers, request_body, request_handler); +} + +void MasqueServerBackend::CloseBackendResponseStream( + QuicSimpleServerBackend::RequestHandler* request_handler) { + QUIC_DLOG(INFO) << "Closing response stream for " + << GetRequestHandlerKey(request_handler); + active_response_map_.erase(GetRequestHandlerKey(request_handler)); + QuicMemoryCacheBackend::CloseBackendResponseStream(request_handler); +} + +void MasqueServerBackend::RegisterBackendClient(QuicConnectionId connection_id, + BackendClient* backend_client) { + QUIC_BUG_IF(backend_clients_.find(connection_id) != backend_clients_.end()) + << connection_id << " already in backend clients map"; + backend_clients_[connection_id] = backend_client; + QUIC_DLOG(INFO) << "Registering backend client for " << connection_id; +} + +void MasqueServerBackend::RemoveBackendClient(QuicConnectionId connection_id) { + backend_clients_.erase(connection_id); + QUIC_DLOG(INFO) << "Removing backend client for " << connection_id; +} + +} // namespace quic
diff --git a/quic/masque/masque_server_backend.h b/quic/masque/masque_server_backend.h new file mode 100644 index 0000000..d8f1474 --- /dev/null +++ b/quic/masque/masque_server_backend.h
@@ -0,0 +1,69 @@ +// Copyright 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. + +#ifndef QUICHE_QUIC_MASQUE_MASQUE_SERVER_BACKEND_H_ +#define QUICHE_QUIC_MASQUE_MASQUE_SERVER_BACKEND_H_ + +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h" + +namespace quic { + +// QUIC server backend that understands MASQUE requests, but otherwise answers +// HTTP queries using an in-memory cache. +class QUIC_EXPORT_PRIVATE MasqueServerBackend : public QuicMemoryCacheBackend { + public: + // Interface meant to be implemented by the owner of the MasqueServerBackend + // instance. + class QUIC_EXPORT_PRIVATE BackendClient { + public: + virtual std::unique_ptr<QuicBackendResponse> HandleMasqueRequest( + const std::string& masque_path, + const spdy::SpdyHeaderBlock& request_headers, + const std::string& request_body, + QuicSimpleServerBackend::RequestHandler* request_handler) = 0; + virtual ~BackendClient() = default; + }; + + MasqueServerBackend(const std::string& server_authority, + const std::string& cache_directory); + + // From QuicMemoryCacheBackend. + void FetchResponseFromBackend( + const spdy::SpdyHeaderBlock& request_headers, + const std::string& request_body, + QuicSimpleServerBackend::RequestHandler* request_handler) override; + + void CloseBackendResponseStream( + QuicSimpleServerBackend::RequestHandler* request_handler) override; + + // Register backend client that can handle MASQUE requests. + void RegisterBackendClient(QuicConnectionId connection_id, + BackendClient* backend_client); + + // Unregister backend client. + void RemoveBackendClient(QuicConnectionId connection_id); + + // Disallow default constructor, copy, and assign. + MasqueServerBackend() = delete; + MasqueServerBackend(const MasqueServerBackend&) = delete; + MasqueServerBackend& operator=(const MasqueServerBackend&) = delete; + + private: + // Handle MASQUE request. + bool MaybeHandleMasqueRequest( + const spdy::SpdyHeaderBlock& request_headers, + const std::string& request_body, + QuicSimpleServerBackend::RequestHandler* request_handler); + + std::string server_authority_; + QuicUnorderedMap<std::string, std::unique_ptr<QuicBackendResponse>> + active_response_map_; + QuicUnorderedMap<QuicConnectionId, BackendClient*, QuicConnectionIdHash> + backend_clients_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_MASQUE_MASQUE_SERVER_BACKEND_H_
diff --git a/quic/masque/masque_server_bin.cc b/quic/masque/masque_server_bin.cc new file mode 100644 index 0000000..9ba475d --- /dev/null +++ b/quic/masque/masque_server_bin.cc
@@ -0,0 +1,55 @@ +// Copyright 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 <memory> + +#include "net/third_party/quiche/src/quic/masque/masque_epoll_server.h" +#include "net/third_party/quiche/src/quic/masque/masque_server_backend.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" + +DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t, + port, + 9661, + "The port the MASQUE server will listen on."); + +DEFINE_QUIC_COMMAND_LINE_FLAG( + std::string, + cache_dir, + "", + "Specifies the directory used during QuicHttpResponseCache " + "construction to seed the cache. Cache directory can be " + "generated using `wget -p --save-headers <url>`"); + +DEFINE_QUIC_COMMAND_LINE_FLAG( + std::string, + server_authority, + "", + "Specifies the authority over which the server will accept MASQUE " + "requests. Defaults to empty which allows all authorities."); + +int main(int argc, char* argv[]) { + const char* usage = "Usage: masque_server [options]"; + std::vector<std::string> non_option_args = + quic::QuicParseCommandLineFlags(usage, argc, argv); + if (!non_option_args.empty()) { + quic::QuicPrintCommandLineFlagHelp(usage); + return 0; + } + + auto backend = std::make_unique<quic::MasqueServerBackend>( + GetQuicFlag(FLAGS_server_authority), GetQuicFlag(FLAGS_cache_dir)); + + auto server = std::make_unique<quic::MasqueEpollServer>(backend.get()); + + if (!server->CreateUDPSocketAndListen(quic::QuicSocketAddress( + quic::QuicIpAddress::Any6(), GetQuicFlag(FLAGS_port)))) { + return 1; + } + + std::cerr << "Started MASQUE server" << std::endl; + server->HandleEventsForever(); + return 0; +}
diff --git a/quic/masque/masque_server_session.cc b/quic/masque/masque_server_session.cc new file mode 100644 index 0000000..eec43b2 --- /dev/null +++ b/quic/masque/masque_server_session.cc
@@ -0,0 +1,127 @@ +// Copyright 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/masque/masque_server_session.h" + +namespace quic { + +MasqueServerSession::MasqueServerSession( + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection, + QuicSession::Visitor* visitor, + Visitor* owner, + QuicCryptoServerStream::Helper* helper, + const QuicCryptoServerConfig* crypto_config, + QuicCompressedCertsCache* compressed_certs_cache, + MasqueServerBackend* masque_server_backend) + : QuicSimpleServerSession(config, + supported_versions, + connection, + visitor, + helper, + crypto_config, + compressed_certs_cache, + masque_server_backend), + masque_server_backend_(masque_server_backend), + owner_(owner), + compression_engine_(this) { + masque_server_backend_->RegisterBackendClient(connection_id(), this); +} + +void MasqueServerSession::OnMessageReceived(QuicStringPiece message) { + QUIC_DVLOG(1) << "Received message of length " << message.length(); + + QuicConnectionId client_connection_id, server_connection_id; + QuicSocketAddress server_socket_address; + std::string packet; + bool version_present; + if (!compression_engine_.DecompressMessage( + message, &client_connection_id, &server_connection_id, + &server_socket_address, &packet, &version_present)) { + return; + } + + QUIC_DVLOG(1) << "Received packet of length " << packet.length() << " for " + << server_socket_address << " client " << client_connection_id; + + if (version_present) { + if (client_connection_id.length() != kQuicDefaultConnectionIdLength) { + QUIC_DLOG(ERROR) + << "Dropping long header with invalid client_connection_id " + << client_connection_id; + return; + } + owner_->RegisterClientConnectionId(client_connection_id, this); + } + + WriteResult write_result = connection()->writer()->WritePacket( + packet.data(), packet.length(), connection()->self_address().host(), + server_socket_address, nullptr); + QUIC_DVLOG(1) << "Got " << write_result << " for " << packet.length() + << " bytes to " << server_socket_address; +} + +void MasqueServerSession::OnMessageAcked(QuicMessageId message_id, + QuicTime /*receive_timestamp*/) { + QUIC_DVLOG(1) << "Received ack for message " << message_id; +} + +void MasqueServerSession::OnMessageLost(QuicMessageId message_id) { + QUIC_DVLOG(1) << "We believe message " << message_id << " was lost"; +} + +void MasqueServerSession::OnConnectionClosed( + const QuicConnectionCloseFrame& /*frame*/, + ConnectionCloseSource /*source*/) { + QUIC_DLOG(INFO) << "Closing connection for " << connection_id(); + masque_server_backend_->RemoveBackendClient(connection_id()); +} + +std::unique_ptr<QuicBackendResponse> MasqueServerSession::HandleMasqueRequest( + const std::string& masque_path, + const spdy::SpdyHeaderBlock& /*request_headers*/, + const std::string& request_body, + QuicSimpleServerBackend::RequestHandler* /*request_handler*/) { + QUIC_DLOG(INFO) << "MasqueServerSession handling MASQUE request"; + + if (masque_path == "init") { + if (masque_initialized_) { + QUIC_DLOG(ERROR) << "Got second MASQUE init request"; + return nullptr; + } + masque_initialized_ = true; + } else if (masque_path == "unregister") { + QuicConnectionId connection_id(request_body.data(), request_body.length()); + QUIC_DLOG(INFO) << "Received MASQUE request to unregister " + << connection_id; + owner_->UnregisterClientConnectionId(connection_id); + compression_engine_.UnregisterClientConnectionId(connection_id); + } else { + if (!masque_initialized_) { + QUIC_DLOG(ERROR) << "Got MASQUE request before init"; + return nullptr; + } + } + + std::string response_body = "foobar_response"; + spdy::SpdyHeaderBlock response_headers; + response_headers[":status"] = "200"; + auto response = std::make_unique<QuicBackendResponse>(); + response->set_response_type(QuicBackendResponse::REGULAR_RESPONSE); + response->set_headers(std::move(response_headers)); + response->set_body(response_body); + + return response; +} + +void MasqueServerSession::HandlePacketFromServer( + const ReceivedPacketInfo& packet_info) { + QUIC_DVLOG(1) << "MasqueServerSession received " << packet_info; + compression_engine_.CompressAndSendPacket( + packet_info.packet.AsStringPiece(), packet_info.destination_connection_id, + packet_info.source_connection_id, packet_info.peer_address); +} + +} // namespace quic
diff --git a/quic/masque/masque_server_session.h b/quic/masque/masque_server_session.h new file mode 100644 index 0000000..1252eba --- /dev/null +++ b/quic/masque/masque_server_session.h
@@ -0,0 +1,77 @@ +// Copyright 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. + +#ifndef QUICHE_QUIC_MASQUE_MASQUE_SERVER_SESSION_H_ +#define QUICHE_QUIC_MASQUE_MASQUE_SERVER_SESSION_H_ + +#include "net/third_party/quiche/src/quic/masque/masque_compression_engine.h" +#include "net/third_party/quiche/src/quic/masque/masque_server_backend.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/tools/quic_simple_server_session.h" + +namespace quic { + +// QUIC server session for connection to MASQUE proxy. +class QUIC_EXPORT_PRIVATE MasqueServerSession + : public QuicSimpleServerSession, + public MasqueServerBackend::BackendClient { + public: + // Interface meant to be implemented by owner of this MasqueServerSession + // instance. + class QUIC_EXPORT_PRIVATE Visitor { + public: + virtual ~Visitor() {} + // Register a client connection ID as being handled by this session. + virtual void RegisterClientConnectionId( + QuicConnectionId client_connection_id, + MasqueServerSession* masque_server_session) = 0; + + // Unregister a client connection ID. + virtual void UnregisterClientConnectionId( + QuicConnectionId client_connection_id) = 0; + }; + + MasqueServerSession(const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection, + QuicSession::Visitor* visitor, + Visitor* owner, + QuicCryptoServerStream::Helper* helper, + const QuicCryptoServerConfig* crypto_config, + QuicCompressedCertsCache* compressed_certs_cache, + MasqueServerBackend* masque_server_backend); + + // From QuicSession. + void OnMessageReceived(QuicStringPiece message) override; + void OnMessageAcked(QuicMessageId message_id, + QuicTime receive_timestamp) override; + void OnMessageLost(QuicMessageId message_id) override; + void OnConnectionClosed(const QuicConnectionCloseFrame& frame, + ConnectionCloseSource source) override; + + // From MasqueServerBackend::BackendClient. + std::unique_ptr<QuicBackendResponse> HandleMasqueRequest( + const std::string& masque_path, + const spdy::SpdyHeaderBlock& request_headers, + const std::string& request_body, + QuicSimpleServerBackend::RequestHandler* request_handler) override; + + // Handle packet for client, meant to be called by MasqueDispatcher. + void HandlePacketFromServer(const ReceivedPacketInfo& packet_info); + + // Disallow default constructor, copy, and assign. + MasqueServerSession() = delete; + MasqueServerSession(const MasqueServerSession&) = delete; + MasqueServerSession& operator=(const MasqueServerSession&) = delete; + + private: + MasqueServerBackend* masque_server_backend_; // Unowned. + Visitor* owner_; // Unowned. + MasqueCompressionEngine compression_engine_; + bool masque_initialized_ = false; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_MASQUE_MASQUE_SERVER_SESSION_H_
diff --git a/quic/masque/masque_utils.cc b/quic/masque/masque_utils.cc new file mode 100644 index 0000000..6ef88ae --- /dev/null +++ b/quic/masque/masque_utils.cc
@@ -0,0 +1,22 @@ +// Copyright 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/masque/masque_utils.h" + +namespace quic { + +ParsedQuicVersionVector MasqueSupportedVersions() { + QuicVersionInitializeSupportForIetfDraft(); + ParsedQuicVersion version(PROTOCOL_TLS1_3, QUIC_VERSION_99); + QuicEnableVersion(version); + return {version}; +} + +QuicConfig MasqueEncapsulatedConfig() { + QuicConfig config; + config.SetMaxPacketSizeToSend(kMasqueMaxEncapsulatedPacketSize); + return config; +} + +} // namespace quic
diff --git a/quic/masque/masque_utils.h b/quic/masque/masque_utils.h new file mode 100644 index 0000000..65e8de6 --- /dev/null +++ b/quic/masque/masque_utils.h
@@ -0,0 +1,25 @@ +// Copyright 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. + +#ifndef QUICHE_QUIC_MASQUE_MASQUE_UTILS_H_ +#define QUICHE_QUIC_MASQUE_MASQUE_UTILS_H_ + +#include "net/third_party/quiche/src/quic/core/quic_config.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_versions.h" + +namespace quic { + +// List of QUIC versions that support MASQUE. Currently restricted to IETF QUIC. +QUIC_EXPORT_PRIVATE ParsedQuicVersionVector MasqueSupportedVersions(); + +// Default QuicConfig for use with MASQUE. Sets a custom max_packet_size. +QUIC_EXPORT_PRIVATE QuicConfig MasqueEncapsulatedConfig(); + +// Maximum packet size for encapsulated connections. +const QuicByteCount kMasqueMaxEncapsulatedPacketSize = 1300; + +} // namespace quic + +#endif // QUICHE_QUIC_MASQUE_MASQUE_UTILS_H_