mini/mini_server.c (413 lines of code) (raw):

#include "mini_server.h" void xqc_mini_svr_init_ssl_config(xqc_engine_ssl_config_t *ssl_cfg, xqc_mini_svr_args_t *args) { ssl_cfg->private_key_file = args->env_cfg.private_key_file; ssl_cfg->cert_file = args->env_cfg.cert_file; ssl_cfg->ciphers = args->quic_cfg.ciphers; ssl_cfg->groups = args->quic_cfg.groups; /* for server, load session ticket key if there exists */ if (args->quic_cfg.session_ticket_key_len <= 0) { ssl_cfg->session_ticket_key_data = NULL; ssl_cfg->session_ticket_key_len = 0; } else { ssl_cfg->session_ticket_key_data = args->quic_cfg.session_ticket_key_data; ssl_cfg->session_ticket_key_len = args->quic_cfg.session_ticket_key_len; } } void xqc_mini_svr_init_args(xqc_mini_svr_args_t *args) { int ret; char *p = NULL; /* init network args */ strncpy(args->net_cfg.ip, DEFAULT_IP, sizeof(args->net_cfg.ip) - 1); args->net_cfg.port = DEFAULT_PORT; /** * init quic config * it's recommended to replace the constant value with option arguments according to actual needs */ p = args->quic_cfg.session_ticket_key_data; ret = xqc_mini_read_file_data(p, SESSION_TICKET_KEY_BUF_LEN, SESSION_TICKET_KEY_FILE); args->quic_cfg.session_ticket_key_len = ret > 0 ? ret : 0; args->quic_cfg.cc = CC_TYPE_BBR; args->quic_cfg.multipath = 1; strncpy(args->quic_cfg.mp_sched, "minrtt", 32); strncpy(args->quic_cfg.ciphers, XQC_TLS_CIPHERS, CIPHER_SUIT_LEN - 1); strncpy(args->quic_cfg.groups, XQC_TLS_GROUPS, TLS_GROUPS_LEN - 1); /* init env config */ strncpy(args->env_cfg.log_path, LOG_PATH, TLS_GROUPS_LEN - 1); strncpy(args->env_cfg.key_out_path, KEY_PATH, PATH_LEN - 1); strncpy(args->env_cfg.private_key_file, PRIV_KEY_PATH, PATH_LEN - 1); strncpy(args->env_cfg.cert_file, CERT_PEM_PATH, PATH_LEN - 1); } int xqc_mini_svr_init_ctx(xqc_mini_svr_ctx_t *ctx, xqc_mini_svr_args_t *args) { memset(ctx, 0, sizeof(xqc_mini_svr_ctx_t)); /* init event base */ struct event_base *eb = event_base_new(); ctx->eb = eb; ctx->args = args; /* init log writer fd */ ctx->log_fd = xqc_mini_svr_open_log_file(ctx); if (ctx->log_fd < 0) { printf("[error] open log file failed\n"); return XQC_ERROR; } /* init keylog writer fd */ ctx->keylog_fd = xqc_mini_svr_open_keylog_file(ctx); if (ctx->keylog_fd < 0) { printf("[error] open keylog file failed\n"); return XQC_ERROR; } return XQC_OK; } /** * @brief init engine & transport callbacks */ void xqc_mini_svr_init_callback(xqc_engine_callback_t *cb, xqc_transport_callbacks_t *tcb, xqc_mini_svr_args_t *args) { static xqc_engine_callback_t callback = { .set_event_timer = xqc_mini_svr_set_event_timer, .log_callbacks = { .xqc_log_write_err = xqc_mini_svr_write_log_file, .xqc_log_write_stat = xqc_mini_svr_write_log_file, .xqc_qlog_event_write = xqc_mini_svr_write_qlog_file }, .keylog_cb = xqc_mini_svr_keylog_cb, }; static xqc_transport_callbacks_t transport_cbs = { .server_accept = xqc_mini_svr_accept, .write_socket = xqc_mini_svr_write_socket, .write_socket_ex = xqc_mini_svr_write_socket_ex, .conn_update_cid_notify = xqc_mini_svr_conn_update_cid_notify, }; *cb = callback; *tcb = transport_cbs; } /** * @brief init xquic server engine */ int xqc_mini_svr_init_xquic_engine(xqc_mini_svr_ctx_t *ctx, xqc_mini_svr_args_t *args) { int ret; xqc_config_t egn_cfg; xqc_engine_callback_t callback; xqc_engine_ssl_config_t ssl_cfg = {0}; xqc_transport_callbacks_t transport_cbs; /* get default parameters of xquic engine */ ret = xqc_engine_get_default_config(&egn_cfg, XQC_ENGINE_SERVER); if (ret < 0) { return XQC_ERROR; } /* init ssl config */ xqc_mini_svr_init_ssl_config(&ssl_cfg, args); /* init engine & transport callbacks */ xqc_mini_svr_init_callback(&callback, &transport_cbs, args); /* create server engine */ ctx->engine = xqc_engine_create(XQC_ENGINE_SERVER, &egn_cfg, &ssl_cfg, &callback, &transport_cbs, ctx); if (ctx->engine == NULL) { printf("[error] xqc_engine_create error\n"); return XQC_ERROR; } ctx->ev_engine = event_new(ctx->eb, -1, 0, xqc_mini_svr_engine_cb, ctx); return XQC_OK; } int xqc_mini_svr_init_env(xqc_mini_svr_ctx_t *ctx, xqc_mini_svr_args_t *args) { int ret = XQC_OK; /* init server args */ xqc_mini_svr_init_args(args); /* init server ctx */ ret = xqc_mini_svr_init_ctx(ctx, args); return ret; } xqc_cong_ctrl_callback_t xqc_mini_svr_get_cc_cb(xqc_mini_svr_args_t *args) { xqc_cong_ctrl_callback_t ccc = xqc_bbr_cb; switch (args->quic_cfg.cc) { case CC_TYPE_BBR: ccc = xqc_bbr_cb; break; case CC_TYPE_CUBIC: ccc = xqc_cubic_cb; break; default: break; } return ccc; } xqc_scheduler_callback_t xqc_mini_svr_get_sched_cb(xqc_mini_svr_args_t *args) { xqc_scheduler_callback_t sched = xqc_minrtt_scheduler_cb; if (strncmp(args->quic_cfg.mp_sched, "minrtt", strlen("minrtt")) == 0) { sched = xqc_minrtt_scheduler_cb; } if (strncmp(args->quic_cfg.mp_sched, "backup", strlen("backup")) == 0) { sched = xqc_backup_scheduler_cb; } return sched; } void xqc_mini_svr_init_conn_settings(xqc_engine_t *engine, xqc_mini_svr_args_t *args) { /* parse congestion control callback */ xqc_cong_ctrl_callback_t ccc = xqc_mini_svr_get_cc_cb(args); /* parse mp scheduler callback */ xqc_scheduler_callback_t sched = xqc_mini_svr_get_sched_cb(args); /* init connection settings */ xqc_conn_settings_t conn_settings = { .cong_ctrl_callback = ccc, .cc_params = { .customize_on = 1, .init_cwnd = 32, .bbr_enable_lt_bw = 1, }, .spurious_loss_detect_on = 1, .init_idle_time_out = 60000, .enable_multipath = args->quic_cfg.multipath, .scheduler_callback = sched, .standby_path_probe_timeout = 1000, .adaptive_ack_frequency = 1, .anti_amplification_limit = 4, }; /* set customized connection settings to engine ctx */ xqc_server_set_conn_settings(engine, &conn_settings); } /* create socket and bind port */ static int xqc_mini_svr_init_socket(int family, uint16_t port, struct sockaddr *local_addr, socklen_t local_addrlen) { int size; int opt_reuseaddr; int flags = 1; int fd = socket(family, SOCK_DGRAM, 0); if (fd < 0) { printf("create socket failed, errno: %d\n", get_sys_errno()); return XQC_ERROR; } /* non-block */ #ifdef XQC_SYS_WINDOWS if (ioctlsocket(fd, FIONBIO, &flags) == SOCKET_ERROR) { goto err; } #else if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { printf("set socket nonblock failed, errno: %d\n", get_sys_errno()); goto err; } #endif /* reuse port */ opt_reuseaddr = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt_reuseaddr, sizeof(opt_reuseaddr)) < 0) { printf("setsockopt failed, errno: %d\n", get_sys_errno()); goto err; } /* send/recv buffer size */ size = 1 * 1024 * 1024; if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(int)) < 0) { printf("setsockopt failed, errno: %d\n", get_sys_errno()); goto err; } if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(int)) < 0) { printf("setsockopt failed, errno: %d\n", get_sys_errno()); goto err; } /* bind port */ if (bind(fd, local_addr, local_addrlen) < 0) { printf("bind socket failed, family: %d, errno: %d, %s\n", family, get_sys_errno(), strerror(get_sys_errno())); goto err; } return fd; err: close(fd); return -1; } static int xqc_mini_svr_create_socket(xqc_mini_svr_user_conn_t *user_conn, xqc_mini_svr_net_config_t* cfg) { /* ipv4 socket */ user_conn->local_addr->sin_family = AF_INET; user_conn->local_addr->sin_port = htons(cfg->port); user_conn->local_addr->sin_addr.s_addr = htonl(INADDR_ANY); user_conn->local_addrlen = sizeof(struct sockaddr_in); user_conn->fd = xqc_mini_svr_init_socket(AF_INET, cfg->port, (struct sockaddr*)user_conn->local_addr, user_conn->local_addrlen); printf("[stats] create ipv4 socket fd: %d success, bind socket to ip: %s, port: %d\n", user_conn->fd, cfg->ip, cfg->port); if (!user_conn->fd) { return -1; } return 0; } int xqc_mini_svr_init_alpn_ctx(xqc_engine_t *engine) { int ret = 0; /* init http3 callbacks */ xqc_h3_callbacks_t h3_cbs = { .h3c_cbs = { .h3_conn_create_notify = xqc_mini_svr_h3_conn_create_notify, .h3_conn_close_notify = xqc_mini_svr_h3_conn_close_notify, .h3_conn_handshake_finished = xqc_mini_svr_h3_conn_handshake_finished, }, .h3r_cbs = { .h3_request_create_notify = xqc_mini_svr_h3_request_create_notify, .h3_request_close_notify = xqc_mini_svr_h3_request_close_notify, .h3_request_read_notify = xqc_mini_svr_h3_request_read_notify, .h3_request_write_notify = xqc_mini_svr_h3_request_write_notify, } }; /* init http3 context */ ret = xqc_h3_ctx_init(engine, &h3_cbs); if (ret != XQC_OK) { printf("init h3 context error, ret: %d\n", ret); return ret; } return ret; } int xqc_mini_svr_init_engine_ctx(xqc_mini_svr_ctx_t *ctx, xqc_mini_svr_args_t *args) { int ret; /* init connection settings */ xqc_mini_svr_init_conn_settings(ctx->engine, args); /* init alpn ctx */ ret = xqc_mini_svr_init_alpn_ctx(ctx->engine); return ret; } void xqc_mini_svr_socket_write_handler(xqc_mini_svr_user_conn_t *user_conn, int fd) { DEBUG printf("[stats] socket write handler\n"); } void xqc_mini_svr_socket_read_handler(xqc_mini_svr_user_conn_t *user_conn, int fd) { DEBUG; ssize_t recv_size, recv_sum; struct sockaddr_in peer_addr = {0}; socklen_t peer_addrlen = sizeof(peer_addr); uint64_t recv_time; xqc_int_t ret; unsigned char packet_buf[XQC_PACKET_BUF_LEN] = {0}; xqc_mini_svr_ctx_t *ctx; ctx = user_conn->ctx; ctx->current_fd = fd; recv_size = recv_sum = 0; do { /* recv quic packet from client */ recv_size = recvfrom(fd, packet_buf, sizeof(packet_buf), 0, (struct sockaddr *) &peer_addr, &peer_addrlen); if (recv_size < 0 && get_sys_errno() == EAGAIN) { break; } memcpy(user_conn->peer_addr, &peer_addr, peer_addrlen); user_conn->peer_addrlen = peer_addrlen; if (recv_size < 0) { printf("recvfrom: recvmsg = %zd err=%s\n", recv_size, strerror(get_sys_errno())); break; } user_conn->local_addrlen = sizeof(struct sockaddr_in6); ret = getsockname(user_conn->fd, (struct sockaddr *)user_conn->local_addr, &user_conn->local_addrlen); if (ret != 0) { printf("[error] getsockname error, errno: %d\n", get_sys_errno()); } // printf("[stats] get sock name %d\n", user_conn->local_addr->sin_family); recv_sum += recv_size; recv_time = xqc_now(); /* process quic packet with xquic engine */ ret = xqc_engine_packet_process(ctx->engine, packet_buf, recv_size, (struct sockaddr *)(user_conn->local_addr), user_conn->local_addrlen, (struct sockaddr *)(user_conn->peer_addr), user_conn->peer_addrlen, (xqc_usec_t)recv_time, user_conn); if (ret != XQC_OK) { printf("[error] server_read_handler: packet process err, ret: %d\n", ret); return; } } while (recv_size > 0); finish_recv: // printf("[stats] xqc_mini_svr_socket_read_handler, recv size:%zu\n", recv_sum); xqc_engine_finish_recv(ctx->engine); } static void xqc_mini_svr_socket_event_callback(int fd, short what, void *arg) { //DEBUG; xqc_mini_svr_user_conn_t *user_conn = (xqc_mini_svr_user_conn_t *)arg; if (what & EV_WRITE) { xqc_mini_svr_socket_write_handler(user_conn, fd); } else if (what & EV_READ) { xqc_mini_svr_socket_read_handler(user_conn, fd); } else { printf("event callback: fd=%d, what=%d\n", fd, what); exit(1); } } void xqc_mini_svr_free_ctx(xqc_mini_svr_ctx_t *ctx) { xqc_mini_svr_close_keylog_file(ctx); xqc_mini_svr_close_log_file(ctx); if (ctx->args) { free(ctx->args); ctx->args = NULL; } } void xqc_mini_svr_free_user_conn(xqc_mini_svr_user_conn_t *user_conn) { if (user_conn->local_addr) { free(user_conn->local_addr); user_conn->local_addr = NULL; } if (user_conn->peer_addr) { free(user_conn->peer_addr); user_conn->peer_addr = NULL; } if (user_conn) { free(user_conn); user_conn = NULL; } } xqc_mini_svr_user_conn_t * xqc_mini_svr_create_user_conn(xqc_mini_svr_ctx_t *ctx) { int ret; xqc_mini_svr_user_conn_t *user_conn = calloc(1, sizeof(xqc_mini_svr_user_conn_t)); user_conn->ctx = ctx; user_conn->local_addr = (struct sockaddr_in *)calloc(1, sizeof(struct sockaddr_in)); user_conn->peer_addr = (struct sockaddr_in *)calloc(1, sizeof(struct sockaddr_in)); /* init server socket and save to ctx->fd */ ret = xqc_mini_svr_create_socket(user_conn, &ctx->args->net_cfg); if (ret < 0) { printf("[error] xqc_create_socket error\n"); goto error; } /* bind socket event callback to fd event */ user_conn->ev_socket = event_new(ctx->eb, user_conn->fd, EV_READ | EV_PERSIST, xqc_mini_svr_socket_event_callback, user_conn); event_add(user_conn->ev_socket, NULL); return user_conn; error: xqc_mini_svr_free_user_conn(user_conn); return NULL; } void xqc_mini_cli_on_connection_finish(xqc_mini_svr_user_conn_t *user_conn) { if (user_conn->ev_timeout) { event_del(user_conn->ev_timeout); user_conn->ev_timeout = NULL; } if (user_conn->ev_socket) { event_del(user_conn->ev_socket); user_conn->ev_timeout = NULL; } } int main(int argc, char *argv[]) { int ret; xqc_mini_svr_ctx_t svr_ctx = {0}, *ctx = &svr_ctx; xqc_mini_svr_args_t *args = NULL; xqc_mini_svr_user_conn_t *user_conn = NULL; args = calloc(1, sizeof(xqc_mini_svr_args_t)); if (args == NULL) { printf("[error] calloc args failed\n"); goto exit; } /* init env (for windows) */ xqc_platform_init_env(); /* init server environment */ ret = xqc_mini_svr_init_env(ctx, args); if (ret < 0) { printf("[error] init server environment failed\n"); goto exit; } /* create & init engine to ctx->engine */ ret = xqc_mini_svr_init_xquic_engine(ctx, args); if (ret < 0) { printf("[error] init xquic engine failed\n"); goto exit; } /* init engine ctx */ ret = xqc_mini_svr_init_engine_ctx(ctx, args); if (ret < 0) { printf("[error] init engine ctx failed\n"); goto exit; } /* initiate user_conn */ user_conn = xqc_mini_svr_create_user_conn(ctx); /* start event loop */ event_base_dispatch(ctx->eb); exit: xqc_engine_destroy(ctx->engine); xqc_mini_svr_free_ctx(ctx); if (user_conn) { xqc_mini_svr_free_user_conn(user_conn); } return 0; }