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_