curl-fuzzer C++ reference
Loading...
Searching...
No Matches
ftp_mock_server.h
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
10#ifndef PROTO_FUZZER_FTP_MOCK_SERVER_H_
11#define PROTO_FUZZER_FTP_MOCK_SERVER_H_
12
13#include <curl/curl.h>
14
15#include <array>
16#include <cstddef>
17#include <memory>
18#include <string>
19#include <string_view>
20
21#include "curl_fuzzer.pb.h"
25
26namespace proto_fuzzer {
27
43class FtpMockServer : public MockServerBase, public IncomingDataObserver {
44 public:
46
47 ~FtpMockServer() override;
48
49 void ObserveState() override;
50
51 const std::string& control_transcript() const;
52
53 const std::string& uploaded_data() const;
54
55 std::size_t opened_data_connection_count() const;
56
57 std::size_t active_pollset_probe_count() const;
58
59 protected:
60 // Create one transport for the control or a passive data connection.
61 // Implicit-TLS subclasses override this factory while retaining the FTP
62 // command parser and data-channel bookkeeping.
63 virtual std::unique_ptr<MockConnection> CreateConnection();
64
65 // Report whether PORT/EPRT listeners can be created on the real loopback
66 // stack. TLS peers whose data channels only exist as wrapped socketpairs
67 // return false so curl's active-mode socket request fails boundedly instead
68 // of leaving an unusable plaintext listener.
69 virtual bool SupportsActiveMode() const;
70
71 // Release every server-owned transport and active descriptor. Derived
72 // classes call this before destroying a transport object that their
73 // connections borrow.
74 void ReleaseConnections();
75
76 void ObserveIncomingData(const unsigned char* data, std::size_t size) override;
77
78 curl_socket_t HandleOpenSocket(curlsocktype purpose = CURLSOCKTYPE_IPCXN,
79 struct curl_sockaddr* address = nullptr) override;
80
81 SocketSetupDisposition GetSocketSetupDisposition(curl_socket_t curlfd, curlsocktype purpose) const override;
82
83 void RunLoop(CURLM* multi, CURL* easy, const curl::fuzzer::proto::Scenario& scenario) override;
84
85 private:
88 enum class TransferDirection {
89 kNone,
90 kDownload,
91 kUpload,
92 };
93
99 struct DataChannel : public IncomingDataObserver {
101 const curl::fuzzer::proto::Connection* script = nullptr;
103 std::unique_ptr<MockConnection> connection;
106 int active_fd = -1;
108 bool transfer_started = false;
110 bool upload = false;
112 std::string incoming_data;
113
114 void ObserveIncomingData(const unsigned char* data, std::size_t size) override;
115 };
116
117 void ResetForScenario(const curl::fuzzer::proto::Scenario& scenario);
118
119 static void ApplyScriptBackpressure(MockConnection* connection, const curl::fuzzer::proto::Connection& script);
120
121 bool ServiceControlConnection();
122
123 bool ServiceDataConnections();
124
125 void HandleControlCommand(std::string_view command);
126
127 void StartNextTransfer(TransferDirection direction);
128
129 void PreloadDownload(DataChannel* channel);
130
131 bool UsesActiveMode() const;
132
133 curl_socket_t OpenActiveListener(struct curl_sockaddr* address);
134
135 void ConnectActiveDataChannel();
136
137 void CloseActiveDescriptors();
138
139 static bool WriteActiveBytes(int fd, const unsigned char* data, std::size_t size);
140
141 static bool ReadActiveBytes(int fd, std::string* output);
142
143 void QueueTransferCompletion();
144
145 const std::string* NextControlReply();
146
147 bool WriteControlBytes(std::string_view bytes);
148
149 static std::string_view CommandVerb(std::string_view command);
150
151 static bool VerbEquals(std::string_view verb, std::string_view expected_uppercase);
152
153 static TransferDirection DirectionForVerb(std::string_view verb);
154
155 bool IsConfiguredCustomDownload(std::string_view verb) const;
156
157 static int FirstReplyCode(std::string_view response);
158
159 static bool TailNeedsOnlyNewline(std::string_view response);
160
161 static void CapturePrefix(std::string_view source, std::size_t limit, std::string* destination);
162
163 void FinishConnections(bool completed);
164
167 static constexpr std::size_t kMaxDataChannels = scenario_limits::kMaxConnections - 1;
168
172 static constexpr std::size_t kMaxCapturedControlBytes = 64 * 1024;
173 static constexpr std::size_t kMaxCapturedUploadBytes = 2 * scenario_limits::kMaxUploadBytes;
174
176 const curl::fuzzer::proto::Scenario* scenario_;
180 std::unique_ptr<MockConnection> control_connection_;
182 std::array<DataChannel, kMaxDataChannels> data_channels_;
184 std::size_t next_data_script_;
187 int active_listener_fd_;
190 curl_socket_t active_listener_client_fd_;
192 std::size_t pending_active_channel_;
194 std::size_t control_reply_count_;
196 std::size_t next_control_reply_;
198 std::string pending_control_bytes_;
200 std::string control_transcript_;
202 std::string uploaded_data_;
204 std::size_t active_pollset_probe_count_;
206 bool control_opened_;
207};
208
209} // namespace proto_fuzzer
210
211#endif // PROTO_FUZZER_FTP_MOCK_SERVER_H_
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 ...
virtual bool SupportsActiveMode() const
Plain FTP can create and publish a real loopback listener for PORT/EPRT.
virtual std::unique_ptr< MockConnection > CreateConnection()
Create the default plaintext transport for the control and passive data connections.
~FtpMockServer() override
Release server-owned descriptors and socketpair peers after curl has returned its client fds.
SocketSetupDisposition GetSocketSetupDisposition(curl_socket_t curlfd, curlsocktype purpose) const override
Control/passive sockets are connected AF_UNIX pairs, while active FTP needs curl to bind and listen o...
curl_socket_t HandleOpenSocket(curlsocktype purpose=CURLSOCKTYPE_IPCXN, struct curl_sockaddr *address=nullptr) override
Allocate one control socket followed by a bounded sequence of passive data sockets,...
const std::string & uploaded_data() const
Expose a bounded upload prefix from the most recent drive while continuing to drain all client bytes,...
std::size_t active_pollset_probe_count() const
Report how many requested zero-timeout multi-poll probes ran during the most recent drive.
void ObserveState() override
Retain protocol outcomes in coverage and introspector builds.
void ObserveIncomingData(const unsigned char *data, std::size_t size) override
Retain decoded client bytes for the control parser.
const std::string & control_transcript() const
Expose the bounded command capture from the most recent drive so tests can verify FTP states without ...
std::size_t opened_data_connection_count() const
Report how many bounded data scripts curl assigned to passive or active transfer sockets,...
void ReleaseConnections()
Close every server-owned transport before the borrowed scenario or a subclass transport disappears.
FtpMockServer()
Begin without a borrowed protobuf or open socketpairs.
Receives bytes after any transport decoding.
Owns one half of a socketpair used to feed canned responses to libcurl.
Definition mock_server.h:29
Optional observer for application bytes consumed by mock peers.
MockServerBase — common plumbing shared by every protocol-specific mock server.
SocketSetupDisposition
Tell curl whether an application-provided socket still needs its normal connect/options setup or alre...
Shared complexity limits for structured scenarios.
constexpr std::size_t kMaxUploadBytes
Maximum callback-backed body bytes visible in one scenario.
constexpr std::size_t kMaxConnections
Four sockets cover redirect/auth flows without permitting mutation-sized connection chains to dominat...