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_tools.h"
6
7 #include <memory>
8 #include <optional>
9 #include <ostream>
10 #include <string>
11 #include <utility>
12
13 #include "absl/strings/str_cat.h"
14 #include "quiche/quic/core/crypto/proof_verifier.h"
15 #include "quiche/quic/core/io/quic_event_loop.h"
16 #include "quiche/quic/core/quic_error_codes.h"
17 #include "quiche/quic/masque/masque_client.h"
18 #include "quiche/quic/masque/masque_client_session.h"
19 #include "quiche/quic/masque/masque_encapsulated_client.h"
20 #include "quiche/quic/masque/masque_utils.h"
21 #include "quiche/quic/platform/api/quic_default_proof_providers.h"
22 #include "quiche/quic/platform/api/quic_logging.h"
23 #include "quiche/quic/platform/api/quic_socket_address.h"
24 #include "quiche/quic/tools/fake_proof_verifier.h"
25 #include "quiche/quic/tools/quic_name_lookup.h"
26 #include "quiche/quic/tools/quic_url.h"
27 #include "quiche/common/platform/api/quiche_logging.h"
28 #include "quiche/common/quiche_ip_address.h"
29 #include "quiche/spdy/core/http2_header_block.h"
30
31 namespace quic {
32 namespace tools {
33
34 namespace {
35
36 // Helper class to ensure a fake address gets properly removed when this goes
37 // out of scope.
38 class FakeAddressRemover {
39 public:
40 FakeAddressRemover() = default;
IngestFakeAddress(const quiche::QuicheIpAddress & fake_address,MasqueClientSession * masque_client_session)41 void IngestFakeAddress(const quiche::QuicheIpAddress& fake_address,
42 MasqueClientSession* masque_client_session) {
43 QUICHE_CHECK(masque_client_session != nullptr);
44 QUICHE_CHECK(!fake_address_.has_value());
45 fake_address_ = fake_address;
46 masque_client_session_ = masque_client_session;
47 }
~FakeAddressRemover()48 ~FakeAddressRemover() {
49 if (fake_address_.has_value()) {
50 masque_client_session_->RemoveFakeAddress(*fake_address_);
51 }
52 }
53
54 private:
55 std::optional<quiche::QuicheIpAddress> fake_address_;
56 MasqueClientSession* masque_client_session_ = nullptr;
57 };
58
59 } // namespace
60
SendEncapsulatedMasqueRequest(MasqueClient * masque_client,QuicEventLoop * event_loop,std::string url_string,bool disable_certificate_verification,int address_family_for_lookup,bool dns_on_client)61 bool SendEncapsulatedMasqueRequest(MasqueClient* masque_client,
62 QuicEventLoop* event_loop,
63 std::string url_string,
64 bool disable_certificate_verification,
65 int address_family_for_lookup,
66 bool dns_on_client) {
67 if (!masque_client->masque_client_session()->SupportsH3Datagram()) {
68 QUIC_LOG(ERROR) << "Refusing to use MASQUE without datagram support";
69 return false;
70 }
71 const QuicUrl url(url_string, "https");
72 std::unique_ptr<ProofVerifier> proof_verifier;
73 if (disable_certificate_verification) {
74 proof_verifier = std::make_unique<FakeProofVerifier>();
75 } else {
76 proof_verifier = CreateDefaultProofVerifier(url.host());
77 }
78
79 // Build the client, and try to connect.
80 QuicSocketAddress addr;
81 FakeAddressRemover fake_address_remover;
82 if (dns_on_client) {
83 addr = LookupAddress(address_family_for_lookup, url.host(),
84 absl::StrCat(url.port()));
85 if (!addr.IsInitialized()) {
86 QUIC_LOG(ERROR) << "Unable to resolve address: " << url.host();
87 return false;
88 }
89 } else {
90 quiche::QuicheIpAddress fake_address =
91 masque_client->masque_client_session()->GetFakeAddress(url.host());
92 fake_address_remover.IngestFakeAddress(
93 fake_address, masque_client->masque_client_session());
94 addr = QuicSocketAddress(fake_address, url.port());
95 QUICHE_CHECK(addr.IsInitialized());
96 }
97 const QuicServerId server_id(url.host(), url.port());
98 auto client = std::make_unique<MasqueEncapsulatedClient>(
99 addr, server_id, event_loop, std::move(proof_verifier), masque_client);
100
101 if (client == nullptr) {
102 QUIC_LOG(ERROR) << "Failed to create MasqueEncapsulatedClient for "
103 << url_string;
104 return false;
105 }
106
107 client->set_initial_max_packet_length(kMasqueMaxEncapsulatedPacketSize);
108 client->set_drop_response_body(false);
109 if (!client->Initialize()) {
110 QUIC_LOG(ERROR) << "Failed to initialize MasqueEncapsulatedClient for "
111 << url_string;
112 return false;
113 }
114
115 if (!client->Connect()) {
116 QuicErrorCode error = client->session()->error();
117 QUIC_LOG(ERROR) << "Failed to connect with client "
118 << client->session()->connection()->client_connection_id()
119 << " server " << client->session()->connection_id()
120 << " to " << url.HostPort()
121 << ". Error: " << QuicErrorCodeToString(error);
122 return false;
123 }
124
125 QUIC_LOG(INFO) << "Connected client "
126 << client->session()->connection()->client_connection_id()
127 << " server " << client->session()->connection_id() << " for "
128 << url_string;
129
130 // Construct the string body from flags, if provided.
131 // TODO(dschinazi) Add support for HTTP POST and non-empty bodies.
132 const std::string body = "";
133
134 // Construct a GET or POST request for supplied URL.
135 spdy::Http2HeaderBlock header_block;
136 header_block[":method"] = "GET";
137 header_block[":scheme"] = url.scheme();
138 header_block[":authority"] = url.HostPort();
139 header_block[":path"] = url.PathParamsQuery();
140
141 // Make sure to store the response, for later output.
142 client->set_store_response(true);
143
144 // Send the MASQUE init request.
145 client->SendRequestAndWaitForResponse(header_block, body,
146 /*fin=*/true);
147
148 if (!client->connected()) {
149 QUIC_LOG(ERROR) << "Request for " << url_string
150 << " caused connection failure. Error: "
151 << QuicErrorCodeToString(client->session()->error());
152 return false;
153 }
154
155 const int response_code = client->latest_response_code();
156 if (response_code < 200 || response_code >= 300) {
157 QUIC_LOG(ERROR) << "Request for " << url_string
158 << " failed with HTTP response code " << response_code;
159 return false;
160 }
161
162 const std::string response_body = client->latest_response_body();
163 QUIC_LOG(INFO) << "Request succeeded for " << url_string << std::endl
164 << response_body;
165
166 return true;
167 }
168
169 } // namespace tools
170 } // namespace quic
171