1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "quiche/quic/masque/masque_client_session.h"
6
7 #include <cstdint>
8 #include <cstring>
9 #include <optional>
10 #include <string>
11 #include <utility>
12 #include <vector>
13
14 #include "absl/container/flat_hash_map.h"
15 #include "absl/container/flat_hash_set.h"
16 #include "absl/strings/escaping.h"
17 #include "absl/strings/str_cat.h"
18 #include "absl/strings/str_split.h"
19 #include "absl/strings/string_view.h"
20 #include "openssl/curve25519.h"
21 #include "quiche/quic/core/crypto/quic_crypto_client_config.h"
22 #include "quiche/quic/core/frames/quic_connection_close_frame.h"
23 #include "quiche/quic/core/http/http_frames.h"
24 #include "quiche/quic/core/http/quic_spdy_client_session.h"
25 #include "quiche/quic/core/http/quic_spdy_client_stream.h"
26 #include "quiche/quic/core/quic_config.h"
27 #include "quiche/quic/core/quic_connection.h"
28 #include "quiche/quic/core/quic_data_reader.h"
29 #include "quiche/quic/core/quic_data_writer.h"
30 #include "quiche/quic/core/quic_error_codes.h"
31 #include "quiche/quic/core/quic_server_id.h"
32 #include "quiche/quic/core/quic_time.h"
33 #include "quiche/quic/core/quic_types.h"
34 #include "quiche/quic/core/quic_utils.h"
35 #include "quiche/quic/core/quic_versions.h"
36 #include "quiche/quic/masque/masque_utils.h"
37 #include "quiche/quic/platform/api/quic_bug_tracker.h"
38 #include "quiche/quic/platform/api/quic_logging.h"
39 #include "quiche/quic/platform/api/quic_socket_address.h"
40 #include "quiche/quic/tools/quic_url.h"
41 #include "quiche/common/capsule.h"
42 #include "quiche/common/platform/api/quiche_googleurl.h"
43 #include "quiche/common/platform/api/quiche_logging.h"
44 #include "quiche/common/platform/api/quiche_url_utils.h"
45 #include "quiche/common/quiche_ip_address.h"
46 #include "quiche/common/quiche_random.h"
47 #include "quiche/common/quiche_text_utils.h"
48 #include "quiche/spdy/core/http2_header_block.h"
49
50 namespace quic {
51
52 namespace {
53
54 using ::quiche::AddressAssignCapsule;
55 using ::quiche::AddressRequestCapsule;
56 using ::quiche::RouteAdvertisementCapsule;
57
58 constexpr uint64_t kConnectIpPayloadContextId = 0;
59 constexpr uint64_t kConnectEthernetPayloadContextId = 0;
60 } // namespace
61
MasqueClientSession(MasqueMode masque_mode,const std::string & uri_template,const QuicConfig & config,const ParsedQuicVersionVector & supported_versions,QuicConnection * connection,const QuicServerId & server_id,QuicCryptoClientConfig * crypto_config,Owner * owner)62 MasqueClientSession::MasqueClientSession(
63 MasqueMode masque_mode, const std::string& uri_template,
64 const QuicConfig& config, const ParsedQuicVersionVector& supported_versions,
65 QuicConnection* connection, const QuicServerId& server_id,
66 QuicCryptoClientConfig* crypto_config, Owner* owner)
67 : QuicSpdyClientSession(config, supported_versions, connection, server_id,
68 crypto_config),
69 masque_mode_(masque_mode),
70 uri_template_(uri_template),
71 owner_(owner) {
72 // We don't currently use `masque_mode_` but will in the future. To silence
73 // clang's `-Wunused-private-field` warning for this when building QUICHE for
74 // Chrome, add a use of it here.
75 (void)masque_mode_;
76 }
77
OnMessageAcked(QuicMessageId message_id,QuicTime)78 void MasqueClientSession::OnMessageAcked(QuicMessageId message_id,
79 QuicTime /*receive_timestamp*/) {
80 QUIC_DVLOG(1) << "Received ack for DATAGRAM frame " << message_id;
81 }
82
OnMessageLost(QuicMessageId message_id)83 void MasqueClientSession::OnMessageLost(QuicMessageId message_id) {
84 QUIC_DVLOG(1) << "We believe DATAGRAM frame " << message_id << " was lost";
85 }
86
87 const MasqueClientSession::ConnectUdpClientState*
GetOrCreateConnectUdpClientState(const QuicSocketAddress & target_server_address,EncapsulatedClientSession * encapsulated_client_session)88 MasqueClientSession::GetOrCreateConnectUdpClientState(
89 const QuicSocketAddress& target_server_address,
90 EncapsulatedClientSession* encapsulated_client_session) {
91 for (const ConnectUdpClientState& client_state : connect_udp_client_states_) {
92 if (client_state.target_server_address() == target_server_address &&
93 client_state.encapsulated_client_session() ==
94 encapsulated_client_session) {
95 // Found existing CONNECT-UDP request.
96 return &client_state;
97 }
98 }
99 // No CONNECT-UDP request found, create a new one.
100 std::string target_host;
101 auto it = fake_addresses_.find(target_server_address.host().ToPackedString());
102 if (it != fake_addresses_.end()) {
103 target_host = it->second;
104 } else {
105 target_host = target_server_address.host().ToString();
106 }
107
108 url::Parsed parsed_uri_template;
109 url::ParseStandardURL(uri_template_.c_str(), uri_template_.length(),
110 &parsed_uri_template);
111 if (!parsed_uri_template.path.is_nonempty()) {
112 QUIC_BUG(bad URI template path)
113 << "Cannot parse path from URI template " << uri_template_;
114 return nullptr;
115 }
116 std::string path = uri_template_.substr(parsed_uri_template.path.begin,
117 parsed_uri_template.path.len);
118 if (parsed_uri_template.query.is_valid()) {
119 absl::StrAppend(&path, "?",
120 uri_template_.substr(parsed_uri_template.query.begin,
121 parsed_uri_template.query.len));
122 }
123 absl::flat_hash_map<std::string, std::string> parameters;
124 parameters["target_host"] = target_host;
125 parameters["target_port"] = absl::StrCat(target_server_address.port());
126 std::string expanded_path;
127 absl::flat_hash_set<std::string> vars_found;
128 bool expanded =
129 quiche::ExpandURITemplate(path, parameters, &expanded_path, &vars_found);
130 if (!expanded || vars_found.find("target_host") == vars_found.end() ||
131 vars_found.find("target_port") == vars_found.end()) {
132 QUIC_DLOG(ERROR) << "Failed to expand URI template \"" << uri_template_
133 << "\" for " << target_host << " port "
134 << target_server_address.port();
135 return nullptr;
136 }
137
138 url::Component expanded_path_component(0, expanded_path.length());
139 url::RawCanonOutput<1024> canonicalized_path_output;
140 url::Component canonicalized_path_component;
141 bool canonicalized = url::CanonicalizePath(
142 expanded_path.c_str(), expanded_path_component,
143 &canonicalized_path_output, &canonicalized_path_component);
144 if (!canonicalized || !canonicalized_path_component.is_nonempty()) {
145 QUIC_DLOG(ERROR) << "Failed to canonicalize URI template \""
146 << uri_template_ << "\" for " << target_host << " port "
147 << target_server_address.port();
148 return nullptr;
149 }
150 std::string canonicalized_path(
151 canonicalized_path_output.data() + canonicalized_path_component.begin,
152 canonicalized_path_component.len);
153
154 QuicSpdyClientStream* stream = CreateOutgoingBidirectionalStream();
155 if (stream == nullptr) {
156 // Stream flow control limits prevented us from opening a new stream.
157 QUIC_DLOG(ERROR) << "Failed to open CONNECT-UDP stream";
158 return nullptr;
159 }
160
161 QuicUrl url(uri_template_);
162 std::string scheme = url.scheme();
163 std::string authority = url.HostPort();
164
165 QUIC_DLOG(INFO) << "Sending CONNECT-UDP request for " << target_host
166 << " port " << target_server_address.port() << " on stream "
167 << stream->id() << " scheme=\"" << scheme << "\" authority=\""
168 << authority << "\" path=\"" << canonicalized_path << "\"";
169
170 // Send the request.
171 spdy::Http2HeaderBlock headers;
172 headers[":method"] = "CONNECT";
173 headers[":protocol"] = "connect-udp";
174 headers[":scheme"] = scheme;
175 headers[":authority"] = authority;
176 headers[":path"] = canonicalized_path;
177 AddAdditionalHeaders(headers, url);
178 QUIC_DVLOG(1) << "Sending request headers: " << headers.DebugString();
179 size_t bytes_sent =
180 stream->SendRequest(std::move(headers), /*body=*/"", /*fin=*/false);
181 if (bytes_sent == 0) {
182 QUIC_DLOG(ERROR) << "Failed to send CONNECT-UDP request";
183 return nullptr;
184 }
185
186 connect_udp_client_states_.push_back(ConnectUdpClientState(
187 stream, encapsulated_client_session, this, target_server_address));
188 return &connect_udp_client_states_.back();
189 }
190
191 const MasqueClientSession::ConnectIpClientState*
GetOrCreateConnectIpClientState(MasqueClientSession::EncapsulatedIpSession * encapsulated_ip_session)192 MasqueClientSession::GetOrCreateConnectIpClientState(
193 MasqueClientSession::EncapsulatedIpSession* encapsulated_ip_session) {
194 for (const ConnectIpClientState& client_state : connect_ip_client_states_) {
195 if (client_state.encapsulated_ip_session() == encapsulated_ip_session) {
196 // Found existing CONNECT-IP request.
197 return &client_state;
198 }
199 }
200 // No CONNECT-IP request found, create a new one.
201 QuicSpdyClientStream* stream = CreateOutgoingBidirectionalStream();
202 if (stream == nullptr) {
203 // Stream flow control limits prevented us from opening a new stream.
204 QUIC_DLOG(ERROR) << "Failed to open CONNECT-IP stream";
205 return nullptr;
206 }
207
208 QuicUrl url(uri_template_);
209 std::string scheme = url.scheme();
210 std::string authority = url.HostPort();
211 std::string path = "/.well-known/masque/ip/*/*/";
212
213 QUIC_DLOG(INFO) << "Sending CONNECT-IP request on stream " << stream->id()
214 << " scheme=\"" << scheme << "\" authority=\"" << authority
215 << "\" path=\"" << path << "\"";
216
217 // Send the request.
218 spdy::Http2HeaderBlock headers;
219 headers[":method"] = "CONNECT";
220 headers[":protocol"] = "connect-ip";
221 headers[":scheme"] = scheme;
222 headers[":authority"] = authority;
223 headers[":path"] = path;
224 headers["connect-ip-version"] = "3";
225 AddAdditionalHeaders(headers, url);
226 QUIC_DVLOG(1) << "Sending request headers: " << headers.DebugString();
227 size_t bytes_sent =
228 stream->SendRequest(std::move(headers), /*body=*/"", /*fin=*/false);
229 if (bytes_sent == 0) {
230 QUIC_DLOG(ERROR) << "Failed to send CONNECT-IP request";
231 return nullptr;
232 }
233
234 connect_ip_client_states_.push_back(
235 ConnectIpClientState(stream, encapsulated_ip_session, this));
236 return &connect_ip_client_states_.back();
237 }
238
239 const MasqueClientSession::ConnectEthernetClientState*
GetOrCreateConnectEthernetClientState(MasqueClientSession::EncapsulatedEthernetSession * encapsulated_ethernet_session)240 MasqueClientSession::GetOrCreateConnectEthernetClientState(
241 MasqueClientSession::EncapsulatedEthernetSession*
242 encapsulated_ethernet_session) {
243 for (const ConnectEthernetClientState& client_state :
244 connect_ethernet_client_states_) {
245 if (client_state.encapsulated_ethernet_session() ==
246 encapsulated_ethernet_session) {
247 // Found existing CONNECT-ETHERNET request.
248 return &client_state;
249 }
250 }
251 // No CONNECT-ETHERNET request found, create a new one.
252 QuicSpdyClientStream* stream = CreateOutgoingBidirectionalStream();
253 if (stream == nullptr) {
254 // Stream flow control limits prevented us from opening a new stream.
255 QUIC_DLOG(ERROR) << "Failed to open CONNECT-ETHERNET stream";
256 return nullptr;
257 }
258
259 QuicUrl url(uri_template_);
260 std::string scheme = url.scheme();
261 std::string authority = url.HostPort();
262 std::string path = "/.well-known/masque/ethernet/";
263
264 QUIC_DLOG(INFO) << "Sending CONNECT-ETHERNET request on stream "
265 << stream->id() << " scheme=\"" << scheme << "\" authority=\""
266 << authority << "\" path=\"" << path << "\"";
267
268 // Send the request.
269 spdy::Http2HeaderBlock headers;
270 headers[":method"] = "CONNECT";
271 headers[":protocol"] = "connect-ethernet";
272 headers[":scheme"] = scheme;
273 headers[":authority"] = authority;
274 headers[":path"] = path;
275 AddAdditionalHeaders(headers, url);
276 QUIC_DVLOG(1) << "Sending request headers: " << headers.DebugString();
277 size_t bytes_sent =
278 stream->SendRequest(std::move(headers), /*body=*/"", /*fin=*/false);
279 if (bytes_sent == 0) {
280 QUIC_DLOG(ERROR) << "Failed to send CONNECT-ETHERNET request";
281 return nullptr;
282 }
283
284 connect_ethernet_client_states_.push_back(
285 ConnectEthernetClientState(stream, encapsulated_ethernet_session, this));
286 return &connect_ethernet_client_states_.back();
287 }
288
SendIpPacket(absl::string_view packet,MasqueClientSession::EncapsulatedIpSession * encapsulated_ip_session)289 void MasqueClientSession::SendIpPacket(
290 absl::string_view packet,
291 MasqueClientSession::EncapsulatedIpSession* encapsulated_ip_session) {
292 const ConnectIpClientState* connect_ip =
293 GetOrCreateConnectIpClientState(encapsulated_ip_session);
294 if (connect_ip == nullptr) {
295 QUIC_DLOG(ERROR) << "Failed to create CONNECT-IP request";
296 return;
297 }
298
299 std::string http_payload;
300 http_payload.resize(
301 QuicDataWriter::GetVarInt62Len(kConnectIpPayloadContextId) +
302 packet.size());
303 QuicDataWriter writer(http_payload.size(), http_payload.data());
304 if (!writer.WriteVarInt62(kConnectIpPayloadContextId)) {
305 QUIC_BUG(IP context write fail) << "Failed to write CONNECT-IP context ID";
306 return;
307 }
308 if (!writer.WriteStringPiece(packet)) {
309 QUIC_BUG(IP packet write fail) << "Failed to write CONNECT-IP packet";
310 return;
311 }
312 MessageStatus message_status =
313 SendHttp3Datagram(connect_ip->stream()->id(), http_payload);
314
315 QUIC_DVLOG(1) << "Sent encapsulated IP packet of length " << packet.size()
316 << " with stream ID " << connect_ip->stream()->id()
317 << " and got message status "
318 << MessageStatusToString(message_status);
319 }
320
SendEthernetFrame(absl::string_view frame,MasqueClientSession::EncapsulatedEthernetSession * encapsulated_ethernet_session)321 void MasqueClientSession::SendEthernetFrame(
322 absl::string_view frame, MasqueClientSession::EncapsulatedEthernetSession*
323 encapsulated_ethernet_session) {
324 const ConnectEthernetClientState* connect_ethernet =
325 GetOrCreateConnectEthernetClientState(encapsulated_ethernet_session);
326 if (connect_ethernet == nullptr) {
327 QUIC_DLOG(ERROR) << "Failed to create CONNECT-ETHERNET request";
328 return;
329 }
330
331 std::string http_payload;
332 http_payload.resize(
333 QuicDataWriter::GetVarInt62Len(kConnectEthernetPayloadContextId) +
334 frame.size());
335 QuicDataWriter writer(http_payload.size(), http_payload.data());
336 if (!writer.WriteVarInt62(kConnectEthernetPayloadContextId)) {
337 QUIC_BUG(IP context write fail)
338 << "Failed to write CONNECT-ETHERNET context ID";
339 return;
340 }
341 if (!writer.WriteStringPiece(frame)) {
342 QUIC_BUG(IP packet write fail) << "Failed to write CONNECT-ETHERNET frame";
343 return;
344 }
345 MessageStatus message_status =
346 SendHttp3Datagram(connect_ethernet->stream()->id(), http_payload);
347
348 QUIC_DVLOG(1) << "Sent encapsulated Ethernet frame of length " << frame.size()
349 << " with stream ID " << connect_ethernet->stream()->id()
350 << " and got message status "
351 << MessageStatusToString(message_status);
352 }
353
SendPacket(absl::string_view packet,const QuicSocketAddress & target_server_address,EncapsulatedClientSession * encapsulated_client_session)354 void MasqueClientSession::SendPacket(
355 absl::string_view packet, const QuicSocketAddress& target_server_address,
356 EncapsulatedClientSession* encapsulated_client_session) {
357 const ConnectUdpClientState* connect_udp = GetOrCreateConnectUdpClientState(
358 target_server_address, encapsulated_client_session);
359 if (connect_udp == nullptr) {
360 QUIC_DLOG(ERROR) << "Failed to create CONNECT-UDP request";
361 return;
362 }
363
364 std::string http_payload;
365 http_payload.resize(1 + packet.size());
366 http_payload[0] = 0;
367 memcpy(&http_payload[1], packet.data(), packet.size());
368 MessageStatus message_status =
369 SendHttp3Datagram(connect_udp->stream()->id(), http_payload);
370
371 QUIC_DVLOG(1) << "Sent packet to " << target_server_address
372 << " compressed with stream ID " << connect_udp->stream()->id()
373 << " and got message status "
374 << MessageStatusToString(message_status);
375 }
376
CloseConnectUdpStream(EncapsulatedClientSession * encapsulated_client_session)377 void MasqueClientSession::CloseConnectUdpStream(
378 EncapsulatedClientSession* encapsulated_client_session) {
379 for (auto it = connect_udp_client_states_.begin();
380 it != connect_udp_client_states_.end();) {
381 if (it->encapsulated_client_session() == encapsulated_client_session) {
382 QUIC_DLOG(INFO) << "Removing CONNECT-UDP state for stream ID "
383 << it->stream()->id();
384 auto* stream = it->stream();
385 it = connect_udp_client_states_.erase(it);
386 if (!stream->write_side_closed()) {
387 stream->Reset(QUIC_STREAM_CANCELLED);
388 }
389 } else {
390 ++it;
391 }
392 }
393 }
394
CloseConnectIpStream(EncapsulatedIpSession * encapsulated_ip_session)395 void MasqueClientSession::CloseConnectIpStream(
396 EncapsulatedIpSession* encapsulated_ip_session) {
397 for (auto it = connect_ip_client_states_.begin();
398 it != connect_ip_client_states_.end();) {
399 if (it->encapsulated_ip_session() == encapsulated_ip_session) {
400 QUIC_DLOG(INFO) << "Removing CONNECT-IP state for stream ID "
401 << it->stream()->id();
402 auto* stream = it->stream();
403 it = connect_ip_client_states_.erase(it);
404 if (!stream->write_side_closed()) {
405 stream->Reset(QUIC_STREAM_CANCELLED);
406 }
407 } else {
408 ++it;
409 }
410 }
411 }
412
CloseConnectEthernetStream(EncapsulatedEthernetSession * encapsulated_ethernet_session)413 void MasqueClientSession::CloseConnectEthernetStream(
414 EncapsulatedEthernetSession* encapsulated_ethernet_session) {
415 for (auto it = connect_ethernet_client_states_.begin();
416 it != connect_ethernet_client_states_.end();) {
417 if (it->encapsulated_ethernet_session() == encapsulated_ethernet_session) {
418 QUIC_DLOG(INFO) << "Removing CONNECT-ETHERNET state for stream ID "
419 << it->stream()->id();
420 auto* stream = it->stream();
421 it = connect_ethernet_client_states_.erase(it);
422 if (!stream->write_side_closed()) {
423 stream->Reset(QUIC_STREAM_CANCELLED);
424 }
425 } else {
426 ++it;
427 }
428 }
429 }
430
OnConnectionClosed(const QuicConnectionCloseFrame & frame,ConnectionCloseSource source)431 void MasqueClientSession::OnConnectionClosed(
432 const QuicConnectionCloseFrame& frame, ConnectionCloseSource source) {
433 QuicSpdyClientSession::OnConnectionClosed(frame, source);
434 // Close all encapsulated sessions.
435 for (const auto& client_state : connect_udp_client_states_) {
436 client_state.encapsulated_client_session()->CloseConnection(
437 QUIC_CONNECTION_CANCELLED, "Underlying MASQUE connection was closed",
438 ConnectionCloseBehavior::SILENT_CLOSE);
439 }
440 for (const auto& client_state : connect_ip_client_states_) {
441 client_state.encapsulated_ip_session()->CloseIpSession(
442 "Underlying MASQUE connection was closed");
443 }
444 }
445
OnStreamClosed(QuicStreamId stream_id)446 void MasqueClientSession::OnStreamClosed(QuicStreamId stream_id) {
447 if (QuicUtils::IsBidirectionalStreamId(stream_id, version()) &&
448 QuicUtils::IsClientInitiatedStreamId(transport_version(), stream_id)) {
449 QuicSpdyClientStream* stream =
450 reinterpret_cast<QuicSpdyClientStream*>(GetActiveStream(stream_id));
451 if (stream != nullptr) {
452 QUIC_DLOG(INFO) << "Stream " << stream_id
453 << " closed, got response headers:"
454 << stream->response_headers().DebugString();
455 }
456 }
457 for (auto it = connect_udp_client_states_.begin();
458 it != connect_udp_client_states_.end();) {
459 if (it->stream()->id() == stream_id) {
460 QUIC_DLOG(INFO) << "Stream " << stream_id
461 << " was closed, removing CONNECT-UDP state";
462 auto* encapsulated_client_session = it->encapsulated_client_session();
463 it = connect_udp_client_states_.erase(it);
464 encapsulated_client_session->CloseConnection(
465 QUIC_CONNECTION_CANCELLED,
466 "Underlying MASQUE CONNECT-UDP stream was closed",
467 ConnectionCloseBehavior::SILENT_CLOSE);
468 } else {
469 ++it;
470 }
471 }
472 for (auto it = connect_ip_client_states_.begin();
473 it != connect_ip_client_states_.end();) {
474 if (it->stream()->id() == stream_id) {
475 QUIC_DLOG(INFO) << "Stream " << stream_id
476 << " was closed, removing CONNECT-IP state";
477 auto* encapsulated_ip_session = it->encapsulated_ip_session();
478 it = connect_ip_client_states_.erase(it);
479 encapsulated_ip_session->CloseIpSession(
480 "Underlying MASQUE CONNECT-IP stream was closed");
481 } else {
482 ++it;
483 }
484 }
485
486 QuicSpdyClientSession::OnStreamClosed(stream_id);
487 }
488
OnSettingsFrame(const SettingsFrame & frame)489 bool MasqueClientSession::OnSettingsFrame(const SettingsFrame& frame) {
490 QUIC_DLOG(INFO) << "Received SETTINGS: " << frame;
491 if (!QuicSpdyClientSession::OnSettingsFrame(frame)) {
492 QUIC_DLOG(ERROR) << "Failed to parse received settings";
493 return false;
494 }
495 if (!SupportsH3Datagram()) {
496 QUIC_DLOG(ERROR) << "Warning: MasqueClientSession without HTTP/3 Datagrams";
497 }
498 QUIC_DLOG(INFO) << "Using HTTP Datagram: " << http_datagram_support();
499 owner_->OnSettingsReceived();
500 return true;
501 }
502
ConnectUdpClientState(QuicSpdyClientStream * stream,EncapsulatedClientSession * encapsulated_client_session,MasqueClientSession * masque_session,const QuicSocketAddress & target_server_address)503 MasqueClientSession::ConnectUdpClientState::ConnectUdpClientState(
504 QuicSpdyClientStream* stream,
505 EncapsulatedClientSession* encapsulated_client_session,
506 MasqueClientSession* masque_session,
507 const QuicSocketAddress& target_server_address)
508 : stream_(stream),
509 encapsulated_client_session_(encapsulated_client_session),
510 masque_session_(masque_session),
511 target_server_address_(target_server_address) {
512 QUICHE_DCHECK_NE(masque_session_, nullptr);
513 this->stream()->RegisterHttp3DatagramVisitor(this);
514 }
515
~ConnectUdpClientState()516 MasqueClientSession::ConnectUdpClientState::~ConnectUdpClientState() {
517 if (stream() != nullptr) {
518 stream()->UnregisterHttp3DatagramVisitor();
519 }
520 }
521
ConnectUdpClientState(MasqueClientSession::ConnectUdpClientState && other)522 MasqueClientSession::ConnectUdpClientState::ConnectUdpClientState(
523 MasqueClientSession::ConnectUdpClientState&& other) {
524 *this = std::move(other);
525 }
526
527 MasqueClientSession::ConnectUdpClientState&
operator =(MasqueClientSession::ConnectUdpClientState && other)528 MasqueClientSession::ConnectUdpClientState::operator=(
529 MasqueClientSession::ConnectUdpClientState&& other) {
530 stream_ = other.stream_;
531 encapsulated_client_session_ = other.encapsulated_client_session_;
532 masque_session_ = other.masque_session_;
533 target_server_address_ = other.target_server_address_;
534 other.stream_ = nullptr;
535 if (stream() != nullptr) {
536 stream()->ReplaceHttp3DatagramVisitor(this);
537 }
538 return *this;
539 }
540
OnHttp3Datagram(QuicStreamId stream_id,absl::string_view payload)541 void MasqueClientSession::ConnectUdpClientState::OnHttp3Datagram(
542 QuicStreamId stream_id, absl::string_view payload) {
543 QUICHE_DCHECK_EQ(stream_id, stream()->id());
544 QuicDataReader reader(payload);
545 uint64_t context_id;
546 if (!reader.ReadVarInt62(&context_id)) {
547 QUIC_DLOG(ERROR) << "Failed to read context ID";
548 return;
549 }
550 if (context_id != 0) {
551 QUIC_DLOG(ERROR) << "Ignoring HTTP Datagram with unexpected context ID "
552 << context_id;
553 return;
554 }
555 absl::string_view http_payload = reader.ReadRemainingPayload();
556 encapsulated_client_session_->ProcessPacket(http_payload,
557 target_server_address_);
558 QUIC_DVLOG(1) << "Sent " << http_payload.size()
559 << " bytes to connection for stream ID " << stream_id;
560 }
561
ConnectIpClientState(QuicSpdyClientStream * stream,EncapsulatedIpSession * encapsulated_ip_session,MasqueClientSession * masque_session)562 MasqueClientSession::ConnectIpClientState::ConnectIpClientState(
563 QuicSpdyClientStream* stream,
564 EncapsulatedIpSession* encapsulated_ip_session,
565 MasqueClientSession* masque_session)
566 : stream_(stream),
567 encapsulated_ip_session_(encapsulated_ip_session),
568 masque_session_(masque_session) {
569 QUICHE_DCHECK_NE(masque_session_, nullptr);
570 this->stream()->RegisterHttp3DatagramVisitor(this);
571 this->stream()->RegisterConnectIpVisitor(this);
572 }
573
~ConnectIpClientState()574 MasqueClientSession::ConnectIpClientState::~ConnectIpClientState() {
575 if (stream() != nullptr) {
576 stream()->UnregisterHttp3DatagramVisitor();
577 stream()->UnregisterConnectIpVisitor();
578 }
579 }
580
ConnectIpClientState(MasqueClientSession::ConnectIpClientState && other)581 MasqueClientSession::ConnectIpClientState::ConnectIpClientState(
582 MasqueClientSession::ConnectIpClientState&& other) {
583 *this = std::move(other);
584 }
585
586 MasqueClientSession::ConnectIpClientState&
operator =(MasqueClientSession::ConnectIpClientState && other)587 MasqueClientSession::ConnectIpClientState::operator=(
588 MasqueClientSession::ConnectIpClientState&& other) {
589 stream_ = other.stream_;
590 encapsulated_ip_session_ = other.encapsulated_ip_session_;
591 masque_session_ = other.masque_session_;
592 other.stream_ = nullptr;
593 if (stream() != nullptr) {
594 stream()->ReplaceHttp3DatagramVisitor(this);
595 stream()->ReplaceConnectIpVisitor(this);
596 }
597 return *this;
598 }
599
OnHttp3Datagram(QuicStreamId stream_id,absl::string_view payload)600 void MasqueClientSession::ConnectIpClientState::OnHttp3Datagram(
601 QuicStreamId stream_id, absl::string_view payload) {
602 QUICHE_DCHECK_EQ(stream_id, stream()->id());
603 QuicDataReader reader(payload);
604 uint64_t context_id;
605 if (!reader.ReadVarInt62(&context_id)) {
606 QUIC_DLOG(ERROR) << "Failed to read context ID";
607 return;
608 }
609 if (context_id != kConnectIpPayloadContextId) {
610 QUIC_DLOG(ERROR) << "Ignoring HTTP Datagram with unexpected context ID "
611 << context_id;
612 return;
613 }
614 absl::string_view http_payload = reader.ReadRemainingPayload();
615 encapsulated_ip_session_->ProcessIpPacket(http_payload);
616 QUIC_DVLOG(1) << "Sent " << http_payload.size()
617 << " IP bytes to connection for stream ID " << stream_id;
618 }
619
OnAddressAssignCapsule(const AddressAssignCapsule & capsule)620 bool MasqueClientSession::ConnectIpClientState::OnAddressAssignCapsule(
621 const AddressAssignCapsule& capsule) {
622 return encapsulated_ip_session_->OnAddressAssignCapsule(capsule);
623 }
624
OnAddressRequestCapsule(const AddressRequestCapsule & capsule)625 bool MasqueClientSession::ConnectIpClientState::OnAddressRequestCapsule(
626 const AddressRequestCapsule& capsule) {
627 return encapsulated_ip_session_->OnAddressRequestCapsule(capsule);
628 }
629
OnRouteAdvertisementCapsule(const RouteAdvertisementCapsule & capsule)630 bool MasqueClientSession::ConnectIpClientState::OnRouteAdvertisementCapsule(
631 const RouteAdvertisementCapsule& capsule) {
632 return encapsulated_ip_session_->OnRouteAdvertisementCapsule(capsule);
633 }
634
OnHeadersWritten()635 void MasqueClientSession::ConnectIpClientState::OnHeadersWritten() {}
636
637 // ConnectEthernetClientState
638
ConnectEthernetClientState(QuicSpdyClientStream * stream,EncapsulatedEthernetSession * encapsulated_ethernet_session,MasqueClientSession * masque_session)639 MasqueClientSession::ConnectEthernetClientState::ConnectEthernetClientState(
640 QuicSpdyClientStream* stream,
641 EncapsulatedEthernetSession* encapsulated_ethernet_session,
642 MasqueClientSession* masque_session)
643 : stream_(stream),
644 encapsulated_ethernet_session_(encapsulated_ethernet_session),
645 masque_session_(masque_session) {
646 QUICHE_DCHECK_NE(masque_session_, nullptr);
647 this->stream()->RegisterHttp3DatagramVisitor(this);
648 }
649
~ConnectEthernetClientState()650 MasqueClientSession::ConnectEthernetClientState::~ConnectEthernetClientState() {
651 if (stream() != nullptr) {
652 stream()->UnregisterHttp3DatagramVisitor();
653 }
654 }
655
ConnectEthernetClientState(MasqueClientSession::ConnectEthernetClientState && other)656 MasqueClientSession::ConnectEthernetClientState::ConnectEthernetClientState(
657 MasqueClientSession::ConnectEthernetClientState&& other) {
658 *this = std::move(other);
659 }
660
661 MasqueClientSession::ConnectEthernetClientState&
operator =(MasqueClientSession::ConnectEthernetClientState && other)662 MasqueClientSession::ConnectEthernetClientState::operator=(
663 MasqueClientSession::ConnectEthernetClientState&& other) {
664 stream_ = other.stream_;
665 encapsulated_ethernet_session_ = other.encapsulated_ethernet_session_;
666 masque_session_ = other.masque_session_;
667 other.stream_ = nullptr;
668 if (stream() != nullptr) {
669 stream()->ReplaceHttp3DatagramVisitor(this);
670 }
671 return *this;
672 }
673
OnHttp3Datagram(QuicStreamId stream_id,absl::string_view payload)674 void MasqueClientSession::ConnectEthernetClientState::OnHttp3Datagram(
675 QuicStreamId stream_id, absl::string_view payload) {
676 QUICHE_DCHECK_EQ(stream_id, stream()->id());
677 QuicDataReader reader(payload);
678 uint64_t context_id;
679 if (!reader.ReadVarInt62(&context_id)) {
680 QUIC_DLOG(ERROR) << "Failed to read context ID";
681 return;
682 }
683 if (context_id != kConnectEthernetPayloadContextId) {
684 QUIC_DLOG(ERROR) << "Ignoring HTTP Datagram with unexpected context ID "
685 << context_id;
686 return;
687 }
688 absl::string_view http_payload = reader.ReadRemainingPayload();
689 encapsulated_ethernet_session_->ProcessEthernetFrame(http_payload);
690 QUIC_DVLOG(1) << "Sent " << http_payload.size()
691 << " ETHERNET bytes to connection for stream ID " << stream_id;
692 }
693
694 // End ConnectEthernetClientState
695
GetFakeAddress(absl::string_view hostname)696 quiche::QuicheIpAddress MasqueClientSession::GetFakeAddress(
697 absl::string_view hostname) {
698 quiche::QuicheIpAddress address;
699 uint8_t address_bytes[16] = {0xFD};
700 quiche::QuicheRandom::GetInstance()->RandBytes(&address_bytes[1],
701 sizeof(address_bytes) - 1);
702 address.FromPackedString(reinterpret_cast<const char*>(address_bytes),
703 sizeof(address_bytes));
704 std::string address_bytes_string(reinterpret_cast<const char*>(address_bytes),
705 sizeof(address_bytes));
706 fake_addresses_[address_bytes_string] = std::string(hostname);
707 return address;
708 }
709
RemoveFakeAddress(const quiche::QuicheIpAddress & fake_address)710 void MasqueClientSession::RemoveFakeAddress(
711 const quiche::QuicheIpAddress& fake_address) {
712 fake_addresses_.erase(fake_address.ToPackedString());
713 }
714
EnableSignatureAuth(absl::string_view key_id,absl::string_view private_key,absl::string_view public_key)715 void MasqueClientSession::EnableSignatureAuth(absl::string_view key_id,
716 absl::string_view private_key,
717 absl::string_view public_key) {
718 QUICHE_CHECK(!key_id.empty());
719 QUICHE_CHECK_EQ(private_key.size(),
720 static_cast<size_t>(ED25519_PRIVATE_KEY_LEN));
721 QUICHE_CHECK_EQ(public_key.size(),
722 static_cast<size_t>(ED25519_PUBLIC_KEY_LEN));
723 signature_auth_key_id_ = key_id;
724 signature_auth_private_key_ = private_key;
725 signature_auth_public_key_ = public_key;
726 }
727
SendGetRequest(absl::string_view path)728 QuicSpdyClientStream* MasqueClientSession::SendGetRequest(
729 absl::string_view path) {
730 QuicSpdyClientStream* stream = CreateOutgoingBidirectionalStream();
731 if (stream == nullptr) {
732 // Stream flow control limits prevented us from opening a new stream.
733 QUIC_DLOG(ERROR) << "Failed to open GET stream";
734 return nullptr;
735 }
736
737 QuicUrl url(uri_template_);
738 std::string scheme = url.scheme();
739 std::string authority = url.HostPort();
740
741 QUIC_DLOG(INFO) << "Sending GET request on stream " << stream->id()
742 << " scheme=\"" << scheme << "\" authority=\"" << authority
743 << "\" path=\"" << path << "\"";
744
745 // Send the request.
746 spdy::Http2HeaderBlock headers;
747 headers[":method"] = "GET";
748 headers[":scheme"] = scheme;
749 headers[":authority"] = authority;
750 headers[":path"] = path;
751 AddAdditionalHeaders(headers, url);
752 QUIC_DVLOG(1) << "Sending request headers: " << headers.DebugString();
753 // Setting the stream visitor is required to enable reading of the response
754 // body from the stream.
755 stream->set_visitor(this);
756 size_t bytes_sent =
757 stream->SendRequest(std::move(headers), /*body=*/"", /*fin=*/true);
758 if (bytes_sent == 0) {
759 QUIC_DLOG(ERROR) << "Failed to send GET request";
760 return nullptr;
761 }
762 return stream;
763 }
764
OnClose(QuicSpdyStream * stream)765 void MasqueClientSession::OnClose(QuicSpdyStream* stream) {
766 QUIC_DVLOG(1) << "Closing stream " << stream->id();
767 }
768
ComputeSignatureAuthHeader(const QuicUrl & url)769 std::optional<std::string> MasqueClientSession::ComputeSignatureAuthHeader(
770 const QuicUrl& url) {
771 if (signature_auth_private_key_.empty()) {
772 return std::nullopt;
773 }
774 std::string scheme = url.scheme();
775 std::string host = url.host();
776 uint16_t port = url.port();
777 std::string realm = "";
778 std::string key_exporter_output;
779 std::string key_exporter_context = ComputeSignatureAuthContext(
780 kEd25519SignatureScheme, signature_auth_key_id_,
781 signature_auth_public_key_, scheme, host, port, realm);
782 QUIC_DVLOG(1) << "key_exporter_context: "
783 << absl::WebSafeBase64Escape(key_exporter_context);
784 QUICHE_DCHECK(!key_exporter_context.empty());
785 if (!GetMutableCryptoStream()->ExportKeyingMaterial(
786 kSignatureAuthLabel, key_exporter_context, kSignatureAuthExporterSize,
787 &key_exporter_output)) {
788 QUIC_LOG(FATAL) << "Signature auth TLS exporter failed";
789 return std::nullopt;
790 }
791 QUICHE_CHECK_EQ(key_exporter_output.size(), kSignatureAuthExporterSize);
792 std::string signature_input =
793 key_exporter_output.substr(0, kSignatureAuthSignatureInputSize);
794 QUIC_DVLOG(1) << "signature_input: "
795 << absl::WebSafeBase64Escape(signature_input);
796 std::string verification = key_exporter_output.substr(
797 kSignatureAuthSignatureInputSize, kSignatureAuthVerificationSize);
798 std::string data_covered_by_signature =
799 SignatureAuthDataCoveredBySignature(signature_input);
800 QUIC_DVLOG(1) << "data_covered_by_signature: "
801 << absl::WebSafeBase64Escape(data_covered_by_signature);
802 uint8_t signature[ED25519_SIGNATURE_LEN];
803 if (ED25519_sign(
804 signature,
805 reinterpret_cast<const uint8_t*>(data_covered_by_signature.data()),
806 data_covered_by_signature.size(),
807 reinterpret_cast<const uint8_t*>(
808 signature_auth_private_key_.data())) != 1) {
809 QUIC_LOG(FATAL) << "Signature auth signature failed";
810 return std::nullopt;
811 }
812 return absl::StrCat(
813 "Signature k=", absl::WebSafeBase64Escape(signature_auth_key_id_),
814 ", a=", absl::WebSafeBase64Escape(signature_auth_public_key_), ", p=",
815 absl::WebSafeBase64Escape(absl::string_view(
816 reinterpret_cast<const char*>(signature), sizeof(signature))),
817 ", s=", kEd25519SignatureScheme,
818 ", v=", absl::WebSafeBase64Escape(verification));
819 }
820
AddAdditionalHeaders(spdy::Http2HeaderBlock & headers,const QuicUrl & url)821 void MasqueClientSession::AddAdditionalHeaders(spdy::Http2HeaderBlock& headers,
822 const QuicUrl& url) {
823 std::optional<std::string> signature_auth_header =
824 ComputeSignatureAuthHeader(url);
825 if (signature_auth_header.has_value()) {
826 headers["authorization"] = *signature_auth_header;
827 }
828 if (additional_headers_.empty()) {
829 return;
830 }
831 for (absl::string_view sp : absl::StrSplit(additional_headers_, ';')) {
832 quiche::QuicheTextUtils::RemoveLeadingAndTrailingWhitespace(&sp);
833 if (sp.empty()) {
834 continue;
835 }
836 std::vector<absl::string_view> kv =
837 absl::StrSplit(sp, absl::MaxSplits(':', 1));
838 quiche::QuicheTextUtils::RemoveLeadingAndTrailingWhitespace(&kv[0]);
839 quiche::QuicheTextUtils::RemoveLeadingAndTrailingWhitespace(&kv[1]);
840 headers[kv[0]] = kv[1];
841 }
842 }
843
844 } // namespace quic
845