xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/masque/masque_client_tools.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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