curl-fuzzer C++ reference
Loading...
Searching...
No Matches
dict_mock_server.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) Max Dymond, <cmeister2@gmail.com>, et al.
3 *
4 * SPDX-License-Identifier: curl
5 */
6
9
11
12#include <cstdlib>
13#include <string>
14#include <string_view>
15
18
19namespace proto_fuzzer {
20
21namespace {
22
23// curl builds and sends the complete DICT request synchronously before the
24// outer peer loop can drain it. RFC 2229 escaping can double every retained
25// path byte; fixed command/version text fits comfortably in the remaining
26// headroom. Rejecting a surprising platform socket configuration is safer
27// than entering curl's blocking POLLOUT wait without an operation deadline.
28constexpr std::size_t kMinClientSendBufferBytes = 64 * 1024;
29constexpr std::size_t kMaxBufferedClientBytes = 2 * scenario_limits::kMaxDictHostPathBytes + 4 * 1024;
30static_assert(kMaxBufferedClientBytes < kMinClientSendBufferBytes,
31 "DICT mutation cap exceeds the verified client send buffer");
32
37class ScopedEnvironmentOverride {
38 public:
39 ScopedEnvironmentOverride(const char* name, const char* value) : name_(name) {
40 const char* previous = std::getenv(name);
41 if (previous != nullptr) {
42 had_previous_ = true;
43 previous_ = previous;
44 }
45 active_ = setenv(name, value, 1) == 0;
46 }
47
48 ~ScopedEnvironmentOverride() {
49 if (!active_) {
50 return;
51 }
52 if (had_previous_) {
53 (void)setenv(name_.c_str(), previous_.c_str(), 1);
54 } else {
55 (void)unsetenv(name_.c_str());
56 }
57 }
58
59 ScopedEnvironmentOverride(const ScopedEnvironmentOverride&) = delete;
60 ScopedEnvironmentOverride& operator=(const ScopedEnvironmentOverride&) = delete;
61
62 private:
63 std::string name_;
64 std::string previous_;
65 bool had_previous_ = false;
66 bool active_ = false;
67};
68
69} // namespace
70
74DictMockServer::DictMockServer() : reply_sent_(false), write_closed_(false) {}
75
80
85void DictMockServer::Install(CURL* easy) {
87 (void)curl_easy_setopt(easy, CURLOPT_TIMEOUT_MS, 0L);
88}
89
95std::unique_ptr<MockConnection> DictMockServer::CreateConnection() {
96 std::unique_ptr<MockConnection> connection = PingpongMockServer::CreateConnection();
97 if (connection == nullptr || !connection->ok() ||
98 !connection->EnsureClientSendBufferSize(kMinClientSendBufferBytes)) {
99 return nullptr;
100 }
101 return connection;
102}
103
112void DictMockServer::RunLoop(CURLM* multi, CURL* easy, const curl::fuzzer::proto::Scenario& scenario) {
113 ScopedEnvironmentOverride partial_writes("CURL_DBG_SOCK_WPARTIAL", "50");
115}
116
121 reply_sent_ = false;
122 write_closed_ = false;
123}
124
126void DictMockServer::FinishResponse() {
127 if (write_closed_) {
128 return;
129 }
130 write_closed_ = true;
131 if (connection_ != nullptr) {
132 connection_->ShutdownWrite();
133 }
134}
135
142void DictMockServer::HandleLine(std::string_view line) {
143 const std::string_view verb = FirstToken(line);
144 if (verb.empty()) {
145 return;
146 }
147
148 if (TokenEquals(verb, "CLIENT")) {
149 return;
150 }
151
152 if (reply_sent_ || TokenEquals(verb, "QUIT")) {
153 if (!reply_sent_) {
154 (void)WriteNextReply();
155 reply_sent_ = true;
156 }
157 FinishResponse();
158 return;
159 }
160
161 // The first lookup command consumes the scripted body. A script without a
162 // reply still closes the response so curl cannot wait for a body forever.
163 (void)WriteNextReply();
164 reply_sent_ = true;
165 FinishResponse();
166}
167
170#if defined(PROTO_FUZZER_OBSERVE_STATE)
172 const volatile std::size_t dict_observation[] = {control_transcript().size(), uploaded_data().size()};
173 (void)dict_observation;
174#endif
175}
176
177} // namespace proto_fuzzer
void Install(CURL *easy) override
Remove the operation deadline so a partial DICT request write exercises sendf()'s explicit no-timeout...
std::unique_ptr< MockConnection > CreateConnection() override
Construct the ordinary plaintext transport only when its client endpoint can queue the complete bound...
void HandleLine(std::string_view line) override
Answer the first lookup command in the request and ignore CLIENT, which is a bare identification line...
void RunLoop(CURLM *multi, CURL *easy, const curl::fuzzer::proto::Scenario &scenario) override
Split each sufficiently large socket write while curl constructs and uses this DICT connection.
void ObserveState() override
Retain protocol outcomes in coverage and introspector builds.
DictMockServer()
Begin without a borrowed protobuf or open socketpair.
~DictMockServer() override
Release the server-owned socketpair half after curl has returned its client descriptor.
void ResetProtocolState() override
Reset DICT's response bookkeeping for a new drive.
virtual void Install(CURL *easy)
Install the common OPENSOCKETFUNCTION / OPENSOCKETDATA / SOCKOPTFUNCTION callbacks on 'easy'.
std::unique_ptr< MockConnection > connection_
The per-scenario MockConnection, lazily created by HandleOpenSocket().
virtual void ObserveState()
Default no-op for peers without protocol-specific observations.
void RunLoop(CURLM *multi, CURL *easy, const curl::fuzzer::proto::Scenario &scenario) override
Alternate curl transitions with immediate peer service until the transfer completes or deterministic ...
const std::string & control_transcript() const
Expose the bounded command capture from the most recent drive so tests can verify protocol states wit...
bool WriteNextReply()
Consume the next scripted reply.
const curl::fuzzer::proto::Scenario * scenario() const
Borrowed scenario for the synchronous drive, or nullptr outside RunLoop().
virtual std::unique_ptr< MockConnection > CreateConnection()
Create the default plaintext transport.
static std::string_view FirstToken(std::string_view line)
Remove optional leading horizontal whitespace and return the first token without allocating.
const std::string & uploaded_data() const
Expose a bounded prefix of bytes drained during the most recent body phase while continuing to drain ...
static bool TokenEquals(std::string_view token, std::string_view expected_uppercase)
Fold only ASCII lowercase command letters.
Command-aware in-process peer for curl's DICT request/response.
MockConnection and MockServer — the in-process peer that feeds canned response bytes to libcurl over ...
Shared complexity limits for structured scenarios.
constexpr std::size_t kMaxDictHostPathBytes
DICT builds its complete command synchronously before the outer mock loop can drain curl's socket.