curl-fuzzer C++ reference
Loading...
Searching...
No Matches
doh_https_rr_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 <arpa/inet.h>
13#include <fcntl.h>
14#include <netinet/in.h>
15#include <sys/socket.h>
16#include <unistd.h>
17
18#include <cerrno>
19#include <cstdint>
20#include <cstdlib>
21#include <cstring>
22#include <string>
23#include <utility>
24
27#include "proto_fuzzer/tls_test_credentials.h"
28
29namespace proto_fuzzer {
30
31namespace {
32
33constexpr char kDohPath[] = "/dns-query";
34constexpr unsigned short kDnsQtypeA = 1;
35constexpr unsigned short kDnsQtypeAAAA = 28;
36constexpr unsigned short kDnsQtypeHTTPS = 65;
37constexpr std::size_t kDnsHeaderSize = 12;
38constexpr std::size_t kDnsAnswerHeaderSize = 12;
39
43bool EchNeedsPendingHttpsRr(const curl::fuzzer::proto::Scenario& scenario) {
44 const std::size_t option_count = RuntimeOptionCount(scenario);
45 for (std::size_t index = 0; index < option_count; ++index) {
46 const auto& option = scenario.options(static_cast<int>(index));
47 if (option.option_id() != curl::fuzzer::proto::CURLOPT_ECH) {
48 continue;
49 }
50 const std::string& value = option.string_value();
51 if (value == "true" || value == "hard" || value.rfind("pn:", 0) == 0) {
52 return true;
53 }
54 }
55 return false;
56}
57
62void SaveEnvironment(const char* name, std::string* value, bool* was_set) {
63 value->clear();
64 *was_set = false;
65 const char* current = std::getenv(name);
66 if (current != nullptr) {
67 value->assign(current);
68 *was_set = true;
69 }
70}
71
77void RestoreEnvironment(const char* name, const std::string& value, bool was_set) {
78 if (was_set) {
79 (void)setenv(name, value.c_str(), 1);
80 } else {
81 (void)unsetenv(name);
82 }
83}
84
88bool MakeNonBlocking(int fd) {
89 const int flags = fcntl(fd, F_GETFL, 0);
90 return flags >= 0 && fcntl(fd, F_SETFL, flags | O_NONBLOCK) >= 0;
91}
92
96std::size_t FindHeaderEnd(const std::string& request) {
97 const std::size_t end = request.find("\r\n\r\n");
98 return end == std::string::npos ? std::string::npos : end + 4;
99}
100
106bool ParseContentLength(const std::string& request, std::size_t header_end, std::size_t* content_length) {
107 std::size_t position = 0;
108 while (position + 1 < header_end) {
109 const std::size_t line_end = request.find("\r\n", position);
110 if (line_end == std::string::npos || line_end >= header_end) {
111 break;
112 }
113 constexpr char kName[] = "content-length:";
114 const std::size_t name_size = sizeof(kName) - 1;
115 if ((line_end - position) > name_size) {
116 bool matches = true;
117 for (std::size_t index = 0; index < name_size; ++index) {
118 const char actual = request[position + index];
119 const char expected = kName[index];
120 const char folded = (actual >= 'A' && actual <= 'Z') ? static_cast<char>(actual - 'A' + 'a') : actual;
121 if (folded != expected) {
122 matches = false;
123 break;
124 }
125 }
126 if (matches) {
127 const char* value = request.c_str() + position + name_size;
128 char* parsed_end = nullptr;
129 const unsigned long parsed = std::strtoul(value, &parsed_end, 10);
130 if (parsed_end != value) {
131 *content_length = static_cast<std::size_t>(parsed);
132 return true;
133 }
134 }
135 }
136 position = line_end + 2;
137 }
138 return false;
139}
140
150bool BuildDnsResponse(const std::string& query, std::string* response, bool* httpsrr) {
151 const auto* bytes = reinterpret_cast<const unsigned char*>(query.data());
152 const std::size_t query_len = query.size();
153 if (query_len < kDnsHeaderSize) {
154 return false;
155 }
156
157 std::size_t position = kDnsHeaderSize;
158 while (position < query_len) {
159 const unsigned char label = bytes[position];
160 if (label == 0) {
161 ++position;
162 break;
163 }
164 if (label & 0xc0) {
165 return false;
166 }
167 if ((position + 1 + label) > query_len) {
168 return false;
169 }
170 position += static_cast<std::size_t>(1) + label;
171 }
172 if ((position + 4) > query_len) {
173 return false;
174 }
175
176 const std::size_t qname_end = position;
177 const unsigned short qtype = static_cast<unsigned short>((bytes[qname_end] << 8) | bytes[qname_end + 1]);
178 const std::size_t question_len = (qname_end + 4) - kDnsHeaderSize;
179 const bool has_answer = qtype == kDnsQtypeA || qtype == kDnsQtypeAAAA || qtype == kDnsQtypeHTTPS;
180
181 std::size_t rdlength = 0;
182 switch (qtype) {
183 case kDnsQtypeA:
184 rdlength = 4;
185 break;
186 case kDnsQtypeAAAA:
187 rdlength = 16;
188 break;
189 case kDnsQtypeHTTPS:
190 // priority, root target, an alpn SvcParam whose length-prefixed values
191 // are "h2" and "h3".
192 rdlength = 13;
193 break;
194 default:
195 break;
196 }
197
198 const std::size_t total = kDnsHeaderSize + question_len + (has_answer ? (kDnsAnswerHeaderSize + rdlength) : 0);
199 if (total > 512) {
200 return false;
201 }
202
203 response->assign(total, '\0');
204 auto* out = reinterpret_cast<unsigned char*>(response->data());
205 out[0] = bytes[0]; // preserve the probe's transaction ID
206 out[1] = bytes[1];
207 out[2] = 0x81; // QR=1, RD=1
208 out[3] = has_answer ? 0x80 : 0x84; // RA=1; rcode 4 (NOTIMP) otherwise
209 out[5] = 1; // QDCOUNT
210 out[7] = has_answer ? 1 : 0; // ANCOUNT
211 std::memcpy(&out[kDnsHeaderSize], &bytes[kDnsHeaderSize], question_len);
212
213 if (has_answer) {
214 unsigned char* answer = &out[kDnsHeaderSize + question_len];
215 *answer++ = 0xc0; // compressed name pointer to the question
216 *answer++ = 0x0c;
217 *answer++ = static_cast<unsigned char>(qtype >> 8);
218 *answer++ = static_cast<unsigned char>(qtype & 0xff);
219 *answer++ = 0;
220 *answer++ = 1; // CLASS IN
221 *answer++ = 0;
222 *answer++ = 0;
223 *answer++ = 0;
224 *answer++ = 60; // TTL
225 *answer++ = 0;
226 *answer++ = static_cast<unsigned char>(rdlength);
227 switch (qtype) {
228 case kDnsQtypeA:
229 *answer++ = 127;
230 *answer++ = 0;
231 *answer++ = 0;
232 *answer++ = 1;
233 break;
234 case kDnsQtypeAAAA:
235 std::memset(answer, 0, 16);
236 answer[15] = 1; // ::1
237 break;
238 case kDnsQtypeHTTPS: {
239 static const unsigned char kHttpsRecord[] = {
240 0, 1, // priority = 1
241 0, // root target (".")
242 0, 1, // SvcParamKey = alpn
243 0, 6, // SvcParamValue length
244 2, 'h', '2', // h2
245 2, 'h', '3' // h3
246 };
247 std::memcpy(answer, kHttpsRecord, sizeof(kHttpsRecord));
248 *httpsrr = true;
249 break;
250 }
251 default:
252 break;
253 }
254 }
255 return true;
256}
257
258} // namespace
259
265void DohHttpsRrMockServer::DohConnection::ObserveIncomingData(const unsigned char* data, std::size_t size) {
266 if (request_overflow || data == nullptr || request.size() > kMaxRequestBytes ||
267 size > kMaxRequestBytes - request.size()) {
268 request_overflow = true;
269 return;
270 }
271 request.append(reinterpret_cast<const char*>(data), size);
272}
273
277DohHttpsRrMockServer::DohHttpsRrMockServer(const curl::fuzzer::proto::Scenario& scenario)
278 : H2OriginMockServer(scenario.tls_certificate_chain()),
279 easy_(nullptr),
280 listener_(-1),
281 option_probe_mode_(scenario.doh().option_probe_mode()),
282 transport_mode_(scenario.doh().transport_mode()),
283 diagnostic_stream_(nullptr),
284 tls_path_fixture_(nullptr),
285 internal_ssl_ctx_callback_count_(0),
286 doh_tls_transport_(
287 transport_mode_ == curl::fuzzer::proto::DOH_TRANSPORT_HTTPS
288 ? std::make_unique<TlsMockTransport>(TlsApplicationProtocol::kHttp11, scenario.tls_certificate_chain())
289 : nullptr),
290 doh_request_count_(0),
291 httpsrr_answer_count_(0),
292 response_mode_(scenario.doh().response_mode()),
293 defer_httpsrr_(EchNeedsPendingHttpsRr(scenario)),
294 setup_ok_(false),
295 proxy_bypass_active_(false),
296 lower_no_proxy_was_set_(false),
297 upper_no_proxy_was_set_(false) {
298 if (option_probe_mode_ == curl::fuzzer::proto::DOH_OPTION_PROBE_CUSTOM_STDERR) {
299 diagnostic_stream_ = std::tmpfile();
300 } else if (option_probe_mode_ == curl::fuzzer::proto::DOH_OPTION_PROBE_TLS_PATHS) {
301 tls_path_fixture_ = std::make_unique<BoundedAnonymousInputFile>(sizeof(tls_test_credentials::kCertificatePem) - 1);
302 (void)tls_path_fixture_->Write(reinterpret_cast<const std::uint8_t*>(tls_test_credentials::kCertificatePem),
303 sizeof(tls_test_credentials::kCertificatePem) - 1);
304 }
305}
306
310 StopListener();
311 EndProxyBypass();
312 if (diagnostic_stream_ != nullptr) {
313 std::fclose(diagnostic_stream_);
314 }
315}
316
322 if (diagnostic_stream_ == nullptr || std::fflush(diagnostic_stream_) != 0) {
323 return {};
324 }
325 const int fd = fileno(diagnostic_stream_);
326 if (fd < 0) {
327 return {};
328 }
329 std::array<char, kMaxDiagnosticBytes> buffer{};
330 ssize_t size;
331 do {
332 size = pread(fd, buffer.data(), buffer.size(), 0);
333 } while (size < 0 && errno == EINTR);
334 return size > 0 ? std::string(buffer.data(), static_cast<std::size_t>(size)) : std::string();
335}
336
339 return tls_path_fixture_ != nullptr && tls_path_fixture_->path() != nullptr;
340}
341
348 easy_ = easy;
349 setup_ok_ = false;
350 if (easy_ == nullptr) {
351 return;
352 }
353 if (!ConfigureHttpsOptions()) {
354 return;
355 }
356 if (!StartListener()) {
357 return;
358 }
359 if (curl_easy_setopt(easy_, CURLOPT_DOH_URL, doh_url_.c_str()) != CURLE_OK) {
360 StopListener();
361 return;
362 }
363 if (!BeginProxyBypass()) {
364 StopListener();
365 return;
366 }
367 setup_ok_ = true;
368}
369
376 setup_ok_ = false;
377 if (easy_ == nullptr || doh_url_.empty() || !proxy_bypass_active_) {
378 return;
379 }
380 if (!ConfigureHttpsOptions()) {
381 return;
382 }
383 if (curl_easy_setopt(easy_, CURLOPT_DOH_URL, doh_url_.c_str()) != CURLE_OK) {
384 return;
385 }
386 setup_ok_ = curl_easy_setopt(easy_, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS) == CURLE_OK;
387}
388
396void DohHttpsRrMockServer::RunLoop(CURLM* multi, CURL* easy, const curl::fuzzer::proto::Scenario& scenario) {
397 if (!setup_ok_) {
398 return;
399 }
400 H2OriginMockServer::RunLoop(multi, easy, scenario);
401}
402
409 ServiceDohConnections();
410}
411
415bool DohHttpsRrMockServer::StartListener() {
416 if (listener_ >= 0) {
417 return true;
418 }
419
420 struct sockaddr_in address;
421 std::memset(&address, 0, sizeof(address));
422 address.sin_family = AF_INET;
423 address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
424 address.sin_port = 0;
425
426 const int fd = socket(AF_INET, SOCK_STREAM, 0);
427 if (fd < 0) {
428 return false;
429 }
430 const int reuse = 1;
431 (void)setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
432 if (bind(fd, reinterpret_cast<struct sockaddr*>(&address), sizeof(address)) < 0 || listen(fd, 8) < 0 ||
433 !MakeNonBlocking(fd)) {
434 close(fd);
435 return false;
436 }
437
438 socklen_t address_len = sizeof(address);
439 if (getsockname(fd, reinterpret_cast<struct sockaddr*>(&address), &address_len) < 0) {
440 close(fd);
441 return false;
442 }
443
444 listener_ = fd;
445 const char* scheme = transport_mode_ == curl::fuzzer::proto::DOH_TRANSPORT_HTTPS ? "https" : "http";
446 doh_url_ =
447 std::string(scheme) + "://127.0.0.1:" + std::to_string(static_cast<int>(ntohs(address.sin_port))) + kDohPath;
448 return true;
449}
450
457bool DohHttpsRrMockServer::ConfigureHttpsOptions() {
458 if (transport_mode_ != curl::fuzzer::proto::DOH_TRANSPORT_HTTPS) {
459 return true;
460 }
461
462 bool configured = true;
463 configured = curl_easy_setopt(easy_, CURLOPT_DOH_SSL_VERIFYHOST, 0L) == CURLE_OK && configured;
464 configured = curl_easy_setopt(easy_, CURLOPT_DOH_SSL_VERIFYPEER, 1L) == CURLE_OK && configured;
465 configured = curl_easy_setopt(easy_, CURLOPT_CERTINFO, 1L) == CURLE_OK && configured;
466 configured = curl_easy_setopt(easy_, CURLOPT_NOSIGNAL, 1L) == CURLE_OK && configured;
467
468 switch (option_probe_mode_) {
469 case curl::fuzzer::proto::DOH_OPTION_PROBE_CUSTOM_STDERR:
470 if (diagnostic_stream_ == nullptr) {
471 return false;
472 }
473 configured = curl_easy_setopt(easy_, CURLOPT_STDERR, diagnostic_stream_) == CURLE_OK && configured;
474 break;
475 case curl::fuzzer::proto::DOH_OPTION_PROBE_TLS_PATHS: {
476 const char* path = tls_path_fixture_ == nullptr ? nullptr : tls_path_fixture_->path();
477 if (path == nullptr) {
478 return false;
479 }
480 configured = curl_easy_setopt(easy_, CURLOPT_CAINFO, path) == CURLE_OK && configured;
481 configured = curl_easy_setopt(easy_, CURLOPT_CAPATH, path) == CURLE_OK && configured;
482 configured = curl_easy_setopt(easy_, CURLOPT_CRLFILE, path) == CURLE_OK && configured;
483 break;
484 }
485 case curl::fuzzer::proto::DOH_OPTION_PROBE_SSL_CTX:
486 configured =
487 curl_easy_setopt(easy_, CURLOPT_SSL_CTX_FUNCTION, &DohHttpsRrMockServer::ObserveSslContext) == CURLE_OK &&
488 configured;
489 configured = curl_easy_setopt(easy_, CURLOPT_SSL_CTX_DATA, this) == CURLE_OK && configured;
490 break;
491 case curl::fuzzer::proto::DOH_OPTION_PROBE_DEFAULT:
492 default:
493 break;
494 }
495 return configured;
496}
497
504CURLcode DohHttpsRrMockServer::ObserveSslContext(CURL* easy, void* /*ssl_context*/, void* user_data) {
505 auto* server = static_cast<DohHttpsRrMockServer*>(user_data);
506 if (server != nullptr && easy != server->easy_) {
507 ++server->internal_ssl_ctx_callback_count_;
508 }
509 return CURLE_OK;
510}
511
513void DohHttpsRrMockServer::StopListener() {
514 if (listener_ >= 0) {
515 close(listener_);
516 listener_ = -1;
517 }
518 for (DohConnection& connection : connections_) {
519 CloseConnection(&connection);
520 }
521 doh_url_.clear();
522}
523
527bool DohHttpsRrMockServer::BeginProxyBypass() {
528 if (proxy_bypass_active_) {
529 return true;
530 }
531 SaveEnvironment("no_proxy", &saved_no_proxy_lower_, &lower_no_proxy_was_set_);
532 SaveEnvironment("NO_PROXY", &saved_no_proxy_upper_, &upper_no_proxy_was_set_);
533 proxy_bypass_active_ = true;
534 if (setenv("no_proxy", "*", 1) != 0 || setenv("NO_PROXY", "*", 1) != 0) {
535 EndProxyBypass();
536 return false;
537 }
538 return true;
539}
540
542void DohHttpsRrMockServer::EndProxyBypass() {
543 if (!proxy_bypass_active_) {
544 return;
545 }
546 RestoreEnvironment("no_proxy", saved_no_proxy_lower_, lower_no_proxy_was_set_);
547 RestoreEnvironment("NO_PROXY", saved_no_proxy_upper_, upper_no_proxy_was_set_);
548 saved_no_proxy_lower_.clear();
549 saved_no_proxy_upper_.clear();
550 lower_no_proxy_was_set_ = false;
551 upper_no_proxy_was_set_ = false;
552 proxy_bypass_active_ = false;
553}
554
557void DohHttpsRrMockServer::CloseConnection(DohConnection* connection) {
558 connection->tls.reset();
559 if (connection->fd >= 0) {
560 close(connection->fd);
561 connection->fd = -1;
562 }
563 connection->request.clear();
564 connection->response.clear();
565 connection->response_offset = 0;
566 connection->deferred_write_turns = 0;
567 connection->writing = false;
568 connection->request_overflow = false;
569}
570
572void DohHttpsRrMockServer::AcceptConnections() {
573 if (listener_ < 0) {
574 return;
575 }
576 for (;;) {
577 const int fd = accept(listener_, nullptr, nullptr);
578 if (fd < 0) {
579 return;
580 }
581 if (!MakeNonBlocking(fd)) {
582 close(fd);
583 continue;
584 }
585 DohConnection* slot = nullptr;
586 for (DohConnection& connection : connections_) {
587 if (!connection.active()) {
588 slot = &connection;
589 break;
590 }
591 }
592 if (slot == nullptr) {
593 close(fd);
594 continue;
595 }
596 slot->request.clear();
597 slot->response.clear();
598 slot->response_offset = 0;
599 slot->deferred_write_turns = 0;
600 slot->writing = false;
601 slot->request_overflow = false;
602 if (transport_mode_ == curl::fuzzer::proto::DOH_TRANSPORT_HTTPS) {
603 if (doh_tls_transport_ == nullptr) {
604 close(fd);
605 continue;
606 }
607 std::unique_ptr<MockConnection> tls = doh_tls_transport_->CreateAcceptedConnection(fd);
608 if (tls == nullptr || !tls->ok()) {
609 continue;
610 }
611 tls->SetIncomingDataObserver(slot);
612 slot->tls = std::move(tls);
613 } else {
614 slot->fd = fd;
615 }
616 }
617}
618
621void DohHttpsRrMockServer::ServiceDohConnections() {
622 AcceptConnections();
623
624 for (DohConnection& connection : connections_) {
625 if (!connection.active()) {
626 continue;
627 }
628
629 if (!connection.writing) {
630 if (connection.tls != nullptr) {
631 (void)connection.tls->DrainIncoming();
632 if (connection.request_overflow) {
633 CloseConnection(&connection);
634 }
635 } else {
636 for (;;) {
637 char buffer[1024];
638 const ssize_t received = recv(connection.fd, buffer, sizeof(buffer), 0);
639 if (received > 0) {
640 if (connection.request.size() + static_cast<std::size_t>(received) > kMaxRequestBytes) {
641 CloseConnection(&connection);
642 break;
643 }
644 connection.request.append(buffer, static_cast<std::size_t>(received));
645 continue;
646 }
647 if (received == 0) {
648 CloseConnection(&connection);
649 break;
650 }
651 if (errno == EINTR) {
652 continue;
653 }
654 if (errno == EAGAIN || errno == EWOULDBLOCK) {
655 break;
656 }
657 CloseConnection(&connection);
658 break;
659 }
660 }
661 if (connection.active() && CompleteRequest(&connection)) {
662 connection.writing = true;
663 }
664 }
665
666 if (!connection.active() || !connection.writing) {
667 continue;
668 }
669
670 // Let an address answer advance the outer connection once before making
671 // its HTTPS resource record readable. This deterministic one-turn skew
672 // exercises the TLS backend's normal "HTTPS-RR still pending" state
673 // without a clock, scheduler race, or external resolver.
674 if (connection.deferred_write_turns != 0) {
675 --connection.deferred_write_turns;
676 continue;
677 }
678
679 if (connection.tls != nullptr) {
680 if (connection.response_offset < connection.response.size()) {
681 if (!connection.tls->WriteAll(reinterpret_cast<const unsigned char*>(connection.response.data()),
682 connection.response.size())) {
683 CloseConnection(&connection);
684 continue;
685 }
686 connection.response_offset = connection.response.size();
687 connection.tls->ShutdownWrite();
688 } else {
689 (void)connection.tls->DrainIncoming();
690 }
691 // TLS writes may require exact retries on later drive turns. Retain the
692 // bounded slot until responder teardown rather than guessing completion
693 // from an OpenSSL progress count.
694 continue;
695 }
696
697 while (connection.response_offset < connection.response.size()) {
698 const ssize_t sent = send(connection.fd, connection.response.data() + connection.response_offset,
699 connection.response.size() - connection.response_offset, MSG_NOSIGNAL);
700 if (sent > 0) {
701 connection.response_offset += static_cast<std::size_t>(sent);
702 continue;
703 }
704 if (sent < 0 && errno == EINTR) {
705 continue;
706 }
707 if (sent < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
708 break;
709 }
710 CloseConnection(&connection);
711 break;
712 }
713 if (connection.fd >= 0 && connection.response_offset >= connection.response.size()) {
714 CloseConnection(&connection);
715 }
716 }
717}
718
724bool DohHttpsRrMockServer::CompleteRequest(DohConnection* connection) {
725 const std::size_t header_end = FindHeaderEnd(connection->request);
726 if (header_end == std::string::npos) {
727 return false;
728 }
729
730 std::size_t content_length = 0;
731 const bool has_length = ParseContentLength(connection->request, header_end, &content_length);
732 if (has_length) {
733 if (content_length > (kMaxRequestBytes - header_end)) {
734 CloseConnection(connection);
735 return false;
736 }
737 if (connection->request.size() < header_end + content_length) {
738 return false;
739 }
740 } else {
741 content_length = connection->request.size() - header_end;
742 }
743
744 if (response_mode_ == curl::fuzzer::proto::DOH_RESPONSE_MODE_TRANSPORT_ERROR) {
745 ++doh_request_count_;
746 CloseConnection(connection);
747 return false;
748 }
749
750 const std::string body = connection->request.substr(header_end, content_length);
751 std::string dns;
752 bool httpsrr = false;
753 if (!BuildDnsResponse(body, &dns, &httpsrr)) {
754 dns.clear();
755 }
756 if (response_mode_ == curl::fuzzer::proto::DOH_RESPONSE_MODE_MALFORMED_DNS) {
757 // A non-empty, header-short message reaches curl's DoH decode error path
758 // after an otherwise successful HTTP exchange. Keeping one byte avoids
759 // conflating the parser failure with an empty response body.
760 dns.assign(1, '\0');
761 httpsrr = false;
762 }
763
764 if (response_mode_ == curl::fuzzer::proto::DOH_RESPONSE_MODE_HTTP_ERROR) {
765 dns.clear();
766 httpsrr = false;
767 }
768
769 connection->response =
770 (response_mode_ == curl::fuzzer::proto::DOH_RESPONSE_MODE_HTTP_ERROR ? "HTTP/1.1 404 DoH\r\n"
771 : "HTTP/1.1 200 DoH\r\n") +
772 std::string(
773 "Content-Type: application/dns-message\r\n"
774 "Content-Length: ") +
775 std::to_string(dns.size()) +
776 "\r\n"
777 "Connection: close\r\n"
778 "\r\n" +
779 dns;
780 if (connection->response.size() > kMaxResponseBytes) {
781 CloseConnection(connection);
782 return false;
783 }
784 connection->response_offset = 0;
785 connection->deferred_write_turns = httpsrr && defer_httpsrr_ ? 1 : 0;
786 ++doh_request_count_;
787 if (httpsrr) {
788 ++httpsrr_answer_count_;
789 }
790 return true;
791}
792
793} // namespace proto_fuzzer
Owns bounded bytes exposed to filename-only APIs through procfs.
void Install(CURL *easy) override
Bind the DoH listener, point CURLOPT_DOH_URL at it, and bypass ambient proxies for curl's internal ha...
void ConfigureRequestData(ScenarioRequestData *request_data) override
Reassert the listener URL and fixed H2 selection after the scenario's option prefix has run.
DohHttpsRrMockServer(const curl::fuzzer::proto::Scenario &scenario)
Construct an idle responder.
~DohHttpsRrMockServer() override
Release the loopback listener and restore the proxy environment before the base carrier tears down it...
void ObserveActiveTransfer(CURL *easy) override
Observe the outer transfer and advance every pending DoH exchange.
void RunLoop(CURLM *multi, CURL *easy, const curl::fuzzer::proto::Scenario &scenario) override
Refuse to enter libcurl when any local resolver setup step failed.
std::string custom_stderr_contents()
Read a bounded prefix without changing the stream position retained by libcurl.
H2OriginMockServer(curl::fuzzer::proto::TlsCertificateChainProfile certificate_chain=curl::fuzzer::proto::TLS_CERTIFICATE_CHAIN_DEFAULT_EC, curl::fuzzer::proto::TlsGroupProfile group_profile=curl::fuzzer::proto::TLS_GROUP_X25519)
Construct an HTTP/2 TLS peer with the selected certificate chain.
void RunLoop(CURLM *multi, CURL *easy, const curl::fuzzer::proto::Scenario &scenario) override
Run the raw HTTP/2 response driver and probe server push and upkeep APIs.
void Install(CURL *easy) override
Keep transport selection out of the mutation grammar.
Builds HTTP headers or TELNET options, MIME state, and upload callbacks whose storage or userdata lib...
void ObserveActiveTransfer(CURL *easy) override
Query while the connection filters are attached.
Reusable TLS carrier for protocol-specific mock peers.
DoH responder that feeds a synthesized HTTPS resource record to curl's HTTPS-connect ALPN filter.
std::size_t RuntimeOptionCount(const curl::fuzzer::proto::Scenario &scenario)
Return the runtime-visible option prefix length.
Translates Scenario SetOption messages into curl_easy_setopt calls, plus the fixed baseline setopts t...
TlsApplicationProtocol
Application protocol selected by the in-process TLS peer.
@ kHttp11
Ordinary HTTPS scripts contain HTTP/1.1 response bytes.