23namespace proto_fuzzer {
26constexpr char kDevNullPath[] =
"/dev/null";
27constexpr char kAltSvcOrigin[] =
"altsvc-origin.test";
28constexpr char kAltSvcLoopbackResolve[] =
"*:80:127.0.1.127";
31int AbortUnexpectedAltSvcResolve(
void*,
void*,
void*) {
return 1; }
33bool HasCanonicalAltSvcAuthority(
const std::string& host_path) {
34 const std::size_t host_size =
sizeof(kAltSvcOrigin) - 1;
35 return host_path.compare(0, host_size, kAltSvcOrigin) == 0 &&
36 (host_path.size() == host_size || host_path[host_size] ==
'/' || host_path[host_size] ==
'?' ||
37 host_path[host_size] ==
'#');
42 bool guard_netrc =
false) {
50 std::string guarded_contents(1,
'\n');
51 guarded_contents.append(contents.data(), size);
52 if (input_file->Write(
reinterpret_cast<const std::uint8_t*
>(guarded_contents.data()), guarded_contents.size())) {
53 *remaining_bytes -= size;
55 }
else if (input_file->Write(
reinterpret_cast<const std::uint8_t*
>(contents.data()), size)) {
56 *remaining_bytes -= size;
64struct TransferSession::ParserInputs {
65 BoundedAnonymousInputFile file_source{scenario_limits::kMaxFileInputBytes};
66 BoundedAnonymousInputFile cookie{scenario_limits::kMaxFileInputBytes};
67 BoundedAnonymousInputFile altsvc{scenario_limits::kMaxFileInputBytes};
68 BoundedAnonymousInputFile hsts{scenario_limits::kMaxFileInputBytes};
69 BoundedAnonymousInputFile netrc{scenario_limits::kMaxFileInputBytes + 1};
70 BoundedAnonymousInputFile crl{scenario_limits::kMaxFileInputBytes};
71 BoundedAnonymousInputFile pinned_public_key{scenario_limits::kMaxFileInputBytes};
74TransferSession::TransferSession() =
default;
75TransferSession::~TransferSession() {
Close(); }
81 easy_.reset(curl_easy_init());
83 return easy_ !=
nullptr;
96 connect_to_ =
ApplyBaselineOptions(easy_.get(), scenario.scheme(), scenario.trace_ids(), scenario.trace_config());
103 if (!easy_ || request_data_ || api_lifecycle_) {
106 curl_easy_reset(easy_.get());
108 altsvc_resolve_.reset();
124 parser_inputs_ = std::make_unique<ParserInputs>();
127 return parser_inputs_->file_source.Write(
reinterpret_cast<const std::uint8_t*
>(scenario.file_source().data()),
132 PrepareInputFile(scenario.cookie_file(), &remaining_bytes, &parser_inputs_->cookie);
133 PrepareInputFile(scenario.altsvc_file(), &remaining_bytes, &parser_inputs_->altsvc);
134 PrepareInputFile(scenario.hsts_file(), &remaining_bytes, &parser_inputs_->hsts);
135 PrepareInputFile(scenario.netrc_file(), &remaining_bytes, &parser_inputs_->netrc,
true);
137 PrepareInputFile(scenario.crl_file(), &remaining_bytes, &parser_inputs_->crl);
138 PrepareInputFile(scenario.pinned_public_key_file(), &remaining_bytes, &parser_inputs_->pinned_public_key);
147 return parser_inputs_ ? parser_inputs_->file_source.path() :
nullptr;
152 if (!easy_ || !parser_inputs_) {
155 if (
const char* path = parser_inputs_->cookie.path()) {
156 (void)curl_easy_setopt(easy_.get(), CURLOPT_COOKIEFILE, path);
160 if (
const char* path = parser_inputs_->altsvc.path()) {
161 (void)curl_easy_setopt(easy_.get(), CURLOPT_ALTSVC, path);
162 (void)curl_easy_setopt(easy_.get(), CURLOPT_ALTSVC, kDevNullPath);
164 if (
const char* path = parser_inputs_->hsts.path()) {
165 (void)curl_easy_setopt(easy_.get(), CURLOPT_HSTS, path);
166 (void)curl_easy_setopt(easy_.get(), CURLOPT_HSTS, kDevNullPath);
168 if (
const char* path = parser_inputs_->netrc.path()) {
169 (void)curl_easy_setopt(easy_.get(), CURLOPT_NETRC_FILE, path);
170 (void)curl_easy_setopt(easy_.get(), CURLOPT_NETRC, CURL_NETRC_REQUIRED);
172 if (
const char* path = parser_inputs_->crl.path()) {
173 (void)curl_easy_setopt(easy_.get(), CURLOPT_CRLFILE, path);
181 if (!easy_ || !parser_inputs_) {
184 if (
const char* path = parser_inputs_->pinned_public_key.path()) {
185 (void)curl_easy_setopt(easy_.get(), CURLOPT_PINNEDPUBLICKEY, path);
192 if (!easy_ || !parser_inputs_ || parser_inputs_->altsvc.path() ==
nullptr ||
193 !HasCanonicalAltSvcAuthority(host_path)) {
196 CurlSlistPtr resolve(curl_slist_append(
nullptr, kAltSvcLoopbackResolve));
197 if (!resolve || curl_easy_setopt(easy_.get(), CURLOPT_RESOLVE, resolve.get()) != CURLE_OK) {
201 altsvc_resolve_ = std::move(resolve);
202 if (curl_easy_setopt(easy_.get(), CURLOPT_RESOLVER_START_FUNCTION, &AbortUnexpectedAltSvcResolve) == CURLE_OK) {
205 (void)curl_easy_setopt(easy_.get(), CURLOPT_CONNECT_TO,
nullptr);
214 bool apply_resolve_entries) {
215 if (!easy_ || request_data_) {
218 request_data_.emplace(easy_.get(), scenario, apply_resolve_entries);
219 return &*request_data_;
227 if (!easy_ || api_lifecycle_) {
230 api_lifecycle_ = std::make_unique<ApiLifecycle>(easy_.get(), plan, url);
231 return api_lifecycle_.get();
236 request_data_.reset();
238 api_lifecycle_.reset();
239 altsvc_resolve_.reset();
241 parser_inputs_.reset();
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...
Builds HTTP headers or TELNET options, MIME state, and upload callbacks whose storage or userdata lib...
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.
void Close()
Release all resources in their required order. Safe to call repeatedly.
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.
std::unique_ptr< curl_slist, CurlSlistDeleter > CurlSlistPtr
Unique ownership of a caller-owned curl_slist.
CurlSlistPtr ApplyBaselineOptions(CURL *easy, curl::fuzzer::proto::Scheme scheme, bool trace_ids, const std::string &trace_config)
Apply deterministic routing, timeout, output, and persistence defaults.
Translates Scenario SetOption messages into curl_easy_setopt calls, plus the fixed baseline setopts t...
Owns pointer-valued HTTP headers, TELNET options, MIME state, and upload callback state derived from ...
Shared complexity limits for structured scenarios.
constexpr std::size_t kMaxFileInputBytes
One filename-only parser input can span many lines and cross curl's normal transfer buffer boundary w...
constexpr std::size_t kMaxFileInputTotalBytes
Share a ceiling across filename-backed fixtures so populating several in one scenario cannot multiply...
ScenarioRunMode
Selects one of the complete, valid RunScenario behaviours.
@ kFileCoverage
Drive file:// without a peer, reading only the canonical local target.
@ kTlsCoverage
Drive HTTPS through a real TLS peer and inspect live TLS result state.
@ kDeepHttpCoverage
Drive deep HTTP plus its bounded filename-backed parser inputs.
Owns an easy handle and the backing storage retained by its options.