xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/masque/masque_client_bin.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 // This file is responsible for the masque_client binary. It allows testing
6 // our MASQUE client code by connecting to a MASQUE proxy and then sending
7 // HTTP/3 requests to web servers tunnelled over that MASQUE connection.
8 // e.g.: masque_client $PROXY_HOST:$PROXY_PORT $URL1 $URL2
9 
10 #include <cstddef>
11 #include <cstdint>
12 #include <iostream>
13 #include <memory>
14 #include <string>
15 #include <utility>
16 #include <vector>
17 
18 #include "absl/strings/escaping.h"
19 #include "absl/strings/match.h"
20 #include "absl/strings/str_cat.h"
21 #include "absl/strings/str_split.h"
22 #include "absl/strings/string_view.h"
23 #include "openssl/curve25519.h"
24 #include "quiche/quic/core/crypto/proof_verifier.h"
25 #include "quiche/quic/core/http/quic_spdy_client_stream.h"
26 #include "quiche/quic/core/io/quic_default_event_loop.h"
27 #include "quiche/quic/core/io/quic_event_loop.h"
28 #include "quiche/quic/core/quic_default_clock.h"
29 #include "quiche/quic/core/quic_time.h"
30 #include "quiche/quic/core/quic_udp_socket.h"
31 #include "quiche/quic/masque/masque_client.h"
32 #include "quiche/quic/masque/masque_client_session.h"
33 #include "quiche/quic/masque/masque_client_tools.h"
34 #include "quiche/quic/masque/masque_utils.h"
35 #include "quiche/quic/platform/api/quic_bug_tracker.h"
36 #include "quiche/quic/platform/api/quic_default_proof_providers.h"
37 #include "quiche/quic/platform/api/quic_ip_address.h"
38 #include "quiche/quic/platform/api/quic_logging.h"
39 #include "quiche/quic/tools/fake_proof_verifier.h"
40 #include "quiche/common/capsule.h"
41 #include "quiche/common/platform/api/quiche_command_line_flags.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_system_event_loop.h"
45 
46 DEFINE_QUICHE_COMMAND_LINE_FLAG(
47     bool, disable_certificate_verification, false,
48     "If true, don't verify the server certificate.");
49 
50 DEFINE_QUICHE_COMMAND_LINE_FLAG(int, address_family, 0,
51                                 "IP address family to use. Must be 0, 4 or 6. "
52                                 "Defaults to 0 which means any.");
53 
54 DEFINE_QUICHE_COMMAND_LINE_FLAG(
55     std::string, masque_mode, "",
56     "Allows setting MASQUE mode, currently only valid value is \"open\".");
57 
58 DEFINE_QUICHE_COMMAND_LINE_FLAG(
59     std::string, proxy_headers, "",
60     "A list of HTTP headers to add to request to the MASQUE proxy. "
61     "Separated with colons and semicolons. "
62     "For example: \"name1:value1;name2:value2\".");
63 
64 DEFINE_QUICHE_COMMAND_LINE_FLAG(
65     std::string, signature_auth, "",
66     "Enables HTTP Signature Authentication. Pass in the string \"new\" to "
67     "generate new keys. Otherwise, pass in the key ID in ASCII followed by a "
68     "colon and the 32-byte private key as hex. For example: \"kid:0123...f\".");
69 
70 DEFINE_QUICHE_COMMAND_LINE_FLAG(
71     bool, bring_up_tun, false,
72     "If set to true, no URLs need to be specified and instead a TUN device "
73     "is brought up with the assigned IP from the MASQUE CONNECT-IP server.");
74 
75 DEFINE_QUICHE_COMMAND_LINE_FLAG(
76     bool, dns_on_client, false,
77     "If set to true, masque_client will perform DNS for encapsulated URLs and "
78     "send the IP litteral in the CONNECT request. If set to false, "
79     "masque_client send the hostname in the CONNECT request.");
80 
81 DEFINE_QUICHE_COMMAND_LINE_FLAG(
82     bool, bring_up_tap, false,
83     "If set to true, no URLs need to be specified and instead a TAP device "
84     "is brought up for a MASQUE CONNECT-ETHERNET session.");
85 
86 namespace quic {
87 
88 namespace {
89 
90 using ::quiche::AddressAssignCapsule;
91 using ::quiche::AddressRequestCapsule;
92 using ::quiche::RouteAdvertisementCapsule;
93 
94 class MasqueTunSession : public MasqueClientSession::EncapsulatedIpSession,
95                          public QuicSocketEventListener {
96  public:
MasqueTunSession(QuicEventLoop * event_loop,MasqueClientSession * session)97   MasqueTunSession(QuicEventLoop* event_loop, MasqueClientSession* session)
98       : event_loop_(event_loop), session_(session) {}
99   ~MasqueTunSession() override = default;
100   // MasqueClientSession::EncapsulatedIpSession
ProcessIpPacket(absl::string_view packet)101   void ProcessIpPacket(absl::string_view packet) override {
102     QUIC_LOG(INFO) << " Received IP packets of length " << packet.length();
103     if (fd_ == -1) {
104       // TUN not open, early return
105       return;
106     }
107     if (write(fd_, packet.data(), packet.size()) == -1) {
108       QUIC_LOG(FATAL) << "Failed to write";
109     }
110   }
CloseIpSession(const std::string & details)111   void CloseIpSession(const std::string& details) override {
112     QUIC_LOG(ERROR) << "Was asked to close IP session: " << details;
113   }
OnAddressAssignCapsule(const AddressAssignCapsule & capsule)114   bool OnAddressAssignCapsule(const AddressAssignCapsule& capsule) override {
115     for (auto assigned_address : capsule.assigned_addresses) {
116       if (assigned_address.ip_prefix.address().IsIPv4()) {
117         QUIC_LOG(INFO) << "MasqueTunSession saving local IPv4 address "
118                        << assigned_address.ip_prefix.address();
119         local_address_ = assigned_address.ip_prefix.address();
120         break;
121       }
122     }
123     // Bring up the TUN
124     QUIC_LOG(ERROR) << "Bringing up tun with address " << local_address_;
125     fd_ = CreateTunInterface(local_address_, false);
126     if (fd_ < 0) {
127       QUIC_LOG(FATAL) << "Failed to create TUN interface";
128     }
129     if (!event_loop_->RegisterSocket(fd_, kSocketEventReadable, this)) {
130       QUIC_LOG(FATAL) << "Failed to register TUN fd with the event loop";
131     }
132     return true;
133   }
OnAddressRequestCapsule(const AddressRequestCapsule &)134   bool OnAddressRequestCapsule(
135       const AddressRequestCapsule& /*capsule*/) override {
136     // Always ignore the address request capsule from the server.
137     return true;
138   }
OnRouteAdvertisementCapsule(const RouteAdvertisementCapsule &)139   bool OnRouteAdvertisementCapsule(
140       const RouteAdvertisementCapsule& /*capsule*/) override {
141     // Consider installing routes.
142     return true;
143   }
144 
145   // QuicSocketEventListener
OnSocketEvent(QuicEventLoop *,QuicUdpSocketFd fd,QuicSocketEventMask events)146   void OnSocketEvent(QuicEventLoop* /*event_loop*/, QuicUdpSocketFd fd,
147                      QuicSocketEventMask events) override {
148     if ((events & kSocketEventReadable) == 0) {
149       QUIC_DVLOG(1) << "Ignoring OnEvent fd " << fd << " event mask " << events;
150       return;
151     }
152     char datagram[kMasqueIpPacketBufferSize];
153     while (true) {
154       ssize_t read_size = read(fd, datagram, sizeof(datagram));
155       if (read_size < 0) {
156         break;
157       }
158       // Packet received from the TUN. Write it to the MASQUE CONNECT-IP
159       // session.
160       session_->SendIpPacket(absl::string_view(datagram, read_size), this);
161     }
162     if (!event_loop_->SupportsEdgeTriggered()) {
163       if (!event_loop_->RearmSocket(fd, kSocketEventReadable)) {
164         QUIC_BUG(MasqueServerSession_ConnectIp_OnSocketEvent_Rearm)
165             << "Failed to re-arm socket " << fd << " for reading";
166       }
167     }
168   }
169 
170  private:
171   QuicEventLoop* event_loop_;
172   MasqueClientSession* session_;
173   QuicIpAddress local_address_;
174   int fd_ = -1;
175 };
176 
177 class MasqueTapSession
178     : public MasqueClientSession::EncapsulatedEthernetSession,
179       public QuicSocketEventListener {
180  public:
MasqueTapSession(QuicEventLoop * event_loop,MasqueClientSession * session)181   MasqueTapSession(QuicEventLoop* event_loop, MasqueClientSession* session)
182       : event_loop_(event_loop), session_(session) {}
183   ~MasqueTapSession() override = default;
184 
CreateInterface(void)185   void CreateInterface(void) {
186     QUIC_LOG(ERROR) << "Bringing up TAP";
187     fd_ = CreateTapInterface();
188     if (fd_ < 0) {
189       QUIC_LOG(FATAL) << "Failed to create TAP interface";
190     }
191     if (!event_loop_->RegisterSocket(fd_, kSocketEventReadable, this)) {
192       QUIC_LOG(FATAL) << "Failed to register TAP fd with the event loop";
193     }
194   }
195 
196   // MasqueClientSession::EncapsulatedEthernetSession
ProcessEthernetFrame(absl::string_view frame)197   void ProcessEthernetFrame(absl::string_view frame) override {
198     QUIC_LOG(INFO) << " Received Ethernet frame of length " << frame.length();
199     if (fd_ == -1) {
200       // TAP not open, early return
201       return;
202     }
203     if (write(fd_, frame.data(), frame.size()) == -1) {
204       QUIC_LOG(FATAL) << "Failed to write";
205     }
206   }
CloseEthernetSession(const std::string & details)207   void CloseEthernetSession(const std::string& details) override {
208     QUIC_LOG(ERROR) << "Was asked to close Ethernet session: " << details;
209   }
210 
211   // QuicSocketEventListener
OnSocketEvent(QuicEventLoop *,QuicUdpSocketFd fd,QuicSocketEventMask events)212   void OnSocketEvent(QuicEventLoop* /*event_loop*/, QuicUdpSocketFd fd,
213                      QuicSocketEventMask events) override {
214     if ((events & kSocketEventReadable) == 0) {
215       QUIC_DVLOG(1) << "Ignoring OnEvent fd " << fd << " event mask " << events;
216       return;
217     }
218     char datagram[kMasqueEthernetFrameBufferSize];
219     while (true) {
220       ssize_t read_size = read(fd, datagram, sizeof(datagram));
221       if (read_size < 0) {
222         break;
223       }
224       // Frame received from the TAP. Write it to the MASQUE CONNECT-ETHERNET
225       // session.
226       session_->SendEthernetFrame(absl::string_view(datagram, read_size), this);
227     }
228     if (!event_loop_->SupportsEdgeTriggered()) {
229       if (!event_loop_->RearmSocket(fd, kSocketEventReadable)) {
230         QUIC_BUG(MasqueServerSession_ConnectIp_OnSocketEvent_Rearm)
231             << "Failed to re-arm socket " << fd << " for reading";
232       }
233     }
234   }
235 
236  private:
237   QuicEventLoop* event_loop_;
238   MasqueClientSession* session_;
239   std::string local_mac_address_;  // string, uint8_t[6], or new wrapper type?
240   int fd_ = -1;
241 };
242 
RunMasqueClient(int argc,char * argv[])243 int RunMasqueClient(int argc, char* argv[]) {
244   const char* usage =
245       "Usage: masque_client [options] <proxy-url> <urls>..\n"
246       "  <proxy-url> is the URI template of the MASQUE server,\n"
247       "  or host:port to use the default template";
248 
249   // The first non-flag argument is the URI template of the MASQUE server.
250   // All subsequent ones are interpreted as URLs to fetch via the MASQUE server.
251   // Note that the URI template expansion currently only supports string
252   // replacement of {target_host} and {target_port}, not
253   // {?target_host,target_port}.
254   std::vector<std::string> urls =
255       quiche::QuicheParseCommandLineFlags(usage, argc, argv);
256 
257   std::string signature_auth_param =
258       quiche::GetQuicheCommandLineFlag(FLAGS_signature_auth);
259   std::string signature_auth_key_id;
260   std::string signature_auth_private_key;
261   std::string signature_auth_public_key;
262   if (!signature_auth_param.empty()) {
263     static constexpr size_t kEd25519Rfc8032PrivateKeySize = 32;
264     uint8_t public_key[ED25519_PUBLIC_KEY_LEN];
265     uint8_t private_key[ED25519_PRIVATE_KEY_LEN];
266     const bool is_new_key_pair = signature_auth_param == "new";
267     if (is_new_key_pair) {
268       ED25519_keypair(public_key, private_key);
269       QUIC_LOG(INFO) << "Generated new Signature Authentication key pair";
270     } else {
271       std::vector<absl::string_view> signature_auth_param_split =
272           absl::StrSplit(signature_auth_param, absl::MaxSplits(':', 1));
273       std::string private_key_seed;
274       if (signature_auth_param_split.size() != 2) {
275         QUIC_LOG(ERROR)
276             << "Signature authentication parameter is missing a colon";
277         return 1;
278       }
279       signature_auth_key_id = signature_auth_param_split[0];
280       if (signature_auth_key_id.empty()) {
281         QUIC_LOG(ERROR) << "Signature authentication key ID cannot be empty";
282         return 1;
283       }
284       if (!absl::HexStringToBytes(signature_auth_param_split[1],
285                                   &private_key_seed)) {
286         QUIC_LOG(ERROR) << "Signature authentication key hex value is invalid";
287         return 1;
288       }
289 
290       if (private_key_seed.size() != kEd25519Rfc8032PrivateKeySize) {
291         QUIC_LOG(ERROR)
292             << "Invalid signature authentication private key length "
293             << private_key_seed.size();
294         return 1;
295       }
296       ED25519_keypair_from_seed(
297           public_key, private_key,
298           reinterpret_cast<uint8_t*>(private_key_seed.data()));
299       QUIC_LOG(INFO) << "Loaded Signature Authentication key pair";
300     }
301     // Note that Ed25519 private keys are 32 bytes long per RFC 8032. However,
302     // to reduce CPU costs, BoringSSL represents private keys in memory as the
303     // concatenation of the 32-byte private key and the corresponding 32-byte
304     // public key - which makes for a total of 64 bytes. The private key log
305     // below relies on this BoringSSL implementation detail to extract the
306     // RFC 8032 private key because BoringSSL does not provide a supported way
307     // to access it. This is required to allow us to print the private key in a
308     // format that can be passed back in to BoringSSL from the command-line. See
309     // curve25519.h for details. The rest of our signature authentication code
310     // uses the BoringSSL representation without relying on this implementation
311     // detail.
312     static_assert(kEd25519Rfc8032PrivateKeySize <=
313                   static_cast<size_t>(ED25519_PRIVATE_KEY_LEN));
314 
315     std::string private_key_hexstr = absl::BytesToHexString(absl::string_view(
316         reinterpret_cast<char*>(private_key), kEd25519Rfc8032PrivateKeySize));
317     std::string public_key_hexstr = absl::BytesToHexString(absl::string_view(
318         reinterpret_cast<char*>(public_key), ED25519_PUBLIC_KEY_LEN));
319     if (is_new_key_pair) {
320       std::cout << "Generated new Signature Authentication key pair."
321                 << std::endl;
322       std::cout << "Private key: " << private_key_hexstr << std::endl;
323       std::cout << "Public key: " << public_key_hexstr << std::endl;
324       return 0;
325     }
326     QUIC_LOG(INFO) << "Private key: " << private_key_hexstr;
327     QUIC_LOG(INFO) << "Public key: " << public_key_hexstr;
328     signature_auth_private_key = std::string(
329         reinterpret_cast<char*>(private_key), ED25519_PRIVATE_KEY_LEN);
330     signature_auth_public_key = std::string(reinterpret_cast<char*>(public_key),
331                                             ED25519_PUBLIC_KEY_LEN);
332   }
333 
334   bool bring_up_tun = quiche::GetQuicheCommandLineFlag(FLAGS_bring_up_tun);
335   bool bring_up_tap = quiche::GetQuicheCommandLineFlag(FLAGS_bring_up_tap);
336   if (urls.empty() && !bring_up_tun && !bring_up_tap) {
337     quiche::QuichePrintCommandLineFlagHelp(usage);
338     return 1;
339   }
340   if (bring_up_tun && bring_up_tap) {
341     quiche::QuichePrintCommandLineFlagHelp(usage);
342     return 1;
343   }
344 
345   quiche::QuicheSystemEventLoop system_event_loop("masque_client");
346   const bool disable_certificate_verification =
347       quiche::GetQuicheCommandLineFlag(FLAGS_disable_certificate_verification);
348   std::unique_ptr<QuicEventLoop> event_loop =
349       GetDefaultEventLoop()->Create(QuicDefaultClock::Get());
350 
351   std::string uri_template = urls[0];
352   if (!absl::StrContains(uri_template, '/')) {
353     // If an authority is passed in instead of a URI template, use the default
354     // URI template.
355     uri_template =
356         absl::StrCat("https://", uri_template,
357                      "/.well-known/masque/udp/{target_host}/{target_port}/");
358   }
359   url::Parsed parsed_uri_template;
360   url::ParseStandardURL(uri_template.c_str(), uri_template.length(),
361                         &parsed_uri_template);
362   if (!parsed_uri_template.scheme.is_nonempty() ||
363       !parsed_uri_template.host.is_nonempty() ||
364       !parsed_uri_template.path.is_nonempty()) {
365     QUIC_LOG(ERROR) << "Failed to parse MASQUE URI template \"" << urls[0]
366                     << "\"";
367     return 1;
368   }
369   std::string host = uri_template.substr(parsed_uri_template.host.begin,
370                                          parsed_uri_template.host.len);
371   std::unique_ptr<ProofVerifier> proof_verifier;
372   if (disable_certificate_verification) {
373     proof_verifier = std::make_unique<FakeProofVerifier>();
374   } else {
375     proof_verifier = CreateDefaultProofVerifier(host);
376   }
377   MasqueMode masque_mode = MasqueMode::kOpen;
378   std::string mode_string = quiche::GetQuicheCommandLineFlag(FLAGS_masque_mode);
379   if (!mode_string.empty()) {
380     if (mode_string == "open") {
381       masque_mode = MasqueMode::kOpen;
382     } else if (mode_string == "connectip" || mode_string == "connect-ip") {
383       masque_mode = MasqueMode::kConnectIp;
384     } else if (mode_string == "connectethernet" ||
385                mode_string == "connect-ethernet") {
386       masque_mode = MasqueMode::kConnectEthernet;
387     } else {
388       QUIC_LOG(ERROR) << "Invalid masque_mode \"" << mode_string << "\"";
389       return 1;
390     }
391   }
392   const int address_family =
393       quiche::GetQuicheCommandLineFlag(FLAGS_address_family);
394   int address_family_for_lookup;
395   if (address_family == 0) {
396     address_family_for_lookup = AF_UNSPEC;
397   } else if (address_family == 4) {
398     address_family_for_lookup = AF_INET;
399   } else if (address_family == 6) {
400     address_family_for_lookup = AF_INET6;
401   } else {
402     QUIC_LOG(ERROR) << "Invalid address_family " << address_family;
403     return 1;
404   }
405   std::unique_ptr<MasqueClient> masque_client = MasqueClient::Create(
406       uri_template, masque_mode, event_loop.get(), std::move(proof_verifier));
407   if (masque_client == nullptr) {
408     return 1;
409   }
410 
411   QUIC_LOG(INFO) << "MASQUE is connected " << masque_client->connection_id()
412                  << " in " << masque_mode << " mode";
413 
414   masque_client->masque_client_session()->set_additional_headers(
415       quiche::GetQuicheCommandLineFlag(FLAGS_proxy_headers));
416   if (!signature_auth_param.empty()) {
417     masque_client->masque_client_session()->EnableSignatureAuth(
418         signature_auth_key_id, signature_auth_private_key,
419         signature_auth_public_key);
420   }
421 
422   if (bring_up_tun) {
423     QUIC_LOG(INFO) << "Bringing up tun";
424     MasqueTunSession tun_session(event_loop.get(),
425                                  masque_client->masque_client_session());
426     masque_client->masque_client_session()->SendIpPacket(
427         absl::string_view("asdf"), &tun_session);
428     while (true) {
429       event_loop->RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(50));
430     }
431     QUICHE_NOTREACHED();
432   }
433   if (bring_up_tap) {
434     MasqueTapSession tap_session(event_loop.get(),
435                                  masque_client->masque_client_session());
436     tap_session.CreateInterface();
437     while (true) {
438       event_loop->RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(50));
439     }
440     QUICHE_NOTREACHED();
441   }
442 
443   const bool dns_on_client =
444       quiche::GetQuicheCommandLineFlag(FLAGS_dns_on_client);
445 
446   for (size_t i = 1; i < urls.size(); ++i) {
447     if (absl::StartsWith(urls[i], "/")) {
448       QuicSpdyClientStream* stream =
449           masque_client->masque_client_session()->SendGetRequest(urls[i]);
450       while (stream->time_to_response_complete().IsInfinite()) {
451         event_loop->RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(50));
452       }
453       // Print the response body to stdout.
454       std::cout << std::endl << stream->data() << std::endl;
455     } else if (!tools::SendEncapsulatedMasqueRequest(
456                    masque_client.get(), event_loop.get(), urls[i],
457                    disable_certificate_verification, address_family_for_lookup,
458                    dns_on_client)) {
459       return 1;
460     }
461   }
462 
463   return 0;
464 }
465 
466 }  // namespace
467 
468 }  // namespace quic
469 
main(int argc,char * argv[])470 int main(int argc, char* argv[]) { return quic::RunMasqueClient(argc, argv); }
471