13#include <curl/header.h>
49#if defined(PROTO_FUZZER_HAS_TLS_MOCK_SERVER)
62#if defined(PROTO_FUZZER_HAS_HTTP3_MOCK_SERVER)
67namespace proto_fuzzer {
71constexpr unsigned int kAllHeaderOrigins = CURLH_HEADER | CURLH_TRAILER | CURLH_CONNECT | CURLH_1XX | CURLH_PSEUDO;
72constexpr std::size_t kMaxResultHeaders = 16;
73constexpr char kResolverUnixSocketPath[] =
"/curl-fuzzer-resolver.sock";
74constexpr char kResolverCancelDuplicateUrl[] =
"http://resolver-queued-cancel.test:81/queued-cancel-duplicate";
75constexpr auto kResolverCancelGateWait = std::chrono::milliseconds(500);
76constexpr int kResolverIdlePollAttempts = 4;
77constexpr int kResolverIdlePollTimeoutMs = 250;
82bool ScenarioRequestsStartTls(
const curl::fuzzer::proto::Scenario& scenario,
ScenarioRunMode mode) {
85 return scenario.scheme() == curl::fuzzer::proto::SCHEME_LDAP && scenario.ldap_starttls();
87 return scenario.scheme() == curl::fuzzer::proto::SCHEME_FTP && scenario.ftp_auth_tls_filter_allocation_failure();
89 return scenario.scheme() == curl::fuzzer::proto::SCHEME_POP3 &&
90 scenario.pop3_starttls_filter_allocation_failure();
96struct ResolverIdleTraceState {
97 explicit ResolverIdleTraceState(CURL* handle) : easy(handle) {}
99 ~ResolverIdleTraceState() {
102 (void)curl_easy_setopt(easy, CURLOPT_VERBOSE, 0L);
103 (void)curl_easy_setopt(easy, CURLOPT_DEBUGDATA,
nullptr);
107 bool observe_idle =
false;
108 bool saw_idle =
false;
111int ObserveResolverIdleTrace(CURL* , curl_infotype type,
char* data, std::size_t size,
void* client_data) {
112 auto* state =
static_cast<ResolverIdleTraceState*
>(client_data);
113 if (state ==
nullptr || !state->observe_idle || type != CURLINFO_TEXT || data ==
nullptr) {
116 const std::string_view trace(data, size);
117 if (trace.find(
"[TPOOL-DNS]") != std::string_view::npos && trace.find(
"idle for") != std::string_view::npos) {
118 state->saw_idle =
true;
123int CountResolverStart(
void* ,
void* ,
void* client_data) {
124 ++*
static_cast<int*
>(client_data);
131bool ApplyResolverMode(CURL* easy, curl::fuzzer::proto::ResolverMode mode) {
133 case curl::fuzzer::proto::RESOLVER_MODE_THREADED_POSITIVE:
134 return curl_easy_setopt(easy, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4) == CURLE_OK &&
135 curl_easy_setopt(easy, CURLOPT_DNS_SHUFFLE_ADDRESSES, 1L) == CURLE_OK;
136 case curl::fuzzer::proto::RESOLVER_MODE_THREADED_PARTIAL:
137 case curl::fuzzer::proto::RESOLVER_MODE_THREADED_QUEUED_CANCEL:
138 return curl_easy_setopt(easy, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER) == CURLE_OK;
139 case curl::fuzzer::proto::RESOLVER_MODE_THREADED_NEGATIVE:
140 return curl_easy_setopt(easy, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER) == CURLE_OK;
141 case curl::fuzzer::proto::RESOLVER_MODE_THREADED_LINK_LOCAL:
142 return curl_easy_setopt(easy, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6) == CURLE_OK;
143 case curl::fuzzer::proto::RESOLVER_MODE_UNIX_SOCKET:
144 return curl_easy_setopt(easy, CURLOPT_UNIX_SOCKET_PATH, kResolverUnixSocketPath) == CURLE_OK;
145 case curl::fuzzer::proto::RESOLVER_MODE_DEFAULT:
146 case curl::fuzzer::proto::RESOLVER_MODE_GLOBAL_LIFECYCLE:
167CURLcode RunQueuedResolverCancellation(CURL* easy,
MockServer* mock,
const curl::fuzzer::proto::Scenario& scenario) {
168 int duplicate_resolver_starts = 0;
169 ResolverIdleTraceState idle_trace(easy);
179 if (multi ==
nullptr || duplicate ==
nullptr ||
180 curl_multi_setopt(multi.get(), CURLMOPT_RESOLVE_THREADS_MAX, 1L) != CURLM_OK ||
181 curl_easy_setopt(easy, CURLOPT_TIMEOUT_MS, 0L) != CURLE_OK ||
182 curl_easy_setopt(easy, CURLOPT_CONNECTTIMEOUT_MS, 0L) != CURLE_OK ||
183 curl_easy_setopt(duplicate.get(), CURLOPT_TIMEOUT_MS, 0L) != CURLE_OK ||
184 curl_easy_setopt(duplicate.get(), CURLOPT_CONNECTTIMEOUT_MS, 0L) != CURLE_OK ||
185 curl_easy_setopt(duplicate.get(), CURLOPT_URL, kResolverCancelDuplicateUrl) != CURLE_OK ||
186 curl_easy_setopt(duplicate.get(), CURLOPT_FRESH_CONNECT, 1L) != CURLE_OK ||
187 curl_easy_setopt(duplicate.get(), CURLOPT_RESOLVER_START_FUNCTION, &CountResolverStart) != CURLE_OK ||
188 curl_easy_setopt(duplicate.get(), CURLOPT_RESOLVER_START_DATA, &duplicate_resolver_starts) != CURLE_OK ||
189 curl_easy_setopt(easy, CURLOPT_DEBUGFUNCTION, &ObserveResolverIdleTrace) != CURLE_OK ||
190 curl_easy_setopt(easy, CURLOPT_DEBUGDATA, &idle_trace) != CURLE_OK ||
191 curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L) != CURLE_OK) {
192 return CURLE_FAILED_INIT;
195 mock->SetScripts(scenario);
196 CURLMcode add_easy_result = curl_multi_add_handle(multi.get(), easy);
197 CURLMcode add_duplicate_result = CURLM_BAD_HANDLE;
198 CURLMcode perform_result = CURLM_BAD_HANDLE;
199 CURLMcode remove_duplicate_result = CURLM_BAD_HANDLE;
200 bool lookup_started =
false;
201 bool duplicate_resolver_queued =
false;
202 bool resolver_worker_idle =
false;
203 int still_running = 0;
204 if (add_easy_result == CURLM_OK) {
206 perform_result = curl_multi_perform(multi.get(), &still_running);
207 if (perform_result == CURLM_OK && still_running > 0) {
212 if (lookup_started) {
213 add_duplicate_result = curl_multi_add_handle(multi.get(), duplicate.get());
215 if (add_duplicate_result == CURLM_OK) {
216 perform_result = curl_multi_perform(multi.get(), &still_running);
221 duplicate_resolver_queued = perform_result == CURLM_OK && still_running == 2 && duplicate_resolver_starts > 0;
225 if (add_duplicate_result == CURLM_OK) {
226 remove_duplicate_result = curl_multi_remove_handle(multi.get(), duplicate.get());
236 if (lookup_started && duplicate_resolver_queued && remove_duplicate_result == CURLM_OK) {
237 idle_trace.observe_idle =
true;
238 for (
int attempt = 0; attempt < kResolverIdlePollAttempts && !idle_trace.saw_idle; ++attempt) {
240 const CURLMcode poll_result = curl_multi_poll(multi.get(),
nullptr, 0, kResolverIdlePollTimeoutMs, &numfds);
241 if (poll_result != CURLM_OK) {
242 perform_result = poll_result;
245 perform_result = curl_multi_perform(multi.get(), &still_running);
246 if (perform_result != CURLM_OK) {
250 resolver_worker_idle = idle_trace.saw_idle;
253 const CURLMcode remove_easy_result =
254 add_easy_result == CURLM_OK ? curl_multi_remove_handle(multi.get(), easy) : CURLM_BAD_HANDLE;
255 return lookup_started && duplicate_resolver_queued && resolver_worker_idle && perform_result == CURLM_OK &&
256 remove_duplicate_result == CURLM_OK && remove_easy_result == CURLM_OK
269void ProbeTransferResults(CURL* easy) {
270 char* string_result =
nullptr;
271 long long_result = 0;
272 double double_result = 0;
273 curl_off_t offset_result = 0;
274 curl_socket_t socket_result = CURL_SOCKET_BAD;
275 struct curl_certinfo* certinfo_result =
nullptr;
277 (void)curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &string_result);
278 (void)curl_easy_getinfo(easy, CURLINFO_RESPONSE_CODE, &long_result);
279 (void)curl_easy_getinfo(easy, CURLINFO_TOTAL_TIME, &double_result);
280 (void)curl_easy_getinfo(easy, CURLINFO_SIZE_DOWNLOAD_T, &offset_result);
281 (void)curl_easy_getinfo(easy, CURLINFO_ACTIVESOCKET, &socket_result);
282 (void)curl_easy_getinfo(easy, CURLINFO_CERTINFO, &certinfo_result);
284 struct curl_header* header =
nullptr;
285 (void)curl_easy_header(easy,
"Content-Type", 0, kAllHeaderOrigins, -1, &header);
287 for (std::size_t index = 0; index < kMaxResultHeaders; ++index) {
288 header = curl_easy_nextheader(easy, kAllHeaderOrigins, -1, header);
289 if (header ==
nullptr) {
296const char* SchemePrefix(curl::fuzzer::proto::Scheme scheme) {
298 case curl::fuzzer::proto::SCHEME_HTTP:
300 case curl::fuzzer::proto::SCHEME_HTTPS:
302 case curl::fuzzer::proto::SCHEME_WS:
304 case curl::fuzzer::proto::SCHEME_WSS:
306 case curl::fuzzer::proto::SCHEME_TELNET:
308 case curl::fuzzer::proto::SCHEME_FTP:
310 case curl::fuzzer::proto::SCHEME_FTPS:
312 case curl::fuzzer::proto::SCHEME_TFTP:
314 case curl::fuzzer::proto::SCHEME_SMTP:
316 case curl::fuzzer::proto::SCHEME_IMAP:
318 case curl::fuzzer::proto::SCHEME_IMAPS:
320 case curl::fuzzer::proto::SCHEME_POP3:
322 case curl::fuzzer::proto::SCHEME_POP3S:
324 case curl::fuzzer::proto::SCHEME_MQTT:
326 case curl::fuzzer::proto::SCHEME_MQTTS:
328 case curl::fuzzer::proto::SCHEME_LDAP:
330 case curl::fuzzer::proto::SCHEME_LDAPS:
332 case curl::fuzzer::proto::SCHEME_RTSP:
334 case curl::fuzzer::proto::SCHEME_IPFS:
336 case curl::fuzzer::proto::SCHEME_IPNS:
338 case curl::fuzzer::proto::SCHEME_SMTPS:
340 case curl::fuzzer::proto::SCHEME_DICT:
342 case curl::fuzzer::proto::SCHEME_GOPHER:
344 case curl::fuzzer::proto::SCHEME_GOPHERS:
346 case curl::fuzzer::proto::SCHEME_FILE:
348 case curl::fuzzer::proto::SCHEME_UNSPECIFIED:
361std::string ScenarioUrl(
const curl::fuzzer::proto::Scenario& scenario,
const char* prefix) {
362 if (scenario.scheme() == curl::fuzzer::proto::SCHEME_IPFS || scenario.scheme() == curl::fuzzer::proto::SCHEME_IPNS) {
365 return std::string(prefix) +
"://" + scenario.host_path();
373std::unique_ptr<MockServerBase> MakeMockServerForScenario(
const curl::fuzzer::proto::Scenario& scenario,
376#if defined(PROTO_FUZZER_HAS_HTTP3_MOCK_SERVER)
377 if (scenario.http3_plan().use_h1_connect_udp_proxy()) {
378 return std::make_unique<ConnectUdpProxyMockServer>();
380 return std::make_unique<Http3MockServer>(scenario.tls_certificate_chain());
387#if defined(PROTO_FUZZER_HAS_TLS_MOCK_SERVER)
388 return std::make_unique<H2ProxyMockServer>(
390 scenario.connection().proxy_protocol());
400 return std::make_unique<H2CleartextMockServer>(scenario.http2_connect_flush_probe());
404#if defined(PROTO_FUZZER_HAS_TLS_MOCK_SERVER)
405 return std::make_unique<H2OriginMockServer>(scenario.tls_certificate_chain(), scenario.tls_group_profile());
414 return std::make_unique<ProxyTransportMockServer>(scenario.socks_proxy_mode(), scenario.haproxy_protocol());
418 if (scenario.proxy_socket_option_mode() == curl::fuzzer::proto::PROXY_SOCKET_OPTION_LOOPBACK_INTERFACE_KEEPALIVE) {
419 return std::make_unique<InterfaceOptionMockServer>();
421 if (scenario.proxy_socket_option_mode() != curl::fuzzer::proto::PROXY_SOCKET_OPTION_DEFAULT) {
422 return std::make_unique<ProxyOptionMockServer>(scenario.proxy_socket_option_mode());
424 if (scenario.http_transport_mode() != curl::fuzzer::proto::HTTP_TRANSPORT_DIRECT) {
425 return std::make_unique<HttpTransportMockServer>(scenario.http_transport_mode(), scenario.haproxy_protocol());
433#if defined(PROTO_FUZZER_HAS_TLS_MOCK_SERVER)
434 return std::make_unique<DohHttpsRrMockServer>(scenario);
447 return std::make_unique<HappyEyeballsMockServer>();
450 switch (scenario.scheme()) {
451 case curl::fuzzer::proto::SCHEME_HTTP:
455 return std::make_unique<MockServer>();
456 case curl::fuzzer::proto::SCHEME_HTTPS:
457#if defined(PROTO_FUZZER_HAS_TLS_MOCK_SERVER)
459 return std::make_unique<TlsMockServer>(scenario.tls_certificate_chain(), scenario.tls_handshake_profile());
464 return std::make_unique<MockServer>();
465 case curl::fuzzer::proto::SCHEME_GOPHER:
468 case curl::fuzzer::proto::SCHEME_GOPHERS:
469#if defined(PROTO_FUZZER_HAS_TLS_MOCK_SERVER)
471 return std::make_unique<TlsMockServer>(scenario.tls_certificate_chain());
477 case curl::fuzzer::proto::SCHEME_WS:
478 return std::make_unique<WebSocketMockServer>();
479 case curl::fuzzer::proto::SCHEME_WSS:
481#if defined(PROTO_FUZZER_HAS_TLS_MOCK_SERVER)
482 return std::make_unique<TlsWebSocketMockServer>(scenario.tls_certificate_chain());
491 return std::make_unique<WebSocketMockServer>();
492 case curl::fuzzer::proto::SCHEME_TELNET:
493 return std::make_unique<TelnetMockServer>();
494 case curl::fuzzer::proto::SCHEME_FTP:
499 return std::make_unique<FtpMockServer>();
502 case curl::fuzzer::proto::SCHEME_FTPS:
507#if defined(PROTO_FUZZER_HAS_TLS_MOCK_SERVER)
509 return std::make_unique<FtpsMockServer>();
513 case curl::fuzzer::proto::SCHEME_TFTP:
518 return std::make_unique<TftpMockServer>();
521 case curl::fuzzer::proto::SCHEME_SMTP:
526 return std::make_unique<SmtpMockServer>(scenario.smtp_probe_mode());
529 case curl::fuzzer::proto::SCHEME_IMAP:
533 return std::make_unique<ImapMockServer>();
536 case curl::fuzzer::proto::SCHEME_POP3:
538 return std::make_unique<Pop3MockServer>();
541 case curl::fuzzer::proto::SCHEME_MQTT:
547 return std::make_unique<MqttMockServer>(scenario.mqtt_probe_mode());
550 case curl::fuzzer::proto::SCHEME_DICT:
555 return std::make_unique<DictMockServer>();
558 case curl::fuzzer::proto::SCHEME_SMTPS:
563#if defined(PROTO_FUZZER_HAS_TLS_MOCK_SERVER)
565 return std::make_unique<SmtpsMockServer>();
569 case curl::fuzzer::proto::SCHEME_IMAPS:
573#if defined(PROTO_FUZZER_HAS_TLS_MOCK_SERVER)
575 return std::make_unique<ImapsMockServer>();
579 case curl::fuzzer::proto::SCHEME_POP3S:
583#if defined(PROTO_FUZZER_HAS_TLS_MOCK_SERVER)
585 return std::make_unique<Pop3sMockServer>();
589 case curl::fuzzer::proto::SCHEME_MQTTS:
594#if defined(PROTO_FUZZER_HAS_TLS_MOCK_SERVER)
596 return std::make_unique<MqttsMockServer>();
600 case curl::fuzzer::proto::SCHEME_FILE:
604 case curl::fuzzer::proto::SCHEME_LDAP:
609 return std::make_unique<LdapMockServer>();
612 case curl::fuzzer::proto::SCHEME_LDAPS:
617#if defined(PROTO_FUZZER_HAS_TLS_MOCK_SERVER)
619 return std::make_unique<LdapsMockServer>();
623 case curl::fuzzer::proto::SCHEME_RTSP:
628 return std::make_unique<RtspMockServer>();
631 case curl::fuzzer::proto::SCHEME_IPFS:
632 case curl::fuzzer::proto::SCHEME_IPNS:
637 return std::make_unique<MockServer>();
640 case curl::fuzzer::proto::SCHEME_UNSPECIFIED:
663 const char* prefix = SchemePrefix(scenario.scheme());
664 if (prefix ==
nullptr) {
671 const bool file_lane =
673 if (scenario.scheme() == curl::fuzzer::proto::SCHEME_FILE && !file_lane) {
676 if (!file_lane && scenario.host_path().empty()) {
680 std::unique_ptr<MockServerBase> mock = MakeMockServerForScenario(scenario, mode);
681 if (!mock && !file_lane) {
694 CURL* easy = transfer.
easy();
698 if (source_path ==
nullptr) {
701 url = std::string(
"file://") + source_path;
703 url = ScenarioUrl(scenario, prefix);
705 const auto configure_easy = [&] {
707 (void)curl_easy_setopt(easy, CURLOPT_URL, url.c_str());
718 rtsp->ConfigureClient(easy, scenario.rtsp_probe_mode());
721 smtp->ConfigureClient(easy);
724 scenario.gopher_probe_mode() == curl::fuzzer::proto::GOPHER_PROBE_TLS_SESSION_KEY_LIMIT) {
725 (void)curl_easy_setopt(easy, CURLOPT_SSL_VERIFYPEER, 0L);
726 (void)curl_easy_setopt(easy, CURLOPT_SSL_OPTIONS,
static_cast<long>(CURLSSLOPT_ALLOW_BEAST));
728 if (ScenarioRequestsStartTls(scenario, mode)) {
729 (void)curl_easy_setopt(easy, CURLOPT_USE_SSL,
static_cast<long>(CURLUSESSL_ALL));
739 if (!configure_easy()) {
751 assert(request_data !=
nullptr);
753 const CURLcode transfer_result = curl_easy_perform(easy);
754 (void)transfer_result;
755 ProbeTransferResults(easy);
763 (void)curl_easy_setopt(easy, CURLOPT_CONNECT_TO,
nullptr);
766 const curl::fuzzer::proto::ApiPlan* api_plan =
773 if (api_plan !=
nullptr) {
775 assert(api_lifecycle !=
nullptr);
776 mock->SetMultiObserver([api_lifecycle](CURLM* multi) { api_lifecycle->
SetActiveMulti(multi); });
783 scenario.resolver_mode() == curl::fuzzer::proto::RESOLVER_MODE_DEFAULT;
785 assert(request_data !=
nullptr);
789 mock->ConfigureRequestData(request_data);
790 const auto drive_mode = api_plan ==
nullptr ? curl::fuzzer::proto::API_DRIVE_MULTI_PERFORM : api_plan->drive_mode();
791 CURLcode transfer_result = CURLE_FAILED_INIT;
793 scenario.resolver_mode() == curl::fuzzer::proto::RESOLVER_MODE_THREADED_QUEUED_CANCEL) {
794 auto* resolver_mock =
dynamic_cast<MockServer*
>(mock.get());
795 if (resolver_mock !=
nullptr) {
796 transfer_result = RunQueuedResolverCancellation(easy, resolver_mock, scenario);
798 }
else if (drive_mode == curl::fuzzer::proto::API_DRIVE_EASY_PERFORM) {
799 transfer_result = mock->DriveEasyScenario(easy, scenario);
800 }
else if (drive_mode == curl::fuzzer::proto::API_DRIVE_EASY_EVENTS) {
801 transfer_result = mock->DriveEasyScenario(easy, scenario,
true);
802 }
else if (drive_mode == curl::fuzzer::proto::API_DRIVE_CONNECT_ONLY) {
803 transfer_result = mock->DriveConnectOnlyScenario(easy, scenario).connect_result;
805 scenario.rtsp_probe_mode() == curl::fuzzer::proto::RTSP_PROBE_CROSS_ORIGIN_REUSE) {
807 if (rtsp !=
nullptr) {
808 transfer_result = rtsp->DriveCrossOriginReuseScenario(easy, scenario);
811 transfer_result = mock->DriveScenario(easy, scenario);
813 if (api_plan !=
nullptr && api_plan->repeat_mime_post() && transfer_result == CURLE_OK) {
819 transfer_result = mock->DriveScenario(easy, scenario);
821 if (api_lifecycle !=
nullptr) {
822 const bool retains_internal_multi = drive_mode == curl::fuzzer::proto::API_DRIVE_EASY_PERFORM ||
823 drive_mode == curl::fuzzer::proto::API_DRIVE_EASY_EVENTS ||
824 drive_mode == curl::fuzzer::proto::API_DRIVE_CONNECT_ONLY;
828 const bool probe_upkeep =
829 retains_internal_multi && transfer_result != CURLE_OUT_OF_MEMORY && transfer_result != CURLE_FAILED_INIT;
832#if defined(PROTO_FUZZER_OBSERVE_STATE)
833 const volatile std::size_t api_observation[] = {
836 (void)api_observation;
839 ProbeTransferResults(easy);
842#if defined(PROTO_FUZZER_OBSERVE_STATE)
848 const volatile std::size_t request_observation[] = {stats.
request_headers,
861 (void)request_observation;
862 mock->ObserveState();
Safe ownership and typed probes for the dedicated public-API lane.
Exercises public easy/share/query APIs while preserving all caller- owned option and callback lifetim...
std::size_t reentrant_recursive_rejections() const
void ProbeEasyDuplication()
Duplicate, reset, and destroy a scratch easy handle while every pointer- valued option copied from th...
std::size_t reentrant_probes_run() const
bool response_pause_returned() const
void SetActiveMulti(CURLM *multi)
Hand the driving multi handle to the reentrancy probes so the multi-entry probes can target the live ...
void ProbeTransferResults(bool probe_upkeep)
Run correctly typed CURLINFO and response-header probes selected by the plan.
std::size_t response_bytes_received() const
Orchestrates a bounded sequence of mock HTTP exchanges: installs socket callbacks on an easy handle,...
Services curl's single-socket RTSP exchange.
Builds HTTP headers or TELNET options, MIME state, and upload callbacks whose storage or userdata lib...
const RequestBuildStats & stats() const
Return resource counts after caps and allocation failures were applied, without exposing or transferr...
const UploadScriptState & upload_state() const
Return the callback state retained for the complete drive.
bool resolve_entries_ready() const
Return whether resolver-only slist construction installed its mandatory final loopback mapping.
bool upload_callbacks_installed() const
Return whether this scenario needed raw upload callbacks.
Hold the first opt-in getaddrinfo call until the public multi owner has queued the cancellation handl...
Reset curl's global trace configuration to a known off baseline for the lifetime of one input.
Services curl's single-connection, line-based SMTP exchange.
Own per-transfer resources with one teardown contract: detach request callbacks and lists,...
bool Initialize()
Allocate the easy lazily so unused concurrent-transfer slots stay empty.
CURL * easy() const
Borrow the owned easy; callers must not clean it themselves.
const char * FileSourcePath() const
Return the pathname of the prepared FILE source/sink.
void ApplyInputFiles()
Apply prepared parser paths after baseline and peer installation.
ScenarioRequestData * InstallRequestData(const curl::fuzzer::proto::Scenario &scenario, bool apply_resolve_entries=false)
Install owned request attachments.
bool ResetConfiguration()
Reset pre-transfer configuration while preserving prepared parser files.
void ConfigureAltSvcRouting(const std::string &host_path)
Enable isolated Alt-Svc routing for the canonical deep-HTTP authority.
void ApplyPinnedPublicKeyFile()
Install the prepared public-key pin path after generic scalar options.
void ApplyBaseline(const curl::fuzzer::proto::Scenario &scenario)
Apply baseline options and retain their CONNECT_TO storage.
bool PrepareInputFiles(const curl::fuzzer::proto::Scenario &scenario, ScenarioRunMode mode)
Copy bounded parser inputs into lazily allocated anonymous files.
ApiLifecycle * InstallApiLifecycle(const curl::fuzzer::proto::ApiPlan &plan, std::string_view url)
Install API/share state that survives easy cleanup.
Cursor and policy behind the request read/seek callbacks.
std::size_t read_step_count() const
std::size_t offset() const
std::size_t seek_calls() const
std::size_t data_size() const
Deterministic HTTP/1.1 CONNECT-UDP proxy for HTTP/3 scenarios.
std::unique_ptr< CURLM, CurlMultiDeleter > CurlMultiPtr
Unique ownership of a CURL multi handle.
std::unique_ptr< CURL, CurlEasyDeleter > CurlEasyPtr
Unique ownership of a CURL easy handle.
Command-aware in-process peer for curl's DICT request/response.
DoH responder that feeds a synthesized HTTPS resource record to curl's HTTPS-connect ALPN filter.
Command-aware in-process peer for curl's FTP state machine.
Implicit-TLS in-process peer for curl's FTPS state machine.
Bounded transport probes for curl's synchronous Gopher selector.
Plaintext HTTP/2 prior-knowledge origin peer.
HTTPS origin peer with a fixed HTTP/2 application protocol.
Deterministic HTTPS/HTTP2 proxy for structured scenarios.
@ kForward
Submit an absolute-form origin request directly as an HTTP/2 stream.
@ kTunnel
Establish CONNECT, then carry a plaintext HTTP/1.1 origin exchange.
HappyEyeballsMockServer — a loopback peer that lets curl's happy-eyeballs connection filter race seve...
Bounded ngtcp2/OpenSSL QUIC peer for HTTP/3 scenarios.
Bounded HTTP proxy and Unix-domain transports for deep scenarios.
Command-aware in-process peer for curl's IMAP state machine.
Implicit-TLS in-process peer for curl's IMAPS state machine.
std::string BuildIpfsGatewayUrl(curl::fuzzer::proto::Scheme scheme, const std::string &host_path)
Build the IPFS/IPNS gateway URL for the given scheme and pinned host_path.
IPFS/IPNS gateway URL construction shared by the scenario runner and its transcript-level tests.
BER/ASN.1-aware in-process peer for curl's OpenLDAP-backed LDAP client.
Implicit-TLS in-process peer for curl's OpenLDAP-backed LDAPS exchange.
MockConnection and MockServer — the in-process peer that feeds canned response bytes to libcurl over ...
MockServerBase — common plumbing shared by every protocol-specific mock server.
@ kFromApiPlan
Read the multi-loop choice and probes from Scenario::api_plan.
Packet-aware in-process peer for curl's binary MQTT state machine.
Implicit-TLS in-process peer for curl's MQTTS state machine.
MultiTransferRunStats RunMultiTransferScenario(const curl::fuzzer::proto::Scenario &scenario)
Drive two to four easy handles through one CURLM and one bounded in-process HTTP peer.
Bounded shared-multi orchestration for concurrent HTTP transfers.
CURLcode ApplyTlsClientCredentialProfile(CURL *easy, curl::fuzzer::proto::TlsClientCredentialProfile profile)
Apply one closed, harness-owned TLS client-credential configuration.
std::size_t ApplyScenarioOptions(CURL *easy, const curl::fuzzer::proto::Scenario &scenario)
Apply the bounded runtime-visible prefix of Scenario.options.
Translates Scenario SetOption messages into curl_easy_setopt calls, plus the fixed baseline setopts t...
Command-aware in-process peer for curl's POP3 state machine.
Implicit-TLS in-process peer for curl's POP3S state machine.
Closed proxy, no-proxy, interface, and TCP-option peers.
Owns pointer-valued HTTP headers, TELNET options, MIME state, and upload callback state derived from ...
Bounded rendezvous for the resolver queued-cancellation scenario.
bool WaitForResolverCancelLookup(std::chrono::milliseconds timeout)
Give curl's single resolver worker a bounded opportunity to take the first family before the main thr...
Request-aligned in-process peer for curl's RTSP state machine.
int RunScenario(const curl::fuzzer::proto::Scenario &scenario, ScenarioRunMode mode)
Execute one scenario under a complete target behaviour.
Orchestrates a single fuzz iteration from a Scenario proto through to a completed curl transfer.
Command-aware in-process peer for curl's SMTP state machine.
Implicit-TLS in-process peer for curl's SMTPS state machine.
Deterministic SOCKS/HAProxy transport for HTTP scenarios.
Counts the resources that survived allocation and runtime budgets.
std::size_t generated_mime_bytes
Materialized bytes supplied by compact generated MIME sources.
std::size_t telnet_options
Number of CURLOPT_TELNETOPTIONS entries retained.
std::size_t mime_parts
Number of top-level and nested curl_mimepart objects constructed.
std::size_t request_headers
Number of top-level CURLOPT_HTTPHEADER entries retained.
std::size_t mime_callback_parts
Number of top-level parts backed by Scenario.upload callbacks.
std::size_t mime_headers
Number of per-part header entries transferred to curl MIME ownership.
std::size_t resolve_entries
Number of fuzzed CURLOPT_RESOLVE entries retained before the fixed loopback mapping was appended.
ScenarioRunMode
Selects one of the complete, valid RunScenario behaviours.
@ kH2ProxyCoverage
Drive raw HTTP/2 proxy frames around one CONNECT tunnel.
@ kFtpCoverage
Drive FTP or FTPS through its concurrent control/passive-data peer.
@ kRtspCoverage
Drive RTSP through its request-aligned control peer, including response bodies and interleaved RTP da...
@ kPop3Coverage
Drive POP3 through its command-aware control peer, including RETR bodies.
@ kFileCoverage
Drive file:// without a peer, reading only the canonical local target.
@ kLdapCoverage
Drive LDAP through its BER-aware single-socket peer, including the bind reply and scripted search-res...
@ kTlsCoverage
Drive HTTPS through a real TLS peer and inspect live TLS result state.
@ kTftpCoverage
Drive TFTP through its datagram-preserving loopback peer.
@ kDictCoverage
Drive DICT lookups through its command-aware stream peer and EOF-framed response body.
@ kTlsHttp2Coverage
Drive an HTTPS origin through fixed ALPN h2 with push/upkeep probes.
@ kHttp2Coverage
Drive a plaintext HTTP/2 prior-knowledge origin with the H2 peer.
@ kFastProtocol
Drive the protocol without charging its hot loop for generic API probes.
@ kImapCoverage
Drive IMAP through its command-aware control peer, including literals.
@ kDeepHttpCoverage
Drive deep HTTP plus its bounded filename-backed parser inputs.
@ kSmtpCoverage
Drive SMTP or SMTPS through its command-aware control peer, including the DATA body exchange.
@ kMqttCoverage
Drive MQTT or MQTTS through its packet-aware single-socket peer.
@ kTlsWebSocketCoverage
Drive WebSocket framing through a verified in-process TLS peer.
@ kMultiTransfer
Honour MultiPlan and drive several HTTP transfers through one multi.
@ kProxyTransportCoverage
Drive request-triggered SOCKS4/SOCKS5 negotiation, optional HAProxy framing, and the tunneled HTTP ex...
@ kResolverCoverage
Let curl resolve the origin while the socket callback still owns I/O.
@ kGopherCoverage
Drive Gopher through the bounded stream peer.
@ kApiLifecycle
Honour ApiPlan and run the dedicated lifecycle and typed-result probes.
@ kHttp3Coverage
Drive ordered plaintext HTTP/3 actions through the QUIC peer.
Non-blocking in-process peer for curl's blocking TELNET driver.
Bounded loopback UDP peer for structured TFTP scenarios.
Reusable TLS transport for structured in-process protocol peers.
Secure WebSocket peer carried by the in-process TLS transport.
Owns an easy handle and the backing storage retained by its options.
WebSocketMockServer — an in-process WebSocket peer that drives a dynamic 101 handshake and then feeds...