/src/curl_fuzzer/proto_fuzzer/socks4_mock_server.cc
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) Max Dymond, <cmeister2@gmail.com>, et al. |
3 | | * |
4 | | * SPDX-License-Identifier: curl |
5 | | */ |
6 | | |
7 | | /// @file |
8 | | /// @brief SOCKS/HAProxy option policy layered over the stream mock. |
9 | | |
10 | | #include "proto_fuzzer/socks4_mock_server.h" |
11 | | |
12 | | #include <curl/curl.h> |
13 | | |
14 | | #include <algorithm> |
15 | | #include <cstddef> |
16 | | #include <memory> |
17 | | |
18 | | namespace proto_fuzzer { |
19 | | |
20 | | namespace { |
21 | | |
22 | 7.93k | long ProxyType(curl::fuzzer::proto::SocksProxyMode mode) { |
23 | 7.93k | switch (mode) { |
24 | 1.30k | case curl::fuzzer::proto::SOCKS_PROXY_SOCKS4A: |
25 | 1.30k | return CURLPROXY_SOCKS4A; |
26 | 173 | case curl::fuzzer::proto::SOCKS_PROXY_SOCKS5: |
27 | 173 | return CURLPROXY_SOCKS5; |
28 | 54 | case curl::fuzzer::proto::SOCKS_PROXY_SOCKS5_HOSTNAME: |
29 | 54 | return CURLPROXY_SOCKS5_HOSTNAME; |
30 | 6.39k | case curl::fuzzer::proto::SOCKS_PROXY_SOCKS4: |
31 | 6.39k | default: |
32 | 6.39k | return CURLPROXY_SOCKS4; |
33 | 7.93k | } |
34 | 7.93k | } |
35 | | |
36 | 7.93k | const char* ProxyUrl(curl::fuzzer::proto::SocksProxyMode mode) { |
37 | 7.93k | switch (mode) { |
38 | 1.30k | case curl::fuzzer::proto::SOCKS_PROXY_SOCKS4A: |
39 | 1.30k | return "socks4a://127.0.0.1:1080"; |
40 | 173 | case curl::fuzzer::proto::SOCKS_PROXY_SOCKS5: |
41 | 173 | return "socks5://127.0.0.1:1080"; |
42 | 54 | case curl::fuzzer::proto::SOCKS_PROXY_SOCKS5_HOSTNAME: |
43 | 54 | return "socks5h://127.0.0.1:1080"; |
44 | 6.39k | case curl::fuzzer::proto::SOCKS_PROXY_SOCKS4: |
45 | 6.39k | default: |
46 | 6.39k | return "socks4://127.0.0.1:1080"; |
47 | 7.93k | } |
48 | 7.93k | } |
49 | | |
50 | | } // namespace |
51 | | |
52 | | /// Select a fixed SOCKS wire version, resolution side, and optional HAProxy |
53 | | /// prefix. Script bytes still decide which advertised SOCKS5 authentication |
54 | | /// method the peer selects. |
55 | | /// @param mode Proxy protocol and hostname-resolution mode to install. |
56 | | /// @param haproxy_protocol Whether to send a fixed protocol-v1 prefix after |
57 | | /// SOCKS tunnel establishment. |
58 | | ProxyTransportMockServer::ProxyTransportMockServer(curl::fuzzer::proto::SocksProxyMode mode, bool haproxy_protocol) |
59 | 7.95k | : mode_(mode), haproxy_protocol_(haproxy_protocol) {} |
60 | | |
61 | | /// Install common socket callbacks and the fixed SOCKS proxy policy. |
62 | | /// @param easy Easy handle to configure. |
63 | 7.93k | void ProxyTransportMockServer::Install(CURL* easy) { |
64 | 7.93k | MockServer::Install(easy); |
65 | 7.93k | client_transcript_.clear(); |
66 | | |
67 | | // CONNECT_TO applies to the origin and would replace the hostname that the |
68 | | // SOCKS request is meant to exercise. The numeric proxy cannot require DNS, |
69 | | // while OPENSOCKETFUNCTION still replaces its outbound socket with the |
70 | | // in-process peer. |
71 | 7.93k | static constexpr char kProxyUser[] = "fuzz-user"; |
72 | 7.93k | static constexpr char kProxyPassword[] = "fuzz-pass"; |
73 | 7.93k | static constexpr char kHaproxyClientIp[] = "127.0.0.1"; |
74 | 7.93k | (void)curl_easy_setopt(easy, CURLOPT_CONNECT_TO, nullptr); |
75 | 7.93k | (void)curl_easy_setopt(easy, CURLOPT_PROXY, ProxyUrl(mode_)); |
76 | 7.93k | (void)curl_easy_setopt(easy, CURLOPT_PROXYTYPE, ProxyType(mode_)); |
77 | 7.93k | (void)curl_easy_setopt(easy, CURLOPT_NOPROXY, ""); |
78 | 7.93k | (void)curl_easy_setopt(easy, CURLOPT_PROXYUSERNAME, kProxyUser); |
79 | 7.93k | (void)curl_easy_setopt(easy, CURLOPT_PROXYPASSWORD, kProxyPassword); |
80 | | // Advertise no-auth plus username/password only. This keeps the client hello |
81 | | // deterministic while letting the scripted method-selection response enter |
82 | | // either SOCKS5 branch. |
83 | 7.93k | (void)curl_easy_setopt(easy, CURLOPT_SOCKS5_AUTH, static_cast<long>(CURLAUTH_BASIC)); |
84 | | // The locally resolved SOCKS5 mode must be deterministic across hosts that |
85 | | // expose both ::1 and 127.0.0.1 for localhost. |
86 | 7.93k | (void)curl_easy_setopt(easy, CURLOPT_IPRESOLVE, static_cast<long>(CURL_IPRESOLVE_V4)); |
87 | 7.93k | (void)curl_easy_setopt(easy, CURLOPT_HAPROXY_CLIENT_IP, haproxy_protocol_ ? kHaproxyClientIp : nullptr); |
88 | 7.93k | (void)curl_easy_setopt(easy, CURLOPT_HAPROXYPROTOCOL, haproxy_protocol_ ? 1L : 0L); |
89 | 7.93k | } |
90 | | |
91 | | /// @return Bounded client bytes observed during the latest transfer. |
92 | 7.93k | const std::string& ProxyTransportMockServer::client_transcript() const { return client_transcript_; } |
93 | | |
94 | | /// Create one socketpair peer and attach the raw-byte transcript observer. |
95 | | /// @return A fresh in-process stream connection. |
96 | 7.62k | std::unique_ptr<MockConnection> ProxyTransportMockServer::CreateConnection() { |
97 | 7.62k | auto connection = std::make_unique<MockConnection>(); |
98 | 7.62k | connection->SetIncomingDataObserver(this); |
99 | 7.62k | return connection; |
100 | 7.62k | } |
101 | | |
102 | | /// @return True because the proxy peer services each bounded perform turn. |
103 | 888 | bool ProxyTransportMockServer::SupportsRepeatedPollsetProbe() const { return true; } |
104 | | |
105 | | /// Retain a bounded prefix of client bytes for strict integration tests. |
106 | | /// @param data First byte in this drained fragment. |
107 | | /// @param size Number of bytes available at `data`. |
108 | 9.02k | void ProxyTransportMockServer::ObserveIncomingData(const unsigned char* data, std::size_t size) { |
109 | 9.02k | if (data == nullptr || size == 0 || client_transcript_.size() >= kMaxTranscriptBytes) { |
110 | 24 | return; |
111 | 24 | } |
112 | 8.99k | const std::size_t remaining = kMaxTranscriptBytes - client_transcript_.size(); |
113 | 8.99k | client_transcript_.append(reinterpret_cast<const char*>(data), std::min(size, remaining)); |
114 | 8.99k | } |
115 | | |
116 | | } // namespace proto_fuzzer |