h2o/fuzz/quicly_mock.c
2025-01-12 15:23:06 +09:00

503 lines
16 KiB
C

/*
* Copyright (c) 2021 Fastly, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <assert.h>
#include "khash.h"
#include "quicly.h"
#include "quicly/sendstate.h"
#include "quicly/recvstate.h"
#include "quicly_mock.h"
KHASH_MAP_INIT_INT64(quicly_stream_t, quicly_stream_t *)
mquicly_context_t mquicly_context;
struct st_quicly_conn_t {
struct _st_quicly_conn_public_t super;
khash_t(quicly_stream_t) * streams;
struct {
quicly_address_t local, remote;
} address;
};
struct st_quicly_send_context_t {
};
static quicly_conn_t *create_connection(quicly_context_t *ctx, int is_client, struct sockaddr *remote_addr,
struct sockaddr *local_addr)
{
quicly_conn_t *conn = calloc(1, sizeof(*conn));
assert(conn != NULL);
conn->super.ctx = ctx;
if (is_client) {
conn->super.local.bidi.next_stream_id = 0;
conn->super.local.uni.next_stream_id = 2;
conn->super.remote.bidi.next_stream_id = 1;
conn->super.remote.uni.next_stream_id = 3;
} else {
conn->super.local.bidi.next_stream_id = 1;
conn->super.local.uni.next_stream_id = 3;
conn->super.remote.bidi.next_stream_id = 0;
conn->super.remote.uni.next_stream_id = 2;
}
conn->streams = kh_init(quicly_stream_t);
conn->address.local.sa = *local_addr;
conn->address.remote.sa = *remote_addr;
return conn;
}
quicly_error_t quicly_accept(quicly_conn_t **conn, quicly_context_t *ctx, struct sockaddr *dest_addr, struct sockaddr *src_addr,
quicly_decoded_packet_t *packet, quicly_address_token_plaintext_t *address_token,
const quicly_cid_plaintext_t *new_cid, ptls_handshake_properties_t *handshake_properties,
void *appdata)
{
*conn = create_connection(ctx, 0, src_addr, dest_addr);
(*conn)->super.state = QUICLY_STATE_CONNECTED;
return 0;
}
int quicly_stream_sync_sendbuf(quicly_stream_t *stream, int activate)
{
int ret;
if (activate) {
if ((ret = quicly_sendstate_activate(&stream->sendstate)) != 0)
return ret;
}
quicly_stream_scheduler_t *scheduler = stream->conn->super.ctx->stream_scheduler;
scheduler->update_state(scheduler, stream);
return 0;
}
void quicly_stream_sync_recvbuf(quicly_stream_t *stream, size_t shift_amount)
{
stream->recvstate.data_off += shift_amount;
}
int quicly_stream_can_send(quicly_stream_t *stream, int at_stream_level)
{
/* return if there is nothing to be sent */
if (stream->sendstate.pending.num_ranges == 0)
return 0;
return 1;
}
ptls_t *quicly_get_tls(quicly_conn_t *conn)
{
/* TODO: is this okay */
return NULL;
}
int quicly_is_blocked(quicly_conn_t *conn)
{
return 0;
}
struct sockaddr *quicly_get_sockname(quicly_conn_t *conn)
{
return &conn->address.local.sa;
}
struct sockaddr *quicly_get_peername(quicly_conn_t *conn)
{
return &conn->address.remote.sa;
}
void quicly_request_stop(quicly_stream_t *stream, quicly_error_t err)
{
stream->_send_aux.stop_sending.sender_state = QUICLY_SENDER_STATE_SEND;
}
void quicly_reset_stream(quicly_stream_t *stream, quicly_error_t err)
{
/* dispose sendbuf state */
quicly_sendstate_reset(&stream->sendstate);
stream->_send_aux.reset_stream.sender_state = QUICLY_SENDER_STATE_SEND;
/* inline expansion of resched_stream_data() */
/* TODO: consider streams_blocked? */
quicly_stream_scheduler_t *scheduler = stream->conn->super.ctx->stream_scheduler;
scheduler->update_state(scheduler, stream);
}
socklen_t quicly_get_socklen(struct sockaddr *sa)
{
switch (sa->sa_family) {
case AF_INET:
return sizeof(struct sockaddr_in);
case AF_INET6:
return sizeof(struct sockaddr_in6);
default:
assert(!"unexpected socket type");
return 0;
}
}
size_t quicly_decode_packet(quicly_context_t *ctx, quicly_decoded_packet_t *packet, const uint8_t *datagram, size_t datagram_size,
size_t *off)
{
assert(0 && "unimplemented");
return 0;
}
quicly_error_t quicly_send_stream(quicly_stream_t *stream, quicly_send_context_t *s)
{
/* quicly_send -> scheduler->do_send -> quicly_send_stream -> on_send_emit */
uint8_t buff[1024];
uint64_t off = stream->sendstate.pending.ranges[0].start, end_off;
size_t capacity = sizeof(buff);
int wrote_all = 0, is_fin;
quicly_error_t ret;
if (!quicly_sendstate_is_open(&stream->sendstate) && off == stream->sendstate.final_size) {
/* special case for emitting FIN only */
end_off = off;
wrote_all = 1;
is_fin = 1;
goto UpdateState;
}
{ /* cap the capacity to the current range */
uint64_t range_capacity = stream->sendstate.pending.ranges[0].end - off;
if (!quicly_sendstate_is_open(&stream->sendstate) && off + range_capacity > stream->sendstate.final_size) {
assert(range_capacity > 1); /* see the special case above */
range_capacity -= 1;
}
if (capacity > range_capacity)
capacity = range_capacity;
}
size_t len = capacity;
size_t emit_off = (size_t)(off - stream->sendstate.acked.ranges[0].end);
stream->callbacks->on_send_emit(stream, emit_off, buff, &len, &wrote_all);
end_off = off + len;
/* determine if the frame incorporates FIN */
if (!quicly_sendstate_is_open(&stream->sendstate) && end_off == stream->sendstate.final_size) {
is_fin = 1;
} else {
is_fin = 0;
}
UpdateState:
/* notify the fuzzing driver of stream send event */
if (mquicly_context.on_stream_send != NULL) {
mquicly_context.on_stream_send->cb(mquicly_context.on_stream_send, stream->conn, stream, buff, off, len, is_fin);
}
if (stream->sendstate.size_inflight < end_off) {
stream->sendstate.size_inflight = end_off;
}
if ((ret = quicly_ranges_subtract(&stream->sendstate.pending, off, end_off + is_fin)) != 0)
return ret;
if (wrote_all) {
if ((ret = quicly_ranges_subtract(&stream->sendstate.pending, stream->sendstate.size_inflight, UINT64_MAX)) != 0)
return ret;
}
return 0;
}
static quicly_stream_t *open_stream(quicly_conn_t *conn, quicly_stream_id_t stream_id)
{
quicly_stream_t *stream;
if ((stream = calloc(1, sizeof(*stream))) == NULL)
return NULL;
stream->conn = conn;
stream->stream_id = stream_id;
stream->callbacks = NULL;
stream->data = NULL;
int r;
khiter_t iter = kh_put(quicly_stream_t, conn->streams, stream_id, &r);
assert(iter != kh_end(conn->streams));
kh_val(conn->streams, iter) = stream;
int is_client = quicly_is_client(stream->conn);
if (quicly_stream_has_send_side(is_client, stream->stream_id)) {
quicly_sendstate_init(&stream->sendstate);
} else {
quicly_sendstate_init_closed(&stream->sendstate);
}
if (quicly_stream_has_receive_side(is_client, stream->stream_id)) {
quicly_recvstate_init(&stream->recvstate);
} else {
quicly_recvstate_init_closed(&stream->recvstate);
}
return stream;
}
static struct st_quicly_conn_streamgroup_state_t *get_streamgroup_state(quicly_conn_t *conn, quicly_stream_id_t stream_id)
{
if (quicly_is_client(conn) == quicly_stream_is_client_initiated(stream_id)) {
return quicly_stream_is_unidirectional(stream_id) ? &conn->super.local.uni : &conn->super.local.bidi;
} else {
return quicly_stream_is_unidirectional(stream_id) ? &conn->super.remote.uni : &conn->super.remote.bidi;
}
}
int mquicly_open_stream(quicly_conn_t *conn, quicly_stream_t **stream, int is_remote_initiated, int unidirectional)
{
struct st_quicly_conn_streamgroup_state_t *group;
if (is_remote_initiated) {
group = unidirectional ? &conn->super.remote.uni : &conn->super.remote.bidi;
} else {
group = unidirectional ? &conn->super.local.uni : &conn->super.local.bidi;
}
*stream = open_stream(conn, group->next_stream_id);
group->next_stream_id += 4;
group->num_streams++;
conn->super.ctx->stream_open->cb(conn->super.ctx->stream_open, *stream);
return 0;
}
static void destroy_stream(quicly_stream_t *stream, int err)
{
quicly_conn_t *conn = stream->conn;
if (stream->callbacks != NULL)
stream->callbacks->on_destroy(stream, err);
khiter_t iter = kh_get(quicly_stream_t, conn->streams, stream->stream_id);
assert(iter != kh_end(conn->streams));
kh_del(quicly_stream_t, conn->streams, iter);
struct st_quicly_conn_streamgroup_state_t *group = get_streamgroup_state(conn, stream->stream_id);
--group->num_streams;
quicly_sendstate_dispose(&stream->sendstate);
quicly_recvstate_dispose(&stream->recvstate);
free(stream);
}
static void destroy_all_streams(quicly_conn_t *conn, int err)
{
quicly_stream_t *stream;
kh_foreach_value(conn->streams, stream, { destroy_stream(stream, err); });
assert(quicly_num_streams(conn) == 0);
}
int mquicly_closed_by_remote(quicly_conn_t *conn, int err, uint64_t frame_type, ptls_iovec_t reason_phrase)
{
/* TODO: invoke conn->super.ctx->closed_by_remote->cb() but h2o does not use it so far */
assert(conn->super.ctx->closed_by_remote == NULL);
conn->super.state = QUICLY_STATE_DRAINING;
destroy_all_streams(conn, err);
return 0;
}
quicly_error_t quicly_open_stream(quicly_conn_t *conn, quicly_stream_t **stream, int unidirectional)
{
return mquicly_open_stream(conn, stream, 0, unidirectional);
}
quicly_error_t quicly_get_or_open_stream(quicly_conn_t *conn, uint64_t stream_id, quicly_stream_t **stream)
{
/* conn->super.ctx->stream_open->cb */
assert(0 && "unimplemented");
return 0;
}
int quicly_is_destination(quicly_conn_t *conn, struct sockaddr *dest_addr, struct sockaddr *src_addr,
quicly_decoded_packet_t *decoded)
{
assert(0 && "unimplemented");
return 0;
}
uint32_t quicly_num_streams_by_group(quicly_conn_t *conn, int uni, int locally_initiated)
{
int server_initiated = quicly_is_client(conn) != locally_initiated;
struct st_quicly_conn_streamgroup_state_t *state = get_streamgroup_state(conn, uni * 2 + server_initiated);
return state->num_streams;
}
quicly_error_t quicly_get_delivery_rate(quicly_conn_t *conn, quicly_rate_t *delivery_rate)
{
/* Do nothing */
*delivery_rate = (quicly_rate_t){};
return 0;
}
quicly_error_t quicly_get_stats(quicly_conn_t *conn, quicly_stats_t *stats)
{
/* Do nothing */
memset(stats, 0, sizeof(*stats));
return 0;
}
void quicly_stream_noop_on_destroy(quicly_stream_t *stream, quicly_error_t err)
{
}
void quicly_stream_noop_on_send_shift(quicly_stream_t *stream, size_t delta)
{
}
void quicly_stream_noop_on_send_emit(quicly_stream_t *stream, size_t off, void *dst, size_t *len, int *wrote_all)
{
}
void quicly_stream_noop_on_send_stop(quicly_stream_t *stream, quicly_error_t err)
{
}
void quicly_stream_noop_on_receive(quicly_stream_t *stream, size_t off, const void *src, size_t len)
{
}
void quicly_stream_noop_on_receive_reset(quicly_stream_t *stream, quicly_error_t err)
{
}
const quicly_stream_callbacks_t quicly_stream_noop_callbacks = {
quicly_stream_noop_on_destroy, quicly_stream_noop_on_send_shift, quicly_stream_noop_on_send_emit,
quicly_stream_noop_on_send_stop, quicly_stream_noop_on_receive, quicly_stream_noop_on_receive_reset};
size_t quicly_send_version_negotiation(quicly_context_t *ctx, ptls_iovec_t dest_cid, ptls_iovec_t src_cid, const uint32_t *versions,
void *payload)
{
assert(0 && "unimplemented");
return 0;
}
size_t quicly_send_stateless_reset(quicly_context_t *ctx, const void *src_cid, void *payload)
{
assert(0 && "unimplemented");
return 0;
}
int quicly_can_send_data(quicly_conn_t *conn, quicly_send_context_t *s)
{
return 1;
}
quicly_error_t quicly_connect(quicly_conn_t **conn, quicly_context_t *ctx, const char *server_name, struct sockaddr *dest_addr,
struct sockaddr *src_addr, const quicly_cid_plaintext_t *new_cid, ptls_iovec_t address_token,
ptls_handshake_properties_t *handshake_properties,
const quicly_transport_parameters_t *resumed_transport_params, void *appdata)
{
assert(0 && "unimplemented");
return 0;
}
int quicly_connection_is_ready(quicly_conn_t *conn)
{
assert(0 && "unimplemented");
return 0;
}
void quicly_amend_ptls_context(ptls_context_t *ptls)
{
assert(0 && "unimplemented");
}
quicly_error_t quicly_receive(quicly_conn_t *conn, struct sockaddr *dest_addr, struct sockaddr *src_addr,
quicly_decoded_packet_t *packet)
{
assert(0 && "unimplemented");
return 0;
}
quicly_error_t quicly_close(quicly_conn_t *conn, quicly_error_t err, const char *reason_phrase)
{
if (conn->super.state >= QUICLY_STATE_CLOSING)
return 0;
conn->super.state = QUICLY_STATE_CLOSING;
return 0;
}
int64_t quicly_get_first_timeout(quicly_conn_t *conn)
{
/* TODO: simulate delay */
return conn->super.ctx->now->cb(conn->super.ctx->now) + 1;
}
void quicly_free(quicly_conn_t *conn)
{
destroy_all_streams(conn, 0);
kh_destroy(quicly_stream_t, conn->streams);
free(conn);
}
quicly_error_t quicly_send(quicly_conn_t *conn, quicly_address_t *dest, quicly_address_t *src, struct iovec *datagrams,
size_t *num_datagrams, void *buf, size_t bufsize)
{
quicly_send_context_t s = {};
quicly_error_t ret;
if (conn->super.state >= QUICLY_STATE_CLOSING) {
ret = QUICLY_ERROR_FREE_CONNECTION;
goto Exit;
}
ret = conn->super.ctx->stream_scheduler->do_send(conn->super.ctx->stream_scheduler, conn, &s);
Exit:
*num_datagrams = 0;
return ret;
}
int64_t quicly_foreach_stream(quicly_conn_t *conn, void *thunk, int64_t (*cb)(void *thunk, quicly_stream_t *stream))
{
assert(0 && "unimplemented");
return 0;
}
quicly_stream_t *quicly_get_stream(quicly_conn_t *conn, quicly_stream_id_t stream_id)
{
assert(0 && "unimplemented");
return NULL;
}
void quicly_send_datagram_frames(quicly_conn_t *conn, ptls_iovec_t *datagrams, size_t num_datagrams)
{
assert(0 && "unimplemented");
}
void quicly_get_max_data(quicly_conn_t *conn, uint64_t *send_permitted, uint64_t *sent, uint64_t *consumed)
{
if (send_permitted != NULL)
*send_permitted = 0xdeadbeef;
if (sent != NULL)
*sent = 0xdeadbeef;
if (consumed != NULL)
*consumed = 0xdeadbeef;
}
quicly_error_t quicly_send_resumption_token(quicly_conn_t *conn)
{
return 0;
}
const uint32_t quicly_supported_versions[] = {QUICLY_PROTOCOL_VERSION_1, QUICLY_PROTOCOL_VERSION_DRAFT29,
QUICLY_PROTOCOL_VERSION_DRAFT27, 0};