curl-fuzzer C++ reference
Loading...
Searching...
No Matches
ldap_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
10
11#ifndef PROTO_FUZZER_LDAP_MOCK_SERVER_H_
12#define PROTO_FUZZER_LDAP_MOCK_SERVER_H_
13
14#include <curl/curl.h>
15
16#include <cstddef>
17#include <cstdint>
18#include <memory>
19#include <string>
20#include <string_view>
21
22#include "curl_fuzzer.pb.h"
25
26namespace proto_fuzzer {
27
72class LdapMockServer : public MockServerBase, public IncomingDataObserver {
73 public:
75
76 ~LdapMockServer() override;
77
78 void ObserveState() override;
79
80 LdapMockServer(const LdapMockServer&) = delete;
81 LdapMockServer& operator=(const LdapMockServer&) = delete;
82
83 // Append one transport-decoded client fragment to the BER parser input.
84 void ObserveIncomingData(const unsigned char* data, std::size_t size) override;
85
86 // Bounded raw capture of complete client LDAPMessages, including envelope.
87 const std::string& packet_transcript() const;
88
89 // Number of bindRequest messages observed in the most recent drive.
90 std::size_t bind_request_count() const;
91
92 // Number of searchRequest messages observed.
93 std::size_t search_request_count() const;
94
95 // Number of root-DSE supportedSASLMechanisms searches observed.
96 std::size_t mechanism_search_request_count() const;
97
98 // Number of StartTLS ExtendedRequest messages observed.
99 std::size_t starttls_request_count() const;
100
101 // Number of unbindRequest messages observed during teardown.
102 std::size_t unbind_request_count() const;
103
104 // Number of server messages written, both scripted and synthesized.
105 std::size_t server_message_count() const;
106
107 // Number of on_readable chunks forwarded during a search.
108 std::size_t scripted_message_count() const;
109
110 // Number of searchResDone messages this peer generated itself.
111 std::size_t synthesized_done_count() const;
112
113 // Number of bind requests answered from initial_response rather than by the
114 // synthesized success response.
115 std::size_t scripted_bind_response_count() const;
116
117 protected:
118 curl_socket_t HandleOpenSocket(curlsocktype purpose = CURLSOCKTYPE_IPCXN,
119 struct curl_sockaddr* address = nullptr) override;
120
121 // Construct the transport wrapping curl's socketpair endpoint. The default
122 // is a plaintext MockConnection; the LDAPS peer overrides this to return a
123 // server-side TLS acceptor over the same socket.
124 virtual std::unique_ptr<MockConnection> CreateConnection();
125
126 void RunLoop(CURLM* multi, CURL* easy, const curl::fuzzer::proto::Scenario& scenario) override;
127
128 private:
130 enum class ParseResult {
131 kNeedMore,
132 kInvalid,
133 kComplete,
134 };
135
137 struct TlvView {
138 unsigned char tag = 0;
139 std::size_t value_offset = 0;
140 std::size_t value_length = 0;
141 std::size_t total_length = 0;
142 };
143
145 struct MessageView {
146 unsigned char op_tag = 0;
147 std::uint32_t message_id = 0;
148 std::size_t total_length = 0;
149 bool sasl_mechanism_query = false;
150 };
151
152 // Decode one definite-length TLV starting at `offset`.
153 static ParseResult ParseTlv(std::string_view bytes, std::size_t offset, TlvView* view);
154
155 // Decode an LDAPMessage SEQUENCE { messageID INTEGER, protocolOp ... }.
156 static ParseResult ParseMessage(std::string_view bytes, MessageView* message);
157
158 // Recognize OpenLDAP's fixed root-DSE SASL mechanism query.
159 static bool IsSaslMechanismSearch(std::string_view value);
160
161 static void AppendBerLength(std::string* out, std::size_t length);
162
163 static void AppendTlv(std::string* out, unsigned char tag, std::string_view value);
164
165 static void AppendBerInteger(std::string* out, std::uint32_t value);
166
167 // Encode LDAPResult-shaped protocol operations (bindResponse, searchResDone).
168 static void BuildResultMessage(std::uint32_t message_id, unsigned char op_tag, int result_code, std::string* out);
169
170 // Encode a root-DSE entry advertising the bounded PLAIN SASL mechanism.
171 static void BuildSaslMechanismsEntry(std::uint32_t message_id, std::string* out);
172
173 // Replace the envelope messageID while preserving the rest of the message.
174 static bool RewriteMessageId(std::string_view message, std::uint32_t message_id, std::string* out);
175
176 static bool MessageOpTag(std::string_view message, unsigned char* op_tag);
177
178 void ResetForScenario(const curl::fuzzer::proto::Scenario& scenario);
179
180 static void ApplyScriptBackpressure(MockConnection* connection, const curl::fuzzer::proto::Connection& script);
181
182 bool ServiceClientConnection();
183
184 void HandleClientMessage(const MessageView& message);
185
186 void HandleBindRequest(std::uint32_t message_id);
187
188 void HandleSearchRequest(std::uint32_t message_id);
189
190 void HandleMechanismSearchRequest(std::uint32_t message_id);
191
192 void HandleStartTlsRequest(std::uint32_t message_id);
193
194 bool ReleaseNextServerChunk();
195
196 bool FinishSearchIfReady();
197
198 bool WriteServerBytes(std::string_view bytes);
199
200 static void CapturePrefix(std::string_view source, std::size_t limit, std::string* destination);
201
202 void FinishConnections();
203
207 static constexpr std::size_t kMaxClientMessageBytes = 64 * 1024;
208
210 static constexpr std::size_t kMaxCapturedMessageBytes = 64 * 1024;
211
213 static constexpr int kMaxMessagesPerService = 16;
214
216 const curl::fuzzer::proto::Scenario* scenario_;
218 std::size_t server_chunk_count_;
220 std::size_t next_server_chunk_;
221 std::size_t bind_request_count_;
222 std::size_t search_request_count_;
223 std::size_t mechanism_search_request_count_;
224 std::size_t starttls_request_count_;
225 std::size_t unbind_request_count_;
226 std::size_t server_message_count_;
227 std::size_t scripted_message_count_;
228 std::size_t synthesized_done_count_;
229 std::size_t scripted_bind_response_count_;
231 std::string pending_client_bytes_;
233 std::string packet_transcript_;
235 std::uint32_t search_message_id_;
237 bool search_active_;
239 bool search_done_sent_;
241 bool scripted_bind_consumed_;
243 bool release_script_chunks_;
245 bool peer_closed_;
247 bool protocol_error_;
249 bool socket_opened_;
250};
251
252} // namespace proto_fuzzer
253
254#endif // PROTO_FUZZER_LDAP_MOCK_SERVER_H_
Receives bytes after any transport decoding.
void ObserveState() override
Retain protocol outcomes in coverage and introspector builds.
LdapMockServer()
Begin without a borrowed protobuf or open socketpair.
std::size_t scripted_bind_response_count() const
std::size_t synthesized_done_count() const
std::size_t starttls_request_count() const
std::size_t search_request_count() const
std::size_t mechanism_search_request_count() const
std::size_t unbind_request_count() const
curl_socket_t HandleOpenSocket(curlsocktype purpose=CURLSOCKTYPE_IPCXN, struct curl_sockaddr *address=nullptr) override
Create the single LDAP socketpair.
~LdapMockServer() override
Release the server-owned socketpair half after curl has returned its client descriptor.
std::size_t scripted_message_count() const
void ObserveIncomingData(const unsigned char *data, std::size_t size) override
Append one fragment after the transport has decoded it.
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 ...
std::size_t server_message_count() const
virtual std::unique_ptr< MockConnection > CreateConnection()
Create the default plaintext transport.
std::size_t bind_request_count() const
const std::string & packet_transcript() const
Optional observer for application bytes consumed by mock peers.
MockServerBase — common plumbing shared by every protocol-specific mock server.