#ifndef QUICHE_H
#define QUICHE_H
#if defined(__cplusplus)
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
#include <winsock2.h>
#include <ws2tcpip.h>
#include <time.h>
#else
#include <sys/socket.h>
#include <sys/time.h>
#endif
#ifdef __unix__
#include <sys/types.h>
#endif
#ifdef _MSC_VER
#include <BaseTsd.h>
#define ssize_t SSIZE_T
#endif
#define QUICHE_PROTOCOL_VERSION 0x00000001
#define QUICHE_MAX_CONN_ID_LEN 20
#define QUICHE_MIN_CLIENT_INITIAL_LEN 1200
enum quiche_error {
QUICHE_ERR_DONE = -1,
QUICHE_ERR_BUFFER_TOO_SHORT = -2,
QUICHE_ERR_UNKNOWN_VERSION = -3,
QUICHE_ERR_INVALID_FRAME = -4,
QUICHE_ERR_INVALID_PACKET = -5,
QUICHE_ERR_INVALID_STATE = -6,
QUICHE_ERR_INVALID_STREAM_STATE = -7,
QUICHE_ERR_INVALID_TRANSPORT_PARAM = -8,
QUICHE_ERR_CRYPTO_FAIL = -9,
QUICHE_ERR_TLS_FAIL = -10,
QUICHE_ERR_FLOW_CONTROL = -11,
QUICHE_ERR_STREAM_LIMIT = -12,
QUICHE_ERR_STREAM_STOPPED = -15,
QUICHE_ERR_STREAM_RESET = -16,
QUICHE_ERR_FINAL_SIZE = -13,
QUICHE_ERR_CONGESTION_CONTROL = -14,
QUICHE_ERR_ID_LIMIT = -17,
QUICHE_ERR_OUT_OF_IDENTIFIERS = -18,
QUICHE_ERR_KEY_UPDATE = -19,
};
const char *quiche_version(void);
int quiche_enable_debug_logging(void (*cb)(const char *line, void *argp),
void *argp);
typedef struct quiche_config quiche_config;
quiche_config *quiche_config_new(uint32_t version);
int quiche_config_load_cert_chain_from_pem_file(quiche_config *config,
const char *path);
int quiche_config_load_priv_key_from_pem_file(quiche_config *config,
const char *path);
int quiche_config_load_verify_locations_from_file(quiche_config *config,
const char *path);
int quiche_config_load_verify_locations_from_directory(quiche_config *config,
const char *path);
void quiche_config_verify_peer(quiche_config *config, bool v);
void quiche_config_grease(quiche_config *config, bool v);
void quiche_config_log_keys(quiche_config *config);
void quiche_config_enable_early_data(quiche_config *config);
int quiche_config_set_application_protos(quiche_config *config,
const uint8_t *protos,
size_t protos_len);
void quiche_config_set_max_idle_timeout(quiche_config *config, uint64_t v);
void quiche_config_set_max_recv_udp_payload_size(quiche_config *config, size_t v);
void quiche_config_set_max_send_udp_payload_size(quiche_config *config, size_t v);
void quiche_config_set_initial_max_data(quiche_config *config, uint64_t v);
void quiche_config_set_initial_max_stream_data_bidi_local(quiche_config *config, uint64_t v);
void quiche_config_set_initial_max_stream_data_bidi_remote(quiche_config *config, uint64_t v);
void quiche_config_set_initial_max_stream_data_uni(quiche_config *config, uint64_t v);
void quiche_config_set_initial_max_streams_bidi(quiche_config *config, uint64_t v);
void quiche_config_set_initial_max_streams_uni(quiche_config *config, uint64_t v);
void quiche_config_set_ack_delay_exponent(quiche_config *config, uint64_t v);
void quiche_config_set_max_ack_delay(quiche_config *config, uint64_t v);
void quiche_config_set_disable_active_migration(quiche_config *config, bool v);
int quiche_config_set_cc_algorithm_name(quiche_config *config, const char *algo);
void quiche_config_set_initial_congestion_window_packets(quiche_config *config, size_t packets);
enum quiche_cc_algorithm {
QUICHE_CC_RENO = 0,
QUICHE_CC_CUBIC = 1,
QUICHE_CC_BBR = 2,
QUICHE_CC_BBR2 = 3,
};
void quiche_config_set_cc_algorithm(quiche_config *config, enum quiche_cc_algorithm algo);
void quiche_config_enable_hystart(quiche_config *config, bool v);
void quiche_config_enable_pacing(quiche_config *config, bool v);
void quiche_config_set_max_pacing_rate(quiche_config *config, uint64_t v);
void quiche_config_enable_dgram(quiche_config *config, bool enabled,
size_t recv_queue_len,
size_t send_queue_len);
void quiche_config_set_max_connection_window(quiche_config *config, uint64_t v);
void quiche_config_set_max_stream_window(quiche_config *config, uint64_t v);
void quiche_config_set_active_connection_id_limit(quiche_config *config, uint64_t v);
void quiche_config_set_stateless_reset_token(quiche_config *config, const uint8_t *v);
void quiche_config_free(quiche_config *config);
int quiche_header_info(const uint8_t *buf, size_t buf_len, size_t dcil,
uint32_t *version, uint8_t *type,
uint8_t *scid, size_t *scid_len,
uint8_t *dcid, size_t *dcid_len,
uint8_t *token, size_t *token_len);
typedef struct quiche_conn quiche_conn;
quiche_conn *quiche_accept(const uint8_t *scid, size_t scid_len,
const uint8_t *odcid, size_t odcid_len,
const struct sockaddr *local, size_t local_len,
const struct sockaddr *peer, size_t peer_len,
quiche_config *config);
quiche_conn *quiche_connect(const char *server_name,
const uint8_t *scid, size_t scid_len,
const struct sockaddr *local, size_t local_len,
const struct sockaddr *peer, size_t peer_len,
quiche_config *config);
ssize_t quiche_negotiate_version(const uint8_t *scid, size_t scid_len,
const uint8_t *dcid, size_t dcid_len,
uint8_t *out, size_t out_len);
ssize_t quiche_retry(const uint8_t *scid, size_t scid_len,
const uint8_t *dcid, size_t dcid_len,
const uint8_t *new_scid, size_t new_scid_len,
const uint8_t *token, size_t token_len,
uint32_t version, uint8_t *out, size_t out_len);
bool quiche_version_is_supported(uint32_t version);
quiche_conn *quiche_conn_new_with_tls(const uint8_t *scid, size_t scid_len,
const uint8_t *odcid, size_t odcid_len,
const struct sockaddr *local, size_t local_len,
const struct sockaddr *peer, size_t peer_len,
const quiche_config *config, void *ssl,
bool is_server);
bool quiche_conn_set_keylog_path(quiche_conn *conn, const char *path);
void quiche_conn_set_keylog_fd(quiche_conn *conn, int fd);
bool quiche_conn_set_qlog_path(quiche_conn *conn, const char *path,
const char *log_title, const char *log_desc);
void quiche_conn_set_qlog_fd(quiche_conn *conn, int fd, const char *log_title,
const char *log_desc);
int quiche_conn_set_session(quiche_conn *conn, const uint8_t *buf, size_t buf_len);
typedef struct {
struct sockaddr *from;
socklen_t from_len;
struct sockaddr *to;
socklen_t to_len;
} quiche_recv_info;
ssize_t quiche_conn_recv(quiche_conn *conn, uint8_t *buf, size_t buf_len,
const quiche_recv_info *info);
typedef struct {
struct sockaddr_storage from;
socklen_t from_len;
struct sockaddr_storage to;
socklen_t to_len;
struct timespec at;
} quiche_send_info;
ssize_t quiche_conn_send(quiche_conn *conn, uint8_t *out, size_t out_len,
quiche_send_info *out_info);
size_t quiche_conn_send_quantum(const quiche_conn *conn);
ssize_t quiche_conn_stream_recv(quiche_conn *conn, uint64_t stream_id,
uint8_t *out, size_t buf_len, bool *fin);
ssize_t quiche_conn_stream_send(quiche_conn *conn, uint64_t stream_id,
const uint8_t *buf, size_t buf_len, bool fin);
enum quiche_shutdown {
QUICHE_SHUTDOWN_READ = 0,
QUICHE_SHUTDOWN_WRITE = 1,
};
int quiche_conn_stream_priority(quiche_conn *conn, uint64_t stream_id,
uint8_t urgency, bool incremental);
int quiche_conn_stream_shutdown(quiche_conn *conn, uint64_t stream_id,
enum quiche_shutdown direction, uint64_t err);
ssize_t quiche_conn_stream_capacity(const quiche_conn *conn, uint64_t stream_id);
bool quiche_conn_stream_readable(const quiche_conn *conn, uint64_t stream_id);
int64_t quiche_conn_stream_readable_next(quiche_conn *conn);
int quiche_conn_stream_writable(quiche_conn *conn, uint64_t stream_id, size_t len);
int64_t quiche_conn_stream_writable_next(quiche_conn *conn);
bool quiche_conn_stream_finished(const quiche_conn *conn, uint64_t stream_id);
typedef struct quiche_stream_iter quiche_stream_iter;
quiche_stream_iter *quiche_conn_readable(const quiche_conn *conn);
quiche_stream_iter *quiche_conn_writable(const quiche_conn *conn);
size_t quiche_conn_max_send_udp_payload_size(const quiche_conn *conn);
uint64_t quiche_conn_timeout_as_nanos(const quiche_conn *conn);
uint64_t quiche_conn_timeout_as_millis(const quiche_conn *conn);
void quiche_conn_on_timeout(quiche_conn *conn);
int quiche_conn_close(quiche_conn *conn, bool app, uint64_t err,
const uint8_t *reason, size_t reason_len);
void quiche_conn_trace_id(const quiche_conn *conn, const uint8_t **out, size_t *out_len);
void quiche_conn_source_id(const quiche_conn *conn, const uint8_t **out, size_t *out_len);
void quiche_conn_destination_id(const quiche_conn *conn, const uint8_t **out, size_t *out_len);
void quiche_conn_application_proto(const quiche_conn *conn, const uint8_t **out,
size_t *out_len);
void quiche_conn_peer_cert(const quiche_conn *conn, const uint8_t **out, size_t *out_len);
void quiche_conn_session(const quiche_conn *conn, const uint8_t **out, size_t *out_len);
bool quiche_conn_is_established(const quiche_conn *conn);
bool quiche_conn_is_in_early_data(const quiche_conn *conn);
bool quiche_conn_is_readable(const quiche_conn *conn);
bool quiche_conn_is_draining(const quiche_conn *conn);
uint64_t quiche_conn_peer_streams_left_bidi(const quiche_conn *conn);
uint64_t quiche_conn_peer_streams_left_uni(const quiche_conn *conn);
bool quiche_conn_is_closed(const quiche_conn *conn);
bool quiche_conn_is_timed_out(const quiche_conn *conn);
bool quiche_conn_peer_error(const quiche_conn *conn,
bool *is_app,
uint64_t *error_code,
const uint8_t **reason,
size_t *reason_len);
bool quiche_conn_local_error(const quiche_conn *conn,
bool *is_app,
uint64_t *error_code,
const uint8_t **reason,
size_t *reason_len);
bool quiche_stream_iter_next(quiche_stream_iter *iter, uint64_t *stream_id);
void quiche_stream_iter_free(quiche_stream_iter *iter);
typedef struct {
size_t recv;
size_t sent;
size_t lost;
size_t retrans;
uint64_t sent_bytes;
uint64_t recv_bytes;
uint64_t lost_bytes;
uint64_t stream_retrans_bytes;
size_t paths_count;
uint64_t reset_stream_count_local;
uint64_t stopped_stream_count_local;
uint64_t reset_stream_count_remote;
uint64_t stopped_stream_count_remote;
} quiche_stats;
void quiche_conn_stats(const quiche_conn *conn, quiche_stats *out);
typedef struct {
uint64_t peer_max_idle_timeout;
uint64_t peer_max_udp_payload_size;
uint64_t peer_initial_max_data;
uint64_t peer_initial_max_stream_data_bidi_local;
uint64_t peer_initial_max_stream_data_bidi_remote;
uint64_t peer_initial_max_stream_data_uni;
uint64_t peer_initial_max_streams_bidi;
uint64_t peer_initial_max_streams_uni;
uint64_t peer_ack_delay_exponent;
uint64_t peer_max_ack_delay;
bool peer_disable_active_migration;
uint64_t peer_active_conn_id_limit;
ssize_t peer_max_datagram_frame_size;
} quiche_transport_params;
bool quiche_conn_peer_transport_params(const quiche_conn *conn, quiche_transport_params *out);
typedef struct {
struct sockaddr_storage local_addr;
socklen_t local_addr_len;
struct sockaddr_storage peer_addr;
socklen_t peer_addr_len;
ssize_t validation_state;
bool active;
size_t recv;
size_t sent;
size_t lost;
size_t retrans;
uint64_t rtt;
size_t cwnd;
uint64_t sent_bytes;
uint64_t recv_bytes;
uint64_t lost_bytes;
uint64_t stream_retrans_bytes;
size_t pmtu;
uint64_t delivery_rate;
} quiche_path_stats;
int quiche_conn_path_stats(const quiche_conn *conn, size_t idx, quiche_path_stats *out);
bool quiche_conn_is_server(const quiche_conn *conn);
ssize_t quiche_conn_dgram_max_writable_len(const quiche_conn *conn);
ssize_t quiche_conn_dgram_recv_front_len(const quiche_conn *conn);
ssize_t quiche_conn_dgram_recv_queue_len(const quiche_conn *conn);
ssize_t quiche_conn_dgram_recv_queue_byte_size(const quiche_conn *conn);
ssize_t quiche_conn_dgram_send_queue_len(const quiche_conn *conn);
ssize_t quiche_conn_dgram_send_queue_byte_size(const quiche_conn *conn);
ssize_t quiche_conn_dgram_recv(quiche_conn *conn, uint8_t *buf,
size_t buf_len);
ssize_t quiche_conn_dgram_send(quiche_conn *conn, const uint8_t *buf,
size_t buf_len);
void quiche_conn_dgram_purge_outgoing(quiche_conn *conn,
bool (*f)(uint8_t *, size_t));
ssize_t quiche_conn_send_ack_eliciting(quiche_conn *conn);
ssize_t quiche_conn_send_ack_eliciting_on_path(quiche_conn *conn,
const struct sockaddr *local, size_t local_len,
const struct sockaddr *peer, size_t peer_len);
void quiche_conn_free(quiche_conn *conn);
int quiche_put_varint(uint8_t *buf, size_t buf_len,
uint64_t val);
ssize_t quiche_get_varint(const uint8_t *buf, size_t buf_len,
uint64_t *val);
#define QUICHE_H3_APPLICATION_PROTOCOL "\x02h3"
enum quiche_h3_error {
QUICHE_H3_ERR_DONE = -1,
QUICHE_H3_ERR_BUFFER_TOO_SHORT = -2,
QUICHE_H3_ERR_INTERNAL_ERROR = -3,
QUICHE_H3_ERR_EXCESSIVE_LOAD = -4,
QUICHE_H3_ERR_ID_ERROR= -5,
QUICHE_H3_ERR_STREAM_CREATION_ERROR = -6,
QUICHE_H3_ERR_CLOSED_CRITICAL_STREAM = -7,
QUICHE_H3_ERR_MISSING_SETTINGS = -8,
QUICHE_H3_ERR_FRAME_UNEXPECTED = -9,
QUICHE_H3_ERR_FRAME_ERROR = -10,
QUICHE_H3_ERR_QPACK_DECOMPRESSION_FAILED = -11,
QUICHE_H3_ERR_STREAM_BLOCKED = -13,
QUICHE_H3_ERR_SETTINGS_ERROR = -14,
QUICHE_H3_ERR_REQUEST_REJECTED = -15,
QUICHE_H3_ERR_REQUEST_CANCELLED = -16,
QUICHE_H3_ERR_REQUEST_INCOMPLETE = -17,
QUICHE_H3_ERR_MESSAGE_ERROR = -18,
QUICHE_H3_ERR_CONNECT_ERROR = -19,
QUICHE_H3_ERR_VERSION_FALLBACK = -20,
QUICHE_H3_TRANSPORT_ERR_DONE = QUICHE_ERR_DONE - 1000,
QUICHE_H3_TRANSPORT_ERR_BUFFER_TOO_SHORT = QUICHE_ERR_BUFFER_TOO_SHORT - 1000,
QUICHE_H3_TRANSPORT_ERR_UNKNOWN_VERSION = QUICHE_ERR_UNKNOWN_VERSION - 1000,
QUICHE_H3_TRANSPORT_ERR_INVALID_FRAME = QUICHE_ERR_INVALID_FRAME - 1000,
QUICHE_H3_TRANSPORT_ERR_INVALID_PACKET = QUICHE_ERR_INVALID_PACKET - 1000,
QUICHE_H3_TRANSPORT_ERR_INVALID_STATE = QUICHE_ERR_INVALID_STATE - 1000,
QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE = QUICHE_ERR_INVALID_STREAM_STATE - 1000,
QUICHE_H3_TRANSPORT_ERR_INVALID_TRANSPORT_PARAM = QUICHE_ERR_INVALID_TRANSPORT_PARAM - 1000,
QUICHE_H3_TRANSPORT_ERR_CRYPTO_FAIL = QUICHE_ERR_CRYPTO_FAIL - 1000,
QUICHE_H3_TRANSPORT_ERR_TLS_FAIL = QUICHE_ERR_TLS_FAIL - 1000,
QUICHE_H3_TRANSPORT_ERR_FLOW_CONTROL = QUICHE_ERR_FLOW_CONTROL - 1000,
QUICHE_H3_TRANSPORT_ERR_STREAM_LIMIT = QUICHE_ERR_STREAM_LIMIT - 1000,
QUICHE_H3_TRANSPORT_ERR_STREAM_STOPPED = QUICHE_ERR_STREAM_STOPPED - 1000,
QUICHE_H3_TRANSPORT_ERR_STREAM_RESET = QUICHE_ERR_STREAM_RESET - 1000,
QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE = QUICHE_ERR_FINAL_SIZE - 1000,
QUICHE_H3_TRANSPORT_ERR_CONGESTION_CONTROL = QUICHE_ERR_CONGESTION_CONTROL - 1000,
QUICHE_H3_TRANSPORT_ERR_ID_LIMIT = QUICHE_ERR_ID_LIMIT - 1000,
QUICHE_H3_TRANSPORT_ERR_OUT_OF_IDENTIFIERS = QUICHE_ERR_OUT_OF_IDENTIFIERS - 1000,
QUICHE_H3_TRANSPORT_ERR_KEY_UPDATE = QUICHE_ERR_KEY_UPDATE - 1000,
};
typedef struct quiche_h3_config quiche_h3_config;
quiche_h3_config *quiche_h3_config_new(void);
void quiche_h3_config_set_max_field_section_size(quiche_h3_config *config, uint64_t v);
void quiche_h3_config_set_qpack_max_table_capacity(quiche_h3_config *config, uint64_t v);
void quiche_h3_config_set_qpack_blocked_streams(quiche_h3_config *config, uint64_t v);
void quiche_h3_config_enable_extended_connect(quiche_h3_config *config, bool enabled);
void quiche_h3_config_free(quiche_h3_config *config);
typedef struct quiche_h3_conn quiche_h3_conn;
quiche_h3_conn *quiche_h3_conn_new_with_transport(quiche_conn *quiche_conn,
quiche_h3_config *config);
enum quiche_h3_event_type {
QUICHE_H3_EVENT_HEADERS,
QUICHE_H3_EVENT_DATA,
QUICHE_H3_EVENT_FINISHED,
QUICHE_H3_EVENT_GOAWAY,
QUICHE_H3_EVENT_RESET,
QUICHE_H3_EVENT_PRIORITY_UPDATE,
};
typedef struct quiche_h3_event quiche_h3_event;
int64_t quiche_h3_conn_poll(quiche_h3_conn *conn, quiche_conn *quic_conn,
quiche_h3_event **ev);
enum quiche_h3_event_type quiche_h3_event_type(quiche_h3_event *ev);
int quiche_h3_event_for_each_header(quiche_h3_event *ev,
int (*cb)(uint8_t *name, size_t name_len,
uint8_t *value, size_t value_len,
void *argp),
void *argp);
int quiche_h3_for_each_setting(quiche_h3_conn *conn,
int (*cb)(uint64_t identifier,
uint64_t value, void *argp),
void *argp);
bool quiche_h3_event_headers_has_body(quiche_h3_event *ev);
bool quiche_h3_extended_connect_enabled_by_peer(quiche_h3_conn *conn);
void quiche_h3_event_free(quiche_h3_event *ev);
typedef struct {
const uint8_t *name;
size_t name_len;
const uint8_t *value;
size_t value_len;
} quiche_h3_header;
typedef struct {
uint8_t urgency;
bool incremental;
} quiche_h3_priority;
int64_t quiche_h3_send_request(quiche_h3_conn *conn, quiche_conn *quic_conn,
quiche_h3_header *headers, size_t headers_len,
bool fin);
int quiche_h3_send_response(quiche_h3_conn *conn, quiche_conn *quic_conn,
uint64_t stream_id, quiche_h3_header *headers,
size_t headers_len, bool fin);
int quiche_h3_send_response_with_priority(quiche_h3_conn *conn,
quiche_conn *quic_conn, uint64_t stream_id,
quiche_h3_header *headers, size_t headers_len,
quiche_h3_priority *priority, bool fin);
ssize_t quiche_h3_send_body(quiche_h3_conn *conn, quiche_conn *quic_conn,
uint64_t stream_id, uint8_t *body, size_t body_len,
bool fin);
ssize_t quiche_h3_recv_body(quiche_h3_conn *conn, quiche_conn *quic_conn,
uint64_t stream_id, uint8_t *out, size_t out_len);
int quiche_h3_send_goaway(quiche_h3_conn *conn, quiche_conn *quic_conn,
uint64_t id);
int quiche_h3_parse_extensible_priority(uint8_t *priority,
size_t priority_len,
quiche_h3_priority *parsed);
int quiche_h3_send_priority_update_for_request(quiche_h3_conn *conn,
quiche_conn *quic_conn,
uint64_t stream_id,
quiche_h3_priority *priority);
int quiche_h3_take_last_priority_update(quiche_h3_conn *conn,
uint64_t prioritized_element_id,
int (*cb)(uint8_t *priority_field_value,
uint64_t priority_field_value_len,
void *argp),
void *argp);
bool quiche_h3_dgram_enabled_by_peer(quiche_h3_conn *conn,
quiche_conn *quic_conn);
void quiche_h3_conn_free(quiche_h3_conn *conn);
#if defined(__cplusplus)
} #endif
#endif