14#include <nghttp3/nghttp3.h>
15#include <ngtcp2/ngtcp2.h>
16#include <ngtcp2/ngtcp2_crypto.h>
17#include <ngtcp2/ngtcp2_crypto_ossl.h>
18#include <openssl/err.h>
19#include <openssl/pem.h>
20#include <openssl/ssl.h>
21#include <sys/socket.h>
39#include "proto_fuzzer/tls_test_credentials.h"
41namespace proto_fuzzer {
45constexpr std::int64_t kNoStreamId = -1;
46constexpr std::size_t kMaxDatagramsPerTurn = 64;
47constexpr std::size_t kServerConnectionIdBytes = 18;
48constexpr std::size_t kReceiveBufferBytes = 64 * 1024;
52constexpr std::size_t kMaxWritesPerTurn = 64;
53constexpr std::size_t kMaxPendingPlaintextBytes = 64 * 1024;
54constexpr int kHttp3IdleIterations = 32;
57constexpr std::array<std::size_t, 4> kQuicVarintByteWidths = {1, 2, 4, 8};
58constexpr unsigned int kQuicVarintTagBits = 2;
59constexpr unsigned int kQuicVarintTagShift = CHAR_BIT - kQuicVarintTagBits;
60static_assert(CHAR_BIT == 8,
"QUIC varints require 8-bit bytes");
62enum StreamRoleIndex : std::size_t {
65 kQpackEncoderStream = 2,
66 kQpackDecoderStream = 3,
72class OpenSslErrorQueueGuard {
74 OpenSslErrorQueueGuard() { ERR_clear_error(); }
75 ~OpenSslErrorQueueGuard() { ERR_clear_error(); }
77 OpenSslErrorQueueGuard(
const OpenSslErrorQueueGuard&) =
delete;
78 OpenSslErrorQueueGuard& operator=(
const OpenSslErrorQueueGuard&) =
delete;
83bool ConfigureSocket(
int fd) {
87 const int descriptor_flags = ::fcntl(fd, F_GETFD, 0);
88 if (descriptor_flags < 0 || ::fcntl(fd, F_SETFD, descriptor_flags | FD_CLOEXEC) < 0) {
91 const int status_flags = ::fcntl(fd, F_GETFL, 0);
92 return status_flags >= 0 && ::fcntl(fd, F_SETFL, status_flags | O_NONBLOCK) == 0;
96int OpenLoopbackSocket(
struct sockaddr_in* bound_address) {
97 if (bound_address ==
nullptr) {
101 const int fd = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
102 if (fd < 0 || !ConfigureSocket(fd)) {
109 struct sockaddr_in requested = {};
110 requested.sin_family = AF_INET;
111 requested.sin_port = htons(0);
112 requested.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
113 if (::bind(fd,
reinterpret_cast<const struct sockaddr*
>(&requested),
sizeof(requested)) != 0) {
118 socklen_t length =
sizeof(*bound_address);
119 std::memset(bound_address, 0,
sizeof(*bound_address));
120 if (::getsockname(fd,
reinterpret_cast<struct sockaddr*
>(bound_address), &length) != 0 ||
121 length !=
sizeof(*bound_address) || bound_address->sin_family != AF_INET) {
129bool RewriteDestination(
struct curl_sockaddr* address,
const struct sockaddr_in& destination) {
130 if (address ==
nullptr ||
sizeof(destination) >
sizeof(address->addr)) {
133 address->family = AF_INET;
134 address->socktype = SOCK_DGRAM;
135 address->protocol = IPPROTO_UDP;
136 address->addrlen =
sizeof(destination);
137 std::memset(&address->addr, 0,
sizeof(address->addr));
138 std::memcpy(&address->addr, &destination,
sizeof(destination));
143int SelectHttp3Alpn(SSL* ,
const unsigned char** selected,
unsigned char* selected_length,
144 const unsigned char* client_protocols,
unsigned int client_protocols_length,
void* ) {
145 static constexpr unsigned char server_protocols[] = {2,
'h',
'3'};
146 unsigned char* match =
nullptr;
147 unsigned char match_length = 0;
148 const int result = SSL_select_next_proto(&match, &match_length, server_protocols,
sizeof(server_protocols),
149 client_protocols, client_protocols_length);
150 if (result != OPENSSL_NPN_NEGOTIATED) {
151 return SSL_TLSEXT_ERR_ALERT_FATAL;
154 *selected_length = match_length;
155 return SSL_TLSEXT_ERR_OK;
160bool AddExtraChainCertificate(SSL_CTX* context,
const char* certificate_pem) {
161 BIO* certificate_bio = BIO_new_mem_buf(certificate_pem, -1);
162 if (certificate_bio ==
nullptr) {
165 X509* certificate = PEM_read_bio_X509(certificate_bio,
nullptr,
nullptr,
nullptr);
166 BIO_free(certificate_bio);
167 if (certificate ==
nullptr) {
170 if (SSL_CTX_add_extra_chain_cert(context, certificate) != 1) {
171 X509_free(certificate);
178bool LoadCredentials(SSL_CTX* context, curl::fuzzer::proto::TlsCertificateChainProfile certificate_chain) {
179 BIO* certificate_bio = BIO_new_mem_buf(tls_test_credentials::kCertificatePem, -1);
180 BIO* key_bio = BIO_new_mem_buf(tls_test_credentials::kPrivateKeyPem, -1);
181 if (certificate_bio ==
nullptr || key_bio ==
nullptr) {
182 BIO_free(certificate_bio);
187 X509* certificate = PEM_read_bio_X509(certificate_bio,
nullptr,
nullptr,
nullptr);
188 EVP_PKEY* key = PEM_read_bio_PrivateKey(key_bio,
nullptr,
nullptr,
nullptr);
189 BIO_free(certificate_bio);
191 if (certificate ==
nullptr || key ==
nullptr) {
192 X509_free(certificate);
197 const bool loaded = SSL_CTX_use_certificate(context, certificate) == 1 && SSL_CTX_use_PrivateKey(context, key) == 1 &&
198 SSL_CTX_check_private_key(context) == 1;
199 X509_free(certificate);
205 if (certificate_chain == curl::fuzzer::proto::TLS_CERTIFICATE_CHAIN_ALL_KEY_TYPES) {
206 return AddExtraChainCertificate(context, tls_test_credentials::kRsaCertificatePem) &&
207 AddExtraChainCertificate(context, tls_test_credentials::kDsaCertificatePem) &&
208 AddExtraChainCertificate(context, tls_test_credentials::kDhCertificatePem);
214SSL_CTX* CreateTlsContext(curl::fuzzer::proto::TlsCertificateChainProfile certificate_chain) {
215 OpenSslErrorQueueGuard error_guard;
216 SSL_CTX* context = SSL_CTX_new(TLS_server_method());
217 if (context ==
nullptr || !LoadCredentials(context, certificate_chain)) {
218 SSL_CTX_free(context);
222 if (SSL_CTX_set_min_proto_version(context, TLS1_3_VERSION) != 1 ||
223 SSL_CTX_set_max_proto_version(context, TLS1_3_VERSION) != 1) {
224 SSL_CTX_free(context);
227 (void)SSL_CTX_set_options(context, SSL_OP_NO_COMPRESSION);
228 SSL_CTX_set_alpn_select_cb(context, &SelectHttp3Alpn,
nullptr);
233void AppendQuicVarint(std::string* output, std::uint64_t value) {
235 for (std::size_t encoding = 0; encoding < kQuicVarintByteWidths.size(); ++encoding) {
236 const std::size_t byte_width = kQuicVarintByteWidths[encoding];
237 const unsigned int value_bits =
static_cast<unsigned int>(byte_width * CHAR_BIT - kQuicVarintTagBits);
238 if (value >= (std::uint64_t{1} << value_bits)) {
242 for (std::size_t
byte = byte_width;
byte != 0; --byte) {
243 const unsigned int shift =
static_cast<unsigned int>((
byte - 1) * CHAR_BIT);
244 unsigned char encoded_byte =
static_cast<unsigned char>(value >> shift);
245 if (
byte == byte_width) {
246 encoded_byte |=
static_cast<unsigned char>(encoding << kQuicVarintTagShift);
248 output->push_back(
static_cast<char>(encoded_byte));
257std::string NormalizeHeaderName(
const std::string& source) {
259 if (result.empty()) {
262 for (
char&
byte : result) {
263 const unsigned char value =
static_cast<unsigned char>(byte);
264 const bool lower = value >=
'a' && value <=
'z';
265 const bool upper = value >=
'A' && value <=
'Z';
266 const bool digit = value >=
'0' && value <=
'9';
267 const bool punctuation = value ==
'!' || value ==
'#' || value ==
'$' || value ==
'%' || value ==
'&' ||
268 value ==
'\'' || value ==
'*' || value ==
'+' || value ==
'-' || value ==
'.' ||
269 value ==
'^' || value ==
'_' || value ==
'`' || value ==
'|' || value ==
'~';
271 byte =
static_cast<char>(value -
'A' +
'a');
272 }
else if (!lower && !digit && !punctuation) {
280std::string NormalizeHeaderValue(
const std::string& source) {
282 for (
char&
byte : result) {
283 const unsigned char value =
static_cast<unsigned char>(byte);
284 if ((value < 0x20U && value !=
'\t') || value == 0x7fU) {
292ngtcp2_tstamp QuicNow() {
293 const auto now = std::chrono::steady_clock::now().time_since_epoch();
294 return static_cast<ngtcp2_tstamp
>(std::chrono::duration_cast<std::chrono::nanoseconds>(now).count());
302class Http3MockServerImpl {
304 explicit Http3MockServerImpl(curl::fuzzer::proto::TlsCertificateChainProfile certificate_chain)
305 : context_(CreateTlsContext(certificate_chain)), crypto_connection_ref_{&GetQuicConnection, this} {
306 role_stream_ids_.fill(kNoStreamId);
307 streams_.reserve(kMaxStreams);
308 retained_writes_.reserve(kMaxWritesPerTurn);
311 ~Http3MockServerImpl() {
313 OpenSslErrorQueueGuard error_guard;
314 SSL_CTX_free(context_);
317 Http3MockServerImpl(
const Http3MockServerImpl&) =
delete;
318 Http3MockServerImpl& operator=(
const Http3MockServerImpl&) =
delete;
320 curl_socket_t OpenSocket(curlsocktype purpose,
struct curl_sockaddr* address) {
321 if (purpose != CURLSOCKTYPE_IPCXN || address ==
nullptr || socket_opened_ || context_ ==
nullptr) {
322 return CURL_SOCKET_BAD;
324 socket_opened_ =
true;
326 struct sockaddr_in listener_address = {};
327 server_fd_ = OpenLoopbackSocket(&listener_address);
328 if (server_fd_ < 0 || !RewriteDestination(address, listener_address)) {
330 return CURL_SOCKET_BAD;
332 server_port_ = ntohs(listener_address.sin_port);
333 local_address_ = listener_address;
335 const int client_fd = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
336 if (client_fd < 0 || !ConfigureSocket(client_fd)) {
337 if (client_fd >= 0) {
338 (void)::close(client_fd);
341 return CURL_SOCKET_BAD;
343 return static_cast<curl_socket_t
>(client_fd);
346 void BeginScenario(
const curl::fuzzer::proto::Scenario& scenario) {
348 scenario_ = &scenario;
349 default_response_.Clear();
350 default_response_.set_status_code(200);
351 default_response_.set_finish_stream(
true);
354 std::size_t DrivePeerTurn() {
355 if (server_fd_ < 0 || transport_failed_) {
359 OpenSslErrorQueueGuard error_guard;
360 std::size_t progress = HandleExpiry();
361 if (transport_failed_) {
364 progress += ReadDatagrams();
365 if (quic_connection_ ==
nullptr || transport_failed_) {
369 if (handshake_complete_ && !close_requested_ && !close_sent_) {
370 progress += CreateServerStreams();
371 progress += DriveApplicationOutput();
373 progress += FlushTransportPackets();
377 bool handshake_complete()
const {
return handshake_complete_; }
379 bool request_headers_received()
const {
return request_headers_received_; }
381 std::size_t executed_action_count()
const {
return next_action_; }
383 std::uint16_t server_port()
const {
return server_port_; }
390 struct ManagedWriteRange {
391 std::uint64_t begin = 0;
392 std::uint64_t end = 0;
395 std::int64_t
id = kNoStreamId;
396 std::uint64_t write_offset = 0;
397 std::vector<ManagedWriteRange> managed_write_ranges;
398 std::vector<std::uint64_t> managed_fin_offsets;
399 std::size_t next_managed_ack_range = 0;
400 std::size_t next_managed_fin_offset = 0;
405 struct PendingWrite {
406 std::int64_t stream_id = kNoStreamId;
407 const std::string* bytes =
nullptr;
408 std::size_t offset = 0;
409 bool finish_stream =
false;
410 bool nghttp3_managed =
false;
411 bool completes_action =
false;
413 bool active()
const {
return stream_id != kNoStreamId; }
416 stream_id = kNoStreamId;
419 finish_stream =
false;
420 nghttp3_managed =
false;
421 completes_action =
false;
427 struct ResponseBodyState {
428 std::vector<std::string> chunks;
429 std::size_t next_chunk = 0;
430 bool finish_stream =
false;
431 bool has_trailers =
false;
434 enum class WriteResult {
443 OpenSslErrorQueueGuard error_guard;
444 pending_write_.Clear();
445 nghttp3_conn_del(http3_connection_);
446 http3_connection_ =
nullptr;
447 ngtcp2_conn_del(quic_connection_);
448 quic_connection_ =
nullptr;
449 if (tls_connection_ !=
nullptr) {
450 SSL_set_app_data(tls_connection_,
nullptr);
451 SSL_free(tls_connection_);
452 tls_connection_ =
nullptr;
454 ngtcp2_crypto_ossl_ctx_del(crypto_context_);
455 crypto_context_ =
nullptr;
457 retained_writes_.clear();
458 if (server_fd_ >= 0) {
459 (void)::close(server_fd_);
464 role_stream_ids_.fill(kNoStreamId);
465 std::memset(&local_address_, 0,
sizeof(local_address_));
466 std::memset(&remote_address_, 0,
sizeof(remote_address_));
467 ngtcp2_path_storage_zero(&path_storage_);
468 response_body_.chunks.clear();
469 response_body_.next_chunk = 0;
470 response_body_.finish_stream =
false;
471 response_body_.has_trailers =
false;
474 socket_opened_ =
false;
475 handshake_complete_ =
false;
476 request_headers_received_ =
false;
477 server_streams_bound_ =
false;
478 bootstrap_drained_ =
false;
479 waiting_for_h3_drain_ =
false;
480 waiting_drain_completes_action_ =
false;
481 final_response_submitted_ =
false;
482 default_response_started_ =
false;
483 http3_failed_ =
false;
484 transport_failed_ =
false;
485 close_requested_ =
false;
487 close_error_code_ = 0;
494 static ngtcp2_conn* GetQuicConnection(ngtcp2_crypto_conn_ref* connection_ref) {
495 auto* self =
static_cast<Http3MockServerImpl*
>(connection_ref->user_data);
496 return self ==
nullptr ? nullptr : self->quic_connection_;
499 static int HandshakeCompleted(ngtcp2_conn* ,
void* user_data) {
500 auto* self =
static_cast<Http3MockServerImpl*
>(user_data);
501 if (self ==
nullptr) {
502 return NGTCP2_ERR_CALLBACK_FAILURE;
504 self->handshake_complete_ =
true;
508 static int ReceiveTransmitKey(ngtcp2_conn* , ngtcp2_encryption_level level,
void* user_data) {
509 auto* self =
static_cast<Http3MockServerImpl*
>(user_data);
510 if (self ==
nullptr) {
511 return NGTCP2_ERR_CALLBACK_FAILURE;
513 if (level != NGTCP2_ENCRYPTION_LEVEL_1RTT || self->server_streams_bound_) {
518 if (!self->CreateHttp3Connection()) {
519 return NGTCP2_ERR_CALLBACK_FAILURE;
521 (void)self->CreateServerStreams();
522 return self->server_streams_bound_ ? 0 : NGTCP2_ERR_CALLBACK_FAILURE;
525 static void GenerateRandomBytes(std::uint8_t* destination, std::size_t length,
526 const ngtcp2_rand_ctx* random_context) {
527 if (random_context ==
nullptr) {
528 std::memset(destination, 0, length);
531 auto* self =
static_cast<Http3MockServerImpl*
>(random_context->native_handle);
532 if (self ==
nullptr) {
533 std::memset(destination, 0, length);
536 self->FillRandom(destination, length);
539 static int GetNewConnectionId(ngtcp2_conn* , ngtcp2_cid* connection_id,
540 ngtcp2_stateless_reset_token* reset_token, std::size_t connection_id_length,
542 auto* self =
static_cast<Http3MockServerImpl*
>(user_data);
543 if (self ==
nullptr || connection_id ==
nullptr || reset_token ==
nullptr ||
544 connection_id_length >
sizeof(connection_id->data)) {
545 return NGTCP2_ERR_CALLBACK_FAILURE;
547 connection_id->datalen = connection_id_length;
548 self->FillRandom(connection_id->data, connection_id_length);
549 self->FillRandom(reset_token->data,
sizeof(reset_token->data));
553 static int OpenRemoteStream(ngtcp2_conn* , std::int64_t stream_id,
void* user_data) {
554 auto* self =
static_cast<Http3MockServerImpl*
>(user_data);
555 if (self ==
nullptr || self->streams_.size() >= kMaxStreams) {
556 return NGTCP2_ERR_CALLBACK_FAILURE;
559 state.id = stream_id;
560 self->streams_.push_back(state);
561 if (self->role_stream_ids_[kResponseStream] == kNoStreamId && ngtcp2_is_bidi_stream(stream_id)) {
562 self->role_stream_ids_[kResponseStream] = stream_id;
570 static int ReceiveQuicStreamData(ngtcp2_conn* connection, std::uint32_t flags, std::int64_t stream_id,
571 std::uint64_t ,
const std::uint8_t* data, std::size_t length,
572 void* user_data,
void* ) {
573 auto* self =
static_cast<Http3MockServerImpl*
>(user_data);
574 if (self ==
nullptr || self->http3_connection_ ==
nullptr || self->http3_failed_) {
575 return NGTCP2_ERR_CALLBACK_FAILURE;
577 const nghttp3_ssize consumed =
578 nghttp3_conn_read_stream2(self->http3_connection_, stream_id, data, length, flags & NGTCP2_STREAM_DATA_FLAG_FIN,
579 ngtcp2_conn_get_timestamp(connection));
581 self->http3_failed_ =
true;
582 return NGTCP2_ERR_CALLBACK_FAILURE;
584 ngtcp2_conn_extend_max_stream_offset(connection, stream_id,
static_cast<std::uint64_t
>(consumed));
585 ngtcp2_conn_extend_max_offset(connection,
static_cast<std::uint64_t
>(consumed));
592 static int AckedStreamDataOffset(ngtcp2_conn* , std::int64_t stream_id, std::uint64_t offset,
593 std::uint64_t length,
void* user_data,
void* ) {
594 auto* self =
static_cast<Http3MockServerImpl*
>(user_data);
595 if (self ==
nullptr || self->http3_connection_ ==
nullptr || self->http3_failed_) {
599 StreamState* stream = self->FindStream(stream_id);
600 if (stream ==
nullptr || offset > UINT64_MAX - length) {
601 return NGTCP2_ERR_CALLBACK_FAILURE;
605 if (stream->next_managed_fin_offset < stream->managed_fin_offsets.size() &&
606 stream->managed_fin_offsets[stream->next_managed_fin_offset] == offset) {
607 ++stream->next_managed_fin_offset;
608 if (nghttp3_conn_add_ack_offset(self->http3_connection_, stream_id, 0) != 0) {
609 self->http3_failed_ =
true;
610 return NGTCP2_ERR_CALLBACK_FAILURE;
616 const std::uint64_t end = offset + length;
617 std::uint64_t managed_length = 0;
618 for (std::size_t index = stream->next_managed_ack_range; index < stream->managed_write_ranges.size(); ++index) {
619 const StreamState::ManagedWriteRange& range = stream->managed_write_ranges[index];
620 if (range.end <= offset) {
621 stream->next_managed_ack_range = index + 1;
624 if (range.begin >= end) {
627 managed_length += std::min(range.end, end) - std::max(range.begin, offset);
628 if (range.end <= end) {
629 stream->next_managed_ack_range = index + 1;
633 if (managed_length != 0 && nghttp3_conn_add_ack_offset(self->http3_connection_, stream_id, managed_length) != 0) {
634 self->http3_failed_ =
true;
635 return NGTCP2_ERR_CALLBACK_FAILURE;
642 static int CloseQuicStream(ngtcp2_conn* , std::uint32_t flags, std::int64_t stream_id,
643 std::uint64_t receive_error_code, std::uint64_t transmit_error_code,
void* user_data,
645 auto* self =
static_cast<Http3MockServerImpl*
>(user_data);
646 if (self ==
nullptr || self->http3_connection_ ==
nullptr || self->http3_failed_) {
649 std::uint32_t http3_flags = NGHTTP3_STREAM_CLOSE_FLAG_NONE;
650 if ((flags & NGTCP2_STREAM_CLOSE2_FLAG_RX_APP_ERROR_CODE_SET) != 0) {
651 http3_flags |= NGHTTP3_STREAM_CLOSE_FLAG_RX_APP_ERROR_CODE_SET;
653 if ((flags & NGTCP2_STREAM_CLOSE2_FLAG_TX_APP_ERROR_CODE_SET) != 0) {
654 http3_flags |= NGHTTP3_STREAM_CLOSE_FLAG_TX_APP_ERROR_CODE_SET;
656 const int result = nghttp3_conn_close_stream2(self->http3_connection_, http3_flags, stream_id, receive_error_code,
657 transmit_error_code);
658 if (result != 0 && result != NGHTTP3_ERR_STREAM_NOT_FOUND) {
659 self->http3_failed_ =
true;
660 return NGTCP2_ERR_CALLBACK_FAILURE;
665 static int ResetQuicStream(ngtcp2_conn* , std::int64_t stream_id, std::uint64_t ,
666 std::uint64_t ,
void* user_data,
void* ) {
667 return ShutdownHttp3StreamRead(stream_id, user_data);
670 static int StopSendingQuicStream(ngtcp2_conn* , std::int64_t stream_id,
671 std::uint64_t ,
void* user_data,
673 return ShutdownHttp3StreamRead(stream_id, user_data);
676 static int ShutdownHttp3StreamRead(std::int64_t stream_id,
void* user_data) {
677 auto* self =
static_cast<Http3MockServerImpl*
>(user_data);
678 if (self ==
nullptr || self->http3_connection_ ==
nullptr || self->http3_failed_) {
681 const int result = nghttp3_conn_shutdown_stream_read(self->http3_connection_, stream_id);
682 if (result != 0 && result != NGHTTP3_ERR_STREAM_NOT_FOUND) {
683 self->http3_failed_ =
true;
684 return NGTCP2_ERR_CALLBACK_FAILURE;
692 bool CreateHttp3Connection() {
693 if (http3_connection_ !=
nullptr) {
696 nghttp3_callbacks callbacks = {};
697 callbacks.recv_data = &ReceiveRequestData;
698 callbacks.deferred_consume = &DeferredConsumeRequestData;
699 callbacks.end_headers = &EndRequestHeaders;
700 callbacks.end_stream = &EndRequestStream;
701 nghttp3_settings settings;
702 nghttp3_settings_default(&settings);
703 settings.qpack_max_dtable_capacity = 0;
704 settings.qpack_encoder_max_dtable_capacity = 0;
705 settings.qpack_blocked_streams = 0;
706 if (nghttp3_conn_server_new(&http3_connection_, &callbacks, &settings,
nullptr,
this) != 0) {
707 http3_failed_ =
true;
710 nghttp3_conn_set_max_concurrent_streams(http3_connection_, kMaxStreams);
717 void FillRandom(std::uint8_t* destination, std::size_t length) {
718 std::uint64_t state = ++random_counter_ * UINT64_C(0x9e3779b97f4a7c15);
719 for (std::size_t index = 0; index < length; ++index) {
720 state ^= state >> 12U;
721 state ^= state << 25U;
722 state ^= state >> 27U;
723 destination[index] =
static_cast<std::uint8_t
>((state * UINT64_C(0x2545f4914f6cdd1d)) >> 56U);
730 bool InitializeConnection(
const ngtcp2_pkt_hd& header,
const struct sockaddr_in& remote_address) {
731 if (quic_connection_ !=
nullptr || header.type != NGTCP2_PKT_INITIAL) {
735 remote_address_ = remote_address;
736 ngtcp2_path_storage_init(&path_storage_,
reinterpret_cast<const ngtcp2_sockaddr*
>(&local_address_),
737 sizeof(local_address_),
reinterpret_cast<const ngtcp2_sockaddr*
>(&remote_address_),
738 sizeof(remote_address_),
nullptr);
740 ngtcp2_callbacks callbacks = {};
741 callbacks.recv_client_initial = ngtcp2_crypto_recv_client_initial_cb;
742 callbacks.recv_crypto_data = ngtcp2_crypto_recv_crypto_data_cb;
743 callbacks.handshake_completed = &HandshakeCompleted;
744 callbacks.encrypt = ngtcp2_crypto_encrypt_cb;
745 callbacks.decrypt = ngtcp2_crypto_decrypt_cb;
746 callbacks.hp_mask = ngtcp2_crypto_hp_mask_cb;
747 callbacks.recv_stream_data = &ReceiveQuicStreamData;
748 callbacks.acked_stream_data_offset = &AckedStreamDataOffset;
749 callbacks.stream_open = &OpenRemoteStream;
750 callbacks.stream_reset = &ResetQuicStream;
751 callbacks.rand = &GenerateRandomBytes;
752 callbacks.update_key = ngtcp2_crypto_update_key_cb;
753 callbacks.delete_crypto_aead_ctx = ngtcp2_crypto_delete_crypto_aead_ctx_cb;
754 callbacks.delete_crypto_cipher_ctx = ngtcp2_crypto_delete_crypto_cipher_ctx_cb;
755 callbacks.stream_stop_sending = &StopSendingQuicStream;
756 callbacks.version_negotiation = ngtcp2_crypto_version_negotiation_cb;
757 callbacks.recv_tx_key = &ReceiveTransmitKey;
758 callbacks.get_new_connection_id2 = &GetNewConnectionId;
759 callbacks.get_path_challenge_data2 = ngtcp2_crypto_get_path_challenge_data2_cb;
760 callbacks.stream_close2 = &CloseQuicStream;
762 ngtcp2_settings settings;
763 ngtcp2_settings_default(&settings);
764 settings.initial_ts = QuicNow();
765 settings.rand_ctx.native_handle =
this;
766 settings.no_pmtud = 1;
767 settings.max_tx_udp_payload_size = packet_buffer_.size();
769 ngtcp2_transport_params parameters;
770 ngtcp2_transport_params_default(¶meters);
771 parameters.initial_max_stream_data_bidi_local = kMaxPendingPlaintextBytes;
772 parameters.initial_max_stream_data_bidi_remote = kMaxPendingPlaintextBytes;
773 parameters.initial_max_stream_data_uni = kMaxPendingPlaintextBytes;
774 parameters.initial_max_data = kMaxPendingPlaintextBytes * 4U;
775 parameters.initial_max_streams_bidi = kMaxStreams;
776 parameters.initial_max_streams_uni = kMaxStreams;
777 parameters.max_idle_timeout = 5U * NGTCP2_SECONDS;
778 parameters.active_connection_id_limit = 2;
779 parameters.original_dcid = header.dcid;
780 parameters.original_dcid_present = 1;
782 ngtcp2_cid server_cid = {};
783 server_cid.datalen = kServerConnectionIdBytes;
784 FillRandom(server_cid.data, server_cid.datalen);
785 if (ngtcp2_conn_server_new(&quic_connection_, &header.scid, &server_cid, &path_storage_.path, header.version,
786 &callbacks, &settings, ¶meters,
nullptr,
this) != 0) {
790 if (ngtcp2_crypto_ossl_ctx_new(&crypto_context_,
nullptr) != 0) {
793 tls_connection_ = SSL_new(context_);
794 if (tls_connection_ ==
nullptr) {
797 ngtcp2_crypto_ossl_ctx_set_ssl(crypto_context_, tls_connection_);
798 if (ngtcp2_crypto_ossl_configure_server_session(tls_connection_) != 0) {
801 SSL_set_app_data(tls_connection_, &crypto_connection_ref_);
802 SSL_set_accept_state(tls_connection_);
803 ngtcp2_conn_set_tls_native_handle(quic_connection_, crypto_context_);
809 std::size_t ReadDatagrams() {
810 std::size_t progress = 0;
811 for (std::size_t packet = 0; packet < kMaxDatagramsPerTurn; ++packet) {
812 struct sockaddr_in remote_address = {};
813 socklen_t remote_length =
sizeof(remote_address);
814 const ssize_t received = ::recvfrom(server_fd_, receive_buffer_.data(), receive_buffer_.size(), 0,
815 reinterpret_cast<struct sockaddr*
>(&remote_address), &remote_length);
817 if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) {
818 transport_failed_ =
true;
822 if (received == 0 || remote_length !=
sizeof(remote_address) || remote_address.sin_family != AF_INET) {
827 if (quic_connection_ ==
nullptr) {
828 ngtcp2_pkt_hd header = {};
829 if (ngtcp2_accept(&header, receive_buffer_.data(),
static_cast<std::size_t
>(received)) != 0) {
832 if (!InitializeConnection(header, remote_address)) {
833 transport_failed_ =
true;
836 }
else if (remote_address.sin_port != remote_address_.sin_port ||
837 remote_address.sin_addr.s_addr != remote_address_.sin_addr.s_addr) {
841 ngtcp2_pkt_info packet_info = {};
842 const int result = ngtcp2_conn_read_pkt(quic_connection_, &path_storage_.path, &packet_info,
843 receive_buffer_.data(),
static_cast<std::size_t
>(received), QuicNow());
845 transport_failed_ =
true;
854 std::size_t HandleExpiry() {
855 if (quic_connection_ ==
nullptr) {
858 const ngtcp2_tstamp now = QuicNow();
859 if (ngtcp2_conn_get_expiry2(quic_connection_) > now) {
862 const int result = ngtcp2_conn_handle_expiry(quic_connection_, now);
864 transport_failed_ =
true;
871 bool SendPacket(
const ngtcp2_path& path,
const std::uint8_t* data, std::size_t length) {
872 const ngtcp2_addr* destination = &path.remote;
873 if (destination->addr ==
nullptr || destination->addrlen == 0) {
874 destination = &path_storage_.path.remote;
876 const ssize_t sent = ::sendto(server_fd_, data, length, 0,
877 reinterpret_cast<const struct sockaddr*
>(destination->addr), destination->addrlen);
878 if (sent ==
static_cast<ssize_t
>(length)) {
881 if (sent < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)) {
884 transport_failed_ =
true;
890 std::size_t FlushTransportPackets() {
891 if (quic_connection_ ==
nullptr || transport_failed_ || close_sent_) {
895 if (close_requested_) {
897 ngtcp2_ccerr_default(&error);
898 ngtcp2_ccerr_set_application_error(&error, close_error_code_,
nullptr, 0);
899 ngtcp2_path_storage output_path;
900 ngtcp2_path_storage_zero(&output_path);
901 ngtcp2_pkt_info packet_info = {};
902 const ngtcp2_tstamp now = QuicNow();
903 const ngtcp2_ssize written = ngtcp2_conn_write_connection_close(
904 quic_connection_, &output_path.path, &packet_info, packet_buffer_.data(), packet_buffer_.size(), &error, now);
906 transport_failed_ =
true;
913 ngtcp2_conn_update_pkt_tx_time(quic_connection_, now);
914 (void)SendPacket(output_path.path, packet_buffer_.data(),
static_cast<std::size_t
>(written));
918 std::size_t progress = 0;
919 for (std::size_t packet = 0; packet < kMaxWritesPerTurn; ++packet) {
920 ngtcp2_path_storage output_path;
921 ngtcp2_path_storage_zero(&output_path);
922 ngtcp2_pkt_info packet_info = {};
923 const ngtcp2_tstamp now = QuicNow();
924 const ngtcp2_ssize written = ngtcp2_conn_write_pkt(quic_connection_, &output_path.path, &packet_info,
925 packet_buffer_.data(), packet_buffer_.size(), now);
927 transport_failed_ =
true;
933 ngtcp2_conn_update_pkt_tx_time(quic_connection_, now);
934 if (!SendPacket(output_path.path, packet_buffer_.data(),
static_cast<std::size_t
>(written))) {
945 std::size_t CreateServerStreams() {
946 if (server_streams_bound_ || quic_connection_ ==
nullptr || http3_connection_ ==
nullptr || http3_failed_) {
950 std::size_t progress = 0;
951 const std::array<StreamRoleIndex, 3> roles = {kControlStream, kQpackEncoderStream, kQpackDecoderStream};
952 for (
const StreamRoleIndex role : roles) {
953 if (role_stream_ids_[role] != kNoStreamId) {
956 const std::int64_t stream_id = OpenUnidirectionalStream();
957 if (stream_id == kNoStreamId) {
960 role_stream_ids_[role] = stream_id;
964 if (role_stream_ids_[kControlStream] == kNoStreamId || role_stream_ids_[kQpackEncoderStream] == kNoStreamId ||
965 role_stream_ids_[kQpackDecoderStream] == kNoStreamId) {
968 if (nghttp3_conn_bind_qpack_streams(http3_connection_, role_stream_ids_[kQpackEncoderStream],
969 role_stream_ids_[kQpackDecoderStream]) != 0 ||
970 nghttp3_conn_bind_control_stream(http3_connection_, role_stream_ids_[kControlStream]) != 0) {
971 http3_failed_ =
true;
974 server_streams_bound_ =
true;
978 std::int64_t OpenUnidirectionalStream() {
979 if (quic_connection_ ==
nullptr || streams_.size() >= kMaxStreams) {
982 std::int64_t stream_id = kNoStreamId;
983 if (ngtcp2_conn_open_uni_stream(quic_connection_, &stream_id,
nullptr) != 0) {
988 state.id = stream_id;
989 streams_.push_back(state);
996 std::size_t DriveApplicationOutput() {
997 std::size_t progress = 0;
999 if (pending_write_.active()) {
1000 const bool completes_action = pending_write_.completes_action;
1001 const WriteResult result = FlushPendingWrite(&progress);
1002 if (result == WriteResult::kBlocked) {
1005 if (result == WriteResult::kFailed && transport_failed_) {
1008 if (completes_action) {
1010 return progress + 1;
1012 if (result == WriteResult::kFailed && waiting_for_h3_drain_) {
1013 waiting_for_h3_drain_ =
false;
1014 if (waiting_drain_completes_action_) {
1017 return progress + 1;
1021 if (!server_streams_bound_) {
1024 if (!bootstrap_drained_) {
1025 if (http3_failed_ || PumpNghttp3(&progress)) {
1026 bootstrap_drained_ =
true;
1031 if (waiting_for_h3_drain_) {
1032 if (http3_failed_ || PumpNghttp3(&progress)) {
1033 waiting_for_h3_drain_ =
false;
1034 if (waiting_drain_completes_action_) {
1037 waiting_drain_completes_action_ =
false;
1045 if (!http3_failed_ && !PumpNghttp3(&progress)) {
1048 if (transport_failed_) {
1052 const std::size_t action_count =
1053 scenario_ ==
nullptr
1055 : std::min<std::size_t>(scenario_limits::kMaxHttp3Actions, scenario_->http3_plan().actions_size());
1056 if (next_action_ >= action_count) {
1058 if (action_count == 0 && !default_response_started_ && request_headers_received_) {
1059 default_response_started_ =
true;
1060 if (SubmitStructuredResponse(default_response_)) {
1061 waiting_for_h3_drain_ =
true;
1062 waiting_drain_completes_action_ =
false;
1069 const auto& action = scenario_->http3_plan().actions(
static_cast<int>(next_action_));
1070 if (action.has_structured_response()) {
1071 if (!request_headers_received_) {
1074 if (SubmitStructuredResponse(action.structured_response())) {
1075 waiting_for_h3_drain_ =
true;
1076 waiting_drain_completes_action_ =
true;
1080 return progress + 1;
1083 if (action.has_stream_write()) {
1084 const auto& write = action.stream_write();
1085 const std::int64_t stream_id = StreamForRole(write.role());
1086 if (stream_id == kNoStreamId) {
1089 const std::size_t length = std::min<std::size_t>(scenario_limits::kMaxHttp3RawWriteBytes, write.data().size());
1090 QueueWrite(stream_id, write.data().substr(0, length), write.finish_stream(),
false,
true);
1091 return progress + 1;
1094 if (action.has_open_unidirectional_stream()) {
1095 const auto& open = action.open_unidirectional_stream();
1096 const std::int64_t stream_id = OpenUnidirectionalStream();
1097 if (stream_id == kNoStreamId) {
1099 return progress + 1;
1101 const std::size_t length = std::min<std::size_t>(scenario_limits::kMaxHttp3RawWriteBytes, open.data().size());
1102 QueueWrite(stream_id, open.data().substr(0, length), open.finish_stream(),
false,
true);
1103 return progress + 1;
1106 if (action.has_stream_reset()) {
1107 const auto& reset = action.stream_reset();
1108 const std::int64_t stream_id = StreamForRole(reset.role());
1109 if (stream_id == kNoStreamId) {
1112 (void)ngtcp2_conn_shutdown_stream_write(quic_connection_, 0, stream_id,
1113 reset.application_error_code() & scenario_limits::kMaxQuicVarint);
1115 return progress + 1;
1118 if (action.has_goaway()) {
1119 const std::int64_t control_stream_id = StreamForRole(curl::fuzzer::proto::HTTP3_STREAM_CONTROL);
1120 if (control_stream_id == kNoStreamId) {
1125 std::string payload;
1126 AppendQuicVarint(&payload, action.goaway().id());
1128 AppendQuicVarint(&frame, 0x07U);
1129 AppendQuicVarint(&frame, payload.size());
1130 frame.append(payload);
1131 QueueWrite(control_stream_id, std::move(frame),
false,
false,
true);
1132 return progress + 1;
1135 if (action.has_connection_close()) {
1136 close_error_code_ = action.connection_close().application_error_code() & scenario_limits::kMaxQuicVarint;
1137 close_requested_ =
true;
1139 return progress + 1;
1143 return progress + 1;
1148 void QueueWrite(std::int64_t stream_id, std::string bytes,
bool finish_stream,
bool nghttp3_managed,
1149 bool completes_action) {
1150 retained_writes_.push_back(std::make_unique<std::string>(std::move(bytes)));
1151 pending_write_.stream_id = stream_id;
1152 pending_write_.bytes = retained_writes_.back().get();
1153 pending_write_.offset = 0;
1154 pending_write_.finish_stream = finish_stream;
1155 pending_write_.nghttp3_managed = nghttp3_managed;
1156 pending_write_.completes_action = completes_action;
1162 WriteResult FlushPendingWrite(std::size_t* progress) {
1163 if (!pending_write_.active()) {
1164 return WriteResult::kComplete;
1167 if (pending_write_.bytes ==
nullptr || pending_write_.offset > pending_write_.bytes->size()) {
1168 pending_write_.Clear();
1169 return WriteResult::kFailed;
1172 const std::size_t remaining = pending_write_.bytes->size() - pending_write_.offset;
1173 if (remaining == 0 && !pending_write_.finish_stream) {
1174 pending_write_.Clear();
1176 return WriteResult::kComplete;
1179 ngtcp2_vec vector = {};
1181 reinterpret_cast<std::uint8_t*
>(
const_cast<char*
>(pending_write_.bytes->data() + pending_write_.offset));
1182 vector.len = remaining;
1183 const std::size_t vector_count = remaining == 0 ? 0 : 1;
1184 const std::uint32_t flags =
1185 pending_write_.finish_stream ? NGTCP2_WRITE_STREAM_FLAG_FIN : NGTCP2_WRITE_STREAM_FLAG_NONE;
1186 ngtcp2_path_storage output_path;
1187 ngtcp2_path_storage_zero(&output_path);
1188 ngtcp2_pkt_info packet_info = {};
1189 ngtcp2_ssize submitted = -1;
1190 const ngtcp2_tstamp now = QuicNow();
1191 const ngtcp2_ssize packet_length = ngtcp2_conn_writev_stream(
1192 quic_connection_, &output_path.path, &packet_info, packet_buffer_.data(), packet_buffer_.size(), &submitted,
1193 flags, pending_write_.stream_id, vector_count == 0 ?
nullptr : &vector, vector_count, now);
1194 if (packet_length == NGTCP2_ERR_STREAM_DATA_BLOCKED) {
1195 return WriteResult::kBlocked;
1197 if (packet_length == NGTCP2_ERR_STREAM_SHUT_WR || packet_length == NGTCP2_ERR_STREAM_NOT_FOUND) {
1198 pending_write_.Clear();
1199 return WriteResult::kFailed;
1201 if (packet_length < 0) {
1202 transport_failed_ =
true;
1203 pending_write_.Clear();
1204 return WriteResult::kFailed;
1206 if (packet_length == 0) {
1207 return WriteResult::kBlocked;
1209 ngtcp2_conn_update_pkt_tx_time(quic_connection_, now);
1210 (void)SendPacket(output_path.path, packet_buffer_.data(),
static_cast<std::size_t
>(packet_length));
1212 if (transport_failed_) {
1213 pending_write_.Clear();
1214 return WriteResult::kFailed;
1217 if (submitted >= 0) {
1218 StreamState* stream = FindStream(pending_write_.stream_id);
1219 if (stream ==
nullptr ||
static_cast<std::uint64_t
>(submitted) > UINT64_MAX - stream->write_offset) {
1220 transport_failed_ =
true;
1221 pending_write_.Clear();
1222 return WriteResult::kFailed;
1224 const std::uint64_t write_begin = stream->write_offset;
1225 stream->write_offset +=
static_cast<std::uint64_t
>(submitted);
1226 if (pending_write_.nghttp3_managed) {
1227 if (nghttp3_conn_add_write_offset(http3_connection_, pending_write_.stream_id,
1228 static_cast<std::uint64_t
>(submitted)) != 0) {
1229 http3_failed_ =
true;
1230 pending_write_.Clear();
1231 return WriteResult::kFailed;
1233 if (submitted != 0) {
1234 stream->managed_write_ranges.push_back({write_begin, stream->write_offset});
1235 }
else if (pending_write_.finish_stream && pending_write_.offset == pending_write_.bytes->size()) {
1236 stream->managed_fin_offsets.push_back(write_begin);
1239 pending_write_.offset +=
static_cast<std::size_t
>(submitted);
1241 if (submitted < 0 || pending_write_.offset != pending_write_.bytes->size()) {
1242 return WriteResult::kBlocked;
1244 pending_write_.Clear();
1245 return WriteResult::kComplete;
1251 bool PumpNghttp3(std::size_t* progress) {
1252 if (http3_connection_ ==
nullptr || http3_failed_) {
1256 for (std::size_t operation = 0; operation < kMaxWritesPerTurn; ++operation) {
1257 if (pending_write_.active()) {
1258 if (!pending_write_.nghttp3_managed) {
1261 const WriteResult result = FlushPendingWrite(progress);
1262 if (result == WriteResult::kBlocked) {
1265 if (result == WriteResult::kFailed) {
1266 http3_failed_ =
true;
1271 std::array<nghttp3_vec, 16> vectors;
1272 std::int64_t stream_id = -1;
1273 int finish_stream = 0;
1274 const nghttp3_ssize vector_count =
1275 nghttp3_conn_writev_stream(http3_connection_, &stream_id, &finish_stream, vectors.data(), vectors.size());
1276 if (vector_count < 0) {
1277 http3_failed_ =
true;
1280 if (vector_count == 0 && stream_id == -1) {
1284 StreamState* stream = FindStream(stream_id);
1285 if (stream ==
nullptr) {
1286 http3_failed_ =
true;
1291 if (vector_count > 0) {
1292 std::size_t total = 0;
1293 for (nghttp3_ssize index = 0; index < vector_count; ++index) {
1294 if (vectors[
static_cast<std::size_t
>(index)].len > kMaxPendingPlaintextBytes - total) {
1295 http3_failed_ =
true;
1298 total += vectors[
static_cast<std::size_t
>(index)].len;
1300 bytes.reserve(total);
1301 for (nghttp3_ssize index = 0; index < vector_count; ++index) {
1302 const nghttp3_vec& vector = vectors[
static_cast<std::size_t
>(index)];
1303 bytes.append(
reinterpret_cast<const char*
>(vector.base), vector.len);
1307 QueueWrite(stream->id, std::move(bytes), finish_stream != 0,
true,
false);
1314 bool SubmitStructuredResponse(
const curl::fuzzer::proto::Http3Response& response) {
1315 if (http3_connection_ ==
nullptr || http3_failed_ || role_stream_ids_[kResponseStream] == kNoStreamId) {
1319 std::uint32_t status_code = response.status_code();
1320 if (status_code < 100U || status_code > 599U) {
1321 status_code = 100U + status_code % 500U;
1323 std::vector<std::pair<std::string, std::string>> fields;
1324 fields.reserve(1 + std::min<std::size_t>(scenario_limits::kMaxHttp3Headers, response.response_headers_size()));
1325 fields.emplace_back(
":status", std::to_string(status_code));
1326 AppendHeaders(&fields, response.response_headers(), scenario_limits::kMaxHttp3Headers);
1327 const std::vector<nghttp3_nv> name_values = MakeNameValues(fields);
1328 const std::int64_t response_stream_id =
static_cast<std::int64_t
>(role_stream_ids_[kResponseStream]);
1330 if (status_code < 200U) {
1331 return nghttp3_conn_submit_info(http3_connection_, response_stream_id, name_values.data(), name_values.size()) ==
1334 if (final_response_submitted_) {
1338 response_body_.chunks.clear();
1339 response_body_.next_chunk = 0;
1340 response_body_.finish_stream = response.finish_stream();
1341 response_body_.has_trailers = response.response_trailers_size() != 0;
1342 std::size_t body_bytes = 0;
1343 const std::size_t body_chunks =
1344 std::min<std::size_t>(scenario_limits::kMaxHttp3BodyChunks, response.body_chunks_size());
1345 response_body_.chunks.reserve(body_chunks);
1346 for (std::size_t index = 0; index < body_chunks && body_bytes < scenario_limits::kMaxHttp3BodyBytes; ++index) {
1347 const std::string& source = response.body_chunks(
static_cast<int>(index));
1348 const std::size_t length = std::min(source.size(), scenario_limits::kMaxHttp3BodyBytes - body_bytes);
1349 response_body_.chunks.emplace_back(source.data(), length);
1350 body_bytes += length;
1353 nghttp3_data_reader reader = {};
1354 reader.read_data = &ReadResponseBody;
1355 if (nghttp3_conn_submit_response(http3_connection_, response_stream_id, name_values.data(), name_values.size(),
1359 final_response_submitted_ =
true;
1361 if (response_body_.has_trailers) {
1362 std::vector<std::pair<std::string, std::string>> trailers;
1363 trailers.reserve(std::min<std::size_t>(scenario_limits::kMaxHttp3Trailers, response.response_trailers_size()));
1364 AppendHeaders(&trailers, response.response_trailers(), scenario_limits::kMaxHttp3Trailers);
1365 const std::vector<nghttp3_nv> trailer_values = MakeNameValues(trailers);
1366 if (nghttp3_conn_submit_trailers(http3_connection_, response_stream_id, trailer_values.data(),
1367 trailer_values.size()) != 0) {
1368 http3_failed_ =
true;
1374 template <
typename RepeatedHeaders>
1375 static void AppendHeaders(std::vector<std::pair<std::string, std::string>>* destination,
1376 const RepeatedHeaders& source, std::size_t count_limit) {
1377 std::size_t remaining = scenario_limits::kMaxHttp3HeaderBytes;
1378 const std::size_t count = std::min<std::size_t>(count_limit, source.size());
1379 for (std::size_t index = 0; index < count && remaining != 0; ++index) {
1380 std::string name = NormalizeHeaderName(source.Get(
static_cast<int>(index)).name());
1381 std::string value = NormalizeHeaderValue(source.Get(
static_cast<int>(index)).value());
1382 if (name.size() > remaining) {
1383 name.resize(remaining);
1385 }
else if (value.size() > remaining - name.size()) {
1386 value.resize(remaining - name.size());
1388 remaining -= name.size() + value.size();
1389 destination->emplace_back(std::move(name), std::move(value));
1393 static std::vector<nghttp3_nv> MakeNameValues(
const std::vector<std::pair<std::string, std::string>>& fields) {
1394 std::vector<nghttp3_nv> name_values;
1395 name_values.reserve(fields.size());
1396 for (
const auto& field : fields) {
1397 nghttp3_nv name_value = {};
1398 name_value.name =
reinterpret_cast<std::uint8_t*
>(
const_cast<char*
>(field.first.data()));
1399 name_value.value =
reinterpret_cast<std::uint8_t*
>(
const_cast<char*
>(field.second.data()));
1400 name_value.namelen = field.first.size();
1401 name_value.valuelen = field.second.size();
1402 name_value.flags = NGHTTP3_NV_FLAG_NONE;
1403 name_values.push_back(name_value);
1408 StreamState* FindStream(std::int64_t stream_id) {
1409 for (StreamState& stream : streams_) {
1410 if (stream.id == stream_id) {
1417 std::int64_t StreamForRole(curl::fuzzer::proto::Http3StreamRole role) {
1418 const int role_value =
static_cast<int>(role);
1419 if (role_value < 0 || role_value >=
static_cast<int>(kStreamRoleCount)) {
1422 return role_stream_ids_[
static_cast<std::size_t
>(role_value)];
1427 static int ReceiveRequestData(nghttp3_conn* , std::int64_t stream_id,
const std::uint8_t* ,
1428 std::size_t data_length,
void* user_data,
void* ) {
1429 auto* self =
static_cast<Http3MockServerImpl*
>(user_data);
1430 if (self !=
nullptr && self->quic_connection_ !=
nullptr) {
1431 ngtcp2_conn_extend_max_stream_offset(self->quic_connection_, stream_id, data_length);
1432 ngtcp2_conn_extend_max_offset(self->quic_connection_, data_length);
1437 static int DeferredConsumeRequestData(nghttp3_conn* , std::int64_t stream_id, std::size_t consumed,
1438 void* user_data,
void* ) {
1439 auto* self =
static_cast<Http3MockServerImpl*
>(user_data);
1440 if (self !=
nullptr && self->quic_connection_ !=
nullptr) {
1441 ngtcp2_conn_extend_max_stream_offset(self->quic_connection_, stream_id, consumed);
1442 ngtcp2_conn_extend_max_offset(self->quic_connection_, consumed);
1447 static int EndRequestHeaders(nghttp3_conn* , std::int64_t stream_id,
int ,
1448 void* user_data,
void* ) {
1449 auto* self =
static_cast<Http3MockServerImpl*
>(user_data);
1450 if (self !=
nullptr) {
1451 if (self->role_stream_ids_[kResponseStream] == kNoStreamId) {
1452 self->role_stream_ids_[kResponseStream] = stream_id;
1453 if (self->FindStream(stream_id) ==
nullptr && self->streams_.size() < kMaxStreams) {
1455 state.id = stream_id;
1456 self->streams_.push_back(state);
1459 if (self->role_stream_ids_[kResponseStream] == stream_id) {
1460 self->request_headers_received_ =
true;
1466 static int EndRequestStream(nghttp3_conn* , std::int64_t ,
void* ,
1473 static nghttp3_ssize ReadResponseBody(nghttp3_conn* , std::int64_t , nghttp3_vec* vectors,
1474 std::size_t vector_count, std::uint32_t* flags,
void* user_data,
1476 auto* self =
static_cast<Http3MockServerImpl*
>(user_data);
1477 if (self ==
nullptr || vectors ==
nullptr || vector_count == 0 || flags ==
nullptr) {
1478 return NGHTTP3_ERR_CALLBACK_FAILURE;
1481 while (self->response_body_.next_chunk < self->response_body_.chunks.size() &&
1482 self->response_body_.chunks[self->response_body_.next_chunk].empty()) {
1483 ++self->response_body_.next_chunk;
1485 if (self->response_body_.next_chunk == self->response_body_.chunks.size()) {
1486 *flags = NGHTTP3_DATA_FLAG_EOF;
1487 if (!self->response_body_.finish_stream && !self->response_body_.has_trailers) {
1488 *flags |= NGHTTP3_DATA_FLAG_NO_END_STREAM;
1493 const std::string& chunk = self->response_body_.chunks[self->response_body_.next_chunk++];
1494 vectors[0].base =
reinterpret_cast<std::uint8_t*
>(
const_cast<char*
>(chunk.data()));
1495 vectors[0].len = chunk.size();
1496 *flags = NGHTTP3_DATA_FLAG_NONE;
1497 if (self->response_body_.next_chunk == self->response_body_.chunks.size()) {
1498 *flags |= NGHTTP3_DATA_FLAG_EOF;
1499 if (!self->response_body_.finish_stream && !self->response_body_.has_trailers) {
1500 *flags |= NGHTTP3_DATA_FLAG_NO_END_STREAM;
1508 SSL_CTX* context_ =
nullptr;
1509 ngtcp2_conn* quic_connection_ =
nullptr;
1510 SSL* tls_connection_ =
nullptr;
1511 ngtcp2_crypto_ossl_ctx* crypto_context_ =
nullptr;
1512 ngtcp2_crypto_conn_ref crypto_connection_ref_;
1513 nghttp3_conn* http3_connection_ =
nullptr;
1514 const curl::fuzzer::proto::Scenario* scenario_ =
nullptr;
1515 std::vector<StreamState> streams_;
1516 std::vector<std::unique_ptr<std::string>> retained_writes_;
1517 std::array<std::int64_t, kStreamRoleCount> role_stream_ids_;
1518 PendingWrite pending_write_;
1519 ResponseBodyState response_body_;
1520 curl::fuzzer::proto::Http3Response default_response_;
1521 ngtcp2_path_storage path_storage_ = {};
1522 struct sockaddr_in local_address_ = {};
1523 struct sockaddr_in remote_address_ = {};
1524 std::array<std::uint8_t, kReceiveBufferBytes> receive_buffer_ = {};
1525 std::array<std::uint8_t, NGTCP2_MAX_UDP_PAYLOAD_SIZE> packet_buffer_ = {};
1526 std::uint64_t random_counter_ = 0;
1527 std::uint64_t close_error_code_ = 0;
1528 std::size_t next_action_ = 0;
1529 int server_fd_ = -1;
1530 std::uint16_t server_port_ = 0;
1531 bool socket_opened_ =
false;
1532 bool handshake_complete_ =
false;
1533 bool request_headers_received_ =
false;
1534 bool server_streams_bound_ =
false;
1535 bool bootstrap_drained_ =
false;
1536 bool waiting_for_h3_drain_ =
false;
1537 bool waiting_drain_completes_action_ =
false;
1538 bool final_response_submitted_ =
false;
1539 bool default_response_started_ =
false;
1540 bool http3_failed_ =
false;
1541 bool transport_failed_ =
false;
1542 bool close_requested_ =
false;
1543 bool close_sent_ =
false;
1553 impl_(std::make_unique<Http3MockServerImpl>(certificate_chain)) {}
1555Http3MockServer::~Http3MockServer() =
default;
1564 struct curl_blob trust_anchor = {
const_cast<char*
>(tls_test_credentials::kCertificatePem),
1565 sizeof(tls_test_credentials::kCertificatePem) - 1, CURL_BLOB_NOCOPY};
1566 (void)curl_easy_setopt(easy, CURLOPT_CAINFO_BLOB, &trust_anchor);
1567 (void)curl_easy_setopt(easy, CURLOPT_SSL_VERIFYPEER, 1L);
1568 (void)curl_easy_setopt(easy, CURLOPT_SSL_VERIFYHOST, 2L);
1569 (void)curl_easy_setopt(easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3ONLY);
1580 return impl_ ==
nullptr ? 0 : impl_->executed_action_count();
1592 return impl_ ==
nullptr ? CURL_SOCKET_BAD : impl_->OpenSocket(purpose, address);
1601 curlsocktype )
const {
1602 return SocketSetupDisposition::kNeedsSetup;
1612 if (impl_ ==
nullptr) {
1615 impl_->BeginScenario(scenario);
1617 int still_running = 1;
1618 int idle_iterations = 0;
1622 CURLMcode result = driver->Start(&still_running);
1623 for (
int iteration = 0; result == CURLM_OK && iteration <
kMaxDriveIterations; ++iteration) {
1624 std::size_t progress = impl_->DrivePeerTurn();
1628 if (still_running == 0) {
1632 result = drive_result.
code;
1634 if (progress != 0) {
1635 idle_iterations = 0;
1636 }
else if (++idle_iterations >= kHttp3IdleIterations) {
1645 for (
int iteration = 0; iteration <
kMaxDriveIterations && still_running != 0; ++iteration) {
1646 const int running_before = still_running;
1647 const CURLMcode result = curl_multi_perform(multi, &still_running);
1648 if (result != CURLM_OK) {
1652 std::size_t progress = still_running == running_before ? 0U : 1U;
1653 progress += impl_->DrivePeerTurn();
1654 if (progress != 0) {
1655 idle_iterations = 0;
1656 }
else if (++idle_iterations >= kHttp3IdleIterations) {
1664#if defined(PROTO_FUZZER_OBSERVE_STATE)
1668 (void)http3_observation;
Http3MockServer()
Construct a peer with the default checked-in EC certificate.
void RunLoop(CURLM *multi, CURL *easy, const curl::fuzzer::proto::Scenario &scenario) override
Alternate nonblocking curl and QUIC server turns under fixed operation and idle budgets.
bool request_headers_received() const
void ObserveState() override
Retain protocol outcomes in coverage and introspector builds.
void Install(CURL *easy) override
Install UDP socket callbacks, the local trust anchor, and HTTP/3-only negotiation on an easy handle.
std::size_t executed_action_count() const
curl_socket_t HandleOpenSocket(curlsocktype purpose, struct curl_sockaddr *address) override
Create curl's real UDP descriptor and rewrite its destination to the private loopback QUIC listener.
bool handshake_complete() const
SocketSetupDisposition GetSocketSetupDisposition(curl_socket_t curlfd, curlsocktype purpose) const override
HTTP/3 returns an unconnected UDP descriptor, so curl must perform its normal socket setup before usi...
std::uint16_t server_port() const
virtual void Install(CURL *easy)
Install the common OPENSOCKETFUNCTION / OPENSOCKETDATA / SOCKOPTFUNCTION callbacks on 'easy'.
virtual void ObserveState()
Default no-op for peers without protocol-specific observations.
static constexpr int kMaxDriveIterations
Hard operation budget for one scenario.
MultiSocketDriver * multi_socket_driver()
Expose the callback state installed for the current socket-action drive.
Owns the socket and timer callback state required by libcurl's event-driven multi API.
Bounded ngtcp2/OpenSSL QUIC peer for HTTP/3 scenarios.
SocketSetupDisposition
Tell curl whether an application-provided socket still needs its normal connect/options setup or alre...
MultiDrivePolicy
Fixed policy for interpreting a scenario's multi-drive fields.
@ kSocketAction
Use the callback-driven curl_multi_socket_action loop.
Bounded event-loop state for driving curl_multi_socket_action.
Shared complexity limits for structured scenarios.
constexpr std::uint64_t kMaxQuicVarint
QUIC variable-length integers reserve their two high bits for encoding.
constexpr std::size_t kMaxHttp3Actions
Ordered H3 work needs enough slots to interleave response fragments with stream and connection state ...
constexpr std::size_t kMaxHttp3HeaderValueBytes
Values use the shared metadata ceiling used by other header-bearing APIs.
constexpr std::size_t kMaxHttp3HeaderNameBytes
Header names are tokens rather than general metadata.
Result from one non-blocking event-loop turn.
bool made_progress
True when an action ran or callback/handle state changed.
CURLMcode code
libcurl's result for the last socket action in the turn.