Coverage Report

Created: 2026-10-28 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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