diff options
Diffstat (limited to 'packages/bun-usockets/src/quic.c')
-rw-r--r-- | packages/bun-usockets/src/quic.c | 1071 |
1 files changed, 1071 insertions, 0 deletions
diff --git a/packages/bun-usockets/src/quic.c b/packages/bun-usockets/src/quic.c new file mode 100644 index 000000000..529074632 --- /dev/null +++ b/packages/bun-usockets/src/quic.c @@ -0,0 +1,1071 @@ +#ifdef LIBUS_USE_QUIC + +/* Todo: quic layer should not use bsd layer directly (sendmmsg) */ +#include "internal/networking/bsd.h" + +#include "quic.h" + + + +#include "lsquic.h" +#include "lsquic_types.h" +#include "lsxpack_header.h" + +/* Todo: remove these */ +#ifndef _WIN32 +#include <netinet/in.h> +#include <errno.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void leave_all(); + +/* +struct sockaddr_in client_addr = { + AF_INET, + 1, + 1 +}; + +struct sockaddr_in server_addr = { + AF_INET, + 2, + 2 +};*/ + + // used in process_quic + lsquic_engine_t *global_engine; + lsquic_engine_t *global_client_engine; + +/* Socket context */ +struct us_quic_socket_context_s { + + struct us_udp_packet_buffer_t *recv_buf; + //struct us_udp_packet_buffer_t *send_buf; + int outgoing_packets; + + //struct us_udp_socket_t *udp_socket; + struct us_loop_t *loop; + lsquic_engine_t *engine; + lsquic_engine_t *client_engine; + + // we store the options the context was created with here + us_quic_socket_context_options_t options; + + void(*on_stream_data)(us_quic_stream_t *s, char *data, int length); + void(*on_stream_end)(us_quic_stream_t *s); + void(*on_stream_headers)(us_quic_stream_t *s); + void(*on_stream_open)(us_quic_stream_t *s, int is_client); + void(*on_stream_close)(us_quic_stream_t *s); + void(*on_stream_writable)(us_quic_stream_t *s); + void(*on_open)(us_quic_socket_t *s, int is_client); + void(*on_close)(us_quic_socket_t *s); +}; + +/* Setters */ +void us_quic_socket_context_on_stream_data(us_quic_socket_context_t *context, void(*on_stream_data)(us_quic_stream_t *s, char *data, int length)) { + context->on_stream_data = on_stream_data; +} +void us_quic_socket_context_on_stream_end(us_quic_socket_context_t *context, void(*on_stream_end)(us_quic_stream_t *s)) { + context->on_stream_end = on_stream_end; +} +void us_quic_socket_context_on_stream_headers(us_quic_socket_context_t *context, void(*on_stream_headers)(us_quic_stream_t *s)) { + context->on_stream_headers = on_stream_headers; +} +void us_quic_socket_context_on_stream_open(us_quic_socket_context_t *context, void(*on_stream_open)(us_quic_stream_t *s, int is_client)) { + context->on_stream_open = on_stream_open; +} +void us_quic_socket_context_on_stream_close(us_quic_socket_context_t *context, void(*on_stream_close)(us_quic_stream_t *s)) { + context->on_stream_close = on_stream_close; +} +void us_quic_socket_context_on_open(us_quic_socket_context_t *context, void(*on_open)(us_quic_socket_t *s, int is_client)) { + context->on_open = on_open; +} +void us_quic_socket_context_on_close(us_quic_socket_context_t *context, void(*on_close)(us_quic_socket_t *s)) { + context->on_close = on_close; +} +void us_quic_socket_context_on_stream_writable(us_quic_socket_context_t *context, void(*on_stream_writable)(us_quic_stream_t *s)) { + context->on_stream_writable = on_stream_writable; +} + +/* UDP handlers */ +void on_udp_socket_writable(struct us_udp_socket_t *s) { + /* Need context from socket here */ + us_quic_socket_context_t *context = us_udp_socket_user(s); + + /* We just continue now */ + lsquic_engine_send_unsent_packets(context->engine); +} + +// we need two differetn handlers to know to put it in client or servcer context +void on_udp_socket_data_client(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) { + + int fd = us_poll_fd((struct us_poll_t *) s); + //printf("Reading on fd: %d\n", fd); + + //printf("UDP (client) socket got data: %p\n", s); + + /* We need to lookup the context from the udp socket */ + //us_udpus_udp_socket_context(s); + // do we have udp socket contexts? or do we just have user data? + + us_quic_socket_context_t *context = us_udp_socket_user(s); + + /* We just shove it to lsquic */ + for (int i = 0; i < packets; i++) { + char *payload = us_udp_packet_buffer_payload(buf, i); + int length = us_udp_packet_buffer_payload_length(buf, i); + int ecn = us_udp_packet_buffer_ecn(buf, i); + void *peer_addr = us_udp_packet_buffer_peer(buf, i); + + //printf("Reading UDP of size %d\n", length); + + char ip[16]; + int ip_length = us_udp_packet_buffer_local_ip(buf, i, ip); + if (!ip_length) { + printf("We got no ip on received packet!\n"); + exit(0); + } + + //printf("Our received destination IP length is: %d\n", ip_length); + + int port = us_udp_socket_bound_port(s); + //printf("We received packet on port: %d\n", port); + + /* We build our address based on what the dest addr is */ + struct sockaddr_storage local_addr = {0}; + if (ip_length == 16) { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *) &local_addr; + + ipv6->sin6_family = AF_INET6; + ipv6->sin6_port = ntohs(port); + memcpy(ipv6->sin6_addr.s6_addr, ip, 16); + } else { + struct sockaddr_in *ipv4 = (struct sockaddr_in *) &local_addr; + + ipv4->sin_family = AF_INET; + ipv4->sin_port = ntohs(port); + memcpy(&ipv4->sin_addr.s_addr, ip, 4); + } + + + int ret = lsquic_engine_packet_in(context->client_engine, payload, length, (struct sockaddr *) &local_addr, peer_addr, (void *) s, 0); + //printf("Engine returned: %d\n", ret); + + + } + + lsquic_engine_process_conns(context->client_engine); + +} + +void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) { + + + //printf("UDP socket got data: %p\n", s); + + /* We need to lookup the context from the udp socket */ + //us_udpus_udp_socket_context(s); + // do we have udp socket contexts? or do we just have user data? + + us_quic_socket_context_t *context = us_udp_socket_user(s); + + // process conns now? to accept new connections? + lsquic_engine_process_conns(context->engine); + + /* We just shove it to lsquic */ + for (int i = 0; i < packets; i++) { + char *payload = us_udp_packet_buffer_payload(buf, i); + int length = us_udp_packet_buffer_payload_length(buf, i); + int ecn = us_udp_packet_buffer_ecn(buf, i); + void *peer_addr = us_udp_packet_buffer_peer(buf, i); + + //printf("Reading UDP of size %d\n", length); + + char ip[16]; + int ip_length = us_udp_packet_buffer_local_ip(buf, i, ip); + if (!ip_length) { + printf("We got no ip on received packet!\n"); + exit(0); + } + + //printf("Our received destination IP length is: %d\n", ip_length); + + int port = us_udp_socket_bound_port(s); + //printf("We received packet on port: %d\n", port); + + /* We build our address based on what the dest addr is */ + struct sockaddr_storage local_addr = {0}; + if (ip_length == 16) { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *) &local_addr; + + ipv6->sin6_family = AF_INET6; + ipv6->sin6_port = ntohs(port); + memcpy(ipv6->sin6_addr.s6_addr, ip, 16); + } else { + + struct sockaddr_in *ipv4 = (struct sockaddr_in *) &local_addr; + + ipv4->sin_family = AF_INET; + ipv4->sin_port = ntohs(port); + memcpy(&ipv4->sin_addr.s_addr, ip, 4); + } + + + int ret = lsquic_engine_packet_in(context->engine, payload, length, (struct sockaddr *) &local_addr, peer_addr, (void *) s, 0); + //printf("Engine returned: %d\n", ret); + + + } + + lsquic_engine_process_conns(context->engine); + +} + +/* Let's use this on Windows and macOS where it is not defined (todo: put in bsd.h) */ +#ifndef UIO_MAXIOV +#define UIO_MAXIOV 1024 + +#ifndef _WIN32 +struct mmsghdr { + struct msghdr msg_hdr; /* Message header */ + unsigned int msg_len; /* Number of bytes transmitted */ +}; +#endif +#endif + +/* Server and client packet out is identical */ +int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) { +#ifndef _WIN32 + us_quic_socket_context_t *context = ctx; + + /* A run is at most UIO_MAXIOV datagrams long */ + struct mmsghdr hdrs[UIO_MAXIOV]; + int run_length = 0; + + /* We assume that thiss whole cb will never be called with 0 specs */ + struct us_udp_socket_t *last_socket = (struct us_udp_socket_t *) specs[0].peer_ctx; + + int sent = 0; + for (int i = 0; i < n_specs; i++) { + /* Send this run if we need to */ + if (run_length == UIO_MAXIOV || specs[i].peer_ctx != last_socket) { + int ret = bsd_sendmmsg(us_poll_fd((struct us_poll_t *) last_socket), hdrs, run_length, 0); + if (ret != run_length) { + if (ret == -1) { + printf("unhandled udp backpressure!\n"); + return sent; + } else { + printf("unhandled udp backpressure!\n"); + errno = EAGAIN; + return sent + ret; + } + } + sent += ret; + run_length = 0; + last_socket = specs[i].peer_ctx; + //printf("different socket breask run!\n"); + } + + /* Continue existing run or start a new one */ + //memset(&hdrs[i].msg_hdr, 0, sizeof(hdrs[i].msg_hdr)); + memset(&hdrs[run_length], 0, sizeof(hdrs[run_length])); + + hdrs[run_length].msg_hdr.msg_name = (void *) specs[i].dest_sa; + hdrs[run_length].msg_hdr.msg_namelen = (AF_INET == specs[i].dest_sa->sa_family ? + sizeof(struct sockaddr_in) : + sizeof(struct sockaddr_in6)), + hdrs[run_length].msg_hdr.msg_iov = specs[i].iov; + hdrs[run_length].msg_hdr.msg_iovlen = specs[i].iovlen; + hdrs[run_length].msg_hdr.msg_flags = 0; + + run_length++; + } + + /* Send last run */ + if (run_length) { + int ret = bsd_sendmmsg(us_poll_fd((struct us_poll_t *) last_socket), hdrs, run_length, 0); + if (ret == -1) { + printf("backpressure! A\n"); + return sent; + } + if (sent + ret != n_specs) { + printf("backpressure! B\n"); + printf("errno is: %d\n", errno); + errno = EAGAIN; + } + //printf("Returning %d of %d\n", sent + ret, n_specs); + return sent + ret; + } + + //printf("Returning %d\n", n_specs); + +#endif + + return n_specs; +} + +lsquic_conn_ctx_t *on_new_conn(void *stream_if_ctx, lsquic_conn_t *c) { + us_quic_socket_context_t *context = stream_if_ctx; + + printf("Context is: %p\n", context); + + /* We need to create some kind of socket here */ + + int is_client = 0; + if (lsquic_conn_get_engine(c) == context->client_engine) { + is_client = 1; + } + + context->on_open((us_quic_socket_t *) c, is_client); + + return (lsquic_conn_ctx_t *) context; +} + +void us_quic_socket_create_stream(us_quic_socket_t *s, int ext_size) { + lsquic_conn_make_stream((lsquic_conn_t *) s); + + // here we need to allocate and attach the user data + +} + +void on_conn_closed(lsquic_conn_t *c) { + us_quic_socket_context_t *context = (us_quic_socket_context_t *) lsquic_conn_get_ctx(c); + + printf("on_conn_closed!\n"); + + context->on_close((us_quic_socket_t *) c); +} + +lsquic_stream_ctx_t *on_new_stream(void *stream_if_ctx, lsquic_stream_t *s) { + + /* In true usockets style we always want read */ + lsquic_stream_wantread(s, 1); + + us_quic_socket_context_t *context = stream_if_ctx; + + // the conn's ctx should point at the udp socket and the socket context + // the ext size of streams and conn's are set by the listen/connect calls, which + // are the calls that create the UDP socket so we need conn to point to the UDP socket + // to get that ext_size set in listen/connect calls, back here. + // todo: hardcoded for now + + int ext_size = 256; + + void *ext = malloc(ext_size); + // yes hello + strcpy(ext, "Hello I am ext!"); + + int is_client = 0; + if (lsquic_conn_get_engine(lsquic_stream_conn(s)) == context->client_engine) { + is_client = 1; + } + + // luckily we can set the ext before we return + lsquic_stream_set_ctx(s, ext); + context->on_stream_open((us_quic_stream_t *) s, is_client); + + return ext; +} + +//#define V(v) (v), strlen(v) + +// header bug is really just an offset buffer - perfect for per context! +// could even use cork buffer or similar +struct header_buf +{ + unsigned off; + char buf[UINT16_MAX]; +}; + +int +header_set_ptr (struct lsxpack_header *hdr, struct header_buf *header_buf, + const char *name, size_t name_len, + const char *val, size_t val_len) +{ + if (header_buf->off + name_len + val_len <= sizeof(header_buf->buf)) + { + memcpy(header_buf->buf + header_buf->off, name, name_len); + memcpy(header_buf->buf + header_buf->off + name_len, val, val_len); + lsxpack_header_set_offset2(hdr, header_buf->buf + header_buf->off, + 0, name_len, name_len, val_len); + header_buf->off += name_len + val_len; + return 0; + } + else + return -1; +} + +/* Static storage should be per context or really per loop */ +struct header_buf hbuf; +struct lsxpack_header headers_arr[10]; + +void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int index, const char *key, int key_length, const char *value, int value_length) { + if (header_set_ptr(&headers_arr[index], &hbuf, key, key_length, value, value_length) != 0) { + printf("CANNOT FORMAT HEADER!\n"); + exit(0); + } +} + +void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num, int has_body) { + + lsquic_http_headers_t headers = { + .count = num, + .headers = headers_arr, + }; + // last here is whether this is eof or not (has body) + if (lsquic_stream_send_headers((lsquic_stream_t *) s, &headers, has_body ? 0 : 1)) {// pass 0 if data + printf("CANNOT SEND HEADERS!\n"); + exit(0); + } + + /* Reset header offset */ + hbuf.off = 0; +} + +int us_quic_stream_is_client(us_quic_stream_t *s) { + us_quic_socket_context_t *context = (us_quic_socket_context_t *) lsquic_conn_get_ctx(lsquic_stream_conn((lsquic_stream_t *) s)); + + int is_client = 0; + if (lsquic_conn_get_engine(lsquic_stream_conn((lsquic_stream_t *) s)) == context->client_engine) { + is_client = 1; + } + return is_client; +} + +us_quic_socket_t *us_quic_stream_socket(us_quic_stream_t *s) { + return (us_quic_socket_t *) lsquic_stream_conn((lsquic_stream_t *) s); +} + +//#include <errno.h> + + +// only for servers? +static void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { + + /* The user data of the connection owning the stream, points to the socket context */ + us_quic_socket_context_t *context = (us_quic_socket_context_t *) lsquic_conn_get_ctx(lsquic_stream_conn(s)); + + /* This object is (and must be) fetched from a stream by + * calling lsquic_stream_get_hset() before the stream can be read. */ + /* This call must precede calls to lsquic_stream_read(), lsquic_stream_readv(), and lsquic_stream_readf(). */ + void *header_set = lsquic_stream_get_hset(s); + if (header_set) { + context->on_stream_headers((us_quic_stream_t *) s); + // header management is obviously broken and needs to be per-stream + leave_all(); + } + + // all of this logic should be moved to uws and WE here should only hand over the data + + char temp[4096] = {0}; + int nr = lsquic_stream_read(s, temp, 4096); + + // emit on_end when we receive fin, regardless of whether we emitted data yet + if (nr == 0) { + // any time we read EOF we stop reading + lsquic_stream_wantread(s, 0); + context->on_stream_end((us_quic_stream_t *) s); + } else if (nr == -1) { + if (errno != EWOULDBLOCK) { + // error handling should not be needed if we use lsquic correctly + printf("UNHANDLED ON_READ ERROR\n"); + exit(0); + } + // if we for some reason could not read even though we were told to read, we just ignore it + // this should not really happen but whatever + } else { + // otherwise if we have data, then emit it + context->on_stream_data((us_quic_stream_t *) s, temp, nr); + } + + // that's it + return; + + //lsquic_stream_readf + + printf("read returned: %d\n", nr); + + // we will get 9, ebadf if we read from a closed stream + if (nr == -1) { + printf("Error in reading! errno is: %d\n", errno); + if (errno != EWOULDBLOCK) { + printf("Errno is not EWOULDBLOCK\n"); + } else { + printf("Errno is would block, fine!\n"); + } + exit(0); + return; + } + + /* We have reached EOF */ + if (nr == 0) { + + /* Are we polling for writable (todo: make this check faster)? */ + if (lsquic_stream_wantwrite(s, 1)) { + + // we happened to be polling for writable so leave the connection open until on_write eventually closes it + printf("we are polling for write, so leaving the stream open!\n"); + + // stop reading though! + lsquic_stream_wantread(s, 0); // I hope this is fine? half open? + + } else { + // we weren't polling for writable so reset it to old value + lsquic_stream_wantwrite(s, 0); + + // I guess we can close it since we have called shutdown before this so data should flow out + lsquic_stream_close(s); + } + + // reached the EOF + //lsquic_stream_close(s); + //lsquic_stream_wantread(s, 0); + return; + } + + //printf("read: %d\n", nr); + + //printf("%s\n", temp); + + // why do we get tons of zero reads? + // maybe it doesn't matter, if we can parse this input then we are fine + //lsquic_stream_wantread(s, 0); + //lsquic_stream_wantwrite(s, 1); + + printf("on_stream_data: %d\n", nr); + context->on_stream_data((us_quic_stream_t *) s, temp, nr); +} + +int us_quic_stream_write(us_quic_stream_t *s, char *data, int length) { + lsquic_stream_t *stream = (lsquic_stream_t *) s; + int ret = lsquic_stream_write((lsquic_stream_t *) s, data, length); + // just like otherwise, we automatically poll for writable when failed + if (ret != length) { + lsquic_stream_wantwrite((lsquic_stream_t *) s, 1); + } else { + lsquic_stream_wantwrite((lsquic_stream_t *) s, 0); + } + return ret; +} + +static void on_write (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { + + us_quic_socket_context_t *context = (us_quic_socket_context_t *) lsquic_conn_get_ctx(lsquic_stream_conn(s)); + + context->on_stream_writable((us_quic_stream_t *) s); + + // here we might want to check if the user did write to failure or not, and if the user did not write, stop polling for writable + // i think that is what we do for http1 +} + +static void on_stream_close (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { + //printf("STREAM CLOSED!\n"); +} + +#include "openssl/ssl.h" + +static char s_alpn[0x100]; + +int add_alpn (const char *alpn) +{ + size_t alpn_len, all_len; + + alpn_len = strlen(alpn); + if (alpn_len > 255) + return -1; + + all_len = strlen(s_alpn); + if (all_len + 1 + alpn_len + 1 > sizeof(s_alpn)) + return -1; + + s_alpn[all_len] = alpn_len; + memcpy(&s_alpn[all_len + 1], alpn, alpn_len); + s_alpn[all_len + 1 + alpn_len] = '\0'; + return 0; +} + +static int select_alpn(SSL *ssl, const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, void *arg) { + int r; + + printf("select_alpn\n"); + + r = SSL_select_next_proto((unsigned char **) out, outlen, in, inlen, + (unsigned char *) s_alpn, strlen(s_alpn)); + if (r == OPENSSL_NPN_NEGOTIATED) { + printf("OPENSSL_NPN_NEGOTIATED\n"); + return SSL_TLSEXT_ERR_OK; + } + else + { + printf("no supported protocol can be selected!\n"); + //LSQ_WARN("no supported protocol can be selected from %.*s", + //(int) inlen, (char *) in); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } +} + +SSL_CTX *old_ctx; + +int server_name_cb(SSL *s, int *al, void *arg) { + printf("yolo SNI server_name_cb\n"); + + SSL_set_SSL_CTX(s, old_ctx); + + printf("existing name is: %s\n", SSL_get_servername(s, TLSEXT_NAMETYPE_host_name)); + + if (!SSL_get_servername(s, TLSEXT_NAMETYPE_host_name)) { + SSL_set_tlsext_host_name(s, "YOLO NAME!"); + printf("set name is: %s\n", SSL_get_servername(s, TLSEXT_NAMETYPE_host_name)); + } + + + return SSL_TLSEXT_ERR_OK; +} + +// this one is required for servers +struct ssl_ctx_st *get_ssl_ctx(void *peer_ctx, const struct sockaddr *local) { + printf("getting ssl ctx now, peer_ctx: %p\n", peer_ctx); + + // peer_ctx point to the us_udp_socket_t that passed the UDP packet in via + // lsquic_engine_packet_in (it got passed as peer_ctx) + // we want the per-context ssl cert from this udp socket + struct us_udp_socket_t *udp_socket = (struct us_udp_socket_t *) peer_ctx; + + // the udp socket of a server points to the context + struct us_quic_socket_context_s *context = us_udp_socket_user(udp_socket); + + if (old_ctx) { + return old_ctx; + } + + // peer_ctx should be the options struct! + us_quic_socket_context_options_t *options = &context->options; + + + SSL_CTX *ctx = SSL_CTX_new(TLS_method()); + + old_ctx = ctx; + + SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION); + + //SSL_CTX_set_default_verify_paths(ctx); + + // probably cannot use this when http is in use? + // alpn is needed + SSL_CTX_set_alpn_select_cb(ctx, select_alpn, NULL); + + // sni is needed + SSL_CTX_set_tlsext_servername_callback(ctx, server_name_cb); + //long SSL_CTX_set_tlsext_servername_arg(SSL_CTX *ctx, void *arg); + + printf("Key: %s\n", options->key_file_name); + printf("Cert: %s\n", options->cert_file_name); + + int a = SSL_CTX_use_certificate_chain_file(ctx, options->cert_file_name); + int b = SSL_CTX_use_PrivateKey_file(ctx, options->key_file_name, SSL_FILETYPE_PEM); + + printf("loaded cert and key? %d, %d\n", a, b); + + return ctx; +} + +SSL_CTX *sni_lookup(void *lsquic_cert_lookup_ctx, const struct sockaddr *local, const char *sni) { + printf("simply returning old ctx in sni\n"); + return old_ctx; +} + +int log_buf_cb(void *logger_ctx, const char *buf, size_t len) { + printf("%.*s\n", (int) len, buf); + return 0; +} + +int us_quic_stream_shutdown_read(us_quic_stream_t *s) { + lsquic_stream_t *stream = (lsquic_stream_t *) s; + + int ret = lsquic_stream_shutdown((lsquic_stream_t *) s, 0); + if (ret != 0) { + printf("cannot shutdown stream!\n"); + exit(0); + } + + return 0; +} + +void *us_quic_stream_ext(us_quic_stream_t *s) { + return lsquic_stream_get_ctx((lsquic_stream_t *) s); +} + +void us_quic_stream_close(us_quic_stream_t *s) { + lsquic_stream_t *stream = (lsquic_stream_t *) s; + + int ret = lsquic_stream_close((lsquic_stream_t *) s); + if (ret != 0) { + printf("cannot close stream!\n"); + exit(0); + } + + return; +} + +int us_quic_stream_shutdown(us_quic_stream_t *s) { + lsquic_stream_t *stream = (lsquic_stream_t *) s; + + int ret = lsquic_stream_shutdown((lsquic_stream_t *) s, 1); + if (ret != 0) { + printf("cannot shutdown stream!\n"); + exit(0); + } + + return 0; +} + +// header of header set +struct header_set_hd { + int offset; +}; + +// let's just store last header set here +struct header_set_hd *last_hset; + +// just a shitty marker for now +struct processed_header { + void *name, *value; + int name_length, value_length; +}; + +int us_quic_socket_context_get_header(us_quic_socket_context_t *context, int index, char **name, int *name_length, char **value, int *value_length) { + + if (index < last_hset->offset) { + + struct processed_header *pd = (struct processed_header *) (last_hset + 1); + + pd = pd + index; + + *name = pd->name; + *value = pd->value; + *value_length = pd->value_length; + *name_length = pd->name_length; + + return 1; + } + + return 0; + +} + +char pool[1000][4096]; +int pool_top = 0; + +void *take() { + if (pool_top == 1000) { + printf("out of memory\n"); + exit(0); + } + return pool[pool_top++]; +} + +void leave_all() { + pool_top = 0; +} + + +// header set callbacks +void *hsi_create_header_set(void *hsi_ctx, lsquic_stream_t *stream, int is_push_promise) { + + //printf("hsi_create_header_set\n"); + + void *hset = take();//malloc(1024); + memset(hset, 0, sizeof(struct header_set_hd)); + + // hsi_ctx is set in engine creation below + + // I guess we just return whatever here, what we return here is gettable via the stream + + // gettable via lsquic_stream_get_hset + + // return user defined header set + + return hset; +} + +void hsi_discard_header_set(void *hdr_set) { + // this is pretty much the destructor of above constructor + + printf("hsi_discard_header!\n"); +} + +// one header set allocates one 8kb buffer from a linked list of available buffers + + +// 8kb of preallocated heap for headers +char header_decode_heap[1024 * 8]; +int header_decode_heap_offset = 0; + +struct lsxpack_header *hsi_prepare_decode(void *hdr_set, struct lsxpack_header *hdr, size_t space) { + + //printf("hsi_prepare_decode\n"); + + if (!hdr) { + char *mem = take(); + hdr = (struct lsxpack_header *) mem;//malloc(sizeof(struct lsxpack_header)); + memset(hdr, 0, sizeof(struct lsxpack_header)); + hdr->buf = mem + sizeof(struct lsxpack_header);//take();//malloc(space); + lsxpack_header_prepare_decode(hdr, hdr->buf, 0, space); + } else { + + if (space > 4096 - sizeof(struct lsxpack_header)) { + printf("not hanlded!\n"); + exit(0); + } + + hdr->val_len = space; + //hdr->buf = realloc(hdr->buf, space); + } + + return hdr; +} + +int hsi_process_header(void *hdr_set, struct lsxpack_header *hdr) { + + // I guess this is the emitting of the header to app space + + //printf("hsi_process_header: %p\n", hdr); + + struct header_set_hd *hd = hdr_set; + struct processed_header *proc_hdr = (struct processed_header *) (hd + 1); + + if (!hdr) { + //printf("end of headers!\n"); + + last_hset = hd; + + // mark end, well we can also just read the offset! + //memset(&proc_hdr[hd->offset], 0, sizeof(struct processed_header)); + + return 0; + } + + /*if (hdr->hpack_index) { + printf("header has hpack index: %d\n", hdr->hpack_index); + } + + if (hdr->qpack_index) { + printf("header has qpack index: %d\n", hdr->qpack_index); + }*/ + + proc_hdr[hd->offset].value = &hdr->buf[hdr->val_offset]; + proc_hdr[hd->offset].name = &hdr->buf[hdr->name_offset]; + proc_hdr[hd->offset].value_length = hdr->val_len; + proc_hdr[hd->offset].name_length = hdr->name_len; + + //printf("header %.*s = %.*s\n", hdr->name_len, &hdr->buf[hdr->name_offset], hdr->val_len, &hdr->buf[hdr->val_offset]); + + hd->offset++; + + return 0; +} + +//extern us_quic_socket_context_t *context; + +void timer_cb(struct us_timer_t *t) { + //printf("Processing conns from timer\n"); + lsquic_engine_process_conns(global_engine); + lsquic_engine_process_conns(global_client_engine); + + // these are handled by this timer, should be polling for udp writable + lsquic_engine_send_unsent_packets(global_engine); + lsquic_engine_send_unsent_packets(global_client_engine); +} + +// lsquic_conn +us_quic_socket_context_t *us_quic_socket_context(us_quic_socket_t *s) { + return (us_quic_socket_context_t *) lsquic_conn_get_ctx((lsquic_conn_t *) s); +} + +void *us_quic_socket_context_ext(us_quic_socket_context_t *context) { + return context + 1; +} + +// this will be for both client and server, but will be only for either h3 or raw quic +us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options, int ext_size) { + + + printf("Creating socket context with ssl: %s\n", options.key_file_name); + + // every _listen_ call creates a new udp socket that feeds inputs to the engine in the context + // every context has its own send buffer and udp send socket (not bound to any port or ip?) + + // or just make it so that once you listen, it will listen on that port for input, and the context will use + // the first udp socket for output as it doesn't matter which one is used + + /* Holds all callbacks */ + us_quic_socket_context_t *context = malloc(sizeof(struct us_quic_socket_context_s) + ext_size); + + // the option is put on the socket context + context->options = options; + + context->loop = loop; + //context->udp_socket = 0; + + /* Allocate per thread, UDP packet buffers */ + context->recv_buf = us_create_udp_packet_buffer(); + //context->send_buf = us_create_udp_packet_buffer(); + + /* Init lsquic engine */ + if (0 != lsquic_global_init(LSQUIC_GLOBAL_CLIENT|LSQUIC_GLOBAL_SERVER)) { + exit(EXIT_FAILURE); + } + + static struct lsquic_stream_if stream_callbacks = { + .on_close = on_stream_close, + .on_conn_closed = on_conn_closed, + .on_write = on_write, + .on_read = on_read, + .on_new_stream = on_new_stream, + .on_new_conn = on_new_conn + }; + + //memset(&stream_callbacks, 13, sizeof(struct lsquic_stream_if)); + + static struct lsquic_hset_if hset_if = { + .hsi_discard_header_set = hsi_discard_header_set, + .hsi_create_header_set = hsi_create_header_set, + .hsi_prepare_decode = hsi_prepare_decode, + .hsi_process_header = hsi_process_header + }; + + + add_alpn("h3"); + + struct lsquic_engine_api engine_api = { + .ea_packets_out = send_packets_out, + .ea_packets_out_ctx = (void *) context, /* For example */ + .ea_stream_if = &stream_callbacks, + .ea_stream_if_ctx = context, + + .ea_get_ssl_ctx = get_ssl_ctx, + + // lookup certificate + .ea_lookup_cert = sni_lookup, + .ea_cert_lu_ctx = 0, + + // these are zero anyways + .ea_hsi_ctx = 0, + .ea_hsi_if = &hset_if, + }; + + ///printf("log: %d\n", lsquic_set_log_level("debug")); + + static struct lsquic_logger_if logger = { + .log_buf = log_buf_cb, + }; + + + + //lsquic_logger_init(&logger, 0, LLTS_NONE); + + /* Create an engine in server mode with HTTP behavior: */ + context->engine = lsquic_engine_new(LSENG_SERVER | LSENG_HTTP, &engine_api); + + struct lsquic_engine_api engine_api_client = { + .ea_packets_out = send_packets_out, + .ea_packets_out_ctx = (void *) context, /* For example */ + .ea_stream_if = &stream_callbacks, + .ea_stream_if_ctx = context, + + //.ea_get_ssl_ctx = get_ssl_ctx, // for client? + + // lookup certificate + //.ea_lookup_cert = sni_lookup, // for client? + //.ea_cert_lu_ctx = 13, // for client? + + // these are zero anyways + .ea_hsi_ctx = 0, + .ea_hsi_if = &hset_if, + }; + + context->client_engine = lsquic_engine_new(LSENG_HTTP, &engine_api_client); + + printf("Engine: %p\n", context->engine); + printf("Client Engine: %p\n", context->client_engine); + + // start a timer to handle connections + struct us_timer_t *delayTimer = us_create_timer(loop, 0, 0); + us_timer_set(delayTimer, timer_cb, 50, 50); + + // used by process_quic + global_engine = context->engine; + global_client_engine = context->client_engine; + + return context; +} + +us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, const char *host, int port, int ext_size) { + /* We literally do create a listen socket */ + return (us_quic_listen_socket_t *) us_create_udp_socket(context->loop, /*context->recv_buf*/ NULL, on_udp_socket_data, on_udp_socket_writable, host, port, context); + //return NULL; +} + +/* A client connection is its own UDP socket, while a server connection makes use of the shared listen UDP socket */ +us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *context, const char *host, int port, int ext_size) { + printf("Connecting..\n"); + + + // localhost 9004 ipv4 + struct sockaddr_storage storage = {0}; + // struct sockaddr_in *addr = (struct sockaddr_in *) &storage; + // addr->sin_addr.s_addr = 16777343; + // addr->sin_port = htons(9004); + // addr->sin_family = AF_INET; + + struct sockaddr_in6 *addr = (struct sockaddr_in6 *) &storage; + addr->sin6_addr.s6_addr[15] = 1; + addr->sin6_port = htons(9004); + addr->sin6_family = AF_INET6; + + // Create the UDP socket binding to ephemeral port + struct us_udp_socket_t *udp_socket = us_create_udp_socket(context->loop, /*context->recv_buf*/ NULL, on_udp_socket_data_client, on_udp_socket_writable, 0, 0, context); + + // Determine what port we got, creating the local sockaddr + int ephemeral = us_udp_socket_bound_port(udp_socket); + + printf("Connecting with udp socket bound to port: %d\n", ephemeral); + + printf("Client udp socket is: %p\n", udp_socket); + + + // let's call ourselves an ipv6 client and see if that solves anything + struct sockaddr_storage local_storage = {0}; + // struct sockaddr_in *local_addr = (struct sockaddr_in *) &local_storage; + // local_addr->sin_addr.s_addr = 16777343; + // local_addr->sin_port = htons(ephemeral); + // local_addr->sin_family = AF_INET; + + struct sockaddr_in6 *local_addr = (struct sockaddr_in6 *) &local_storage; + local_addr->sin6_addr.s6_addr[15] = 1; + local_addr->sin6_port = htons(ephemeral); + local_addr->sin6_family = AF_INET6; + + // Refer to the UDP socket, and from that, get the context? + + // Create an UDP socket with host-picked port, or well, any port for now + + // we need 1 socket for servers, then we bind multiple ports to that one socket + + void *client = lsquic_engine_connect(context->client_engine, LSQVER_I001, (struct sockaddr *) local_addr, (struct sockaddr *) addr, udp_socket, (lsquic_conn_ctx_t *) udp_socket, "sni", 0, 0, 0, 0, 0); + + printf("Client: %p\n", client); + + // this is requiored to even have packetgs sending out (run this in post) + lsquic_engine_process_conns(context->client_engine); + + return client; +} + +#endif |