diff --git a/auto/modules b/auto/modules index c199d89bf..0c40991f4 100644 --- a/auto/modules +++ b/auto/modules @@ -792,6 +792,17 @@ if [ $HTTP = YES ]; then . auto/module fi + if [ $HTTP_PROXY = YES -a $HTTP_V3 = YES ]; then + ngx_module_name=ngx_http_v3_proxy_module + ngx_module_incs= + ngx_module_deps= + ngx_module_srcs=src/http/v3/ngx_http_v3_proxy_module.c + ngx_module_libs= + ngx_module_link=$HTTP_V3 + + . auto/module + fi + if [ $HTTP_PERL != NO ]; then ngx_module_name=ngx_http_perl_module ngx_module_incs=src/http/modules/perl diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c index 7cae295eb..2674f18e6 100644 --- a/src/core/ngx_connection.c +++ b/src/core/ngx_connection.c @@ -1527,6 +1527,10 @@ ngx_tcp_nodelay(ngx_connection_t *c) { int tcp_nodelay; + if (c->type != SOCK_STREAM) { + return NGX_OK; + } + if (c->tcp_nodelay != NGX_TCP_NODELAY_UNSET) { return NGX_OK; } diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h index 84dd80442..04294224e 100644 --- a/src/core/ngx_connection.h +++ b/src/core/ngx_connection.h @@ -150,7 +150,7 @@ struct ngx_connection_s { ngx_proxy_protocol_t *proxy_protocol; #if (NGX_QUIC || NGX_COMPAT) - ngx_quic_stream_t *quic; + ngx_quic_t *quic; #endif #if (NGX_SSL || NGX_COMPAT) diff --git a/src/core/ngx_core.h b/src/core/ngx_core.h index 02890b843..eeed2a260 100644 --- a/src/core/ngx_core.h +++ b/src/core/ngx_core.h @@ -28,9 +28,10 @@ typedef struct ngx_thread_task_s ngx_thread_task_t; typedef struct ngx_ssl_s ngx_ssl_t; typedef struct ngx_ssl_cache_s ngx_ssl_cache_t; typedef struct ngx_proxy_protocol_s ngx_proxy_protocol_t; -typedef struct ngx_quic_stream_s ngx_quic_stream_t; typedef struct ngx_ssl_connection_s ngx_ssl_connection_t; typedef struct ngx_udp_connection_s ngx_udp_connection_t; +typedef struct ngx_quic_s ngx_quic_t; +typedef struct ngx_quic_conf_s ngx_quic_conf_t; typedef void (*ngx_event_handler_pt)(ngx_event_t *ev); typedef void (*ngx_connection_handler_pt)(ngx_connection_t *c); diff --git a/src/event/ngx_event.c b/src/event/ngx_event.c index ef525d93b..1fd94ae20 100644 --- a/src/event/ngx_event.c +++ b/src/event/ngx_event.c @@ -267,18 +267,14 @@ ngx_process_events_and_timers(ngx_cycle_t *cycle) ngx_int_t ngx_handle_read_event(ngx_event_t *rev, ngx_uint_t flags) { -#if (NGX_QUIC) - ngx_connection_t *c; c = rev->data; - if (c->quic) { + if (c->shared) { return NGX_OK; } -#endif - if (ngx_event_flags & NGX_USE_CLEAR_EVENT) { /* kqueue, epoll */ @@ -351,11 +347,9 @@ ngx_handle_write_event(ngx_event_t *wev, size_t lowat) c = wev->data; -#if (NGX_QUIC) - if (c->quic) { + if (c->shared) { return NGX_OK; } -#endif if (lowat) { if (ngx_send_lowat(c, lowat) == NGX_ERROR) { diff --git a/src/event/quic/ngx_event_quic.c b/src/event/quic/ngx_event_quic.c index 6a59aaf93..45aa1e877 100644 --- a/src/event/quic/ngx_event_quic.c +++ b/src/event/quic/ngx_event_quic.c @@ -11,16 +11,15 @@ static ngx_quic_connection_t *ngx_quic_new_connection(ngx_connection_t *c, - ngx_quic_conf_t *conf, ngx_quic_header_t *pkt); + ngx_quic_header_t *pkt); static ngx_int_t ngx_quic_handle_stateless_reset(ngx_connection_t *c, ngx_quic_header_t *pkt); -static void ngx_quic_input_handler(ngx_event_t *rev); +static void ngx_quic_read_handler(ngx_event_t *rev); +static void ngx_quic_write_handler(ngx_event_t *wev); static void ngx_quic_close_handler(ngx_event_t *ev); -static ngx_int_t ngx_quic_handle_datagram(ngx_connection_t *c, ngx_buf_t *b, - ngx_quic_conf_t *conf); static ngx_int_t ngx_quic_handle_packet(ngx_connection_t *c, - ngx_quic_conf_t *conf, ngx_quic_header_t *pkt); + ngx_quic_header_t *pkt); static ngx_int_t ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt); static ngx_int_t ngx_quic_check_csid(ngx_quic_connection_t *qc, @@ -82,6 +81,7 @@ ngx_quic_connstate_dbg(ngx_connection_t *c) } p = ngx_slprintf(p, last, "%s", qc->shutdown ? " shutdown" : ""); + p = ngx_slprintf(p, last, "%s", qc->lingering ? " lingering" : ""); p = ngx_slprintf(p, last, "%s", qc->closing ? " closing" : ""); p = ngx_slprintf(p, last, "%s", qc->draining ? " draining" : ""); p = ngx_slprintf(p, last, "%s", qc->key_phase ? " kp" : ""); @@ -187,8 +187,8 @@ ngx_quic_apply_transport_params(ngx_connection_t *c, ngx_quic_tp_t *ctp) qc->tp.max_idle_timeout = ctp->max_idle_timeout; } - qc->streams.server_max_streams_bidi = ctp->initial_max_streams_bidi; - qc->streams.server_max_streams_uni = ctp->initial_max_streams_uni; + qc->streams.local_max_streams_bidi = ctp->initial_max_streams_bidi; + qc->streams.local_max_streams_uni = ctp->initial_max_streams_uni; ngx_memcpy(&qc->ctp, ctp, sizeof(ngx_quic_tp_t)); @@ -196,56 +196,143 @@ ngx_quic_apply_transport_params(ngx_connection_t *c, ngx_quic_tp_t *ctp) } -void -ngx_quic_run(ngx_connection_t *c, ngx_quic_conf_t *conf) +ngx_int_t +ngx_quic_create_connection(ngx_quic_conf_t *conf, ngx_connection_t *c, + ngx_uint_t flags) +{ + ngx_quic_t *quic; + ngx_quic_connection_t *qc; + + qc = ngx_pcalloc(c->pool, sizeof(ngx_quic_connection_t)); + if (qc == NULL) { + return NGX_ERROR; + } + + qc->conf = conf; + qc->is_server = (flags & NGX_SSL_CLIENT) ? 0 : 1; + + quic = ngx_palloc(c->pool, sizeof(ngx_quic_t)); + if (quic == NULL) { + return NGX_ERROR; + } + + quic->connection = qc; + quic->stream = NULL; + + c->quic = quic; + c->start_time = ngx_current_msec; + + if (qc->is_server) { + /* initialize server during handshake to save resources */ + return NGX_OK; + } + + if (ngx_quic_new_connection(c, NULL) == NULL) { + return NGX_ERROR; + } + + return ngx_quic_init_connection(c, flags); +} + + +ngx_int_t +ngx_quic_handshake(ngx_connection_t *c) { ngx_int_t rc; ngx_quic_connection_t *qc; - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic run"); + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic handshake"); + + if (c->buffer) { + rc = ngx_quic_handle_datagram(c, c->buffer); + + } else { + rc = ngx_quic_do_handshake(c); + } - rc = ngx_quic_handle_datagram(c, c->buffer, conf); if (rc != NGX_OK) { - ngx_quic_close_connection(c, rc); - return; + if (c->ssl) { + c->ssl->no_wait_shutdown = 1; + } + + return NGX_ERROR; } - /* quic connection is now created */ + /* quic connection is now initialized */ qc = ngx_quic_get_connection(c); - ngx_add_timer(c->read, qc->tp.max_idle_timeout); - - if (!qc->streams.initialized) { - ngx_add_timer(&qc->close, qc->conf->handshake_timeout); - } + ngx_add_timer(&qc->close, qc->tp.max_idle_timeout); ngx_quic_connstate_dbg(c); - c->read->handler = ngx_quic_input_handler; + c->read->handler = ngx_quic_read_handler; + c->write->handler = ngx_quic_write_handler; - return; + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + return NGX_ERROR; + } + + return c->ssl->handshaked ? NGX_OK : NGX_AGAIN; } static ngx_quic_connection_t * -ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf, - ngx_quic_header_t *pkt) +ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_header_t *pkt) { + ngx_str_t *secret, scid; ngx_uint_t i; ngx_quic_tp_t *ctp; + ngx_quic_conf_t *conf; ngx_quic_connection_t *qc; + u_char scid_buf[NGX_QUIC_SERVER_CID_LEN]; - qc = ngx_pcalloc(c->pool, sizeof(ngx_quic_connection_t)); - if (qc == NULL) { - return NULL; - } + qc = c->quic->connection; + conf = qc->conf; qc->keys = ngx_pcalloc(c->pool, sizeof(ngx_quic_keys_t)); if (qc->keys == NULL) { return NULL; } - qc->version = pkt->version; + if (ngx_quic_init_transport_params(&qc->tp, conf) != NGX_OK) { + return NULL; + } + + if (pkt == NULL) { + /* client */ + + if (RAND_bytes(scid_buf, NGX_QUIC_SERVER_CID_LEN) != 1) { + return NULL; + } + + scid.data = scid_buf; + scid.len = NGX_QUIC_SERVER_CID_LEN; + secret = &scid; + + qc->validated = 1; + qc->version = 0x01; + + } else { + secret = &pkt->dcid; + scid = pkt->scid; + + qc->validated = pkt->validated; + qc->version = pkt->version; + + if (pkt->validated && pkt->retried) { + qc->tp.retry_scid.len = pkt->dcid.len; + qc->tp.retry_scid.data = ngx_pstrdup(c->pool, &pkt->dcid); + if (qc->tp.retry_scid.data == NULL) { + return NULL; + } + } + + qc->tp.original_dcid.len = pkt->odcid.len; + qc->tp.original_dcid.data = ngx_pstrdup(c->pool, &pkt->odcid); + if (qc->tp.original_dcid.data == NULL) { + return NULL; + } + } ngx_rbtree_init(&qc->streams.tree, &qc->streams.sentinel, ngx_quic_rbtree_insert_stream); @@ -268,32 +355,28 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf, ngx_quic_init_rtt(qc); - qc->pto.log = c->log; + qc->pto.log = ngx_cycle->log; qc->pto.data = c; qc->pto.handler = ngx_quic_pto_handler; - qc->push.log = c->log; + qc->push.log = ngx_cycle->log; qc->push.data = c; qc->push.handler = ngx_quic_push_handler; - qc->close.log = c->log; + qc->close.log = ngx_cycle->log; qc->close.data = c; qc->close.handler = ngx_quic_close_handler; - qc->path_validation.log = c->log; + qc->path_validation.log = ngx_cycle->log; qc->path_validation.data = c; qc->path_validation.handler = ngx_quic_path_handler; - qc->key_update.log = c->log; + qc->key_update.log = ngx_cycle->log; qc->key_update.data = c; qc->key_update.handler = ngx_quic_keys_update; qc->conf = conf; - if (ngx_quic_init_transport_params(&qc->tp, conf) != NGX_OK) { - return NULL; - } - ctp = &qc->ctp; /* defaults to be used before actual client parameters are received */ @@ -308,8 +391,8 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf, qc->streams.recv_max_data = qc->tp.initial_max_data; qc->streams.recv_window = qc->streams.recv_max_data; - qc->streams.client_max_streams_uni = qc->tp.initial_max_streams_uni; - qc->streams.client_max_streams_bidi = qc->tp.initial_max_streams_bidi; + qc->streams.remote_max_streams_uni = qc->tp.initial_max_streams_uni; + qc->streams.remote_max_streams_bidi = qc->tp.initial_max_streams_bidi; qc->congestion.window = ngx_min(10 * NGX_QUIC_MIN_INITIAL_SIZE, ngx_max(2 * NGX_QUIC_MIN_INITIAL_SIZE, @@ -322,29 +405,26 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf, + conf->max_concurrent_streams_bidi) * conf->stream_buffer_size / 2000; - if (pkt->validated && pkt->retried) { - qc->tp.retry_scid.len = pkt->dcid.len; - qc->tp.retry_scid.data = ngx_pstrdup(c->pool, &pkt->dcid); - if (qc->tp.retry_scid.data == NULL) { - return NULL; - } - } - - if (ngx_quic_keys_set_initial_secret(qc->keys, &pkt->dcid, c->log) + if (ngx_quic_keys_set_initial_secret(qc->keys, secret, qc->is_server, + c->log) != NGX_OK) { return NULL; } - qc->validated = pkt->validated; - - if (ngx_quic_open_sockets(c, qc, pkt) != NGX_OK) { + if (ngx_quic_open_sockets(c, qc, &scid, pkt ? &pkt->dcid : NULL) != NGX_OK) + { ngx_quic_keys_cleanup(qc->keys); return NULL; } + if (qc->validated) { + qc->path->validated = 1; + } + c->idle = 1; - ngx_reusable_connection(c, 1); + + qc->initialized = 1; ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic connection created"); @@ -401,88 +481,116 @@ ngx_quic_handle_stateless_reset(ngx_connection_t *c, ngx_quic_header_t *pkt) static void -ngx_quic_input_handler(ngx_event_t *rev) +ngx_quic_read_handler(ngx_event_t *rev) { - ngx_int_t rc; - ngx_buf_t *b; - ngx_connection_t *c; - ngx_quic_connection_t *qc; + ssize_t n; + ngx_buf_t buf; + ngx_connection_t *c; + static u_char buffer[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE]; - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, 0, "quic input handler"); + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, 0, "quic read handler"); c = rev->data; - qc = ngx_quic_get_connection(c); c->log->action = "handling quic input"; - if (rev->timedout) { - ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, - "quic client timed out"); - ngx_quic_close_connection(c, NGX_DONE); - return; - } + while (rev->ready) { + n = c->recv(c, buffer, sizeof(buffer)); - if (c->close) { - c->close = 0; - - if (!ngx_exiting || !qc->streams.initialized) { - qc->error = NGX_QUIC_ERR_NO_ERROR; - qc->error_reason = "graceful shutdown"; - ngx_quic_close_connection(c, NGX_ERROR); - return; + if (n <= 0) { + break; } - if (!qc->closing && qc->conf->shutdown) { - qc->conf->shutdown(c); - } + ngx_memzero(&buf, sizeof(ngx_buf_t)); - return; + buf.pos = buffer; + buf.last = buffer + n; + buf.start = buf.pos; + buf.end = buffer + sizeof(buffer); + + if (ngx_quic_handle_datagram(c, &buf) == NGX_ERROR) { + ngx_quic_set_error(c, NGX_QUIC_ERR_INTERNAL_ERROR, + "datagram handling error"); + break; + } } - b = c->udp->buffer; - if (b == NULL) { - return; + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_quic_set_error(c, NGX_QUIC_ERR_INTERNAL_ERROR, "socket error"); } - rc = ngx_quic_handle_datagram(c, b, NULL); + ngx_quic_end_handler(c); +} - if (rc == NGX_ERROR) { - ngx_quic_close_connection(c, NGX_ERROR); - return; - } - if (rc == NGX_DONE) { +static void +ngx_quic_write_handler(ngx_event_t *wev) +{ + ngx_connection_t *c; + + c = wev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic write handler"); + + ngx_del_event(c->write, NGX_WRITE_EVENT, 0); +} + + +void +ngx_quic_end_handler(ngx_connection_t *c) +{ + ngx_quic_connection_t *qc; + + ngx_quic_connstate_dbg(c); + + if (c->ssl == NULL || c->ssl->handler == NULL) { return; } - /* rc == NGX_OK */ + qc = ngx_quic_get_connection(c); - qc->send_timer_set = 0; - ngx_add_timer(rev, qc->tp.max_idle_timeout); + if (!c->ssl->handshaked && qc->error == 0) { + return; + } - ngx_quic_connstate_dbg(c); + c->ssl->handler(c); } -void -ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc) +ngx_int_t +ngx_quic_shutdown(ngx_connection_t *c) { - ngx_uint_t i; - ngx_pool_t *pool; + ngx_uint_t i, no_wait; ngx_quic_send_ctx_t *ctx; ngx_quic_connection_t *qc; + if (c->quic->stream) { + /* QUIC stream */ + return NGX_OK; + } + qc = ngx_quic_get_connection(c); - if (qc == NULL) { - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic packet rejected rc:%i, cleanup connection", rc); + if (!qc->initialized) { goto quic_done; } - ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic close %s rc:%i", - qc->closing ? "resumed": "initiated", rc); + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic close %s", + qc->lingering || qc->closing ? "resumed": "initiated"); + + no_wait = (c->ssl == NULL || c->ssl->no_wait_shutdown) ? 1 : 0; + + if (!no_wait && !qc->closing) { + if (ngx_quic_linger_streams(c) == NGX_AGAIN) { + + if (!qc->lingering) { + ngx_add_timer(&qc->close, 3000); + qc->lingering = 1; + } + + return NGX_AGAIN; + } + } if (!qc->closing) { @@ -496,7 +604,7 @@ ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc) ngx_del_timer(&qc->close); } - if (rc == NGX_DONE) { + if (qc->error == NGX_QUIC_ERR_CLOSE) { /* * RFC 9000, 10.1. Idle Timeout @@ -506,7 +614,7 @@ ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc) * closed and its state is discarded when it remains idle */ - /* this case also handles some errors from ngx_quic_run() */ + /* this case also handles some errors from ngx_quic_handshake() */ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic close silent drain:%d timedout:%d", @@ -520,15 +628,15 @@ ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc) * to terminate the connection immediately. */ - if (qc->error == 0 && rc == NGX_ERROR) { - qc->error = NGX_QUIC_ERR_INTERNAL_ERROR; - qc->error_app = 0; + if (qc->error == 0) { + ngx_quic_set_error(c, NGX_QUIC_ERR_INTERNAL_ERROR, + "internal error"); } ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic close immediate term:%d drain:%d " + "quic close immediate term:%ui drain:%d " "%serror:%ui \"%s\"", - rc == NGX_ERROR ? 1 : 0, qc->draining, + no_wait, qc->draining, qc->error_app ? "app " : "", qc->error, qc->error_reason ? qc->error_reason : ""); @@ -542,22 +650,22 @@ ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc) qc->error_level = ctx->level; (void) ngx_quic_send_cc(c); - if (rc == NGX_OK) { + if (!no_wait) { ngx_add_timer(&qc->close, 3 * ngx_quic_pto(c, ctx)); } } } + qc->lingering = 0; qc->closing = 1; } - if (rc == NGX_ERROR && qc->close.timer_set) { - /* do not wait for timer in case of fatal error */ + if (no_wait && qc->close.timer_set) { ngx_del_timer(&qc->close); } - if (ngx_quic_close_streams(c, qc) == NGX_AGAIN) { - return; + if (ngx_quic_close_streams(c) == NGX_AGAIN) { + return NGX_AGAIN; } if (qc->push.timer_set) { @@ -581,7 +689,7 @@ ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc) } if (qc->close.timer_set) { - return; + return NGX_AGAIN; } if (qc->close.posted) { @@ -600,6 +708,7 @@ ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc) quic_done: if (c->ssl) { + c->ssl->no_wait_shutdown = 1; (void) ngx_ssl_shutdown(c); } @@ -607,72 +716,98 @@ quic_done: ngx_del_timer(c->read); } -#if (NGX_STAT_STUB) - (void) ngx_atomic_fetch_add(ngx_stat_active, -1); -#endif + return NGX_OK; +} + + +void +ngx_quic_set_error(ngx_connection_t *c, ngx_uint_t err, const char *reason) +{ + ngx_quic_connection_t *qc; - c->destroyed = 1; + qc = ngx_quic_get_connection(c); - pool = c->pool; + if (qc->error) { + return; + } - ngx_close_connection(c); + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic error %ui \"%s\"", + err, reason); - ngx_destroy_pool(pool); + qc->error = err; + qc->error_reason = reason; + qc->error_ftype = 0; } void -ngx_quic_finalize_connection(ngx_connection_t *c, ngx_uint_t err, - const char *reason) +ngx_quic_set_app_error(ngx_connection_t *c, ngx_uint_t err, const char *reason) { ngx_quic_connection_t *qc; qc = ngx_quic_get_connection(c); - if (qc->closing) { + if (qc->error) { return; } + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic app error %ui \"%s\"", + err, reason); + qc->error = err; qc->error_reason = reason; qc->error_app = 1; qc->error_ftype = 0; +} + + +ngx_uint_t +ngx_quic_get_error(ngx_connection_t *c) +{ + ngx_quic_connection_t *qc; + + qc = ngx_quic_get_connection(c); - ngx_post_event(&qc->close, &ngx_posted_events); + return qc->error; } void -ngx_quic_shutdown_connection(ngx_connection_t *c, ngx_uint_t err, - const char *reason) +ngx_quic_reject_streams(ngx_connection_t *c) { ngx_quic_connection_t *qc; qc = ngx_quic_get_connection(c); qc->shutdown = 1; - qc->shutdown_code = err; - qc->shutdown_reason = reason; - - ngx_quic_shutdown_quic(c); } static void ngx_quic_close_handler(ngx_event_t *ev) { - ngx_connection_t *c; - - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "quic close handler"); + ngx_connection_t *c; + ngx_quic_connection_t *qc; c = ev->data; - ngx_quic_close_connection(c, NGX_OK); + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic close handler"); + + if (ev->timedout) { + ev->timedout = 0; + ngx_quic_set_error(c, NGX_QUIC_ERR_CLOSE, ""); + + qc = ngx_quic_get_connection(c); + if (qc->lingering) { + c->ssl->no_wait_shutdown = 1; + } + } + + ngx_quic_end_handler(c); } -static ngx_int_t -ngx_quic_handle_datagram(ngx_connection_t *c, ngx_buf_t *b, - ngx_quic_conf_t *conf) +ngx_int_t +ngx_quic_handle_datagram(ngx_connection_t *c, ngx_buf_t *b) { size_t size; u_char *p, *start; @@ -686,9 +821,11 @@ ngx_quic_handle_datagram(ngx_connection_t *c, ngx_buf_t *b, path = NULL; size = b->last - b->pos; - p = start = b->pos; + qc = ngx_quic_get_connection(c); + qc->received += size; + while (p < b->last) { ngx_memzero(&pkt, sizeof(ngx_quic_header_t)); @@ -701,7 +838,7 @@ ngx_quic_handle_datagram(ngx_connection_t *c, ngx_buf_t *b, pkt.flags = p[0]; pkt.raw->pos++; - rc = ngx_quic_handle_packet(c, conf, &pkt); + rc = ngx_quic_handle_packet(c, &pkt); #if (NGX_DEBUG) if (pkt.parsed) { @@ -755,32 +892,30 @@ ngx_quic_handle_datagram(ngx_connection_t *c, ngx_buf_t *b, return NGX_DONE; } - qc = ngx_quic_get_connection(c); - - if (qc) { - qc->received += size; - - if ((uint64_t) (c->sent + qc->received) / 8 > - (qc->streams.sent + qc->streams.recv_last) + 1048576) - { - ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic flood detected"); + if ((uint64_t) (c->sent + qc->received) / 8 > + (qc->streams.sent + qc->streams.recv_last) + 1048576) + { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic flood detected"); - qc->error = NGX_QUIC_ERR_NO_ERROR; - qc->error_reason = "QUIC flood detected"; - return NGX_ERROR; - } + qc->error = NGX_QUIC_ERR_INTERNAL_ERROR; + qc->error_reason = "QUIC flood detected"; + return NGX_ERROR; } + qc->send_timer_set = 0; + ngx_add_timer(&qc->close, qc->tp.max_idle_timeout); + return NGX_OK; } static ngx_int_t -ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf, - ngx_quic_header_t *pkt) +ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_header_t *pkt) { ngx_int_t rc; + ngx_quic_conf_t *conf; ngx_quic_socket_t *qsock; + ngx_quic_client_id_t *cid; ngx_quic_connection_t *qc; c->log->action = "parsing quic packet"; @@ -815,7 +950,7 @@ ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf, qc = ngx_quic_get_connection(c); - if (qc) { + if (qc->initialized) { if (rc == NGX_ABORT) { ngx_log_error(NGX_LOG_INFO, c->log, 0, @@ -845,14 +980,35 @@ ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf, } } - if (ngx_quic_check_csid(qc, pkt) != NGX_OK) { - return NGX_DECLINED; + if (qc->is_server) { + if (ngx_quic_check_csid(qc, pkt) != NGX_OK) { + return NGX_DECLINED; + } } - } rc = ngx_quic_handle_payload(c, pkt); + if (rc == NGX_OK + && !qc->is_server + && !qc->scid_set + && pkt->level == NGX_QUIC_ENCRYPTION_INITIAL) + { + /* RFC 9000, 7.2. Negotiating Connection IDs + * + * After processing the first Initial packet, each endpoint sets + * the Destination Connection ID field in subsequent packets it + * sends to the value of the Source Connection ID field that it + * received. + */ + + qc->scid_set = 1; + + cid = qc->path->cid; + ngx_memcpy(cid->id, pkt->scid.data, pkt->scid.len); + cid->len = pkt->scid.len; + } + if (rc == NGX_DECLINED && pkt->level == NGX_QUIC_ENCRYPTION_APPLICATION) { @@ -861,7 +1017,7 @@ ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf, "quic stateless reset packet detected"); qc->draining = 1; - ngx_post_event(&qc->close, &ngx_posted_events); + qc->error = NGX_QUIC_ERR_CLOSE; return NGX_OK; } @@ -870,7 +1026,9 @@ ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf, return rc; } - /* packet does not belong to a connection */ + /* connection has not been initialized yet */ + + conf = c->quic->connection->conf; if (rc == NGX_ABORT) { return ngx_quic_negotiate_version(c, pkt); @@ -944,7 +1102,7 @@ ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf, c->log->action = "creating quic connection"; - qc = ngx_quic_new_connection(c, conf, pkt); + qc = ngx_quic_new_connection(c, pkt); if (qc == NULL) { return NGX_ERROR; } @@ -963,9 +1121,6 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt) qc = ngx_quic_get_connection(c); - qc->error = 0; - qc->error_reason = NULL; - c->log->action = "decrypting packet"; if (!ngx_quic_keys_available(qc->keys, pkt->level, 0)) { @@ -1011,7 +1166,7 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt) } if (c->ssl == NULL) { - if (ngx_quic_init_connection(c) != NGX_OK) { + if (ngx_quic_init_connection(c, 0) != NGX_OK) { return NGX_ERROR; } } @@ -1201,7 +1356,7 @@ ngx_quic_handle_frames(ngx_connection_t *c, ngx_quic_header_t *pkt) chain.next = NULL; frame.data = &chain; - len = ngx_quic_parse_frame(pkt, p, end, &frame); + len = ngx_quic_parse_frame(pkt, p, end, &frame, qc->is_server); if (len < 0) { qc->error = pkt->error; @@ -1263,6 +1418,14 @@ ngx_quic_handle_frames(ngx_connection_t *c, ngx_quic_header_t *pkt) case NGX_QUIC_FT_PING: break; + case NGX_QUIC_FT_NEW_TOKEN: + + if (ngx_quic_handle_new_token_frame(c, &frame) != NGX_OK) { + return NGX_ERROR; + } + + break; + case NGX_QUIC_FT_STREAM: if (ngx_quic_handle_stream_frame(c, pkt, &frame) != NGX_OK) { @@ -1400,6 +1563,14 @@ ngx_quic_handle_frames(ngx_connection_t *c, ngx_quic_header_t *pkt) break; + case NGX_QUIC_FT_HANDSHAKE_DONE: + + if (ngx_quic_handle_handshake_done_frame(c) != NGX_OK) { + return NGX_ERROR; + } + + break; + default: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic missing frame handler"); @@ -1417,7 +1588,7 @@ ngx_quic_handle_frames(ngx_connection_t *c, ngx_quic_header_t *pkt) if (do_close) { qc->draining = 1; - ngx_post_event(&qc->close, &ngx_posted_events); + qc->error = NGX_QUIC_ERR_CLOSE; } if (pkt->path != qc->path && nonprobing) { @@ -1447,26 +1618,13 @@ ngx_quic_push_handler(ngx_event_t *ev) { ngx_connection_t *c; - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "quic push handler"); - c = ev->data; + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic push handler"); + if (ngx_quic_output(c) != NGX_OK) { - ngx_quic_close_connection(c, NGX_ERROR); - return; + ngx_quic_set_error(c, NGX_QUIC_ERR_INTERNAL_ERROR, "output error"); } - ngx_quic_connstate_dbg(c); -} - - -void -ngx_quic_shutdown_quic(ngx_connection_t *c) -{ - ngx_quic_connection_t *qc; - - if (c->reusable) { - qc = ngx_quic_get_connection(c); - ngx_quic_finalize_connection(c, qc->shutdown_code, qc->shutdown_reason); - } + ngx_quic_end_handler(c); } diff --git a/src/event/quic/ngx_event_quic.h b/src/event/quic/ngx_event_quic.h index 4f899ec0a..ed2f55c76 100644 --- a/src/event/quic/ngx_event_quic.h +++ b/src/event/quic/ngx_event_quic.h @@ -44,6 +44,7 @@ #define NGX_QUIC_STREAM_UNIDIRECTIONAL 0x02 +typedef struct ngx_quic_connection_s ngx_quic_connection_t; typedef ngx_int_t (*ngx_quic_init_pt)(ngx_connection_t *c); typedef void (*ngx_quic_shutdown_pt)(ngx_connection_t *c); @@ -77,13 +78,12 @@ typedef struct { } ngx_quic_buffer_t; -typedef struct { +struct ngx_quic_conf_s { ngx_ssl_t *ssl; ngx_flag_t retry; ngx_flag_t gso_enabled; ngx_flag_t disable_active_migration; - ngx_msec_t handshake_timeout; ngx_msec_t idle_timeout; ngx_str_t host_key; size_t stream_buffer_size; @@ -94,15 +94,12 @@ typedef struct { ngx_int_t stream_reject_code_uni; ngx_int_t stream_reject_code_bidi; - ngx_quic_init_pt init; - ngx_quic_shutdown_pt shutdown; - u_char av_token_key[NGX_QUIC_AV_KEY_LEN]; u_char sr_token_key[NGX_QUIC_SR_KEY_LEN]; -} ngx_quic_conf_t; +}; -struct ngx_quic_stream_s { +typedef struct { ngx_rbtree_node_t node; ngx_queue_t queue; ngx_connection_t *parent; @@ -122,21 +119,31 @@ struct ngx_quic_stream_s { ngx_quic_buffer_t recv; ngx_quic_stream_send_state_e send_state; ngx_quic_stream_recv_state_e recv_state; - unsigned cancelable:1; unsigned fin_acked:1; +} ngx_quic_stream_t; + + +struct ngx_quic_s { + ngx_quic_connection_t *connection; + ngx_quic_stream_t *stream; }; void ngx_quic_recvmsg(ngx_event_t *ev); -void ngx_quic_run(ngx_connection_t *c, ngx_quic_conf_t *conf); +ngx_int_t ngx_quic_create_connection(ngx_quic_conf_t *conf, ngx_connection_t *c, + ngx_uint_t flags); +ngx_int_t ngx_quic_handshake(ngx_connection_t *c); +ngx_connection_t *ngx_quic_accept_stream(ngx_connection_t *c); +ngx_int_t ngx_quic_has_streams(ngx_connection_t *c, ngx_uint_t local, + ngx_uint_t bidi); ngx_connection_t *ngx_quic_open_stream(ngx_connection_t *c, ngx_uint_t bidi); -void ngx_quic_finalize_connection(ngx_connection_t *c, ngx_uint_t err, - const char *reason); -void ngx_quic_shutdown_connection(ngx_connection_t *c, ngx_uint_t err, +void ngx_quic_set_app_error(ngx_connection_t *c, ngx_uint_t err, const char *reason); +ngx_uint_t ngx_quic_get_error(ngx_connection_t *c); +ngx_int_t ngx_quic_shutdown(ngx_connection_t *c); +void ngx_quic_reject_streams(ngx_connection_t *c); ngx_int_t ngx_quic_reset_stream(ngx_connection_t *c, ngx_uint_t err); ngx_int_t ngx_quic_shutdown_stream(ngx_connection_t *c, int how); -void ngx_quic_cancelable_stream(ngx_connection_t *c); ngx_int_t ngx_quic_get_packet_dcid(ngx_log_t *log, u_char *data, size_t len, ngx_str_t *dcid); ngx_int_t ngx_quic_derive_key(ngx_log_t *log, const char *label, diff --git a/src/event/quic/ngx_event_quic_ack.c b/src/event/quic/ngx_event_quic_ack.c index abd3f7ade..b72acafa3 100644 --- a/src/event/quic/ngx_event_quic_ack.c +++ b/src/event/quic/ngx_event_quic_ack.c @@ -810,8 +810,8 @@ ngx_quic_resend_frames(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx) case NGX_QUIC_FT_MAX_STREAMS: case NGX_QUIC_FT_MAX_STREAMS2: f->u.max_streams.limit = f->u.max_streams.bidi - ? qc->streams.client_max_streams_bidi - : qc->streams.client_max_streams_uni; + ? qc->streams.remote_max_streams_bidi + : qc->streams.remote_max_streams_uni; ngx_quic_queue_frame(qc, f); break; @@ -1091,11 +1091,10 @@ void ngx_quic_lost_handler(ngx_event_t *ev) c = ev->data; if (ngx_quic_detect_lost(c, NULL) != NGX_OK) { - ngx_quic_close_connection(c, NGX_ERROR); - return; + ngx_quic_set_error(c, NGX_QUIC_ERR_INTERNAL_ERROR, "lost detect error"); } - ngx_quic_connstate_dbg(c); + ngx_quic_end_handler(c); } @@ -1111,12 +1110,12 @@ ngx_quic_pto_handler(ngx_event_t *ev) ngx_quic_send_ctx_t *ctx; ngx_quic_connection_t *qc; - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "quic pto timer"); - c = ev->data; qc = ngx_quic_get_connection(c); now = ngx_current_msec; + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic pto timer"); + for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) { ctx = &qc->send_ctx[i]; @@ -1148,7 +1147,9 @@ ngx_quic_pto_handler(ngx_event_t *ev) f = ngx_quic_alloc_frame(c); if (f == NULL) { - goto failed; + ngx_quic_set_error(c, NGX_QUIC_ERR_INTERNAL_ERROR, + "memory error"); + goto done; } f->level = ctx->level; @@ -1156,7 +1157,9 @@ ngx_quic_pto_handler(ngx_event_t *ev) f->ignore_congestion = 1; if (ngx_quic_frame_sendto(c, f, 0, qc->path) == NGX_ERROR) { - goto failed; + ngx_quic_set_error(c, NGX_QUIC_ERR_INTERNAL_ERROR, + "send error"); + goto done; } } } @@ -1165,14 +1168,9 @@ ngx_quic_pto_handler(ngx_event_t *ev) ngx_quic_set_lost_timer(c); - ngx_quic_connstate_dbg(c); - - return; - -failed: +done: - ngx_quic_close_connection(c, NGX_ERROR); - return; + ngx_quic_end_handler(c); } diff --git a/src/event/quic/ngx_event_quic_connection.h b/src/event/quic/ngx_event_quic_connection.h index 33922cf80..879052bf4 100644 --- a/src/event/quic/ngx_event_quic_connection.h +++ b/src/event/quic/ngx_event_quic_connection.h @@ -26,7 +26,6 @@ #define NGX_QUIC_SEND_CTX_LAST (NGX_QUIC_ENCRYPTION_LAST - 1) -typedef struct ngx_quic_connection_s ngx_quic_connection_t; typedef struct ngx_quic_server_id_s ngx_quic_server_id_t; typedef struct ngx_quic_client_id_s ngx_quic_client_id_t; typedef struct ngx_quic_send_ctx_s ngx_quic_send_ctx_t; @@ -67,8 +66,7 @@ typedef struct ngx_quic_keys_s ngx_quic_keys_t; : (((level) == NGX_QUIC_ENCRYPTION_HANDSHAKE) ? &((qc)->send_ctx[1]) \ : &((qc)->send_ctx[2])) -#define ngx_quic_get_connection(c) \ - (((c)->udp) ? (((ngx_quic_socket_t *)((c)->udp))->quic) : NULL) +#define ngx_quic_get_connection(c) ((c)->quic->connection) #define ngx_quic_get_socket(c) ((ngx_quic_socket_t *)((c)->udp)) @@ -156,18 +154,15 @@ typedef struct { uint64_t send_offset; uint64_t send_max_data; - uint64_t server_max_streams_uni; - uint64_t server_max_streams_bidi; - uint64_t server_streams_uni; - uint64_t server_streams_bidi; + uint64_t local_max_streams_uni; + uint64_t local_max_streams_bidi; + uint64_t local_streams_uni; + uint64_t local_streams_bidi; - uint64_t client_max_streams_uni; - uint64_t client_max_streams_bidi; - uint64_t client_streams_uni; - uint64_t client_streams_bidi; - - ngx_uint_t initialized; - /* unsigned initialized:1; */ + uint64_t remote_max_streams_uni; + uint64_t remote_max_streams_bidi; + uint64_t remote_streams_uni; + uint64_t remote_streams_bidi; } ngx_quic_streams_t; @@ -290,17 +285,19 @@ struct ngx_quic_connection_s { ngx_uint_t error_ftype; const char *error_reason; - ngx_uint_t shutdown_code; - const char *shutdown_reason; - + unsigned initialized:1; + unsigned handshaked:1; + unsigned is_server:1; + unsigned scid_set:1; unsigned error_app:1; unsigned send_timer_set:1; + unsigned lingering:1; unsigned closing:1; unsigned shutdown:1; unsigned draining:1; unsigned key_phase:1; unsigned validated:1; - unsigned client_tp_done:1; + unsigned peer_tp_done:1; #if (NGX_QUIC_OPENSSL_API) unsigned read_level:2; @@ -309,11 +306,13 @@ struct ngx_quic_connection_s { }; +ngx_int_t ngx_quic_handle_datagram(ngx_connection_t *c, ngx_buf_t *b); ngx_int_t ngx_quic_apply_transport_params(ngx_connection_t *c, ngx_quic_tp_t *ctp); void ngx_quic_discard_ctx(ngx_connection_t *c, ngx_uint_t level); -void ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc); -void ngx_quic_shutdown_quic(ngx_connection_t *c); +void ngx_quic_end_handler(ngx_connection_t *c); +void ngx_quic_set_error(ngx_connection_t *c, ngx_uint_t err, + const char *reason); #if (NGX_DEBUG) void ngx_quic_connstate_dbg(ngx_connection_t *c); diff --git a/src/event/quic/ngx_event_quic_connid.c b/src/event/quic/ngx_event_quic_connid.c index 4e7b8dc22..340b736e7 100644 --- a/src/event/quic/ngx_event_quic_connid.c +++ b/src/event/quic/ngx_event_quic_connid.c @@ -51,6 +51,10 @@ ngx_quic_bpf_attach_id(ngx_connection_t *c, u_char *id) uint64_t cookie; socklen_t optlen; + if (c->listening == NULL) { + return NGX_OK; + } + fd = c->listening->fd; optlen = sizeof(cookie); diff --git a/src/event/quic/ngx_event_quic_migration.c b/src/event/quic/ngx_event_quic_migration.c index 42354ca66..568020488 100644 --- a/src/event/quic/ngx_event_quic_migration.c +++ b/src/event/quic/ngx_event_quic_migration.c @@ -313,12 +313,6 @@ ngx_quic_set_path(ngx_connection_t *c, ngx_quic_header_t *pkt) len = pkt->raw->last - pkt->raw->start; - if (c->udp->buffer == NULL) { - /* first ever packet in connection, path already exists */ - path = qc->path; - goto update; - } - probe = NULL; for (q = ngx_queue_head(&qc->paths); @@ -724,21 +718,25 @@ ngx_quic_path_handler(ngx_event_t *ev) switch (path->state) { case NGX_QUIC_PATH_VALIDATING: if (ngx_quic_expire_path_validation(c, path) != NGX_OK) { - goto failed; + ngx_quic_set_error(c, NGX_QUIC_ERR_NO_VIABLE_PATH, + "path error"); + goto done; } break; case NGX_QUIC_PATH_WAITING: if (ngx_quic_expire_path_mtu_delay(c, path) != NGX_OK) { - goto failed; + ngx_quic_set_error(c, NGX_QUIC_ERR_INTERNAL_ERROR, "mtu error"); + goto done; } break; case NGX_QUIC_PATH_MTUD: if (ngx_quic_expire_path_mtu_discovery(c, path) != NGX_OK) { - goto failed; + ngx_quic_set_error(c, NGX_QUIC_ERR_INTERNAL_ERROR, "mtu error"); + goto done; } break; @@ -750,11 +748,9 @@ ngx_quic_path_handler(ngx_event_t *ev) ngx_quic_set_path_timer(c); - return; - -failed: +done: - ngx_quic_close_connection(c, NGX_ERROR); + ngx_quic_end_handler(c); } diff --git a/src/event/quic/ngx_event_quic_openssl_compat.c b/src/event/quic/ngx_event_quic_openssl_compat.c index 58298dcb8..fb6345b4d 100644 --- a/src/event/quic/ngx_event_quic_openssl_compat.c +++ b/src/event/quic/ngx_event_quic_openssl_compat.c @@ -116,6 +116,8 @@ ngx_quic_compat_keylog_callback(const SSL *ssl, const char *line) return; } + qc = ngx_quic_get_connection(c); + p = (u_char *) line; for (start = p; *p && *p != ' '; p++); @@ -129,27 +131,27 @@ ngx_quic_compat_keylog_callback(const SSL *ssl, const char *line) && ngx_strncmp(start, NGX_QUIC_COMPAT_CLIENT_HANDSHAKE, n) == 0) { level = ssl_encryption_handshake; - write = 0; + write = !qc->is_server; } else if (n == sizeof(NGX_QUIC_COMPAT_SERVER_HANDSHAKE) - 1 && ngx_strncmp(start, NGX_QUIC_COMPAT_SERVER_HANDSHAKE, n) == 0) { level = ssl_encryption_handshake; - write = 1; + write = qc->is_server; } else if (n == sizeof(NGX_QUIC_COMPAT_CLIENT_APPLICATION) - 1 && ngx_strncmp(start, NGX_QUIC_COMPAT_CLIENT_APPLICATION, n) == 0) { level = ssl_encryption_application; - write = 0; + write = !qc->is_server; } else if (n == sizeof(NGX_QUIC_COMPAT_SERVER_APPLICATION) - 1 && ngx_strncmp(start, NGX_QUIC_COMPAT_SERVER_APPLICATION, n) == 0) { level = ssl_encryption_application; - write = 1; + write = qc->is_server; } else { return; @@ -201,7 +203,6 @@ ngx_quic_compat_keylog_callback(const SSL *ssl, const char *line) } } - qc = ngx_quic_get_connection(c); com = qc->compat; cipher = SSL_get_current_cipher(ssl); diff --git a/src/event/quic/ngx_event_quic_output.c b/src/event/quic/ngx_event_quic_output.c index 8c3350504..a1a51d036 100644 --- a/src/event/quic/ngx_event_quic_output.c +++ b/src/event/quic/ngx_event_quic_output.c @@ -101,7 +101,7 @@ ngx_quic_output(ngx_connection_t *c) if (!qc->send_timer_set) { qc->send_timer_set = 1; - ngx_add_timer(c->read, qc->tp.max_idle_timeout); + ngx_add_timer(&qc->close, qc->tp.max_idle_timeout); } ngx_quic_set_lost_timer(c); @@ -710,7 +710,24 @@ ngx_quic_init_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx, pkt->keys = qc->keys; + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic packet tx %s", ngx_quic_level_name(pkt->level)); + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic packet tx dcid len:%uz %*xs", + pkt->dcid.len, pkt->dcid.len, pkt->dcid.data); + + if (pkt->level != NGX_QUIC_ENCRYPTION_APPLICATION) { + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic packet tx scid len:%uz %*xs", + pkt->scid.len, pkt->scid.len, pkt->scid.data); + } + ngx_quic_set_packet_number(pkt, ctx); + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic packet tx number:%uL len:%d", + pkt->number, pkt->num_len); } @@ -952,7 +969,7 @@ ngx_quic_send_early_cc(ngx_connection_t *c, ngx_quic_header_t *inpkt, pkt.keys = &keys; - if (ngx_quic_keys_set_initial_secret(pkt.keys, &inpkt->dcid, c->log) + if (ngx_quic_keys_set_initial_secret(pkt.keys, &inpkt->dcid, 1, c->log) != NGX_OK) { return NGX_ERROR; @@ -1322,7 +1339,7 @@ ngx_quic_frame_sendto(ngx_connection_t *c, ngx_quic_frame_t *frame, if (!qc->send_timer_set) { qc->send_timer_set = 1; - ngx_add_timer(c->read, qc->tp.max_idle_timeout); + ngx_add_timer(&qc->close, qc->tp.max_idle_timeout); } ngx_quic_set_lost_timer(c); diff --git a/src/event/quic/ngx_event_quic_protection.c b/src/event/quic/ngx_event_quic_protection.c index 2f28737a2..8c2fd764c 100644 --- a/src/event/quic/ngx_event_quic_protection.c +++ b/src/event/quic/ngx_event_quic_protection.c @@ -119,7 +119,7 @@ ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers) ngx_int_t ngx_quic_keys_set_initial_secret(ngx_quic_keys_t *keys, ngx_str_t *secret, - ngx_log_t *log) + ngx_uint_t is_server, ngx_log_t *log) { size_t is_len; uint8_t is[SHA256_DIGEST_LENGTH]; @@ -136,8 +136,14 @@ ngx_quic_keys_set_initial_secret(ngx_quic_keys_t *keys, ngx_str_t *secret, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a }; - client = &keys->secrets[NGX_QUIC_ENCRYPTION_INITIAL].client; - server = &keys->secrets[NGX_QUIC_ENCRYPTION_INITIAL].server; + if (is_server) { + client = &keys->secrets[NGX_QUIC_ENCRYPTION_INITIAL].read; + server = &keys->secrets[NGX_QUIC_ENCRYPTION_INITIAL].write; + + } else { + client = &keys->secrets[NGX_QUIC_ENCRYPTION_INITIAL].write; + server = &keys->secrets[NGX_QUIC_ENCRYPTION_INITIAL].read; + } /* * RFC 9001, section 5. Packet Protection @@ -200,13 +206,13 @@ ngx_quic_keys_set_initial_secret(ngx_quic_keys_t *keys, ngx_str_t *secret, return NGX_ERROR; } - if (ngx_quic_crypto_init(ciphers.c, client, &client_key, 0, log) + if (ngx_quic_crypto_init(ciphers.c, client, &client_key, !is_server, log) == NGX_ERROR) { return NGX_ERROR; } - if (ngx_quic_crypto_init(ciphers.c, server, &server_key, 1, log) + if (ngx_quic_crypto_init(ciphers.c, server, &server_key, is_server, log) == NGX_ERROR) { goto failed; @@ -673,8 +679,8 @@ ngx_quic_keys_set_encryption_secret(ngx_log_t *log, ngx_uint_t is_write, ngx_quic_secret_t *peer_secret; ngx_quic_ciphers_t ciphers; - peer_secret = is_write ? &keys->secrets[level].server - : &keys->secrets[level].client; + peer_secret = is_write ? &keys->secrets[level].write + : &keys->secrets[level].read; keys->cipher = SSL_CIPHER_get_id(cipher); @@ -732,35 +738,35 @@ ngx_quic_keys_available(ngx_quic_keys_t *keys, ngx_uint_t level, ngx_uint_t is_write) { if (is_write == 0) { - return keys->secrets[level].client.ctx != NULL; + return keys->secrets[level].read.ctx != NULL; } - return keys->secrets[level].server.ctx != NULL; + return keys->secrets[level].write.ctx != NULL; } void ngx_quic_keys_discard(ngx_quic_keys_t *keys, ngx_uint_t level) { - ngx_quic_secret_t *client, *server; + ngx_quic_secret_t *read, *write; - client = &keys->secrets[level].client; - server = &keys->secrets[level].server; + read = &keys->secrets[level].read; + write = &keys->secrets[level].write; - ngx_quic_crypto_cleanup(client); - ngx_quic_crypto_cleanup(server); + ngx_quic_crypto_cleanup(read); + ngx_quic_crypto_cleanup(write); - ngx_quic_crypto_hp_cleanup(client); - ngx_quic_crypto_hp_cleanup(server); + ngx_quic_crypto_hp_cleanup(read); + ngx_quic_crypto_hp_cleanup(write); - if (client->secret.len) { - ngx_explicit_memzero(client->secret.data, client->secret.len); - client->secret.len = 0; + if (read->secret.len) { + ngx_explicit_memzero(read->secret.data, read->secret.len); + read->secret.len = 0; } - if (server->secret.len) { - ngx_explicit_memzero(server->secret.data, server->secret.len); - server->secret.len = 0; + if (write->secret.len) { + ngx_explicit_memzero(write->secret.data, write->secret.len); + write->secret.len = 0; } } @@ -773,8 +779,8 @@ ngx_quic_keys_switch(ngx_connection_t *c, ngx_quic_keys_t *keys) current = &keys->secrets[NGX_QUIC_ENCRYPTION_APPLICATION]; next = &keys->next_key; - ngx_quic_crypto_cleanup(¤t->client); - ngx_quic_crypto_cleanup(¤t->server); + ngx_quic_crypto_cleanup(¤t->read); + ngx_quic_crypto_cleanup(¤t->write); tmp = *current; *current = *next; @@ -787,7 +793,7 @@ ngx_quic_keys_update(ngx_event_t *ev) { ngx_int_t key_len; ngx_uint_t i; - ngx_quic_md_t client_key, server_key; + ngx_quic_md_t read_key, write_key; ngx_quic_hkdf_t seq[6]; ngx_quic_keys_t *keys; ngx_connection_t *c; @@ -809,69 +815,71 @@ ngx_quic_keys_update(ngx_event_t *ev) key_len = ngx_quic_ciphers(keys->cipher, &ciphers); if (key_len == NGX_ERROR) { - goto failed; + ngx_quic_set_error(c, NGX_QUIC_ERR_KEY_UPDATE_ERROR, "key error"); + goto done; } - client_key.len = key_len; - server_key.len = key_len; + read_key.len = key_len; + write_key.len = key_len; - next->client.secret.len = current->client.secret.len; - next->client.iv.len = NGX_QUIC_IV_LEN; - next->client.hp = current->client.hp; - next->client.hp_ctx = current->client.hp_ctx; + next->read.secret.len = current->read.secret.len; + next->read.iv.len = NGX_QUIC_IV_LEN; + next->read.hp = current->read.hp; + next->read.hp_ctx = current->read.hp_ctx; - next->server.secret.len = current->server.secret.len; - next->server.iv.len = NGX_QUIC_IV_LEN; - next->server.hp = current->server.hp; - next->server.hp_ctx = current->server.hp_ctx; + next->write.secret.len = current->write.secret.len; + next->write.iv.len = NGX_QUIC_IV_LEN; + next->write.hp = current->write.hp; + next->write.hp_ctx = current->write.hp_ctx; ngx_quic_hkdf_set(&seq[0], "tls13 quic ku", - &next->client.secret, ¤t->client.secret); + &next->read.secret, ¤t->read.secret); ngx_quic_hkdf_set(&seq[1], "tls13 quic key", - &client_key, &next->client.secret); + &read_key, &next->read.secret); ngx_quic_hkdf_set(&seq[2], "tls13 quic iv", - &next->client.iv, &next->client.secret); + &next->read.iv, &next->read.secret); ngx_quic_hkdf_set(&seq[3], "tls13 quic ku", - &next->server.secret, ¤t->server.secret); + &next->write.secret, ¤t->write.secret); ngx_quic_hkdf_set(&seq[4], "tls13 quic key", - &server_key, &next->server.secret); + &write_key, &next->write.secret); ngx_quic_hkdf_set(&seq[5], "tls13 quic iv", - &next->server.iv, &next->server.secret); + &next->write.iv, &next->write.secret); for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) { if (ngx_quic_hkdf_expand(&seq[i], ciphers.d, c->log) != NGX_OK) { - goto failed; + ngx_quic_set_error(c, NGX_QUIC_ERR_KEY_UPDATE_ERROR, "key error"); + goto done; } } - if (ngx_quic_crypto_init(ciphers.c, &next->client, &client_key, 0, c->log) + if (ngx_quic_crypto_init(ciphers.c, &next->read, &read_key, 0, c->log) == NGX_ERROR) { - goto failed; + ngx_quic_set_error(c, NGX_QUIC_ERR_KEY_UPDATE_ERROR, "key error"); + goto done; } - if (ngx_quic_crypto_init(ciphers.c, &next->server, &server_key, 1, c->log) + if (ngx_quic_crypto_init(ciphers.c, &next->write, &write_key, 1, c->log) == NGX_ERROR) { - goto failed; + ngx_quic_set_error(c, NGX_QUIC_ERR_KEY_UPDATE_ERROR, "key error"); + goto done; } - ngx_explicit_memzero(current->client.secret.data, - current->client.secret.len); - ngx_explicit_memzero(current->server.secret.data, - current->server.secret.len); + ngx_explicit_memzero(current->read.secret.data, + current->read.secret.len); + ngx_explicit_memzero(current->write.secret.data, + current->write.secret.len); - current->client.secret.len = 0; - current->server.secret.len = 0; + current->read.secret.len = 0; + current->write.secret.len = 0; - ngx_explicit_memzero(client_key.data, client_key.len); - ngx_explicit_memzero(server_key.data, server_key.len); + ngx_explicit_memzero(read_key.data, read_key.len); + ngx_explicit_memzero(write_key.data, write_key.len); - return; - -failed: +done: - ngx_quic_close_connection(c, NGX_ERROR); + ngx_quic_end_handler(c); } @@ -887,19 +895,19 @@ ngx_quic_keys_cleanup(ngx_quic_keys_t *keys) next = &keys->next_key; - ngx_quic_crypto_cleanup(&next->client); - ngx_quic_crypto_cleanup(&next->server); + ngx_quic_crypto_cleanup(&next->read); + ngx_quic_crypto_cleanup(&next->write); - if (next->client.secret.len) { - ngx_explicit_memzero(next->client.secret.data, - next->client.secret.len); - next->client.secret.len = 0; + if (next->read.secret.len) { + ngx_explicit_memzero(next->read.secret.data, + next->read.secret.len); + next->read.secret.len = 0; } - if (next->server.secret.len) { - ngx_explicit_memzero(next->server.secret.data, - next->server.secret.len); - next->server.secret.len = 0; + if (next->write.secret.len) { + ngx_explicit_memzero(next->write.secret.data, + next->write.secret.len); + next->write.secret.len = 0; } } @@ -924,7 +932,7 @@ ngx_quic_create_packet(ngx_quic_header_t *pkt, ngx_str_t *res) "quic ad len:%uz %xV", ad.len, &ad); #endif - secret = &pkt->keys->secrets[pkt->level].server; + secret = &pkt->keys->secrets[pkt->level].write; ngx_memcpy(nonce, secret->iv.data, secret->iv.len); ngx_quic_compute_nonce(nonce, sizeof(nonce), pkt->number); @@ -1137,7 +1145,7 @@ ngx_quic_decrypt(ngx_quic_header_t *pkt, uint64_t *largest_pn) ngx_quic_secret_t *secret; uint8_t nonce[NGX_QUIC_IV_LEN], mask[NGX_QUIC_HP_LEN]; - secret = &pkt->keys->secrets[pkt->level].client; + secret = &pkt->keys->secrets[pkt->level].read; p = pkt->raw->pos; len = pkt->data + pkt->len - p; @@ -1169,8 +1177,8 @@ ngx_quic_decrypt(ngx_quic_header_t *pkt, uint64_t *largest_pn) key_phase = (pkt->flags & NGX_QUIC_PKT_KPHASE) != 0; if (key_phase != pkt->key_phase) { - if (pkt->keys->next_key.client.ctx != NULL) { - secret = &pkt->keys->next_key.client; + if (pkt->keys->next_key.read.ctx != NULL) { + secret = &pkt->keys->next_key.read; pkt->key_update = 1; } else { diff --git a/src/event/quic/ngx_event_quic_protection.h b/src/event/quic/ngx_event_quic_protection.h index 7c5cf3153..f5f8cf1de 100644 --- a/src/event/quic/ngx_event_quic_protection.h +++ b/src/event/quic/ngx_event_quic_protection.h @@ -55,8 +55,8 @@ typedef struct { typedef struct { - ngx_quic_secret_t client; - ngx_quic_secret_t server; + ngx_quic_secret_t read; + ngx_quic_secret_t write; } ngx_quic_secrets_t; @@ -92,7 +92,7 @@ typedef struct { ngx_int_t ngx_quic_keys_set_initial_secret(ngx_quic_keys_t *keys, - ngx_str_t *secret, ngx_log_t *log); + ngx_str_t *secret, ngx_uint_t is_server, ngx_log_t *log); ngx_int_t ngx_quic_keys_set_encryption_secret(ngx_log_t *log, ngx_uint_t is_write, ngx_quic_keys_t *keys, ngx_uint_t level, const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len); diff --git a/src/event/quic/ngx_event_quic_socket.c b/src/event/quic/ngx_event_quic_socket.c index c2bc822a5..b08ba47e4 100644 --- a/src/event/quic/ngx_event_quic_socket.c +++ b/src/event/quic/ngx_event_quic_socket.c @@ -12,8 +12,10 @@ ngx_int_t ngx_quic_open_sockets(ngx_connection_t *c, ngx_quic_connection_t *qc, - ngx_quic_header_t *pkt) + ngx_str_t *scid, ngx_str_t *dcid) { + u_char *p; + ngx_str_t sid; ngx_quic_socket_t *qsock, *tmp; ngx_quic_client_id_t *cid; @@ -35,44 +37,45 @@ ngx_quic_open_sockets(ngx_connection_t *c, ngx_quic_connection_t *qc, ngx_queue_init(&qc->client_ids); ngx_queue_init(&qc->free_client_ids); - qc->tp.original_dcid.len = pkt->odcid.len; - qc->tp.original_dcid.data = ngx_pstrdup(c->pool, &pkt->odcid); - if (qc->tp.original_dcid.data == NULL) { - return NGX_ERROR; - } - - /* socket to use for further processing (id auto-generated) */ qsock = ngx_quic_create_socket(c, qc); if (qsock == NULL) { return NGX_ERROR; } - /* socket is listening at new server id */ if (ngx_quic_listen(c, qc, qsock) != NGX_OK) { return NGX_ERROR; } qsock->used = 1; - qc->tp.initial_scid.len = qsock->sid.len; - qc->tp.initial_scid.data = ngx_pnalloc(c->pool, qsock->sid.len); - if (qc->tp.initial_scid.data == NULL) { + ngx_memcpy(&qsock->sockaddr, c->sockaddr, c->socklen); + qsock->socklen = c->socklen; + + c->udp = &qsock->udp; + + sid.data = qsock->sid.id; + sid.len = qsock->sid.len; + + if (ngx_quic_new_sr_token(c, &sid, qc->conf->sr_token_key, qc->tp.sr_token) + != NGX_OK) + { goto failed; } - ngx_memcpy(qc->tp.initial_scid.data, qsock->sid.id, qsock->sid.len); - /* for all packets except first, this is set at udp layer */ - c->udp = &qsock->udp; + p = ngx_pnalloc(c->pool, qsock->sid.len); + if (p == NULL) { + goto failed; + } - /* ngx_quic_get_connection(c) macro is now usable */ + ngx_memcpy(p, qsock->sid.id, qsock->sid.len); + qc->tp.initial_scid.data = p; + qc->tp.initial_scid.len = qsock->sid.len; - /* we have a client identified by scid */ - cid = ngx_quic_create_client_id(c, &pkt->scid, 0, NULL); + cid = ngx_quic_create_client_id(c, scid, 0, NULL); if (cid == NULL) { goto failed; } - /* path of the first packet is our initial active path */ qc->path = ngx_quic_new_path(c, c->sockaddr, c->socklen, cid); if (qc->path == NULL) { goto failed; @@ -80,31 +83,32 @@ ngx_quic_open_sockets(ngx_connection_t *c, ngx_quic_connection_t *qc, qc->path->tag = NGX_QUIC_PATH_ACTIVE; - if (pkt->validated) { - qc->path->validated = 1; - } - ngx_quic_path_dbg(c, "set active", qc->path); - tmp = ngx_pcalloc(c->pool, sizeof(ngx_quic_socket_t)); - if (tmp == NULL) { - goto failed; - } + if (dcid) { + tmp = ngx_pcalloc(c->pool, sizeof(ngx_quic_socket_t)); + if (tmp == NULL) { + goto failed; + } - tmp->sid.seqnum = NGX_QUIC_UNSET_PN; /* temporary socket */ + tmp->sid.seqnum = NGX_QUIC_UNSET_PN; /* temporary socket */ - ngx_memcpy(tmp->sid.id, pkt->dcid.data, pkt->dcid.len); - tmp->sid.len = pkt->dcid.len; + ngx_memcpy(tmp->sid.id, dcid->data, dcid->len); + tmp->sid.len = dcid->len; - if (ngx_quic_listen(c, qc, tmp) != NGX_OK) { - goto failed; + if (ngx_quic_listen(c, qc, tmp) != NGX_OK) { + goto failed; + } } return NGX_OK; failed: - ngx_rbtree_delete(&c->listening->rbtree, &qsock->udp.node); + if (c->listening) { + ngx_rbtree_delete(&c->listening->rbtree, &qsock->udp.node); + } + c->udp = NULL; return NGX_ERROR; @@ -155,7 +159,10 @@ ngx_quic_close_socket(ngx_connection_t *c, ngx_quic_socket_t *qsock) ngx_queue_remove(&qsock->queue); ngx_queue_insert_head(&qc->free_sockets, &qsock->queue); - ngx_rbtree_delete(&c->listening->rbtree, &qsock->udp.node); + if (c->listening) { + ngx_rbtree_delete(&c->listening->rbtree, &qsock->udp.node); + } + qc->nsockets--; ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, @@ -179,17 +186,20 @@ ngx_quic_listen(ngx_connection_t *c, ngx_quic_connection_t *qc, qsock->udp.connection = c; qsock->udp.node.key = ngx_crc32_long(id.data, id.len); qsock->udp.key = id; + qsock->quic = qc; - ngx_rbtree_insert(&c->listening->rbtree, &qsock->udp.node); + if (c->listening) { + ngx_rbtree_insert(&c->listening->rbtree, &qsock->udp.node); + } ngx_queue_insert_tail(&qc->sockets, &qsock->queue); qc->nsockets++; - qsock->quic = qc; - ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic socket seq:%L listening at sid:%xV nsock:%ui", - (int64_t) sid->seqnum, &id, qc->nsockets); + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic socket seq:%L listening at sid:%*xs nsock:%ui", + (int64_t) qsock->sid.seqnum, qsock->sid.len, qsock->sid.id, + qc->nsockets); return NGX_OK; } diff --git a/src/event/quic/ngx_event_quic_socket.h b/src/event/quic/ngx_event_quic_socket.h index 68ecc063d..1afd170aa 100644 --- a/src/event/quic/ngx_event_quic_socket.h +++ b/src/event/quic/ngx_event_quic_socket.h @@ -13,7 +13,7 @@ ngx_int_t ngx_quic_open_sockets(ngx_connection_t *c, - ngx_quic_connection_t *qc, ngx_quic_header_t *pkt); + ngx_quic_connection_t *qc, ngx_str_t *scid, ngx_str_t *dcid); void ngx_quic_close_sockets(ngx_connection_t *c); ngx_quic_socket_t *ngx_quic_create_socket(ngx_connection_t *c, diff --git a/src/event/quic/ngx_event_quic_ssl.c b/src/event/quic/ngx_event_quic_ssl.c index 18992ae1b..880a90637 100644 --- a/src/event/quic/ngx_event_quic_ssl.c +++ b/src/event/quic/ngx_event_quic_ssl.c @@ -59,7 +59,6 @@ static int ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, #endif -static ngx_int_t ngx_quic_handshake(ngx_connection_t *c); static ngx_int_t ngx_quic_crypto_provide(ngx_connection_t *c, ngx_uint_t level); @@ -85,18 +84,20 @@ ngx_quic_cbs_send(ngx_ssl_conn_t *ssl_conn, *consumed = 0; - SSL_get0_alpn_selected(ssl_conn, &alpn_data, &alpn_len); + if (qc->is_server) { + SSL_get0_alpn_selected(ssl_conn, &alpn_data, &alpn_len); - if (alpn_len == 0) { - qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_NO_APPLICATION_PROTOCOL); - qc->error_reason = "missing ALPN extension"; + if (alpn_len == 0) { + qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_NO_APPLICATION_PROTOCOL); + qc->error_reason = "missing ALPN extension"; - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "quic missing ALPN extension"); - return 1; + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "quic missing ALPN extension"); + return 1; + } } - if (!qc->client_tp_done) { + if (!qc->peer_tp_done) { /* RFC 9001, 8.2. QUIC Transport Parameters Extension */ qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_MISSING_EXTENSION); qc->error_reason = "missing transport parameters"; @@ -295,7 +296,7 @@ ngx_quic_cbs_got_transport_params(ngx_ssl_conn_t *ssl_conn, return 1; } - qc->client_tp_done = 1; + qc->peer_tp_done = 1; return 1; } @@ -491,68 +492,75 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn, ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic ngx_quic_add_handshake_data"); - if (!qc->client_tp_done) { - /* - * things to do once during handshake: check ALPN and transport - * parameters; we want to break handshake if something is wrong - * here; - */ + if (!qc->peer_tp_done) { - SSL_get0_alpn_selected(ssl_conn, &alpn_data, &alpn_len); + if (qc->is_server) { + /* + * things to do once during handshake: check ALPN and transport + * parameters; we want to break handshake if something is wrong + * here; + */ - if (alpn_len == 0) { - if (qc->error == 0) { - qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_NO_APPLICATION_PROTOCOL); - qc->error_reason = "missing ALPN extension"; + SSL_get0_alpn_selected(ssl_conn, &alpn_data, &alpn_len); - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "quic missing ALPN extension"); - } + if (alpn_len == 0) { + if (qc->error == 0) { + qc->error = + NGX_QUIC_ERR_CRYPTO(SSL_AD_NO_APPLICATION_PROTOCOL); + qc->error_reason = "missing ALPN extension"; - return 1; + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "quic missing ALPN extension"); + } + + return 1; + } } - SSL_get_peer_quic_transport_params(ssl_conn, &client_params, - &client_params_len); + if (qc->received > 0) { + SSL_get_peer_quic_transport_params(ssl_conn, &client_params, + &client_params_len); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic SSL_get_peer_quic_transport_params():" + " params_len:%ui", client_params_len); - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic SSL_get_peer_quic_transport_params():" - " params_len:%ui", client_params_len); + if (client_params_len == 0) { + /* RFC 9001, 8.2. QUIC Transport Parameters Extension */ - if (client_params_len == 0) { - /* RFC 9001, 8.2. QUIC Transport Parameters Extension */ + if (qc->error == 0) { + qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_MISSING_EXTENSION); + qc->error_reason = "missing transport parameters"; - if (qc->error == 0) { - qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_MISSING_EXTENSION); - qc->error_reason = "missing transport parameters"; + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "missing transport parameters"); + } - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "missing transport parameters"); + return 1; } - return 1; - } + p = (u_char *) client_params; + end = p + client_params_len; - p = (u_char *) client_params; - end = p + client_params_len; + /* defaults for parameters not sent by client */ + ngx_memcpy(&ctp, &qc->ctp, sizeof(ngx_quic_tp_t)); - /* defaults for parameters not sent by client */ - ngx_memcpy(&ctp, &qc->ctp, sizeof(ngx_quic_tp_t)); + if (ngx_quic_parse_transport_params(p, end, &ctp, qc->is_server, + c->log) + != NGX_OK) + { + qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR; + qc->error_reason = "failed to process transport parameters"; - if (ngx_quic_parse_transport_params(p, end, &ctp, c->log) - != NGX_OK) - { - qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR; - qc->error_reason = "failed to process transport parameters"; + return 1; + } - return 1; - } + if (ngx_quic_apply_transport_params(c, &ctp) != NGX_OK) { + return 1; + } - if (ngx_quic_apply_transport_params(c, &ctp) != NGX_OK) { - return 1; + qc->peer_tp_done = 1; } - - qc->client_tp_done = 1; } ctx = ngx_quic_get_send_ctx(qc, level); @@ -681,14 +689,15 @@ ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt, return NGX_ERROR; } - return ngx_quic_handshake(c); + return ngx_quic_do_handshake(c); } -static ngx_int_t -ngx_quic_handshake(ngx_connection_t *c) +ngx_int_t +ngx_quic_do_handshake(ngx_connection_t *c) { int n, sslerr; + ngx_int_t rc; ngx_ssl_conn_t *ssl_conn; ngx_quic_frame_t *frame; ngx_quic_connection_t *qc; @@ -732,11 +741,9 @@ ngx_quic_handshake(ngx_connection_t *c) if (!SSL_is_init_finished(ssl_conn)) { if (ngx_quic_keys_available(qc->keys, NGX_QUIC_ENCRYPTION_EARLY_DATA, 0) - && qc->client_tp_done) + && qc->peer_tp_done) { - if (ngx_quic_init_streams(c) != NGX_OK) { - return NGX_ERROR; - } + goto done; } return NGX_OK; @@ -746,21 +753,37 @@ ngx_quic_handshake(ngx_connection_t *c) ngx_ssl_handshake_log(c); #endif - c->ssl->handshaked = 1; - - frame = ngx_quic_alloc_frame(c); - if (frame == NULL) { - return NGX_ERROR; + if (qc->handshaked) { + return NGX_OK; } - frame->level = NGX_QUIC_ENCRYPTION_APPLICATION; - frame->type = NGX_QUIC_FT_HANDSHAKE_DONE; - ngx_quic_queue_frame(qc, frame); + qc->handshaked = 1; - if (qc->conf->retry) { - if (ngx_quic_send_new_token(c, qc->path) != NGX_OK) { + if (qc->is_server) { + frame = ngx_quic_alloc_frame(c); + if (frame == NULL) { return NGX_ERROR; } + + frame->level = NGX_QUIC_ENCRYPTION_APPLICATION; + frame->type = NGX_QUIC_FT_HANDSHAKE_DONE; + ngx_quic_queue_frame(qc, frame); + + if (qc->conf->retry) { + if (ngx_quic_send_new_token(c, qc->path) != NGX_OK) { + return NGX_ERROR; + } + } + + /* + * RFC 9001, 4.9.2. Discarding Handshake Keys + * + * An endpoint MUST discard its Handshake keys + * when the TLS handshake is confirmed. + */ + ngx_quic_discard_ctx(c, NGX_QUIC_ENCRYPTION_HANDSHAKE); + + ngx_quic_discover_path_mtu(c, qc->path); } /* @@ -771,25 +794,25 @@ ngx_quic_handshake(ngx_connection_t *c) ngx_post_event(&qc->key_update, &ngx_posted_events); - /* - * RFC 9001, 4.9.2. Discarding Handshake Keys - * - * An endpoint MUST discard its Handshake keys - * when the TLS handshake is confirmed. - */ - ngx_quic_discard_ctx(c, NGX_QUIC_ENCRYPTION_HANDSHAKE); - - ngx_quic_discover_path_mtu(c, qc->path); - /* start accepting clients on negotiated number of server ids */ if (ngx_quic_create_sockets(c) != NGX_OK) { return NGX_ERROR; } - if (ngx_quic_init_streams(c) != NGX_OK) { +done: + + rc = ngx_ssl_ocsp_validate(c); + + if (rc == NGX_ERROR) { return NGX_ERROR; } + if (rc == NGX_AGAIN) { + return NGX_OK; + } + + c->ssl->handshaked = 1; + return NGX_OK; } @@ -849,7 +872,33 @@ ngx_quic_crypto_provide(ngx_connection_t *c, ngx_uint_t level) ngx_int_t -ngx_quic_init_connection(ngx_connection_t *c) +ngx_quic_handle_handshake_done_frame(ngx_connection_t *c) +{ + ngx_quic_connection_t *qc; + + qc = ngx_quic_get_connection(c); + + if (qc->is_server) { + return NGX_ERROR; + } + + /* + * RFC 9001, 4.1.2 Handshake Confirmed. + * + * At the client, the handshake is considered confirmed + * when a HANDSHAKE_DONE frame is received. + */ + + ngx_quic_discard_ctx(c, NGX_QUIC_ENCRYPTION_HANDSHAKE); + + ngx_quic_discover_path_mtu(c, qc->path); + + return NGX_OK; +} + + +ngx_int_t +ngx_quic_init_connection(ngx_connection_t *c, ngx_uint_t flags) { u_char *p; size_t clen; @@ -888,12 +937,10 @@ ngx_quic_init_connection(ngx_connection_t *c) qc = ngx_quic_get_connection(c); - if (ngx_ssl_create_connection(qc->conf->ssl, c, 0) != NGX_OK) { + if (ngx_ssl_create_connection(qc->conf->ssl, c, flags) != NGX_OK) { return NGX_ERROR; } - c->ssl->no_wait_shutdown = 1; - ssl_conn = c->ssl->connection; #if (NGX_QUIC_OPENSSL_API) @@ -947,7 +994,8 @@ ngx_quic_init_connection(ngx_connection_t *c) return NGX_ERROR; } - len = ngx_quic_create_transport_params(NULL, NULL, &qc->tp, &clen); + len = ngx_quic_create_transport_params(NULL, NULL, &qc->tp, &clen, + qc->is_server); /* always succeeds */ p = ngx_pnalloc(c->pool, len); @@ -955,7 +1003,8 @@ ngx_quic_init_connection(ngx_connection_t *c) return NGX_ERROR; } - len = ngx_quic_create_transport_params(p, p + len, &qc->tp, NULL); + len = ngx_quic_create_transport_params(p, p + len, &qc->tp, NULL, + qc->is_server); if (len < 0) { return NGX_ERROR; } diff --git a/src/event/quic/ngx_event_quic_ssl.h b/src/event/quic/ngx_event_quic_ssl.h index ee0aa07c9..09026f7ef 100644 --- a/src/event/quic/ngx_event_quic_ssl.h +++ b/src/event/quic/ngx_event_quic_ssl.h @@ -11,9 +11,11 @@ #include #include -ngx_int_t ngx_quic_init_connection(ngx_connection_t *c); +ngx_int_t ngx_quic_init_connection(ngx_connection_t *c, ngx_uint_t flags); +ngx_int_t ngx_quic_do_handshake(ngx_connection_t *c); ngx_int_t ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt, ngx_quic_frame_t *frame); +ngx_int_t ngx_quic_handle_handshake_done_frame(ngx_connection_t *c); #endif /* _NGX_EVENT_QUIC_SSL_H_INCLUDED_ */ diff --git a/src/event/quic/ngx_event_quic_streams.c b/src/event/quic/ngx_event_quic_streams.c index 18fffeabe..da3cdb07f 100644 --- a/src/event/quic/ngx_event_quic_streams.c +++ b/src/event/quic/ngx_event_quic_streams.c @@ -13,15 +13,13 @@ #define NGX_QUIC_STREAM_GONE (void *) -1 +static ngx_uint_t ngx_quic_is_stream_local(ngx_connection_t *c, uint64_t id); static ngx_int_t ngx_quic_do_reset_stream(ngx_quic_stream_t *qs, ngx_uint_t err); static ngx_int_t ngx_quic_shutdown_stream_send(ngx_connection_t *c); static ngx_int_t ngx_quic_shutdown_stream_recv(ngx_connection_t *c); static ngx_quic_stream_t *ngx_quic_get_stream(ngx_connection_t *c, uint64_t id); static ngx_int_t ngx_quic_reject_stream(ngx_connection_t *c, uint64_t id); -static void ngx_quic_init_stream_handler(ngx_event_t *ev); -static void ngx_quic_init_streams_handler(ngx_connection_t *c); -static ngx_int_t ngx_quic_do_init_streams(ngx_connection_t *c); static ngx_quic_stream_t *ngx_quic_create_stream(ngx_connection_t *c, uint64_t id); static void ngx_quic_empty_handler(ngx_event_t *ev); @@ -29,12 +27,13 @@ static ssize_t ngx_quic_stream_recv(ngx_connection_t *c, u_char *buf, size_t size); static ssize_t ngx_quic_stream_send(ngx_connection_t *c, u_char *buf, size_t size); +static ssize_t ngx_quic_stream_recv_chain(ngx_connection_t *c, + ngx_chain_t *in, off_t limit); static ngx_chain_t *ngx_quic_stream_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit); static ngx_int_t ngx_quic_stream_flush(ngx_quic_stream_t *qs); static void ngx_quic_stream_cleanup_handler(void *data); static ngx_int_t ngx_quic_close_stream(ngx_quic_stream_t *qs); -static ngx_int_t ngx_quic_can_shutdown(ngx_connection_t *c); static ngx_int_t ngx_quic_control_flow(ngx_quic_stream_t *qs, uint64_t last); static ngx_int_t ngx_quic_update_flow(ngx_quic_stream_t *qs, uint64_t last); static ngx_int_t ngx_quic_update_max_stream_data(ngx_quic_stream_t *qs); @@ -42,6 +41,66 @@ static ngx_int_t ngx_quic_update_max_data(ngx_connection_t *c); static void ngx_quic_set_event(ngx_event_t *ev); +static ngx_uint_t +ngx_quic_is_stream_local(ngx_connection_t *c, uint64_t id) +{ + ngx_quic_connection_t *qc; + + qc = ngx_quic_get_connection(c); + + if (qc->is_server) { + return (id & NGX_QUIC_STREAM_SERVER_INITIATED) ? 1 : 0; + + } else { + return (id & NGX_QUIC_STREAM_SERVER_INITIATED) ? 0 : 1; + } +} + + +ngx_int_t +ngx_quic_has_streams(ngx_connection_t *c, ngx_uint_t local, ngx_uint_t bidi) +{ + uint64_t type; + ngx_rbtree_t *tree; + ngx_rbtree_node_t *node; + ngx_quic_stream_t *qs; + ngx_quic_connection_t *qc; + + qc = ngx_quic_get_connection(c); + + /* TODO optimize */ + + type = 0; + + if ((qc->is_server && local) || (!qc->is_server && !local)) { + type |= NGX_QUIC_STREAM_SERVER_INITIATED; + } + + if (!bidi) { + type |= NGX_QUIC_STREAM_UNIDIRECTIONAL; + } + + tree = &qc->streams.tree; + + if (tree->root == tree->sentinel) { + return NGX_DECLINED; + } + + node = ngx_rbtree_min(tree->root, tree->sentinel); + + while (node) { + qs = (ngx_quic_stream_t *) node; + node = ngx_rbtree_next(tree, node); + + if ((qs->id & 0x03) == type) { + return NGX_OK; + } + } + + return NGX_DECLINED; +} + + ngx_connection_t * ngx_quic_open_stream(ngx_connection_t *c, ngx_uint_t bidi) { @@ -50,7 +109,7 @@ ngx_quic_open_stream(ngx_connection_t *c, ngx_uint_t bidi) ngx_quic_stream_t *qs; ngx_quic_connection_t *qc; - pc = c->quic ? c->quic->parent : c; + pc = c->quic->stream ? c->quic->stream->parent : c; qc = ngx_quic_get_connection(pc); if (qc->closing) { @@ -58,47 +117,53 @@ ngx_quic_open_stream(ngx_connection_t *c, ngx_uint_t bidi) } if (bidi) { - if (qc->streams.server_streams_bidi - >= qc->streams.server_max_streams_bidi) + if (qc->streams.local_streams_bidi + >= qc->streams.local_max_streams_bidi) { ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic too many server bidi streams:%uL", - qc->streams.server_streams_bidi); + "quic too many local bidi streams:%uL", + qc->streams.local_streams_bidi); return NULL; } - id = (qc->streams.server_streams_bidi << 2) - | NGX_QUIC_STREAM_SERVER_INITIATED; + id = (qc->streams.local_streams_bidi << 2); + + if (qc->is_server) { + id |= NGX_QUIC_STREAM_SERVER_INITIATED; + } ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic creating server bidi stream" + "quic creating local bidi stream" " streams:%uL max:%uL id:0x%xL", - qc->streams.server_streams_bidi, - qc->streams.server_max_streams_bidi, id); + qc->streams.local_streams_bidi, + qc->streams.local_max_streams_bidi, id); - qc->streams.server_streams_bidi++; + qc->streams.local_streams_bidi++; } else { - if (qc->streams.server_streams_uni - >= qc->streams.server_max_streams_uni) + if (qc->streams.local_streams_uni + >= qc->streams.local_max_streams_uni) { ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic too many server uni streams:%uL", - qc->streams.server_streams_uni); + "quic too many local uni streams:%uL", + qc->streams.local_streams_uni); return NULL; } - id = (qc->streams.server_streams_uni << 2) - | NGX_QUIC_STREAM_SERVER_INITIATED + id = (qc->streams.local_streams_uni << 2) | NGX_QUIC_STREAM_UNIDIRECTIONAL; + if (qc->is_server) { + id |= NGX_QUIC_STREAM_SERVER_INITIATED; + } + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic creating server uni stream" + "quic creating local uni stream" " streams:%uL max:%uL id:0x%xL", - qc->streams.server_streams_uni, - qc->streams.server_max_streams_uni, id); + qc->streams.local_streams_uni, + qc->streams.local_max_streams_uni, id); - qc->streams.server_streams_uni++; + qc->streams.local_streams_uni++; } qs = ngx_quic_create_stream(pc, id); @@ -169,16 +234,51 @@ ngx_quic_find_stream(ngx_rbtree_t *rbtree, uint64_t id) return NULL; } +ngx_int_t +ngx_quic_linger_streams(ngx_connection_t *c) +{ + ngx_rbtree_t *tree; + ngx_rbtree_node_t *node; + ngx_quic_stream_t *qs; + ngx_quic_connection_t *qc; + + qc = ngx_quic_get_connection(c); + tree = &qc->streams.tree; + + if (tree->root == tree->sentinel) { + return NGX_OK; + } + + for (node = ngx_rbtree_min(tree->root, tree->sentinel); + node; + node = ngx_rbtree_next(tree, node)) + { + qs = (ngx_quic_stream_t *) node; + + if (qs->sent != qs->acked) { + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic stream id:0x%xL lingering unacked:%uL", + qs->id, qs->sent - qs->acked); + return NGX_AGAIN; + } + } + + return NGX_OK; +} + ngx_int_t -ngx_quic_close_streams(ngx_connection_t *c, ngx_quic_connection_t *qc) +ngx_quic_close_streams(ngx_connection_t *c) { - ngx_pool_t *pool; - ngx_queue_t *q, posted_events; - ngx_rbtree_t *tree; - ngx_connection_t *sc; - ngx_rbtree_node_t *node; - ngx_quic_stream_t *qs; + ngx_pool_t *pool; + ngx_queue_t *q, posted_events; + ngx_rbtree_t *tree; + ngx_connection_t *sc; + ngx_rbtree_node_t *node; + ngx_quic_stream_t *qs; + ngx_quic_connection_t *qc; + + qc = ngx_quic_get_connection(c); while (!ngx_queue_empty(&qc->streams.uninitialized)) { q = ngx_queue_head(&qc->streams.uninitialized); @@ -244,7 +344,7 @@ ngx_quic_close_streams(ngx_connection_t *c, ngx_quic_connection_t *qc) ngx_int_t ngx_quic_reset_stream(ngx_connection_t *c, ngx_uint_t err) { - return ngx_quic_do_reset_stream(c->quic, err); + return ngx_quic_do_reset_stream(c->quic->stream, err); } @@ -318,7 +418,7 @@ ngx_quic_shutdown_stream_send(ngx_connection_t *c) { ngx_quic_stream_t *qs; - qs = c->quic; + qs = c->quic->stream; if (qs->send_state != NGX_QUIC_STREAM_SEND_READY && qs->send_state != NGX_QUIC_STREAM_SEND_SEND) @@ -344,7 +444,7 @@ ngx_quic_shutdown_stream_recv(ngx_connection_t *c) ngx_quic_stream_t *qs; ngx_quic_connection_t *qc; - qs = c->quic; + qs = c->quic->stream; if (qs->recv_state != NGX_QUIC_STREAM_RECV_RECV && qs->recv_state != NGX_QUIC_STREAM_RECV_SIZE_KNOWN) @@ -382,7 +482,6 @@ static ngx_quic_stream_t * ngx_quic_get_stream(ngx_connection_t *c, uint64_t id) { uint64_t min_id; - ngx_event_t *rev; ngx_quic_stream_t *qs; ngx_quic_connection_t *qc; @@ -403,8 +502,8 @@ ngx_quic_get_stream(ngx_connection_t *c, uint64_t id) if (id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { - if (id & NGX_QUIC_STREAM_SERVER_INITIATED) { - if ((id >> 2) < qc->streams.server_streams_uni) { + if (ngx_quic_is_stream_local(c, id)) { + if ((id >> 2) < qc->streams.local_streams_uni) { return NGX_QUIC_STREAM_GONE; } @@ -412,23 +511,28 @@ ngx_quic_get_stream(ngx_connection_t *c, uint64_t id) return NULL; } - if ((id >> 2) < qc->streams.client_streams_uni) { + if ((id >> 2) < qc->streams.remote_streams_uni) { return NGX_QUIC_STREAM_GONE; } - if ((id >> 2) >= qc->streams.client_max_streams_uni) { + if ((id >> 2) >= qc->streams.remote_max_streams_uni) { qc->error = NGX_QUIC_ERR_STREAM_LIMIT_ERROR; return NULL; } - min_id = (qc->streams.client_streams_uni << 2) + min_id = (qc->streams.remote_streams_uni << 2) | NGX_QUIC_STREAM_UNIDIRECTIONAL; - qc->streams.client_streams_uni = (id >> 2) + 1; + + if (!qc->is_server) { + min_id |= NGX_QUIC_STREAM_SERVER_INITIATED; + } + + qc->streams.remote_streams_uni = (id >> 2) + 1; } else { - if (id & NGX_QUIC_STREAM_SERVER_INITIATED) { - if ((id >> 2) < qc->streams.server_streams_bidi) { + if (ngx_quic_is_stream_local(c, id)) { + if ((id >> 2) < qc->streams.local_streams_bidi) { return NGX_QUIC_STREAM_GONE; } @@ -436,17 +540,22 @@ ngx_quic_get_stream(ngx_connection_t *c, uint64_t id) return NULL; } - if ((id >> 2) < qc->streams.client_streams_bidi) { + if ((id >> 2) < qc->streams.remote_streams_bidi) { return NGX_QUIC_STREAM_GONE; } - if ((id >> 2) >= qc->streams.client_max_streams_bidi) { + if ((id >> 2) >= qc->streams.remote_max_streams_bidi) { qc->error = NGX_QUIC_ERR_STREAM_LIMIT_ERROR; return NULL; } - min_id = (qc->streams.client_streams_bidi << 2); - qc->streams.client_streams_bidi = (id >> 2) + 1; + min_id = (qc->streams.remote_streams_bidi << 2); + + if (!qc->is_server) { + min_id |= NGX_QUIC_STREAM_SERVER_INITIATED; + } + + qc->streams.remote_streams_bidi = (id >> 2) + 1; } /* @@ -474,24 +583,6 @@ ngx_quic_get_stream(ngx_connection_t *c, uint64_t id) } ngx_queue_insert_tail(&qc->streams.uninitialized, &qs->queue); - - rev = qs->connection->read; - rev->handler = ngx_quic_init_stream_handler; - - if (qc->streams.initialized) { - ngx_post_event(rev, &ngx_posted_events); - - if (qc->push.posted) { - /* - * The posted stream can produce output immediately. - * By postponing the push event, we coalesce the stream - * output with queued frames in one UDP datagram. - */ - - ngx_delete_posted_event(&qc->push); - ngx_post_event(&qc->push, &ngx_posted_events); - } - } } if (qs == NULL) { @@ -551,98 +642,39 @@ ngx_quic_reject_stream(ngx_connection_t *c, uint64_t id) } -static void -ngx_quic_init_stream_handler(ngx_event_t *ev) -{ - ngx_connection_t *c; - ngx_quic_stream_t *qs; - - c = ev->data; - qs = c->quic; - - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic init stream"); - - if ((qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0) { - c->write->active = 1; - c->write->ready = 1; - } - - c->read->active = 1; - - ngx_queue_remove(&qs->queue); - - c->listening->handler(c); -} - - -ngx_int_t -ngx_quic_init_streams(ngx_connection_t *c) +ngx_connection_t * +ngx_quic_accept_stream(ngx_connection_t *c) { - ngx_int_t rc; + ngx_queue_t *q; + ngx_connection_t *sc; + ngx_quic_stream_t *qs; ngx_quic_connection_t *qc; qc = ngx_quic_get_connection(c); - if (qc->streams.initialized) { - return NGX_OK; - } - - rc = ngx_ssl_ocsp_validate(c); - - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - - if (rc == NGX_AGAIN) { - c->ssl->handler = ngx_quic_init_streams_handler; - return NGX_OK; - } - - return ngx_quic_do_init_streams(c); -} - - -static void -ngx_quic_init_streams_handler(ngx_connection_t *c) -{ - if (ngx_quic_do_init_streams(c) != NGX_OK) { - ngx_quic_close_connection(c, NGX_ERROR); + if (ngx_queue_empty(&qc->streams.uninitialized)) { + return NULL; } -} - -static ngx_int_t -ngx_quic_do_init_streams(ngx_connection_t *c) -{ - ngx_queue_t *q; - ngx_quic_stream_t *qs; - ngx_quic_connection_t *qc; + q = ngx_queue_head(&qc->streams.uninitialized); + qs = ngx_queue_data(q, ngx_quic_stream_t, queue); - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic init streams"); + ngx_queue_remove(&qs->queue); - qc = ngx_quic_get_connection(c); + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic accept stream id:%uL", qs->id); - if (qc->conf->init) { - if (qc->conf->init(c) != NGX_OK) { - return NGX_ERROR; - } - } + sc = qs->connection; - for (q = ngx_queue_head(&qc->streams.uninitialized); - q != ngx_queue_sentinel(&qc->streams.uninitialized); - q = ngx_queue_next(q)) - { - qs = ngx_queue_data(q, ngx_quic_stream_t, queue); - ngx_post_event(qs->connection->read, &ngx_posted_events); + if ((qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0) { + sc->write->active = 1; + sc->write->ready = 1; } - qc->streams.initialized = 1; + sc->read->active = 1; + sc->read->ready = 1; - if (!qc->closing && qc->close.timer_set) { - ngx_del_timer(&qc->close); - } - - return NGX_OK; + return sc; } @@ -653,6 +685,7 @@ ngx_quic_create_stream(ngx_connection_t *c, uint64_t id) ngx_log_t *log; ngx_pool_t *pool; ngx_uint_t reusable; + ngx_quic_t *quic; ngx_queue_t *q; struct sockaddr *sockaddr; ngx_connection_t *sc; @@ -706,6 +739,16 @@ ngx_quic_create_stream(ngx_connection_t *c, uint64_t id) *log = *c->log; pool->log = log; + quic = ngx_palloc(pool, sizeof(ngx_quic_t)); + if (quic == NULL) { + ngx_destroy_pool(pool); + ngx_queue_insert_tail(&qc->streams.free, &qs->queue); + return NULL; + } + + quic->connection = qc; + quic->stream = qs; + sockaddr = ngx_palloc(pool, c->socklen); if (sockaddr == NULL) { ngx_destroy_pool(pool); @@ -742,9 +785,11 @@ ngx_quic_create_stream(ngx_connection_t *c, uint64_t id) return NULL; } + ngx_reusable_connection(c, reusable); + qs->connection = sc; - sc->quic = qs; + sc->quic = quic; sc->shared = 1; sc->type = SOCK_STREAM; sc->pool = pool; @@ -761,6 +806,7 @@ ngx_quic_create_stream(ngx_connection_t *c, uint64_t id) sc->recv = ngx_quic_stream_recv; sc->send = ngx_quic_stream_send; + sc->recv_chain = ngx_quic_stream_recv_chain; sc->send_chain = ngx_quic_stream_send_chain; sc->read->log = log; @@ -772,7 +818,7 @@ ngx_quic_create_stream(ngx_connection_t *c, uint64_t id) log->connection = sc->number; if (id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { - if (id & NGX_QUIC_STREAM_SERVER_INITIATED) { + if (ngx_quic_is_stream_local(c, id)) { qs->send_max_data = qc->ctp.initial_max_stream_data_uni; qs->recv_state = NGX_QUIC_STREAM_RECV_DATA_READ; qs->send_state = NGX_QUIC_STREAM_SEND_READY; @@ -784,7 +830,7 @@ ngx_quic_create_stream(ngx_connection_t *c, uint64_t id) } } else { - if (id & NGX_QUIC_STREAM_SERVER_INITIATED) { + if (ngx_quic_is_stream_local(c, id)) { qs->send_max_data = qc->ctp.initial_max_stream_data_bidi_remote; qs->recv_max_data = qc->tp.initial_max_stream_data_bidi_local; @@ -804,7 +850,6 @@ ngx_quic_create_stream(ngx_connection_t *c, uint64_t id) ngx_close_connection(sc); ngx_destroy_pool(pool); ngx_queue_insert_tail(&qc->streams.free, &qs->queue); - ngx_reusable_connection(c, reusable); return NULL; } @@ -817,31 +862,6 @@ ngx_quic_create_stream(ngx_connection_t *c, uint64_t id) } -void -ngx_quic_cancelable_stream(ngx_connection_t *c) -{ - ngx_connection_t *pc; - ngx_quic_stream_t *qs; - ngx_quic_connection_t *qc; - - qs = c->quic; - pc = qs->parent; - qc = ngx_quic_get_connection(pc); - - if (!qs->cancelable) { - qs->cancelable = 1; - - if (ngx_quic_can_shutdown(pc) == NGX_OK) { - ngx_reusable_connection(pc, 1); - - if (qc->shutdown) { - ngx_quic_shutdown_quic(pc); - } - } - } -} - - static void ngx_quic_empty_handler(ngx_event_t *ev) { @@ -858,7 +878,7 @@ ngx_quic_stream_recv(ngx_connection_t *c, u_char *buf, size_t size) ngx_connection_t *pc; ngx_quic_stream_t *qs; - qs = c->quic; + qs = c->quic->stream; pc = qs->parent; rev = c->read; @@ -948,6 +968,71 @@ ngx_quic_stream_send(ngx_connection_t *c, u_char *buf, size_t size) } +static ssize_t +ngx_quic_stream_recv_chain(ngx_connection_t *c, ngx_chain_t *cl, off_t limit) +{ + u_char *last; + ssize_t n, bytes, size; + ngx_buf_t *b; + + /* TODO optimize */ + + bytes = 0; + + b = cl->buf; + last = b->last; + + for ( ;; ) { + size = b->end - last; + + if (limit) { + if (bytes >= limit) { + return bytes; + } + + if (bytes + size > limit) { + size = (ssize_t) (limit - bytes); + } + } + + n = ngx_quic_stream_recv(c, last, size); + + if (n > 0) { + last += n; + bytes += n; + + if (!c->read->ready) { + return bytes; + } + + if (last == b->end) { + cl = cl->next; + + if (cl == NULL) { + return bytes; + } + + b = cl->buf; + last = b->last; + } + + continue; + } + + if (bytes) { + + if (n == 0 || n == NGX_ERROR) { + c->read->ready = 1; + } + + return bytes; + } + + return n; + } +} + + static ngx_chain_t * ngx_quic_stream_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) { @@ -957,7 +1042,7 @@ ngx_quic_stream_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) ngx_quic_stream_t *qs; ngx_quic_connection_t *qc; - qs = c->quic; + qs = c->quic->stream; pc = qs->parent; qc = ngx_quic_get_connection(pc); wev = c->write; @@ -1099,7 +1184,7 @@ ngx_quic_stream_cleanup_handler(void *data) ngx_quic_stream_t *qs; ngx_quic_connection_t *qc; - qs = c->quic; + qs = c->quic->stream; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, qs->parent->log, 0, "quic stream id:0x%xL cleanup", qs->id); @@ -1165,16 +1250,7 @@ ngx_quic_close_stream(ngx_quic_stream_t *qs) return NGX_OK; } - if (!pc->reusable && ngx_quic_can_shutdown(pc) == NGX_OK) { - ngx_reusable_connection(pc, 1); - } - - if (qc->shutdown) { - ngx_quic_shutdown_quic(pc); - return NGX_OK; - } - - if ((qs->id & NGX_QUIC_STREAM_SERVER_INITIATED) == 0) { + if (!ngx_quic_is_stream_local(pc, qs->id)) { frame = ngx_quic_alloc_frame(pc); if (frame == NULL) { return NGX_ERROR; @@ -1184,11 +1260,11 @@ ngx_quic_close_stream(ngx_quic_stream_t *qs) frame->type = NGX_QUIC_FT_MAX_STREAMS; if (qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { - frame->u.max_streams.limit = ++qc->streams.client_max_streams_uni; + frame->u.max_streams.limit = ++qc->streams.remote_max_streams_uni; frame->u.max_streams.bidi = 0; } else { - frame->u.max_streams.limit = ++qc->streams.client_max_streams_bidi; + frame->u.max_streams.limit = ++qc->streams.remote_max_streams_bidi; frame->u.max_streams.bidi = 1; } @@ -1199,35 +1275,6 @@ ngx_quic_close_stream(ngx_quic_stream_t *qs) } -static ngx_int_t -ngx_quic_can_shutdown(ngx_connection_t *c) -{ - ngx_rbtree_t *tree; - ngx_rbtree_node_t *node; - ngx_quic_stream_t *qs; - ngx_quic_connection_t *qc; - - qc = ngx_quic_get_connection(c); - - tree = &qc->streams.tree; - - if (tree->root != tree->sentinel) { - for (node = ngx_rbtree_min(tree->root, tree->sentinel); - node; - node = ngx_rbtree_next(tree, node)) - { - qs = (ngx_quic_stream_t *) node; - - if (!qs->cancelable) { - return NGX_DECLINED; - } - } - } - - return NGX_OK; -} - - ngx_int_t ngx_quic_handle_stream_frame(ngx_connection_t *c, ngx_quic_header_t *pkt, ngx_quic_frame_t *frame) @@ -1241,7 +1288,7 @@ ngx_quic_handle_stream_frame(ngx_connection_t *c, ngx_quic_header_t *pkt, f = &frame->u.stream; if ((f->stream_id & NGX_QUIC_STREAM_UNIDIRECTIONAL) - && (f->stream_id & NGX_QUIC_STREAM_SERVER_INITIATED)) + && ngx_quic_is_stream_local(c, f->stream_id)) { qc->error = NGX_QUIC_ERR_STREAM_STATE_ERROR; return NGX_ERROR; @@ -1386,7 +1433,7 @@ ngx_quic_handle_stream_data_blocked_frame(ngx_connection_t *c, qc = ngx_quic_get_connection(c); if ((f->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) - && (f->id & NGX_QUIC_STREAM_SERVER_INITIATED)) + && ngx_quic_is_stream_local(c, f->id)) { qc->error = NGX_QUIC_ERR_STREAM_STATE_ERROR; return NGX_ERROR; @@ -1416,7 +1463,7 @@ ngx_quic_handle_max_stream_data_frame(ngx_connection_t *c, qc = ngx_quic_get_connection(c); if ((f->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) - && (f->id & NGX_QUIC_STREAM_SERVER_INITIATED) == 0) + && !ngx_quic_is_stream_local(c, f->id)) { qc->error = NGX_QUIC_ERR_STREAM_STATE_ERROR; return NGX_ERROR; @@ -1459,7 +1506,7 @@ ngx_quic_handle_reset_stream_frame(ngx_connection_t *c, qc = ngx_quic_get_connection(c); if ((f->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) - && (f->id & NGX_QUIC_STREAM_SERVER_INITIATED)) + && ngx_quic_is_stream_local(c, f->id)) { qc->error = NGX_QUIC_ERR_STREAM_STATE_ERROR; return NGX_ERROR; @@ -1528,7 +1575,7 @@ ngx_quic_handle_stop_sending_frame(ngx_connection_t *c, qc = ngx_quic_get_connection(c); if ((f->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) - && (f->id & NGX_QUIC_STREAM_SERVER_INITIATED) == 0) + && !ngx_quic_is_stream_local(c, f->id)) { qc->error = NGX_QUIC_ERR_STREAM_STATE_ERROR; return NGX_ERROR; @@ -1567,16 +1614,16 @@ ngx_quic_handle_max_streams_frame(ngx_connection_t *c, qc = ngx_quic_get_connection(c); if (f->bidi) { - if (qc->streams.server_max_streams_bidi < f->limit) { - qc->streams.server_max_streams_bidi = f->limit; + if (qc->streams.local_max_streams_bidi < f->limit) { + qc->streams.local_max_streams_bidi = f->limit; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic max_streams_bidi:%uL", f->limit); } } else { - if (qc->streams.server_max_streams_uni < f->limit) { - qc->streams.server_max_streams_uni = f->limit; + if (qc->streams.local_max_streams_uni < f->limit) { + qc->streams.local_max_streams_uni = f->limit; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic max_streams_uni:%uL", f->limit); diff --git a/src/event/quic/ngx_event_quic_streams.h b/src/event/quic/ngx_event_quic_streams.h index fb6dbbd8f..4b837db10 100644 --- a/src/event/quic/ngx_event_quic_streams.h +++ b/src/event/quic/ngx_event_quic_streams.h @@ -33,12 +33,11 @@ ngx_int_t ngx_quic_handle_stop_sending_frame(ngx_connection_t *c, ngx_int_t ngx_quic_handle_max_streams_frame(ngx_connection_t *c, ngx_quic_header_t *pkt, ngx_quic_max_streams_frame_t *f); -ngx_int_t ngx_quic_init_streams(ngx_connection_t *c); void ngx_quic_rbtree_insert_stream(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); ngx_quic_stream_t *ngx_quic_find_stream(ngx_rbtree_t *rbtree, uint64_t id); -ngx_int_t ngx_quic_close_streams(ngx_connection_t *c, - ngx_quic_connection_t *qc); +ngx_int_t ngx_quic_linger_streams(ngx_connection_t *c); +ngx_int_t ngx_quic_close_streams(ngx_connection_t *c); #endif /* _NGX_EVENT_QUIC_STREAMS_H_INCLUDED_ */ diff --git a/src/event/quic/ngx_event_quic_tokens.c b/src/event/quic/ngx_event_quic_tokens.c index c1da0d472..7edbfae23 100644 --- a/src/event/quic/ngx_event_quic_tokens.c +++ b/src/event/quic/ngx_event_quic_tokens.c @@ -307,3 +307,25 @@ bad_token: return NGX_DECLINED; } + + +ngx_int_t +ngx_quic_handle_new_token_frame(ngx_connection_t *c, ngx_quic_frame_t *frame) +{ + ngx_quic_connection_t *qc; + + qc = ngx_quic_get_connection(c); + + if (qc->is_server) { + return NGX_ERROR; + } + +#ifdef NGX_QUIC_DEBUG_PACKETS + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic received token len:%uz %*xs", frame->u.token.length, + frame->data->buf->last - frame->data->buf->pos, + frame->data->buf->pos); +#endif + + return NGX_OK; +} diff --git a/src/event/quic/ngx_event_quic_tokens.h b/src/event/quic/ngx_event_quic_tokens.h index ee3fe5b9a..428767ef0 100644 --- a/src/event/quic/ngx_event_quic_tokens.h +++ b/src/event/quic/ngx_event_quic_tokens.h @@ -30,5 +30,7 @@ ngx_int_t ngx_quic_new_token(ngx_log_t *log, struct sockaddr *sockaddr, time_t expires, ngx_uint_t is_retry); ngx_int_t ngx_quic_validate_token(ngx_connection_t *c, u_char *key, ngx_quic_header_t *pkt); +ngx_int_t ngx_quic_handle_new_token_frame(ngx_connection_t *c, + ngx_quic_frame_t *frame); #endif /* _NGX_EVENT_QUIC_TOKENS_H_INCLUDED_ */ diff --git a/src/event/quic/ngx_event_quic_transport.c b/src/event/quic/ngx_event_quic_transport.c index ba6211c33..72b545615 100644 --- a/src/event/quic/ngx_event_quic_transport.c +++ b/src/event/quic/ngx_event_quic_transport.c @@ -97,7 +97,7 @@ static size_t ngx_quic_create_short_header(ngx_quic_header_t *pkt, u_char *out, u_char **pnp); static ngx_int_t ngx_quic_frame_allowed(ngx_quic_header_t *pkt, - ngx_uint_t frame_type); + ngx_uint_t frame_type, ngx_uint_t is_server); static size_t ngx_quic_create_ping(u_char *p); static size_t ngx_quic_create_ack(u_char *p, ngx_quic_ack_frame_t *ack, ngx_chain_t *ranges); @@ -742,7 +742,7 @@ ngx_quic_create_retry_itag(ngx_quic_header_t *pkt, u_char *out, ssize_t ngx_quic_parse_frame(ngx_quic_header_t *pkt, u_char *start, u_char *end, - ngx_quic_frame_t *f) + ngx_quic_frame_t *f, ngx_uint_t is_server) { u_char *p; uint64_t varint; @@ -770,7 +770,7 @@ ngx_quic_parse_frame(ngx_quic_header_t *pkt, u_char *start, u_char *end, f->type = varint; - if (ngx_quic_frame_allowed(pkt, f->type) != NGX_OK) { + if (ngx_quic_frame_allowed(pkt, f->type, is_server) != NGX_OK) { pkt->error = NGX_QUIC_ERR_PROTOCOL_VIOLATION; return NGX_ERROR; } @@ -952,6 +952,22 @@ ngx_quic_parse_frame(ngx_quic_header_t *pkt, u_char *start, u_char *end, break; + case NGX_QUIC_FT_NEW_TOKEN: + + p = ngx_quic_parse_int(p, end, &f->u.token.length); + if (p == NULL) { + goto error; + } + + p = ngx_quic_read_bytes(p, end, f->u.token.length, &b->pos); + if (p == NULL) { + goto error; + } + + b->last = p; + + break; + case NGX_QUIC_FT_STREAM: case NGX_QUIC_FT_STREAM1: case NGX_QUIC_FT_STREAM2: @@ -1133,6 +1149,10 @@ ngx_quic_parse_frame(ngx_quic_header_t *pkt, u_char *start, u_char *end, break; + case NGX_QUIC_FT_HANDSHAKE_DONE: + + break; + default: ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "quic unknown frame type 0x%xi", f->type); @@ -1158,7 +1178,8 @@ error: static ngx_int_t -ngx_quic_frame_allowed(ngx_quic_header_t *pkt, ngx_uint_t frame_type) +ngx_quic_frame_allowed(ngx_quic_header_t *pkt, ngx_uint_t frame_type, + ngx_uint_t is_server) { uint8_t ptype; @@ -1168,37 +1189,37 @@ ngx_quic_frame_allowed(ngx_quic_header_t *pkt, ngx_uint_t frame_type) * Frame permissions per packet: 4 bits: IH01 */ static uint8_t ngx_quic_frame_masks[] = { - /* PADDING */ 0xF, - /* PING */ 0xF, - /* ACK */ 0xD, - /* ACK_ECN */ 0xD, - /* RESET_STREAM */ 0x3, - /* STOP_SENDING */ 0x3, - /* CRYPTO */ 0xD, - /* NEW_TOKEN */ 0x0, /* only sent by server */ - /* STREAM */ 0x3, - /* STREAM1 */ 0x3, - /* STREAM2 */ 0x3, - /* STREAM3 */ 0x3, - /* STREAM4 */ 0x3, - /* STREAM5 */ 0x3, - /* STREAM6 */ 0x3, - /* STREAM7 */ 0x3, - /* MAX_DATA */ 0x3, - /* MAX_STREAM_DATA */ 0x3, - /* MAX_STREAMS */ 0x3, - /* MAX_STREAMS2 */ 0x3, - /* DATA_BLOCKED */ 0x3, - /* STREAM_DATA_BLOCKED */ 0x3, - /* STREAMS_BLOCKED */ 0x3, - /* STREAMS_BLOCKED2 */ 0x3, - /* NEW_CONNECTION_ID */ 0x3, - /* RETIRE_CONNECTION_ID */ 0x3, - /* PATH_CHALLENGE */ 0x3, - /* PATH_RESPONSE */ 0x1, - /* CONNECTION_CLOSE */ 0xF, - /* CONNECTION_CLOSE2 */ 0x3, - /* HANDSHAKE_DONE */ 0x0, /* only sent by server */ + /* PADDING */ 0xFF, + /* PING */ 0xFF, + /* ACK */ 0xDD, + /* ACK_ECN */ 0xDD, + /* RESET_STREAM */ 0x33, + /* STOP_SENDING */ 0x33, + /* CRYPTO */ 0xDD, + /* NEW_TOKEN */ 0x01, /* only sent by server */ + /* STREAM */ 0x33, + /* STREAM1 */ 0x33, + /* STREAM2 */ 0x33, + /* STREAM3 */ 0x33, + /* STREAM4 */ 0x33, + /* STREAM5 */ 0x33, + /* STREAM6 */ 0x33, + /* STREAM7 */ 0x33, + /* MAX_DATA */ 0x33, + /* MAX_STREAM_DATA */ 0x33, + /* MAX_STREAMS */ 0x33, + /* MAX_STREAMS2 */ 0x33, + /* DATA_BLOCKED */ 0x33, + /* STREAM_DATA_BLOCKED */ 0x33, + /* STREAMS_BLOCKED */ 0x33, + /* STREAMS_BLOCKED2 */ 0x33, + /* NEW_CONNECTION_ID */ 0x33, + /* RETIRE_CONNECTION_ID */ 0x33, + /* PATH_CHALLENGE */ 0x33, + /* PATH_RESPONSE */ 0x11, + /* CONNECTION_CLOSE */ 0xFF, + /* CONNECTION_CLOSE2 */ 0x33, + /* HANDSHAKE_DONE */ 0x01, /* only sent by server */ }; if (ngx_quic_long_pkt(pkt->flags)) { @@ -1217,6 +1238,10 @@ ngx_quic_frame_allowed(ngx_quic_header_t *pkt, ngx_uint_t frame_type) ptype = 1; /* application data */ } + if (is_server) { + ptype <<= 4; + } + if (ptype & ngx_quic_frame_masks[frame_type]) { return NGX_OK; } @@ -1723,7 +1748,7 @@ ngx_quic_parse_transport_param(u_char *p, u_char *end, uint16_t id, ngx_int_t ngx_quic_parse_transport_params(u_char *p, u_char *end, ngx_quic_tp_t *tp, - ngx_log_t *log) + ngx_uint_t is_server, ngx_log_t *log) { uint64_t id, len; ngx_int_t rc; @@ -1736,15 +1761,17 @@ ngx_quic_parse_transport_params(u_char *p, u_char *end, ngx_quic_tp_t *tp, return NGX_ERROR; } - switch (id) { - case NGX_QUIC_TP_ORIGINAL_DCID: - case NGX_QUIC_TP_PREFERRED_ADDRESS: - case NGX_QUIC_TP_RETRY_SCID: - case NGX_QUIC_TP_SR_TOKEN: - ngx_log_error(NGX_LOG_INFO, log, 0, - "quic client sent forbidden transport param" - " id:0x%xL", id); - return NGX_ERROR; + if (is_server) { + switch (id) { + case NGX_QUIC_TP_ORIGINAL_DCID: + case NGX_QUIC_TP_PREFERRED_ADDRESS: + case NGX_QUIC_TP_RETRY_SCID: + case NGX_QUIC_TP_SR_TOKEN: + ngx_log_error(NGX_LOG_INFO, log, 0, + "quic client sent forbidden transport param" + " id:0x%xL", id); + return NGX_ERROR; + } } p = ngx_quic_parse_int(p, end, &len); @@ -2028,7 +2055,7 @@ ngx_quic_init_transport_params(ngx_quic_tp_t *tp, ngx_quic_conf_t *qcf) ssize_t ngx_quic_create_transport_params(u_char *pos, u_char *end, ngx_quic_tp_t *tp, - size_t *clen) + size_t *clen, ngx_uint_t is_server) { u_char *p; size_t len; @@ -2099,16 +2126,19 @@ ngx_quic_create_transport_params(u_char *pos, u_char *end, ngx_quic_tp_t *tp, len += ngx_quic_tp_len(NGX_QUIC_TP_ACK_DELAY_EXPONENT, tp->ack_delay_exponent); - len += ngx_quic_tp_strlen(NGX_QUIC_TP_ORIGINAL_DCID, tp->original_dcid); len += ngx_quic_tp_strlen(NGX_QUIC_TP_INITIAL_SCID, tp->initial_scid); - if (tp->retry_scid.len) { - len += ngx_quic_tp_strlen(NGX_QUIC_TP_RETRY_SCID, tp->retry_scid); - } + if (is_server) { + len += ngx_quic_tp_strlen(NGX_QUIC_TP_ORIGINAL_DCID, tp->original_dcid); - len += ngx_quic_varint_len(NGX_QUIC_TP_SR_TOKEN); - len += ngx_quic_varint_len(NGX_QUIC_SR_TOKEN_LEN); - len += NGX_QUIC_SR_TOKEN_LEN; + if (tp->retry_scid.len) { + len += ngx_quic_tp_strlen(NGX_QUIC_TP_RETRY_SCID, tp->retry_scid); + } + + len += ngx_quic_varint_len(NGX_QUIC_TP_SR_TOKEN); + len += ngx_quic_varint_len(NGX_QUIC_SR_TOKEN_LEN); + len += NGX_QUIC_SR_TOKEN_LEN; + } if (pos == NULL) { return len; @@ -2154,16 +2184,19 @@ ngx_quic_create_transport_params(u_char *pos, u_char *end, ngx_quic_tp_t *tp, ngx_quic_tp_vint(NGX_QUIC_TP_ACK_DELAY_EXPONENT, tp->ack_delay_exponent); - ngx_quic_tp_str(NGX_QUIC_TP_ORIGINAL_DCID, tp->original_dcid); ngx_quic_tp_str(NGX_QUIC_TP_INITIAL_SCID, tp->initial_scid); - if (tp->retry_scid.len) { - ngx_quic_tp_str(NGX_QUIC_TP_RETRY_SCID, tp->retry_scid); - } + if (is_server) { + ngx_quic_tp_str(NGX_QUIC_TP_ORIGINAL_DCID, tp->original_dcid); + + if (tp->retry_scid.len) { + ngx_quic_tp_str(NGX_QUIC_TP_RETRY_SCID, tp->retry_scid); + } - ngx_quic_build_int(&p, NGX_QUIC_TP_SR_TOKEN); - ngx_quic_build_int(&p, NGX_QUIC_SR_TOKEN_LEN); - p = ngx_cpymem(p, tp->sr_token, NGX_QUIC_SR_TOKEN_LEN); + ngx_quic_build_int(&p, NGX_QUIC_TP_SR_TOKEN); + ngx_quic_build_int(&p, NGX_QUIC_SR_TOKEN_LEN); + p = ngx_cpymem(p, tp->sr_token, NGX_QUIC_SR_TOKEN_LEN); + } return p - pos; } diff --git a/src/event/quic/ngx_event_quic_transport.h b/src/event/quic/ngx_event_quic_transport.h index 656cb09fb..2319d6bdf 100644 --- a/src/event/quic/ngx_event_quic_transport.h +++ b/src/event/quic/ngx_event_quic_transport.h @@ -112,6 +112,9 @@ #define NGX_QUIC_ERR_CRYPTO(e) (NGX_QUIC_ERR_CRYPTO_ERROR + (e)) +/* The special code to close connection without any response */ +#define NGX_QUIC_ERR_CLOSE 0x10000000 + /* 22.3. QUIC Transport Parameters Registry */ #define NGX_QUIC_TP_ORIGINAL_DCID 0x00 @@ -379,7 +382,7 @@ size_t ngx_quic_create_retry_itag(ngx_quic_header_t *pkt, u_char *out, u_char **start); ssize_t ngx_quic_parse_frame(ngx_quic_header_t *pkt, u_char *start, u_char *end, - ngx_quic_frame_t *frame); + ngx_quic_frame_t *frame, ngx_uint_t is_server); ssize_t ngx_quic_create_frame(u_char *p, ngx_quic_frame_t *f); ssize_t ngx_quic_parse_ack_range(ngx_log_t *log, u_char *start, @@ -389,9 +392,9 @@ size_t ngx_quic_create_ack_range(u_char *p, uint64_t gap, uint64_t range); ngx_int_t ngx_quic_init_transport_params(ngx_quic_tp_t *tp, ngx_quic_conf_t *qcf); ngx_int_t ngx_quic_parse_transport_params(u_char *p, u_char *end, - ngx_quic_tp_t *tp, ngx_log_t *log); + ngx_quic_tp_t *tp, ngx_uint_t is_server, ngx_log_t *log); ssize_t ngx_quic_create_transport_params(u_char *p, u_char *end, - ngx_quic_tp_t *tp, size_t *clen); + ngx_quic_tp_t *tp, size_t *clen, ngx_uint_t is_server); void ngx_quic_dcid_encode_key(u_char *dcid, uint64_t key); diff --git a/src/event/quic/ngx_event_quic_udp.c b/src/event/quic/ngx_event_quic_udp.c index 15b54bc82..95933359c 100644 --- a/src/event/quic/ngx_event_quic_udp.c +++ b/src/event/quic/ngx_event_quic_udp.c @@ -174,6 +174,8 @@ ngx_quic_recvmsg(ngx_event_t *ev) } #endif + c->log->action = "handling quic input"; + ngx_memzero(&buf, sizeof(ngx_buf_t)); buf.pos = buffer; @@ -186,20 +188,12 @@ ngx_quic_recvmsg(ngx_event_t *ev) ngx_memcpy(&qsock->sockaddr, sockaddr, socklen); qsock->socklen = socklen; - c->udp->buffer = &buf; - - rev = c->read; - rev->ready = 1; - rev->active = 0; - - rev->handler(rev); - - if (c->udp) { - c->udp->buffer = NULL; + if (ngx_quic_handle_datagram(c, &buf) == NGX_ERROR) { + ngx_quic_set_error(c, NGX_QUIC_ERR_INTERNAL_ERROR, + "datagram handling error"); } - rev->ready = 0; - rev->active = 1; + ngx_quic_end_handler(c); goto next; } @@ -291,8 +285,6 @@ ngx_quic_recvmsg(ngx_event_t *ev) c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1); - c->start_time = ngx_current_msec; - #if (NGX_STAT_STUB) (void) ngx_atomic_fetch_add(ngx_stat_handled, 1); #endif diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c index 7897b3f4b..ea09fcf61 100644 --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -10,6 +10,10 @@ #include #include +#if (NGX_QUIC_OPENSSL_COMPAT) +#include +#endif + #define NGX_HTTP_PROXY_COOKIE_SECURE 0x0001 #define NGX_HTTP_PROXY_COOKIE_SECURE_ON 0x0002 @@ -200,6 +204,9 @@ static ngx_conf_enum_t ngx_http_proxy_http_version[] = { { ngx_string("1.1"), NGX_HTTP_VERSION_11 }, #if (NGX_HTTP_V2) { ngx_string("2"), NGX_HTTP_VERSION_20 }, +#endif +#if (NGX_HTTP_V3) + { ngx_string("3"), NGX_HTTP_VERSION_30 }, #endif { ngx_null_string, 0 } }; @@ -706,6 +713,24 @@ static ngx_command_t ngx_http_proxy_commands[] = { offsetof(ngx_http_proxy_loc_conf_t, ssl_conf_commands), &ngx_http_proxy_ssl_conf_command_post }, +#endif + +#if (NGX_QUIC) + + { ngx_string("proxy_quic_idle_timeout"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, quic_idle_timeout), + NULL }, + + { ngx_string("proxy_quic_stream_buffer_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, quic_stream_buffer_size), + NULL }, + #endif ngx_null_command @@ -886,6 +911,12 @@ ngx_http_proxy_handler(ngx_http_request_t *r) } #endif +#if (NGX_HTTP_V3) + if (plcf->http_version == NGX_HTTP_VERSION_30) { + return ngx_http_v3_proxy_handler(r); + } +#endif + if (ngx_http_upstream_create(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -3605,6 +3636,11 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf) conf->ssl_conf_commands = NGX_CONF_UNSET_PTR; #endif +#if (NGX_QUIC) + conf->quic_idle_timeout = NGX_CONF_UNSET_MSEC; + conf->quic_stream_buffer_size = NGX_CONF_UNSET_SIZE; +#endif + /* the hardcoded values */ conf->upstream.cyclic_temp_file = 0; conf->upstream.change_buffering = 1; @@ -3968,6 +4004,28 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_ERROR; } +#endif + +#if (NGX_QUIC) + + ngx_conf_merge_msec_value(conf->quic_idle_timeout, + prev->quic_idle_timeout, 60000); + ngx_conf_merge_size_value(conf->quic_stream_buffer_size, + prev->quic_stream_buffer_size, 65536); + + conf->upstream.quic = ngx_pcalloc(cf->pool, sizeof(ngx_quic_conf_t)); + if (conf->upstream.quic == NULL) { + return NGX_CONF_ERROR; + } + + conf->upstream.quic->max_concurrent_streams_bidi = 0; + conf->upstream.quic->max_concurrent_streams_uni = 3; + conf->upstream.quic->stream_close_code = NGX_HTTP_V3_ERR_NO_ERROR; + conf->upstream.quic->active_connection_id_limit = 2; + conf->upstream.quic->stream_buffer_size = conf->quic_stream_buffer_size; + conf->upstream.quic->idle_timeout = conf->quic_idle_timeout; + conf->upstream.quic->ssl = conf->upstream.ssl; + #endif ngx_conf_merge_ptr_value(conf->method, prev->method, NULL); @@ -5374,6 +5432,12 @@ ngx_http_proxy_set_ssl(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *plcf) return NGX_ERROR; } +#if (NGX_QUIC_OPENSSL_COMPAT) + if (ngx_quic_compat_init(cf, plcf->upstream.ssl->ctx) != NGX_OK) { + return NGX_ERROR; + } +#endif + return NGX_OK; } diff --git a/src/http/modules/ngx_http_proxy_module.h b/src/http/modules/ngx_http_proxy_module.h index a0f79bc13..77de83e0d 100644 --- a/src/http/modules/ngx_http_proxy_module.h +++ b/src/http/modules/ngx_http_proxy_module.h @@ -85,6 +85,11 @@ typedef struct { ngx_str_t ssl_crl; ngx_array_t *ssl_conf_commands; #endif + +#if (NGX_QUIC || NGX_COMPAT) + ngx_msec_t quic_idle_timeout; + size_t quic_stream_buffer_size; +#endif } ngx_http_proxy_loc_conf_t; @@ -120,6 +125,10 @@ ngx_int_t ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, ngx_int_t ngx_http_proxy_v2_handler(ngx_http_request_t *r); #endif +#if (NGX_HTTP_V3) +ngx_int_t ngx_http_v3_proxy_handler(ngx_http_request_t *r); +#endif + extern ngx_module_t ngx_http_proxy_module; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 41c1cda04..35117e30d 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -4002,7 +4002,7 @@ ngx_http_close_connection(ngx_connection_t *c) #endif #if (NGX_HTTP_V3) - if (c->quic) { + if (c->quic && c->quic->stream) { ngx_http_v3_reset_stream(c); } #endif diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index 2e4b2b48f..4023811f2 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -1658,6 +1658,16 @@ ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u) } } + c->sockaddr = ngx_palloc(c->pool, u->peer.socklen); + if (c->sockaddr == NULL) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + ngx_memcpy(c->sockaddr, u->peer.sockaddr, u->peer.socklen); + c->socklen = u->peer.socklen; + c->log = r->connection->log; c->pool->log = c->log; c->read->log = c->log; @@ -1742,10 +1752,18 @@ ngx_http_upstream_ssl_init_connection(ngx_http_request_t *r, return; } - if (ngx_ssl_create_connection(u->conf->ssl, c, - NGX_SSL_BUFFER|NGX_SSL_CLIENT) - != NGX_OK) +#if (NGX_QUIC) + if (u->quic) { + rc = ngx_quic_create_connection(u->conf->quic, c, NGX_SSL_CLIENT); + + } else +#endif { + rc = ngx_ssl_create_connection(u->conf->ssl, c, + NGX_SSL_BUFFER|NGX_SSL_CLIENT); + } + + if (rc != NGX_OK) { ngx_http_upstream_finalize_request(r, u, NGX_HTTP_INTERNAL_SERVER_ERROR); return; @@ -1808,9 +1826,19 @@ ngx_http_upstream_ssl_init_connection(ngx_http_request_t *r, } } - r->connection->log->action = "SSL handshaking to upstream"; +#if (NGX_QUIC) + if (u->quic) { + r->connection->log->action = "QUIC handshaking to upstream"; + + rc = ngx_quic_handshake(c); + + } else +#endif + { + r->connection->log->action = "SSL handshaking to upstream"; - rc = ngx_ssl_handshake(c); + rc = ngx_ssl_handshake(c); + } if (rc == NGX_AGAIN) { @@ -1875,6 +1903,14 @@ ngx_http_upstream_ssl_handshake(ngx_http_request_t *r, ngx_http_upstream_t *u, } } + if (u->create_stream) { + if (u->create_stream(r) != NGX_OK) { + goto failed; + } + + c = u->peer.connection; + } + if (!c->ssl->sendfile) { c->sendfile = 0; u->output.sendfile = 0; @@ -2232,6 +2268,16 @@ ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t *u, u->write_event_handler = ngx_http_upstream_dummy_handler; } +#if (NGX_QUIC) + if (u->quic) { + if (ngx_quic_shutdown_stream(c, NGX_WRITE_SHUTDOWN) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + } +#endif + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { ngx_http_upstream_finalize_request(r, u, NGX_HTTP_INTERNAL_SERVER_ERROR); @@ -4694,7 +4740,15 @@ ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u, u->peer.connection->ssl->no_wait_shutdown = 1; u->peer.connection->ssl->no_send_shutdown = 1; - (void) ngx_ssl_shutdown(u->peer.connection); +#if (NGX_QUIC) + if (u->quic) { + (void) ngx_quic_shutdown(u->peer.connection); + + } else +#endif + { + (void) ngx_ssl_shutdown(u->peer.connection); + } } #endif @@ -4782,7 +4836,15 @@ ngx_http_upstream_finalize_request(ngx_http_request_t *r, u->peer.connection->ssl->no_wait_shutdown = 1; - (void) ngx_ssl_shutdown(u->peer.connection); +#if (NGX_QUIC) + if (u->quic) { + (void) ngx_quic_shutdown(u->peer.connection); + + } else +#endif + { + (void) ngx_ssl_shutdown(u->peer.connection); + } } #endif diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h index 3afe6e8f9..dc6b20e40 100644 --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -251,6 +251,10 @@ typedef struct { ngx_array_t *ssl_passwords; #endif +#if (NGX_QUIC || NGX_COMPAT) + ngx_quic_conf_t *quic; +#endif + ngx_str_t module; NGX_COMPAT_BEGIN(6) @@ -370,6 +374,7 @@ struct ngx_http_upstream_s { ngx_int_t (*create_key)(ngx_http_request_t *r); #endif ngx_int_t (*create_request)(ngx_http_request_t *r); + ngx_int_t (*create_stream)(ngx_http_request_t *r); ngx_int_t (*reinit_request)(ngx_http_request_t *r); ngx_int_t (*process_header)(ngx_http_request_t *r); void (*abort_request)(ngx_http_request_t *r); @@ -399,6 +404,7 @@ struct ngx_http_upstream_s { unsigned cacheable:1; unsigned accel:1; unsigned ssl:1; + unsigned quic:1; #if (NGX_HTTP_CACHE) unsigned cache_status:3; #endif diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c index d8597ec5c..43cd1f39d 100644 --- a/src/http/v3/ngx_http_v3.c +++ b/src/http/v3/ngx_http_v3.c @@ -10,7 +10,6 @@ #include -static void ngx_http_v3_keepalive_handler(ngx_event_t *ev); static void ngx_http_v3_cleanup_session(void *data); @@ -18,11 +17,8 @@ ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c) { ngx_pool_cleanup_t *cln; - ngx_http_connection_t *hc; ngx_http_v3_session_t *h3c; - hc = c->data; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init session"); h3c = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_session_t)); @@ -30,13 +26,9 @@ ngx_http_v3_init_session(ngx_connection_t *c) goto failed; } - h3c->http_connection = hc; - - ngx_queue_init(&h3c->blocked); + h3c->connection = c; - h3c->keepalive.log = c->log; - h3c->keepalive.data = c; - h3c->keepalive.handler = ngx_http_v3_keepalive_handler; + ngx_queue_init(&h3c->queue); h3c->table.send_insert_count.log = c->log; h3c->table.send_insert_count.data = c; @@ -61,20 +53,6 @@ failed: } -static void -ngx_http_v3_keepalive_handler(ngx_event_t *ev) -{ - ngx_connection_t *c; - - c = ev->data; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 keepalive handler"); - - ngx_http_v3_shutdown_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, - "keepalive timeout"); -} - - static void ngx_http_v3_cleanup_session(void *data) { @@ -82,10 +60,6 @@ ngx_http_v3_cleanup_session(void *data) ngx_http_v3_cleanup_table(h3c); - if (h3c->keepalive.timer_set) { - ngx_del_timer(&h3c->keepalive); - } - if (h3c->table.send_insert_count.posted) { ngx_delete_posted_event(&h3c->table.send_insert_count); } @@ -109,3 +83,27 @@ ngx_http_v3_check_flood(ngx_connection_t *c) return NGX_OK; } + + +void +ngx_http_v3_close_connection(ngx_connection_t *c) +{ + ngx_pool_t *pool; + + if (ngx_quic_shutdown(c) == NGX_AGAIN) { + c->ssl->handler = ngx_http_v3_close_connection; + return; + } + +#if (NGX_STAT_STUB) + (void) ngx_atomic_fetch_add(ngx_stat_active, -1); +#endif + + c->destroyed = 1; + + pool = c->pool; + + ngx_close_connection(c); + + ngx_destroy_pool(pool); +} diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index 8fd212c1f..b43ad7253 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -79,11 +79,12 @@ #define ngx_http_v3_get_session(c) \ - ((ngx_http_v3_session_t *) ((c)->quic ? (c)->quic->parent->data \ - : (c)->data)) + ((ngx_http_v3_session_t *) ((c)->quic->stream \ + ? (c)->quic->stream->parent->data \ + : (c)->data)) #define ngx_http_quic_get_connection(c) \ - (ngx_http_v3_get_session(c)->http_connection) + ((ngx_http_connection_t *) ngx_http_v3_get_session(c)->data) #define ngx_http_v3_get_module_loc_conf(c, module) \ ngx_http_get_module_loc_conf(ngx_http_quic_get_connection(c)->conf_ctx, \ @@ -94,12 +95,11 @@ module) #define ngx_http_v3_finalize_connection(c, code, reason) \ - ngx_quic_finalize_connection((c)->quic ? (c)->quic->parent : (c), \ - code, reason) - -#define ngx_http_v3_shutdown_connection(c, code, reason) \ - ngx_quic_shutdown_connection((c)->quic ? (c)->quic->parent : (c), \ - code, reason) + ngx_quic_set_app_error((c)->quic->stream ? (c)->quic->stream->parent \ + : (c), code, reason); \ + ngx_post_event((c)->quic->stream ? (c)->quic->stream->parent->read \ + : (c)->read, \ + &ngx_posted_events) typedef struct { @@ -121,15 +121,19 @@ struct ngx_http_v3_parse_s { struct ngx_http_v3_session_s { - ngx_http_connection_t *http_connection; + ngx_connection_t *connection; + + void *data; ngx_http_v3_dynamic_table_t table; - ngx_event_t keepalive; - ngx_uint_t nrequests; + ngx_queue_t queue; - ngx_queue_t blocked; + size_t max_table_capacity; + ngx_uint_t max_blocked_streams; + ngx_uint_t nrequests; ngx_uint_t nblocked; + ngx_uint_t max_literal; uint64_t next_request_id; @@ -147,8 +151,7 @@ void ngx_http_v3_init_stream(ngx_connection_t *c); void ngx_http_v3_reset_stream(ngx_connection_t *c); ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c); ngx_int_t ngx_http_v3_check_flood(ngx_connection_t *c); -ngx_int_t ngx_http_v3_init(ngx_connection_t *c); -void ngx_http_v3_shutdown(ngx_connection_t *c); +void ngx_http_v3_close_connection(ngx_connection_t *c); ngx_int_t ngx_http_v3_read_request_body(ngx_http_request_t *r); ngx_int_t ngx_http_v3_read_unbuffered_request_body(ngx_http_request_t *r); diff --git a/src/http/v3/ngx_http_v3_encode.c b/src/http/v3/ngx_http_v3_encode.c index fb089c413..763792914 100644 --- a/src/http/v3/ngx_http_v3_encode.c +++ b/src/http/v3/ngx_http_v3_encode.c @@ -180,7 +180,8 @@ ngx_http_v3_encode_field_lri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index, uintptr_t -ngx_http_v3_encode_field_l(u_char *p, ngx_str_t *name, ngx_str_t *value) +ngx_http_v3_encode_field_l(u_char *p, u_char *name, size_t name_len, + u_char *value, size_t value_len) { size_t hlen; u_char *p1, *p2; @@ -188,18 +189,18 @@ ngx_http_v3_encode_field_l(u_char *p, ngx_str_t *name, ngx_str_t *value) /* Literal Field Line With Literal Name */ if (p == NULL) { - return ngx_http_v3_encode_prefix_int(NULL, name->len, 3) - + name->len - + ngx_http_v3_encode_prefix_int(NULL, value->len, 7) - + value->len; + return ngx_http_v3_encode_prefix_int(NULL, name_len, 3) + + name_len + + ngx_http_v3_encode_prefix_int(NULL, value_len, 7) + + value_len; } p1 = p; *p = 0x20; - p = (u_char *) ngx_http_v3_encode_prefix_int(p, name->len, 3); + p = (u_char *) ngx_http_v3_encode_prefix_int(p, name_len, 3); p2 = p; - hlen = ngx_http_huff_encode(name->data, name->len, p, 1); + hlen = ngx_http_huff_encode(name, name_len, p, 1); if (hlen) { p = p1; @@ -213,16 +214,16 @@ ngx_http_v3_encode_field_l(u_char *p, ngx_str_t *name, ngx_str_t *value) p += hlen; } else { - ngx_strlow(p, name->data, name->len); - p += name->len; + ngx_strlow(p, name, name_len); + p += name_len; } p1 = p; *p = 0; - p = (u_char *) ngx_http_v3_encode_prefix_int(p, value->len, 7); + p = (u_char *) ngx_http_v3_encode_prefix_int(p, value_len, 7); p2 = p; - hlen = ngx_http_huff_encode(value->data, value->len, p, 0); + hlen = ngx_http_huff_encode(value, value_len, p, 0); if (hlen) { p = p1; @@ -236,7 +237,7 @@ ngx_http_v3_encode_field_l(u_char *p, ngx_str_t *name, ngx_str_t *value) p += hlen; } else { - p = ngx_cpymem(p, value->data, value->len); + p = ngx_cpymem(p, value, value_len); } return (uintptr_t) p; diff --git a/src/http/v3/ngx_http_v3_encode.h b/src/http/v3/ngx_http_v3_encode.h index fca376da5..e227b6f77 100644 --- a/src/http/v3/ngx_http_v3_encode.h +++ b/src/http/v3/ngx_http_v3_encode.h @@ -24,8 +24,8 @@ uintptr_t ngx_http_v3_encode_field_ri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index); uintptr_t ngx_http_v3_encode_field_lri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index, u_char *data, size_t len); -uintptr_t ngx_http_v3_encode_field_l(u_char *p, ngx_str_t *name, - ngx_str_t *value); +uintptr_t ngx_http_v3_encode_field_l(u_char *p, u_char *name, size_t name_len, + u_char *data, size_t value_len); uintptr_t ngx_http_v3_encode_field_pbi(u_char *p, ngx_uint_t index); uintptr_t ngx_http_v3_encode_field_lpbi(u_char *p, ngx_uint_t index, u_char *data, size_t len); diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c index e3f15368f..cb1938b7d 100644 --- a/src/http/v3/ngx_http_v3_filter_module.c +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -10,26 +10,6 @@ #include -/* static table indices */ -#define NGX_HTTP_V3_HEADER_AUTHORITY 0 -#define NGX_HTTP_V3_HEADER_PATH_ROOT 1 -#define NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO 4 -#define NGX_HTTP_V3_HEADER_DATE 6 -#define NGX_HTTP_V3_HEADER_LAST_MODIFIED 10 -#define NGX_HTTP_V3_HEADER_LOCATION 12 -#define NGX_HTTP_V3_HEADER_METHOD_GET 17 -#define NGX_HTTP_V3_HEADER_SCHEME_HTTP 22 -#define NGX_HTTP_V3_HEADER_SCHEME_HTTPS 23 -#define NGX_HTTP_V3_HEADER_STATUS_103 24 -#define NGX_HTTP_V3_HEADER_STATUS_200 25 -#define NGX_HTTP_V3_HEADER_ACCEPT_ENCODING 31 -#define NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN 53 -#define NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING 59 -#define NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE 72 -#define NGX_HTTP_V3_HEADER_SERVER 92 -#define NGX_HTTP_V3_HEADER_USER_AGENT 95 - - typedef struct { ngx_chain_t *free; ngx_chain_t *busy; @@ -313,8 +293,9 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) continue; } - len += ngx_http_v3_encode_field_l(NULL, &header[i].key, - &header[i].value); + len += ngx_http_v3_encode_field_l(NULL, + header[i].key.data, header[i].key.len, + header[i].value.data, header[i].value.len); } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 header len:%uz", len); @@ -500,8 +481,8 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) &header[i].key, &header[i].value); b->last = (u_char *) ngx_http_v3_encode_field_l(b->last, - &header[i].key, - &header[i].value); + header[i].key.data, header[i].key.len, + header[i].value.data, header[i].value.len); } if (r->header_only) { @@ -631,8 +612,9 @@ ngx_http_v3_early_hints_filter(ngx_http_request_t *r) continue; } - len += ngx_http_v3_encode_field_l(NULL, &header[i].key, - &header[i].value); + len += ngx_http_v3_encode_field_l(NULL, + header[i].key.data, header[i].key.len, + header[i].value.data, header[i].value.len); } if (len == 0) { @@ -685,8 +667,8 @@ ngx_http_v3_early_hints_filter(ngx_http_request_t *r) &header[i].key, &header[i].value); b->last = (u_char *) ngx_http_v3_encode_field_l(b->last, - &header[i].key, - &header[i].value); + header[i].key.data, header[i].key.len, + header[i].value.data, header[i].value.len); } b->flush = 1; @@ -888,8 +870,9 @@ ngx_http_v3_create_trailers(ngx_http_request_t *r, continue; } - len += ngx_http_v3_encode_field_l(NULL, &header[i].key, - &header[i].value); + len += ngx_http_v3_encode_field_l(NULL, + header[i].key.data, header[i].key.len, + header[i].value.data, header[i].value.len); } cl = ngx_chain_get_free_buf(r->pool, &ctx->free); @@ -945,8 +928,8 @@ ngx_http_v3_create_trailers(ngx_http_request_t *r, &header[i].key, &header[i].value); b->last = (u_char *) ngx_http_v3_encode_field_l(b->last, - &header[i].key, - &header[i].value); + header[i].key.data, header[i].key.len, + header[i].value.data, header[i].value.len); } n = b->last - b->pos; diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c index 139bd65f3..ea2587435 100644 --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -210,9 +210,6 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *cf) h3scf->quic.stream_reject_code_bidi = NGX_HTTP_V3_ERR_REQUEST_REJECTED; h3scf->quic.active_connection_id_limit = NGX_CONF_UNSET_UINT; - h3scf->quic.init = ngx_http_v3_init; - h3scf->quic.shutdown = ngx_http_v3_shutdown; - return h3scf; } @@ -223,8 +220,7 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_http_v3_srv_conf_t *prev = parent; ngx_http_v3_srv_conf_t *conf = child; - ngx_http_ssl_srv_conf_t *sscf; - ngx_http_core_srv_conf_t *cscf; + ngx_http_ssl_srv_conf_t *sscf; ngx_conf_merge_value(conf->enable, prev->enable, 1); @@ -282,9 +278,6 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_ERROR; } - cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module); - conf->quic.handshake_timeout = cscf->client_header_timeout; - sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module); conf->quic.ssl = &sscf->ssl; diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index bcbf0dbe1..e37a7de5e 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -20,8 +20,6 @@ static void ngx_http_v3_parse_end_local(ngx_buf_t *b, ngx_buf_t *loc, ngx_uint_t *n); static ngx_int_t ngx_http_v3_parse_skip(ngx_buf_t *b, ngx_uint_t *length); -static ngx_int_t ngx_http_v3_parse_varlen_int(ngx_connection_t *c, - ngx_http_v3_parse_varlen_int_t *st, ngx_buf_t *b); static ngx_int_t ngx_http_v3_parse_prefix_int(ngx_connection_t *c, ngx_http_v3_parse_prefix_int_t *st, ngx_uint_t prefix, ngx_buf_t *b); @@ -42,24 +40,14 @@ static ngx_int_t ngx_http_v3_parse_field_pbi(ngx_connection_t *c, static ngx_int_t ngx_http_v3_parse_field_lpbi(ngx_connection_t *c, ngx_http_v3_parse_field_t *st, ngx_buf_t *b); -static ngx_int_t ngx_http_v3_parse_control(ngx_connection_t *c, - ngx_http_v3_parse_control_t *st, ngx_buf_t *b); static ngx_int_t ngx_http_v3_parse_settings(ngx_connection_t *c, ngx_http_v3_parse_settings_t *st, ngx_buf_t *b); -static ngx_int_t ngx_http_v3_parse_encoder(ngx_connection_t *c, - ngx_http_v3_parse_encoder_t *st, ngx_buf_t *b); static ngx_int_t ngx_http_v3_parse_field_inr(ngx_connection_t *c, ngx_http_v3_parse_field_t *st, ngx_buf_t *b); static ngx_int_t ngx_http_v3_parse_field_iln(ngx_connection_t *c, ngx_http_v3_parse_field_t *st, ngx_buf_t *b); -static ngx_int_t ngx_http_v3_parse_decoder(ngx_connection_t *c, - ngx_http_v3_parse_decoder_t *st, ngx_buf_t *b); - -static ngx_int_t ngx_http_v3_parse_lookup(ngx_connection_t *c, - ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *name, ngx_str_t *value); - static void ngx_http_v3_parse_start_local(ngx_buf_t *b, ngx_buf_t *loc, ngx_uint_t n) @@ -94,7 +82,7 @@ ngx_http_v3_parse_skip(ngx_buf_t *b, ngx_uint_t *length) } -static ngx_int_t +ngx_int_t ngx_http_v3_parse_varlen_int(ngx_connection_t *c, ngx_http_v3_parse_varlen_int_t *st, ngx_buf_t *b) { @@ -220,8 +208,6 @@ ngx_http_v3_parse_prefix_int(ngx_connection_t *c, if (st->shift == 56 && ((ch & 0x80) || (st->value & 0xc000000000000000))) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client exceeded integer size limit"); return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; } @@ -248,8 +234,10 @@ ngx_int_t ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, ngx_buf_t *b) { - ngx_buf_t loc; - ngx_int_t rc; + ngx_buf_t loc; + ngx_int_t rc; + ngx_http_v3_parse_field_t *f; + enum { sw_start = 0, sw_type, @@ -348,14 +336,32 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, return rc; } + st->field_rep.max_literal = st->max_literal; + st->state = sw_verify; break; case sw_verify: - rc = ngx_http_v3_check_insert_count(c, st->prefix.insert_count); - if (rc != NGX_OK) { - return rc; + if (st->prefix.insert_count > 0) { + rc = st->process_insert_count(st->data, + &st->prefix.insert_count); + if (rc != NGX_OK) { + return rc; + } + + st->insert_count = st->prefix.insert_count; + + if (st->prefix.sign) { + if (st->insert_count <= st->prefix.delta_base) { + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; + } + + st->base = st->insert_count - st->prefix.delta_base - 1; + + } else { + st->base = st->insert_count + st->prefix.delta_base; + } } st->state = sw_field_rep; @@ -366,8 +372,7 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, ngx_http_v3_parse_start_local(b, &loc, st->length); - rc = ngx_http_v3_parse_field_rep(c, &st->field_rep, st->prefix.base, - &loc); + rc = ngx_http_v3_parse_field_rep(c, &st->field_rep, st->base, &loc); ngx_http_v3_parse_end_local(b, &loc, &st->length); @@ -379,11 +384,20 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, return rc; } + f = &st->field_rep.field; + + rc = st->process_header(st->data, f->has_name ? &f->name : NULL, + f->has_value ? &f->value : NULL, + f->index, f->dynamic); + if (rc != NGX_OK) { + return rc; + } + if (st->length == 0) { goto done; } - return NGX_OK; + break; } } @@ -391,14 +405,6 @@ done: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse headers done"); - if (st->prefix.insert_count > 0) { - if (ngx_http_v3_send_ack_section(c, c->quic->id) != NGX_OK) { - return NGX_ERROR; - } - - ngx_http_v3_ack_insert_count(c, st->prefix.insert_count); - } - st->state = sw_start; return NGX_DONE; } @@ -468,27 +474,10 @@ ngx_http_v3_parse_field_section_prefix(ngx_connection_t *c, done: - rc = ngx_http_v3_decode_insert_count(c, &st->insert_count); - if (rc != NGX_OK) { - return rc; - } - - if (st->sign) { - if (st->insert_count <= st->delta_base) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent negative base"); - return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; - } - - st->base = st->insert_count - st->delta_base - 1; - - } else { - st->base = st->insert_count + st->delta_base; - } - - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse field section prefix done " - "insert_count:%ui, sign:%ui, delta_base:%ui, base:%ui", - st->insert_count, st->sign, st->delta_base, st->base); + "insert_count:%ui, sign:%ui, delta_base:%ui", + st->insert_count, st->sign, st->delta_base); st->state = sw_start; return NGX_DONE; @@ -521,9 +510,8 @@ ngx_http_v3_parse_field_rep(ngx_connection_t *c, ch = *b->pos; - ngx_memzero(&st->field, sizeof(ngx_http_v3_parse_field_t)); - st->field.base = base; + st->field.max_literal = st->max_literal; if (ch & 0x80) { /* Indexed Field Line */ @@ -594,9 +582,8 @@ static ngx_int_t ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st, ngx_buf_t *b) { - u_char ch; - ngx_uint_t n; - ngx_http_core_srv_conf_t *cscf; + u_char ch; + ngx_uint_t n; enum { sw_start = 0, sw_value @@ -614,14 +601,6 @@ ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st, n = st->length; - cscf = ngx_http_v3_get_module_srv_conf(c, ngx_http_core_module); - - if (n > cscf->large_client_header_buffers.size) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent too large field line"); - return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; - } - if (st->huffman) { if (n > NGX_MAX_INT_T_VALUE / 8) { ngx_log_error(NGX_LOG_INFO, c->log, 0, @@ -633,6 +612,7 @@ ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st, st->huffstate = 0; } + /* XXX this allocation is bad in uni streams */ st->last = ngx_pnalloc(c->pool, n + 1); if (st->last == NULL) { return NGX_ERROR; @@ -656,8 +636,6 @@ ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st, st->length == 1, c->log) != NGX_OK) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid encoded field line"); return NGX_ERROR; } @@ -734,16 +712,13 @@ done: "http3 parse field ri done %s%ui]", st->dynamic ? "dynamic[-" : "static[", st->index); + st->has_name = 0; + st->has_value = 0; + if (st->dynamic) { st->index = st->base - st->index - 1; } - rc = ngx_http_v3_parse_lookup(c, st->dynamic, st->index, &st->name, - &st->value); - if (rc != NGX_OK) { - return rc; - } - st->state = sw_start; return NGX_DONE; } @@ -820,6 +795,11 @@ ngx_http_v3_parse_field_lri(ngx_connection_t *c, goto done; } + if (st->literal.length > st->max_literal) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "field value too large"); + return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; + } + st->state = sw_value; break; @@ -842,15 +822,13 @@ done: st->dynamic ? "dynamic[-" : "static[", st->index, &st->value); + st->has_name = 0; + st->has_value = 1; + if (st->dynamic) { st->index = st->base - st->index - 1; } - rc = ngx_http_v3_parse_lookup(c, st->dynamic, st->index, &st->name, NULL); - if (rc != NGX_OK) { - return rc; - } - st->state = sw_start; return NGX_DONE; } @@ -903,6 +881,11 @@ ngx_http_v3_parse_field_l(ngx_connection_t *c, return NGX_ERROR; } + if (st->literal.length > st->max_literal) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "field name too large"); + return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; + } + st->state = sw_name; break; @@ -943,6 +926,11 @@ ngx_http_v3_parse_field_l(ngx_connection_t *c, goto done; } + if (st->literal.length > st->max_literal) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "field value too large"); + return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; + } + st->state = sw_value; break; @@ -964,6 +952,11 @@ done: "http3 parse field l done \"%V\" \"%V\"", &st->name, &st->value); + st->has_name = 1; + st->has_value = 1; + st->index = 0; + st->dynamic = 0; + st->state = sw_start; return NGX_DONE; } @@ -1009,11 +1002,10 @@ done: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse field pbi done dynamic[+%ui]", st->index); - rc = ngx_http_v3_parse_lookup(c, 1, st->base + st->index, &st->name, - &st->value); - if (rc != NGX_OK) { - return rc; - } + st->has_name = 0; + st->has_value = 0; + st->index += st->base; + st->dynamic = 1; st->state = sw_start; return NGX_DONE; @@ -1084,6 +1076,11 @@ ngx_http_v3_parse_field_lpbi(ngx_connection_t *c, goto done; } + if (st->literal.length > st->max_literal) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "field value too large"); + return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; + } + st->state = sw_value; break; @@ -1105,61 +1102,17 @@ done: "http3 parse field lpbi done dynamic[+%ui] \"%V\"", st->index, &st->value); - rc = ngx_http_v3_parse_lookup(c, 1, st->base + st->index, &st->name, NULL); - if (rc != NGX_OK) { - return rc; - } + st->has_name = 0; + st->has_value = 1; + st->index += st->base; + st->dynamic = 1; st->state = sw_start; return NGX_DONE; } -static ngx_int_t -ngx_http_v3_parse_lookup(ngx_connection_t *c, ngx_uint_t dynamic, - ngx_uint_t index, ngx_str_t *name, ngx_str_t *value) -{ - u_char *p; - - if (!dynamic) { - if (ngx_http_v3_lookup_static(c, index, name, value) != NGX_OK) { - return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; - } - - return NGX_OK; - } - - if (ngx_http_v3_lookup(c, index, name, value) != NGX_OK) { - return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; - } - - if (name) { - p = ngx_pnalloc(c->pool, name->len + 1); - if (p == NULL) { - return NGX_ERROR; - } - - ngx_memcpy(p, name->data, name->len); - p[name->len] = '\0'; - name->data = p; - } - - if (value) { - p = ngx_pnalloc(c->pool, value->len + 1); - if (p == NULL) { - return NGX_ERROR; - } - - ngx_memcpy(p, value->data, value->len); - p[value->len] = '\0'; - value->data = p; - } - - return NGX_OK; -} - - -static ngx_int_t +ngx_int_t ngx_http_v3_parse_control(ngx_connection_t *c, ngx_http_v3_parse_control_t *st, ngx_buf_t *b) { @@ -1246,6 +1199,9 @@ ngx_http_v3_parse_control(ngx_connection_t *c, ngx_http_v3_parse_control_t *st, switch (st->type) { case NGX_HTTP_V3_FRAME_SETTINGS: + st->settings.set_param = st->set_param; + st->settings.data = st->data; + st->state = sw_settings; break; @@ -1335,7 +1291,7 @@ ngx_http_v3_parse_settings(ngx_connection_t *c, return rc; } - if (ngx_http_v3_set_param(c, st->id, st->vlint.value) != NGX_OK) { + if (st->set_param(st->data, st->id, st->vlint.value) != NGX_OK) { return NGX_HTTP_V3_ERR_SETTINGS_ERROR; } @@ -1352,7 +1308,7 @@ done: } -static ngx_int_t +ngx_int_t ngx_http_v3_parse_encoder(ngx_connection_t *c, ngx_http_v3_parse_encoder_t *st, ngx_buf_t *b) { @@ -1377,6 +1333,8 @@ ngx_http_v3_parse_encoder(ngx_connection_t *c, ngx_http_v3_parse_encoder_t *st, return NGX_AGAIN; } + st->field.max_literal = st->max_literal; + ch = *b->pos; if (ch & 0x80) { @@ -1410,6 +1368,12 @@ ngx_http_v3_parse_encoder(ngx_connection_t *c, ngx_http_v3_parse_encoder_t *st, return rc; } + rc = st->ref_insert(st->data, st->field.dynamic, st->field.index, + &st->field.value); + if (rc != NGX_OK) { + return rc; + } + st->state = sw_start; break; @@ -1420,6 +1384,11 @@ ngx_http_v3_parse_encoder(ngx_connection_t *c, ngx_http_v3_parse_encoder_t *st, return rc; } + rc = st->insert(st->data, &st->field.name, &st->field.value); + if (rc != NGX_OK) { + return rc; + } + st->state = sw_start; break; @@ -1430,7 +1399,7 @@ ngx_http_v3_parse_encoder(ngx_connection_t *c, ngx_http_v3_parse_encoder_t *st, return rc; } - rc = ngx_http_v3_set_capacity(c, st->pint.value); + rc = st->set_capacity(st->data, st->pint.value); if (rc != NGX_OK) { return rc; } @@ -1445,7 +1414,7 @@ ngx_http_v3_parse_encoder(ngx_connection_t *c, ngx_http_v3_parse_encoder_t *st, return rc; } - rc = ngx_http_v3_duplicate(c, st->pint.value); + rc = st->duplicate(st->data, st->pint.value); if (rc != NGX_OK) { return rc; } @@ -1528,6 +1497,11 @@ ngx_http_v3_parse_field_inr(ngx_connection_t *c, goto done; } + if (st->literal.length > st->max_literal) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "field value too large"); + return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; + } + st->state = sw_value; break; @@ -1550,11 +1524,6 @@ done: st->dynamic ? "dynamic" : "static", st->index, &st->value); - rc = ngx_http_v3_ref_insert(c, st->dynamic, st->index, &st->value); - if (rc != NGX_OK) { - return rc; - } - st->state = sw_start; return NGX_DONE; } @@ -1607,6 +1576,11 @@ ngx_http_v3_parse_field_iln(ngx_connection_t *c, return NGX_ERROR; } + if (st->literal.length > st->max_literal) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "field name too large"); + return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; + } + st->state = sw_name; break; @@ -1647,6 +1621,11 @@ ngx_http_v3_parse_field_iln(ngx_connection_t *c, goto done; } + if (st->literal.length > st->max_literal) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "field value too large"); + return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; + } + st->state = sw_value; break; @@ -1668,17 +1647,12 @@ done: "http3 parse field iln done \"%V\":\"%V\"", &st->name, &st->value); - rc = ngx_http_v3_insert(c, &st->name, &st->value); - if (rc != NGX_OK) { - return rc; - } - st->state = sw_start; return NGX_DONE; } -static ngx_int_t +ngx_int_t ngx_http_v3_parse_decoder(ngx_connection_t *c, ngx_http_v3_parse_decoder_t *st, ngx_buf_t *b) { @@ -1730,7 +1704,7 @@ ngx_http_v3_parse_decoder(ngx_connection_t *c, ngx_http_v3_parse_decoder_t *st, return rc; } - rc = ngx_http_v3_ack_section(c, st->pint.value); + rc = st->ack_section(st->data, st->pint.value); if (rc != NGX_OK) { return rc; } @@ -1745,7 +1719,7 @@ ngx_http_v3_parse_decoder(ngx_connection_t *c, ngx_http_v3_parse_decoder_t *st, return rc; } - rc = ngx_http_v3_cancel_stream(c, st->pint.value); + rc = st->cancel_stream(st->data, st->pint.value); if (rc != NGX_OK) { return rc; } @@ -1760,7 +1734,7 @@ ngx_http_v3_parse_decoder(ngx_connection_t *c, ngx_http_v3_parse_decoder_t *st, return rc; } - rc = ngx_http_v3_inc_insert_count(c, st->pint.value); + rc = st->inc_insert_count(st->data, st->pint.value); if (rc != NGX_OK) { return rc; } @@ -1806,7 +1780,8 @@ ngx_http_v3_parse_data(ngx_connection_t *c, ngx_http_v3_parse_data_t *st, st->type = st->vlint.value; if (st->type == NGX_HTTP_V3_FRAME_HEADERS) { - /* trailers */ + /* parse trailers later */ + b->pos--; goto done; } @@ -1863,80 +1838,3 @@ done: st->state = sw_start; return NGX_DONE; } - - -ngx_int_t -ngx_http_v3_parse_uni(ngx_connection_t *c, ngx_http_v3_parse_uni_t *st, - ngx_buf_t *b) -{ - ngx_int_t rc; - enum { - sw_start = 0, - sw_type, - sw_control, - sw_encoder, - sw_decoder, - sw_unknown - }; - - for ( ;; ) { - - switch (st->state) { - case sw_start: - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse uni"); - - st->state = sw_type; - - /* fall through */ - - case sw_type: - - rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b); - if (rc != NGX_DONE) { - return rc; - } - - rc = ngx_http_v3_register_uni_stream(c, st->vlint.value); - if (rc != NGX_OK) { - return rc; - } - - switch (st->vlint.value) { - case NGX_HTTP_V3_STREAM_CONTROL: - st->state = sw_control; - break; - - case NGX_HTTP_V3_STREAM_ENCODER: - st->state = sw_encoder; - break; - - case NGX_HTTP_V3_STREAM_DECODER: - st->state = sw_decoder; - break; - - default: - st->state = sw_unknown; - } - - break; - - case sw_control: - - return ngx_http_v3_parse_control(c, &st->u.control, b); - - case sw_encoder: - - return ngx_http_v3_parse_encoder(c, &st->u.encoder, b); - - case sw_decoder: - - return ngx_http_v3_parse_decoder(c, &st->u.decoder, b); - - case sw_unknown: - - b->pos = b->last; - return NGX_AGAIN; - } - } -} diff --git a/src/http/v3/ngx_http_v3_parse.h b/src/http/v3/ngx_http_v3_parse.h index ba004db5d..af484093e 100644 --- a/src/http/v3/ngx_http_v3_parse.h +++ b/src/http/v3/ngx_http_v3_parse.h @@ -14,6 +14,31 @@ #include +typedef ngx_int_t (*ngx_http_v3_process_insert_count_pt)(void *data, + ngx_uint_t *insert_count); +typedef ngx_int_t (*ngx_http_v3_process_header_pt)(void *data, ngx_str_t *name, + ngx_str_t *value, ngx_uint_t index, ngx_uint_t dynamic); + +typedef ngx_int_t (*ngx_http_v3_set_param_pt)(void *data, uint64_t id, + uint64_t value); + +typedef ngx_int_t (*ngx_http_v3_ref_insert_pt)(void *data, ngx_uint_t dynamic, + ngx_uint_t index, ngx_str_t *value); +typedef ngx_int_t (*ngx_http_v3_insert_pt)(void *data, ngx_str_t *name, + ngx_str_t *value); +typedef ngx_int_t (*ngx_http_v3_duplicate_pt)(void *data, ngx_uint_t index); +typedef ngx_int_t (*ngx_http_v3_set_capacity_pt)(void *data, + ngx_uint_t capacity); + + +typedef ngx_int_t (*ngx_http_v3_ack_section_pt)(void *data, + ngx_uint_t stream_id); +typedef ngx_int_t (*ngx_http_v3_cancel_stream_pt)(void *data, + ngx_uint_t stream_id); +typedef ngx_int_t (*ngx_http_v3_inc_insert_count_pt)(void *data, + ngx_uint_t inc); + + typedef struct { ngx_uint_t state; uint64_t value; @@ -31,6 +56,9 @@ typedef struct { ngx_uint_t state; uint64_t id; ngx_http_v3_parse_varlen_int_t vlint; + + ngx_http_v3_set_param_pt set_param; + void *data; } ngx_http_v3_parse_settings_t; @@ -59,18 +87,23 @@ typedef struct { ngx_uint_t index; ngx_uint_t base; ngx_uint_t dynamic; + ngx_uint_t max_literal; ngx_str_t name; ngx_str_t value; ngx_http_v3_parse_prefix_int_t pint; ngx_http_v3_parse_literal_t literal; + + unsigned has_name:1; + unsigned has_value:1; } ngx_http_v3_parse_field_t; typedef struct { ngx_uint_t state; ngx_http_v3_parse_field_t field; + ngx_uint_t max_literal; } ngx_http_v3_parse_field_rep_t; @@ -78,22 +111,44 @@ typedef struct { ngx_uint_t state; ngx_uint_t type; ngx_uint_t length; + ngx_uint_t base; + ngx_uint_t insert_count; ngx_http_v3_parse_varlen_int_t vlint; ngx_http_v3_parse_field_section_prefix_t prefix; ngx_http_v3_parse_field_rep_t field_rep; + ngx_uint_t max_literal; + + ngx_http_v3_process_insert_count_pt + process_insert_count; + ngx_http_v3_process_header_pt process_header; + void *data; } ngx_http_v3_parse_headers_t; typedef struct { ngx_uint_t state; + ngx_uint_t max_literal; ngx_http_v3_parse_field_t field; ngx_http_v3_parse_prefix_int_t pint; + + ngx_http_v3_ref_insert_pt ref_insert; + ngx_http_v3_insert_pt insert; + ngx_http_v3_duplicate_pt duplicate; + ngx_http_v3_set_capacity_pt set_capacity; + void *data; } ngx_http_v3_parse_encoder_t; typedef struct { ngx_uint_t state; + ngx_uint_t max_literal; ngx_http_v3_parse_prefix_int_t pint; + + ngx_http_v3_ack_section_pt ack_section; + ngx_http_v3_cancel_stream_pt cancel_stream; + ngx_http_v3_inc_insert_count_pt + inc_insert_count; + void *data; } ngx_http_v3_parse_decoder_t; @@ -101,20 +156,13 @@ typedef struct { ngx_uint_t state; ngx_uint_t type; ngx_uint_t length; + ngx_uint_t max_literal; ngx_http_v3_parse_varlen_int_t vlint; ngx_http_v3_parse_settings_t settings; -} ngx_http_v3_parse_control_t; - -typedef struct { - ngx_uint_t state; - ngx_http_v3_parse_varlen_int_t vlint; - union { - ngx_http_v3_parse_encoder_t encoder; - ngx_http_v3_parse_decoder_t decoder; - ngx_http_v3_parse_control_t control; - } u; -} ngx_http_v3_parse_uni_t; + ngx_http_v3_set_param_pt set_param; + void *data; +} ngx_http_v3_parse_control_t; typedef struct { @@ -139,8 +187,15 @@ ngx_int_t ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, ngx_buf_t *b); ngx_int_t ngx_http_v3_parse_data(ngx_connection_t *c, ngx_http_v3_parse_data_t *st, ngx_buf_t *b); -ngx_int_t ngx_http_v3_parse_uni(ngx_connection_t *c, - ngx_http_v3_parse_uni_t *st, ngx_buf_t *b); + +ngx_int_t ngx_http_v3_parse_varlen_int(ngx_connection_t *c, + ngx_http_v3_parse_varlen_int_t *st, ngx_buf_t *b); +ngx_int_t ngx_http_v3_parse_control(ngx_connection_t *c, + ngx_http_v3_parse_control_t *st, ngx_buf_t *b); +ngx_int_t ngx_http_v3_parse_encoder(ngx_connection_t *c, + ngx_http_v3_parse_encoder_t *st, ngx_buf_t *b); +ngx_int_t ngx_http_v3_parse_decoder(ngx_connection_t *c, + ngx_http_v3_parse_decoder_t *st, ngx_buf_t *b); #endif /* _NGX_HTTP_V3_PARSE_H_INCLUDED_ */ diff --git a/src/http/v3/ngx_http_v3_proxy_module.c b/src/http/v3/ngx_http_v3_proxy_module.c new file mode 100644 index 000000000..71e5abc38 --- /dev/null +++ b/src/http/v3/ngx_http_v3_proxy_module.c @@ -0,0 +1,1773 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include + + +typedef struct { + ngx_http_proxy_ctx_t ctx; + ngx_http_v3_parse_headers_t parse_headers; + ngx_http_v3_parse_data_t parse_data; + off_t body_received; + ngx_uint_t pseudo_done; /* unsigned pseudo_done:1; */ +} ngx_http_v3_proxy_ctx_t; + + +static ngx_int_t ngx_http_v3_proxy_create_request(ngx_http_request_t *r); +static ngx_int_t ngx_http_v3_proxy_create_stream(ngx_http_request_t *r); +static void ngx_http_v3_proxy_quic_handler(ngx_connection_t *c); +static ngx_int_t ngx_http_v3_proxy_handle_quic_connection(ngx_connection_t *c); +static ngx_int_t ngx_http_v3_proxy_reinit_request(ngx_http_request_t *r); +static ngx_int_t ngx_http_v3_proxy_body_output_filter(void *data, + ngx_chain_t *in); +static ngx_int_t ngx_http_v3_proxy_process_response(ngx_http_request_t *r); +static ngx_int_t ngx_http_v3_proxy_process_header(void *data, + ngx_str_t *name, ngx_str_t *value, ngx_uint_t index, ngx_uint_t dynamic); +static ngx_int_t ngx_http_v3_proxy_process_insert_count(void *data, + ngx_uint_t *insert_count); +static ngx_int_t ngx_http_v3_proxy_input_filter_init(void *data); +static ngx_int_t ngx_http_v3_proxy_body_filter(ngx_event_pipe_t *p, + ngx_buf_t *buf); +static ngx_int_t ngx_http_v3_proxy_non_buffered_body_filter(void *data, + ssize_t bytes); +static ngx_int_t ngx_http_v3_proxy_process_trailer(ngx_http_request_t *r, + ngx_buf_t *buf); +static void ngx_http_v3_proxy_abort_request(ngx_http_request_t *r); +static void ngx_http_v3_proxy_finalize_request(ngx_http_request_t *r, + ngx_int_t rc); + + +ngx_module_t ngx_http_v3_proxy_module; + + +static ngx_http_module_t ngx_http_v3_proxy_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + NULL, /* create location configuration */ + NULL /* merge location configuration */ +}; + + +ngx_module_t ngx_http_v3_proxy_module = { + NGX_MODULE_V1, + &ngx_http_v3_proxy_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +ngx_int_t +ngx_http_v3_proxy_handler(ngx_http_request_t *r) +{ + ngx_int_t rc; + ngx_http_upstream_t *u; + ngx_http_v3_proxy_ctx_t *ctx; + ngx_http_proxy_loc_conf_t *plcf; +#if (NGX_HTTP_CACHE) + ngx_http_proxy_main_conf_t *pmcf; +#endif + ngx_http_v3_parse_headers_t *st; + + if (ngx_http_upstream_create(r) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); + + if (!plcf->ssl) { + ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, + "unsupported \"http\" scheme"); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_v3_proxy_ctx_t)); + if (ctx == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + st = &ctx->parse_headers; + st->max_literal = plcf->upstream.buffer_size; + st->process_insert_count = ngx_http_v3_proxy_process_insert_count; + st->process_header = ngx_http_v3_proxy_process_header; + st->data = r; + + ngx_http_set_ctx(r, ctx, ngx_http_v3_proxy_module); + + ngx_http_set_ctx(r, &ctx->ctx, ngx_http_proxy_module); + + u = r->upstream; + + if (plcf->proxy_lengths == NULL) { + ctx->ctx.vars = plcf->vars; + u->schema = plcf->vars.schema; + u->ssl = 1; + + } else { + if (ngx_http_proxy_eval(r, &ctx->ctx, plcf) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (!u->ssl) { + ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, + "unsupported \"http\" scheme"); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + } + + ngx_str_set(&u->ssl_alpn_protocol, NGX_HTTP_V3_ALPN_PROTO); + + u->output.tag = (ngx_buf_tag_t) &ngx_http_proxy_module; + + u->conf = &plcf->upstream; + +#if (NGX_HTTP_CACHE) + pmcf = ngx_http_get_module_main_conf(r, ngx_http_proxy_module); + + u->caches = &pmcf->caches; + u->create_key = ngx_http_proxy_create_key; +#endif + + u->create_request = ngx_http_v3_proxy_create_request; + u->create_stream = ngx_http_v3_proxy_create_stream; + u->reinit_request = ngx_http_v3_proxy_reinit_request; + u->process_header = ngx_http_v3_proxy_process_response; + u->abort_request = ngx_http_v3_proxy_abort_request; + u->finalize_request = ngx_http_v3_proxy_finalize_request; + r->state = 0; + + if (plcf->redirects) { + u->rewrite_redirect = ngx_http_proxy_rewrite_redirect; + } + + if (plcf->cookie_domains || plcf->cookie_paths || plcf->cookie_flags) { + u->rewrite_cookie = ngx_http_proxy_rewrite_cookie; + } + + u->buffering = plcf->upstream.buffering; + + u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t)); + if (u->pipe == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + u->pipe->input_filter = ngx_http_v3_proxy_body_filter; + u->pipe->input_ctx = r; + + u->input_filter_init = ngx_http_v3_proxy_input_filter_init; + u->input_filter = ngx_http_v3_proxy_non_buffered_body_filter; + u->input_filter_ctx = r; + + u->accel = 1; + u->quic = 1; + u->peer.type = SOCK_DGRAM; + + if (!plcf->upstream.request_buffering + && plcf->body_values == NULL && plcf->upstream.pass_request_body) + { + r->request_body_no_buffering = 1; + } + + rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init); + + if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_v3_proxy_create_request(ngx_http_request_t *r) +{ + u_char *p, *key, *val; + size_t len, uri_len, loc_len, header_len, body_len, + key_len, val_len; + uintptr_t escape; + ngx_buf_t *b; + ngx_str_t method, *host; + ngx_uint_t i, unparsed_uri, internal_chunked; + ngx_chain_t *cl, *body; + ngx_list_part_t *part; + ngx_table_elt_t *header; + ngx_http_upstream_t *u; + ngx_http_v3_proxy_ctx_t *ctx; + ngx_http_script_code_pt code; + ngx_http_proxy_headers_t *headers; + ngx_http_script_engine_t e, le; + ngx_http_proxy_loc_conf_t *plcf; + ngx_http_script_len_code_pt lcode; + + u = r->upstream; + + plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); + +#if (NGX_HTTP_CACHE) + headers = u->cacheable ? &plcf->headers_cache : &plcf->headers; +#else + headers = &plcf->headers; +#endif + + if (u->method.len) { + /* HEAD was changed to GET to cache response */ + method = u->method; + + } else if (plcf->method) { + if (ngx_http_complex_value(r, plcf->method, &method) != NGX_OK) { + return NGX_ERROR; + } + + } else { + method = r->method_name; + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_v3_proxy_module); + + len = ngx_http_v3_encode_field_section_prefix(NULL, 0, 0, 0); + + /* :method header */ + + if (method.len == 4 + && ngx_strncasecmp(method.data, (u_char *) "HEAD", 4) == 0) + { + ctx->ctx.head = 1; + + len += ngx_http_v3_encode_field_ri(NULL, 0, + NGX_HTTP_V3_HEADER_METHOD_HEAD); + + } else if (method.len == sizeof("GET") - 1 + && ngx_strncasecmp(method.data, (u_char *) "GET", 3) == 0) + { + len += ngx_http_v3_encode_field_ri(NULL, 0, + NGX_HTTP_V3_HEADER_METHOD_GET); + + } else if (method.len == sizeof("POST") - 1 + && ngx_strncasecmp(method.data, (u_char *) "POST", 4) == 0) + { + len += ngx_http_v3_encode_field_ri(NULL, 0, + NGX_HTTP_V3_HEADER_METHOD_POST); + + } else if (method.len == sizeof("PUT") - 1 + && ngx_strncasecmp(method.data, (u_char *) "PUT", 3) == 0) + { + len += ngx_http_v3_encode_field_ri(NULL, 0, + NGX_HTTP_V3_HEADER_METHOD_PUT); + + } else { + len += ngx_http_v3_encode_field_lri(NULL, 0, + NGX_HTTP_V3_HEADER_METHOD_GET, + NULL, method.len); + } + + /* :scheme header */ + + len += ngx_http_v3_encode_field_ri(NULL, 0, + NGX_HTTP_V3_HEADER_SCHEME_HTTPS); + + /* :path header */ + + escape = 0; + loc_len = 0; + unparsed_uri = 0; + internal_chunked = 0; + + if (plcf->proxy_lengths && ctx->ctx.vars.uri.len) { + uri_len = ctx->ctx.vars.uri.len; + + } else if (ctx->ctx.vars.uri.len == 0 && r->valid_unparsed_uri) { + unparsed_uri = 1; + uri_len = r->unparsed_uri.len; + + } else { + loc_len = (r->valid_location && ctx->ctx.vars.uri.len) + ? ngx_min(plcf->location.len, r->uri.len) : 0; + + if (r->quoted_uri || r->internal) { + escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len, + r->uri.len - loc_len, NGX_ESCAPE_URI); + } + + uri_len = ctx->ctx.vars.uri.len + r->uri.len - loc_len + escape + + sizeof("?") - 1 + r->args.len; + } + + if (uri_len == 0) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "zero length URI to proxy"); + return NGX_ERROR; + } + + len += ngx_http_v3_encode_field_lri(NULL, 0, NGX_HTTP_V3_HEADER_PATH_ROOT, + NULL, uri_len); + + /* :authority header */ + + host = &ctx->ctx.vars.host_header; + + if (!plcf->host_set) { + len += ngx_http_v3_encode_field_lri(NULL, 0, + NGX_HTTP_V3_HEADER_AUTHORITY, + NULL, host->len); + } + + /* other headers */ + + ngx_memzero(&le, sizeof(ngx_http_script_engine_t)); + + ngx_http_script_flush_no_cacheable_variables(r, plcf->body_flushes); + ngx_http_script_flush_no_cacheable_variables(r, headers->flushes); + + body_len = 0; + + if (plcf->body_lengths) { + le.ip = plcf->body_lengths->elts; + le.request = r; + le.flushed = 1; + + while (*(uintptr_t *) le.ip) { + lcode = *(ngx_http_script_len_code_pt *) le.ip; + body_len += lcode(&le); + } + + ctx->ctx.internal_body_length = body_len; + + len += ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_DATA); + len += ngx_http_v3_encode_varlen_int(NULL, body_len); + + } else if (r->headers_in.chunked && r->reading_body) { + ctx->ctx.internal_body_length = -1; + internal_chunked = 1; + + } else { + ctx->ctx.internal_body_length = r->headers_in.content_length_n; + + len += ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_DATA); + len += ngx_http_v3_encode_varlen_int(NULL, + ctx->ctx.internal_body_length); + } + + + le.ip = headers->lengths->elts; + le.request = r; + le.flushed = 1; + + while (*(uintptr_t *) le.ip) { + + lcode = *(ngx_http_script_len_code_pt *) le.ip; + key_len = lcode(&le); + + for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { + lcode = *(ngx_http_script_len_code_pt *) le.ip; + } + le.ip += sizeof(uintptr_t); + + if (val_len == 0) { + continue; + } + + len += ngx_http_v3_encode_field_l(NULL, NULL, key_len, NULL, val_len) + * 2; + } + + if (plcf->upstream.pass_request_headers) { + part = &r->headers_in.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (ngx_hash_find(&headers->hash, header[i].hash, + header[i].lowcase_key, header[i].key.len)) + { + continue; + } + + len += ngx_http_v3_encode_field_l(NULL, NULL, header[i].key.len, + NULL, header[i].value.len); + } + } + + header_len = len; + + len += ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_HEADERS) + + ngx_http_v3_encode_varlen_int(NULL, header_len); + + b = ngx_create_temp_buf(r->pool, len); + if (b == NULL) { + return NGX_ERROR; + } + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + + b->last += ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_HEADERS) + + ngx_http_v3_encode_varlen_int(NULL, header_len); + + p = b->last; + + b->last = (u_char *) ngx_http_v3_encode_field_section_prefix(b->last, + 0, 0, 0); + + if (method.len == 4 + && ngx_strncasecmp(method.data, (u_char *) "HEAD", 4) == 0) + { + b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, + NGX_HTTP_V3_HEADER_METHOD_HEAD); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy header: \":method: HEAD\""); + + } else if (method.len == sizeof("GET") - 1 + && ngx_strncasecmp(method.data, (u_char *) "GET", 3) == 0) + { + b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, + NGX_HTTP_V3_HEADER_METHOD_GET); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy header: \":method: GET\""); + + } else if (method.len == sizeof("POST") - 1 + && ngx_strncasecmp(method.data, (u_char *) "POST", 4) == 0) + { + b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, + NGX_HTTP_V3_HEADER_METHOD_POST); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy header: \":method: POST\""); + + } else if (method.len == sizeof("PUT") - 1 + && ngx_strncasecmp(method.data, (u_char *) "PUT", 3) == 0) + { + b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, + NGX_HTTP_V3_HEADER_METHOD_PUT); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy header: \":method: PUT\""); + + } else { + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, + NGX_HTTP_V3_HEADER_METHOD_GET, + method.data, method.len); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy header: \":method: %V\"", &method); + } + + b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, + NGX_HTTP_V3_HEADER_SCHEME_HTTPS); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy header: \":scheme: https\""); + + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, + NGX_HTTP_V3_HEADER_PATH_ROOT, + NULL, uri_len); + u->uri.data = b->last; + + if (plcf->proxy_lengths && ctx->ctx.vars.uri.len) { + b->last = ngx_copy(b->last, ctx->ctx.vars.uri.data, + ctx->ctx.vars.uri.len); + + } else if (unparsed_uri) { + b->last = ngx_copy(b->last, r->unparsed_uri.data, r->unparsed_uri.len); + + } else { + if (r->valid_location) { + b->last = ngx_copy(b->last, ctx->ctx.vars.uri.data, + ctx->ctx.vars.uri.len); + } + + if (escape) { + ngx_escape_uri(b->last, r->uri.data + loc_len, + r->uri.len - loc_len, NGX_ESCAPE_URI); + b->last += r->uri.len - loc_len + escape; + + } else { + b->last = ngx_copy(b->last, r->uri.data + loc_len, + r->uri.len - loc_len); + } + + if (r->args.len > 0) { + *b->last++ = '?'; + b->last = ngx_copy(b->last, r->args.data, r->args.len); + } + } + + u->uri.len = b->last - u->uri.data; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy header: \":path: %V\"", &u->uri); + + if (!plcf->host_set) { + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, + NGX_HTTP_V3_HEADER_AUTHORITY, + host->data, host->len); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy header: \":authority: %V\"", host); + } + + ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); + + e.ip = headers->values->elts; + e.pos = b->last; + e.request = r; + e.flushed = 1; + + le.ip = headers->lengths->elts; + + while (*(uintptr_t *) le.ip) { + + lcode = *(ngx_http_script_len_code_pt *) le.ip; + key_len = lcode(&le); + + for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { + lcode = *(ngx_http_script_len_code_pt *) le.ip; + } + le.ip += sizeof(uintptr_t); + + if (val_len == 0) { + e.skip = 1; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + } + e.ip += sizeof(uintptr_t); + + e.skip = 0; + + continue; + } + + e.pos += ngx_http_v3_encode_field_l(NULL, NULL, key_len, NULL, val_len); + key = e.pos; + + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + + val = e.pos; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + } + e.ip += sizeof(uintptr_t); + + b->last = (u_char *) ngx_http_v3_encode_field_l(b->last, key, key_len, + val, val_len); + e.pos = b->last; + } + + b->last = e.pos; + + + if (plcf->upstream.pass_request_headers) { + part = &r->headers_in.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (ngx_hash_find(&headers->hash, header[i].hash, + header[i].lowcase_key, header[i].key.len)) + { + continue; + } + + b->last = (u_char *) ngx_http_v3_encode_field_l(b->last, + header[i].key.data, header[i].key.len, + header[i].value.data, header[i].value.len); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 proxy header: \"%V: %V\"", + &header[i].key, &header[i].value); + } + } + + len = b->last - p; + b->pos += (ngx_http_v3_encode_varlen_int(NULL, header_len) + - ngx_http_v3_encode_varlen_int(NULL, len)); + p = (u_char *) ngx_http_v3_encode_varlen_int(b->pos, + NGX_HTTP_V3_FRAME_HEADERS); + (void) ngx_http_v3_encode_varlen_int(p, len); + + if (plcf->body_values) { + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, + NGX_HTTP_V3_FRAME_DATA); + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, body_len); + + e.ip = plcf->body_values->elts; + e.pos = b->last; + e.skip = 0; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + } + + b->last = e.pos; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 proxy header:%N\"%*xs\"", + (size_t) (b->last - b->pos), b->pos); + + if (r->request_body_no_buffering) { + + u->request_bufs = cl; + + if (internal_chunked) { + u->output.output_filter = ngx_http_v3_proxy_body_output_filter; + u->output.filter_ctx = r; + + } else { + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, + NGX_HTTP_V3_FRAME_DATA); + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, + ctx->ctx.internal_body_length); + } + + } else if (plcf->body_values == NULL && plcf->upstream.pass_request_body) { + + body = u->request_bufs; + body_len = 0; + + while (body) { + body_len += ngx_buf_size(body->buf); + body = body->next; + } + + body = u->request_bufs; + u->request_bufs = cl; + + len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_DATA) + + ngx_http_v3_encode_varlen_int(NULL, body_len); + + b = ngx_create_temp_buf(r->pool, len); + if (b == NULL) { + return NGX_ERROR; + } + + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, + NGX_HTTP_V3_FRAME_DATA); + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, body_len); + + cl->next = ngx_alloc_chain_link(r->pool); + if (cl->next == NULL) { + return NGX_ERROR; + } + + cl = cl->next; + cl->buf = b; + + while (body) { + b = ngx_alloc_buf(r->pool); + if (b == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(b, body->buf, sizeof(ngx_buf_t)); + + cl->next = ngx_alloc_chain_link(r->pool); + if (cl->next == NULL) { + return NGX_ERROR; + } + + cl = cl->next; + cl->buf = b; + + body = body->next; + } + + } else { + u->request_bufs = cl; + } + + b->flush = 1; + cl->next = NULL; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_proxy_create_stream(ngx_http_request_t *r) +{ + ngx_log_t *log; + ngx_connection_t *c, *sc; + ngx_http_upstream_t *u; + ngx_http_v3_session_t *h3c; + + u = r->upstream; + c = u->peer.connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 proxy create stream"); + + c->ssl->handler = ngx_http_v3_proxy_quic_handler; + + if (ngx_http_v3_init_session(c) != NGX_OK) { + return NGX_ERROR; + } + + h3c = ngx_http_v3_get_session(c); + h3c->max_literal = u->conf->buffer_size; + + /* + * h3c->max_table_capacity = 0; + * h3c->max_blocked_streams = 0; + */ + + if (ngx_http_v3_send_settings(c) != NGX_OK) { + return NGX_ERROR; + } + + log = ngx_palloc(c->pool, sizeof(ngx_log_t)); + if (log == NULL) { + return NGX_ERROR; + } + + /* XXX */ + *log = r->connection->listening->log; + log->connection = c->number; + + /* XXX do not create log here */ + sc = ngx_quic_open_stream(c, 1); + if (sc == NULL) { + return NGX_ERROR; + } + + sc->log = c->log; + sc->pool->log = sc->log; + sc->read->log = sc->log; + sc->write->log = sc->log; + + /* QUIC connection may outlive client connection */ + + c->log = log; + c->pool->log = c->log; + c->read->log = c->log; + c->write->log = c->log; + + sc->data = r; + + sc->requests++; + c->requests++; + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + u->peer.connection = sc; + u->writer.connection = sc; + + if (ngx_http_v3_proxy_handle_quic_connection(c) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +static void +ngx_http_v3_proxy_quic_handler(ngx_connection_t *c) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 proxy handler"); + + if (c->close) { + ngx_http_v3_close_connection(c); + return; + } + + if (ngx_http_v3_proxy_handle_quic_connection(c) != NGX_OK) { + ngx_http_v3_close_connection(c); + } +} + + +static ngx_int_t +ngx_http_v3_proxy_handle_quic_connection(ngx_connection_t *c) +{ + ngx_connection_t *sc; + + if (c->read->timedout) { + ngx_quic_set_app_error(c, NGX_HTTP_V3_ERR_NO_ERROR, + "keepalive shutdown"); + return NGX_DONE; + } + + while (!ngx_quic_get_error(c)) { + + sc = ngx_quic_accept_stream(c); + if (sc == NULL) { + break; + } + + if (!(sc->quic->stream->id & NGX_QUIC_STREAM_UNIDIRECTIONAL)) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "upstream opened a quic bidi stream"); + return NGX_ERROR; + } + + ngx_http_v3_init_uni_stream(sc); + } + + if (ngx_quic_has_streams(c, 1, 1) == NGX_DECLINED) { + ngx_quic_set_app_error(c, NGX_HTTP_V3_ERR_NO_ERROR, "shutdown"); + return NGX_DONE; + } + + if (ngx_quic_get_error(c)) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_proxy_reinit_request(ngx_http_request_t *r) +{ + ngx_http_v3_proxy_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_v3_proxy_module); + + if (ctx == NULL) { + return NGX_OK; + } + + ctx->pseudo_done = 0; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_proxy_body_output_filter(void *data, ngx_chain_t *in) +{ + ngx_http_request_t *r = data; + + off_t size; + size_t len; + u_char *chunk; + ngx_int_t rc; + ngx_buf_t *b; + ngx_chain_t *out, *cl, *tl, **ll, **fl; + ngx_http_proxy_ctx_t *ctx; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 proxy output filter"); + + ctx = ngx_http_get_module_ctx(r, ngx_http_v3_proxy_module); + + if (in == NULL) { + out = in; + goto out; + } + + out = NULL; + ll = &out; + + if (!ctx->header_sent) { + /* first buffer contains headers, pass it unmodified */ + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 proxy output header"); + + ctx->header_sent = 1; + + tl = ngx_alloc_chain_link(r->pool); + if (tl == NULL) { + return NGX_ERROR; + } + + tl->buf = in->buf; + *ll = tl; + ll = &tl->next; + + in = in->next; + + if (in == NULL) { + tl->next = NULL; + goto out; + } + } + + size = 0; + cl = in; + fl = ll; + + for ( ;; ) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 proxy output chunk: %O", ngx_buf_size(cl->buf)); + + size += ngx_buf_size(cl->buf); + + if (cl->buf->flush + || cl->buf->sync + || ngx_buf_in_memory(cl->buf) + || cl->buf->in_file) + { + tl = ngx_alloc_chain_link(r->pool); + if (tl == NULL) { + return NGX_ERROR; + } + + tl->buf = cl->buf; + *ll = tl; + ll = &tl->next; + } + + if (cl->next == NULL) { + break; + } + + cl = cl->next; + } + + if (size) { + tl = ngx_chain_get_free_buf(r->pool, &ctx->free); + if (tl == NULL) { + return NGX_ERROR; + } + + b = tl->buf; + chunk = b->start; + + if (chunk == NULL) { + len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_DATA) + + ngx_http_v3_encode_varlen_int(NULL, 0xffffffffffffffffull); + + chunk = ngx_palloc(r->pool, len); + if (chunk == NULL) { + return NGX_ERROR; + } + + b->start = chunk; + b->end = chunk + len; + } + + b->tag = (ngx_buf_tag_t) &ngx_http_v3_proxy_body_output_filter; + b->memory = 0; + b->temporary = 1; + b->pos = chunk; + b->last_buf = cl->buf->last_buf; + + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, + NGX_HTTP_V3_FRAME_DATA); + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, size); + + cl->buf->last_buf = 0; + + tl->next = *fl; + *fl = tl; + + } else if (cl->buf->last_buf) { + + tl = ngx_chain_get_free_buf(r->pool, &ctx->free); + if (tl == NULL) { + return NGX_ERROR; + } + + b = tl->buf; + + b->tag = (ngx_buf_tag_t) &ngx_http_v3_proxy_body_output_filter; + b->temporary = 0; + b->memory = 0; + b->last_buf = 1; + b->pos = b->last; + + cl->buf->last_buf = 0; + + *ll = tl; + } + + *ll = NULL; + +out: + + rc = ngx_chain_writer(&r->upstream->writer, out); + + ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out, + (ngx_buf_tag_t) &ngx_http_v3_proxy_body_output_filter); + + return rc; +} + + +static ngx_int_t +ngx_http_v3_proxy_process_response(ngx_http_request_t *r) +{ + ngx_int_t rc; + ngx_table_elt_t *h; + ngx_connection_t *c; + ngx_http_upstream_t *u; + ngx_http_v3_proxy_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_v3_proxy_module); + + if (ctx == NULL) { + return NGX_ERROR; + } + + c = r->connection; + u = r->upstream; + + rc = ngx_http_v3_parse_headers(c, &ctx->parse_headers, &u->buffer); + + if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) { + return NGX_HTTP_UPSTREAM_INVALID_HEADER; + } + + if (rc > 0) { + if (u->peer.connection) { + ngx_quic_reset_stream(u->peer.connection, rc); + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "upstream sent invalid header"); + } + + return NGX_HTTP_UPSTREAM_INVALID_HEADER; + } + + /* XXX flood check */ + + if (rc != NGX_DONE) { + return rc; + } + + /* a whole header has been parsed successfully */ + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 proxy header done"); + + if (u->headers_in.status_n == 0) { + return NGX_HTTP_UPSTREAM_INVALID_HEADER; + } + + if (u->headers_in.status_n == NGX_HTTP_EARLY_HINTS) { + ctx->pseudo_done = 0; + return NGX_HTTP_UPSTREAM_EARLY_HINTS; + } + + /* + * if no "Server" and "Date" in header line, + * then add the special empty headers + */ + + if (r->upstream->headers_in.server == NULL) { + h = ngx_list_push(&r->upstream->headers_in.headers); + if (h == NULL) { + return NGX_ERROR; + } + + h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash( + ngx_hash('s', 'e'), 'r'), 'v'), 'e'), 'r'); + + ngx_str_set(&h->key, "Server"); + ngx_str_null(&h->value); + h->lowcase_key = (u_char *) "server"; + h->next = NULL; + } + + if (r->upstream->headers_in.date == NULL) { + h = ngx_list_push(&r->upstream->headers_in.headers); + if (h == NULL) { + return NGX_ERROR; + } + + h->hash = ngx_hash(ngx_hash(ngx_hash('d', 'a'), 't'), 'e'); + + ngx_str_set(&h->key, "Date"); + ngx_str_null(&h->value); + h->lowcase_key = (u_char *) "date"; + h->next = NULL; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_proxy_process_header(void *data, ngx_str_t *name, ngx_str_t *value, + ngx_uint_t index, ngx_uint_t dynamic) +{ + ngx_http_request_t *r = data; + + ngx_int_t rc; + ngx_str_t namet, valuet; + ngx_table_elt_t *h; + ngx_http_upstream_t *u; + ngx_http_v3_proxy_ctx_t *ctx; + ngx_http_upstream_header_t *hh; + ngx_http_upstream_main_conf_t *umcf; + + if (name == NULL) { + + if (dynamic) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream is using dynamic table"); + return NGX_ERROR; + } + + if (ngx_http_v3_lookup_static(r->connection, index, &namet, &valuet) + != NGX_OK) + { + return NGX_ERROR; + } + + name = &namet; + + if (value == NULL) { + value = &valuet; + } + } + + umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); + + u = r->upstream; + + ctx = ngx_http_get_module_ctx(r, ngx_http_v3_proxy_module); + + if (name->len && name->data[0] == ':') { + + if (ctx->pseudo_done) { + return NGX_HTTP_UPSTREAM_INVALID_HEADER; + } + + if (name->len == 7 && ngx_strncmp(name->data, ":status", 7) + == 0) + { + rc = ngx_atoi(value->data, value->len); + + if (rc == NGX_ERROR) { + return NGX_HTTP_UPSTREAM_INVALID_HEADER; + } + + u->headers_in.status_n = rc; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 proxy status %ui", + u->headers_in.status_n); + + } else { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 proxy pseudo header: \"%V: %V\"", + name, value); + } + + return NGX_OK; + } + + ctx->pseudo_done = 1; + + h = ngx_list_push(ctx->ctx.trailers ? &r->upstream->headers_in.trailers + : &r->upstream->headers_in.headers); + if (h == NULL) { + return NGX_ERROR; + } + + h->hash = r->header_hash; + + h->key.len = name->len; + h->value.len = value->len; + + h->key.data = ngx_pnalloc(r->pool, + h->key.len + 1 + h->value.len + 1 + h->key.len); + if (h->key.data == NULL) { + h->hash = 0; + return NGX_ERROR; + } + + h->value.data = h->key.data + h->key.len + 1; + h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1; + + ngx_memcpy(h->key.data, name->data, h->key.len); + h->key.data[h->key.len] = '\0'; + ngx_memcpy(h->value.data, value->data, h->value.len); + h->value.data[h->value.len] = '\0'; + + if (h->key.len == r->lowcase_index) { + ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len); + + } else { + ngx_strlow(h->lowcase_key, h->key.data, h->key.len); + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 proxy header: \"%V: %V\"", + name, value); + + if (u->headers_in.status_n == NGX_HTTP_EARLY_HINTS || ctx->ctx.trailers) { + return NGX_OK; + } + + hh = ngx_hash_find(&umcf->headers_in_hash, h->hash, + h->lowcase_key, h->key.len); + + if (hh) { + rc = hh->handler(r, h, hh->offset); + + if (rc != NGX_OK) { + return rc; + } + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_proxy_process_insert_count(void *data, ngx_uint_t *insert_count) +{ + ngx_http_request_t *r = data; + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream is using dynamic table"); + + return NGX_ERROR; +} + + +static ngx_int_t +ngx_http_v3_proxy_input_filter_init(void *data) +{ + ngx_http_request_t *r = data; + ngx_http_upstream_t *u; + ngx_http_proxy_ctx_t *ctx; + + u = r->upstream; + ctx = ngx_http_get_module_ctx(r, ngx_http_v3_proxy_module); + + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 proxy filter init s:%ui h:%d l:%O", + u->headers_in.status_n, ctx->head, + u->headers_in.content_length_n); + + /* as per RFC9110, 6.4.1. Content Semantics */ + + if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT + || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED + || ctx->head) + { + /* 1xx, 204, and 304 and replies to HEAD requests */ + /* no 1xx since we don't send Expect and Upgrade */ + + u->pipe->length = 0; + u->length = 0; + + } else if (u->headers_in.content_length_n == 0) { + /* empty body: special case as filter won't be called */ + + u->pipe->length = 0; + u->length = 0; + + } else { + u->pipe->length = 1; + u->length = 1; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_proxy_body_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) +{ + off_t n; + ngx_int_t rc; + ngx_buf_t *b, **prev; + ngx_chain_t *cl; + ngx_http_request_t *r; + ngx_http_upstream_t *u; + ngx_http_v3_proxy_ctx_t *ctx; + ngx_http_v3_parse_data_t *st; + ngx_http_proxy_loc_conf_t *plcf; + + if (buf->pos == buf->last) { + return NGX_OK; + } + + r = p->input_ctx; + ctx = ngx_http_get_module_ctx(r, ngx_http_v3_proxy_module); + + if (ctx == NULL) { + return NGX_ERROR; + } + + u = r->upstream; + + st = &ctx->parse_data; + + if (p->upstream_done) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0, + "http3 proxy data after close"); + return NGX_OK; + } + + if (p->length == 0) { + + ngx_log_error(NGX_LOG_WARN, p->log, 0, + "upstream sent data after trailers"); + + p->upstream_done = 1; + + return NGX_OK; + } + + b = NULL; + + if (ctx->ctx.trailers) { + rc = ngx_http_v3_proxy_process_trailer(r, buf); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_OK) { + + /* a whole response has been parsed successfully */ + + p->length = 0; + + if (buf->pos != buf->last) { + ngx_log_error(NGX_LOG_WARN, p->log, 0, + "upstream sent data after trailers"); + } + } + + goto free_buf; + } + + n = r->headers_in.content_length_n; + + plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); + + prev = &buf->shadow; + + while (buf->pos < buf->last) { + + if (st->length == 0) { + rc = ngx_http_v3_parse_data(r->connection, st, buf); + + /* XXX check flood */ + + if (rc == NGX_AGAIN) { + continue; + } + + if (rc == NGX_DONE) { + + if (plcf->upstream.pass_trailers) { + rc = ngx_http_v3_proxy_process_trailer(r, buf); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_AGAIN) { + p->length = 1; + goto free_buf; + } + } + + p->length = 0; + goto free_buf; + } + + if (rc > 0) { + + if (u->peer.connection) { + ngx_quic_reset_stream(u->peer.connection, rc); + } + + ngx_log_error(NGX_LOG_ERR, p->log, 0, + "upstream sent invalid body"); + return NGX_ERROR; + } + + if (rc == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, p->log, 0, + "upstream sent invalid body"); + return NGX_ERROR; + } + + /* rc == NGX_OK */ + } + + if (n != -1 && n - ctx->body_received < (off_t) st->length) { + ngx_log_error(NGX_LOG_WARN, p->log, 0, + "upstream sent more data than specified in " + "\"Content-Length\" header"); + return NGX_ERROR; + } + + cl = ngx_chain_get_free_buf(p->pool, &p->free); + if (cl == NULL) { + return NGX_ERROR; + } + + b = cl->buf; + + ngx_memzero(b, sizeof(ngx_buf_t)); + + b->pos = buf->pos; + b->start = buf->start; + b->end = buf->end; + b->tag = p->tag; + b->temporary = 1; + b->recycled = 1; + + *prev = b; + prev = &b->shadow; + + if (p->in) { + *p->last_in = cl; + } else { + p->in = cl; + } + p->last_in = &cl->next; + + if (buf->last - buf->pos > (ssize_t) st->length) { + ctx->body_received += st->length; + buf->pos += st->length; + st->length = 0; + + } else { + ctx->body_received += (buf->last - buf->pos); + st->length -= buf->last - buf->pos; + buf->pos = buf->last; + } + + b->last = buf->pos; + } + + if (st->length == 0) { + + if (n != -1 && ctx->body_received < n) { + p->length = 1; + + } else { + /* possible trailers */ + p->length = -1; + } + + } else { + p->length = st->length; + } + +free_buf: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, p->log, 0, + "http3 proxy body wait length %O", p->length); + + if (b) { + b->shadow = buf; + b->last_shadow = 1; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0, + "input buf %p %z", b->pos, b->last - b->pos); + + return NGX_OK; + } + + /* there is no data record in the buf, add it to free chain */ + + if (ngx_event_pipe_add_free_buf(p, buf) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_proxy_non_buffered_body_filter(void *data, ssize_t bytes) +{ + ngx_http_request_t *r = data; + + off_t n; + ngx_int_t rc; + ngx_buf_t *b, *buf; + ngx_chain_t *cl, **ll; + ngx_http_upstream_t *u; + ngx_http_v3_proxy_ctx_t *ctx; + ngx_http_v3_parse_data_t *st; + ngx_http_proxy_loc_conf_t *plcf; + + plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); + + ctx = ngx_http_get_module_ctx(r, ngx_http_v3_proxy_module); + + if (ctx == NULL) { + return NGX_ERROR; + } + + u = r->upstream; + st = &ctx->parse_data; + + n = r->headers_in.content_length_n; + + buf = &u->buffer; + + buf->pos = buf->last; + buf->last += bytes; + + if (ctx->ctx.trailers) { + rc = ngx_http_v3_proxy_process_trailer(r, buf); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_OK) { + + /* a whole response has been parsed successfully */ + + r->upstream->keepalive = !u->headers_in.connection_close; + u->length = 0; + + if (buf->pos != buf->last) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "upstream sent data after trailers"); + u->keepalive = 0; + } + } + + return NGX_OK; + } + + for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) { + ll = &cl->next; + } + + while (buf->pos < buf->last) { + + if (st->length == 0) { + rc = ngx_http_v3_parse_data(r->connection, st, buf); + + /* XXX check flood */ + + if (rc == NGX_AGAIN) { + continue; + } + + if (rc == NGX_DONE) { + + if (plcf->upstream.pass_trailers) { + rc = ngx_http_v3_proxy_process_trailer(r, buf); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_AGAIN) { + u->length = 1; + return NGX_OK; + } + } + + u->length = 0; + return NGX_OK; + } + + if (rc > 0) { + + if (u->peer.connection) { + ngx_quic_reset_stream(u->peer.connection, rc); + } + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent invalid body"); + return NGX_ERROR; + } + + if (rc == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent invalid body"); + return NGX_ERROR; + } + + /* rc == NGX_OK */ + } + + if (n != -1 && n - ctx->body_received < (off_t) st->length) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "upstream sent more data than specified in " + "\"Content-Length\" header"); + return NGX_ERROR; + } + + cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs); + if (cl == NULL) { + return NGX_ERROR; + } + + *ll = cl; + ll = &cl->next; + + b = cl->buf; + + b->flush = 1; + b->memory = 1; + + b->pos = buf->pos; + b->tag = u->output.tag; + + if (buf->last - buf->pos > (ssize_t) st->length) { + ctx->body_received += st->length; + buf->pos += st->length; + st->length = 0; + + } else { + ctx->body_received += (buf->last - buf->pos); + st->length -= buf->last - buf->pos; + buf->pos = buf->last; + } + + b->last = buf->pos; + } + + if (st->length == 0) { + + if (n != -1 && ctx->body_received < n) { + u->length = 1; + + } else { + /* possible trailers */ + u->length = -1; + } + + } else { + u->length = st->length; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_proxy_process_trailer(ngx_http_request_t *r, ngx_buf_t *buf) +{ + size_t len; + ngx_int_t rc; + ngx_buf_t *b; + ngx_http_upstream_t *u; + ngx_http_v3_proxy_ctx_t *ctx; + ngx_http_proxy_loc_conf_t *plcf; + + plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); + + ctx = ngx_http_get_module_ctx(r, ngx_http_v3_proxy_module); + + if (ctx->ctx.trailers == NULL) { + ctx->ctx.trailers = ngx_create_temp_buf(r->pool, + plcf->upstream.buffer_size); + if (ctx->ctx.trailers == NULL) { + return NGX_ERROR; + } + } + + u = r->upstream; + + b = ctx->ctx.trailers; + len = ngx_min(buf->last - buf->pos, b->end - b->last); + + b->last = ngx_cpymem(b->last, buf->pos, len); + buf->pos += len; + + rc = ngx_http_v3_parse_headers(r->connection, &ctx->parse_headers, b); + + if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) { + return NGX_HTTP_UPSTREAM_INVALID_HEADER; + } + + if (rc > 0) { + if (u->peer.connection) { + ngx_quic_reset_stream(u->peer.connection, rc); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent invalid header"); + } + + return NGX_HTTP_UPSTREAM_INVALID_HEADER; + } + + /* XXX flood check */ + + if (rc != NGX_DONE) { + return rc; + } + + /* a whole trailer has been parsed successfully */ + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 proxy trailer done"); + + return NGX_OK; +} + + +static void +ngx_http_v3_proxy_abort_request(ngx_http_request_t *r) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "abort http3 proxy request"); + + return; +} + + +static void +ngx_http_v3_proxy_finalize_request(ngx_http_request_t *r, ngx_int_t rc) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "finalize http3 proxy request"); + + return; +} diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 6865e1466..325eeb2a2 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -10,13 +10,18 @@ #include +static void ngx_http_v3_handshake_handler(ngx_connection_t *c); +static ngx_int_t ngx_http_v3_init(ngx_connection_t *c); +static void ngx_http_v3_handler(ngx_connection_t *c); static void ngx_http_v3_init_request_stream(ngx_connection_t *c); static void ngx_http_v3_wait_request_handler(ngx_event_t *rev); static void ngx_http_v3_cleanup_connection(void *data); static void ngx_http_v3_cleanup_request(void *data); static void ngx_http_v3_process_request(ngx_event_t *rev); -static ngx_int_t ngx_http_v3_process_header(ngx_http_request_t *r, - ngx_str_t *name, ngx_str_t *value); +static ngx_int_t ngx_http_v3_process_header(void *data, + ngx_str_t *name, ngx_str_t *value, ngx_uint_t index, ngx_uint_t dynamic); +static ngx_int_t ngx_http_v3_process_insert_count(void *data, + ngx_uint_t *insert_count); static ngx_int_t ngx_http_v3_validate_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value); static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, @@ -58,9 +63,11 @@ static const struct { void ngx_http_v3_init_stream(ngx_connection_t *c) { + ngx_int_t rc; ngx_http_connection_t *hc, *phc; ngx_http_v3_srv_conf_t *h3scf; ngx_http_core_loc_conf_t *clcf; + ngx_http_core_srv_conf_t *cscf; hc = c->data; @@ -72,7 +79,25 @@ ngx_http_v3_init_stream(ngx_connection_t *c) h3scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module); h3scf->quic.idle_timeout = clcf->keepalive_timeout; - ngx_quic_run(c, &h3scf->quic); + if (ngx_quic_create_connection(&h3scf->quic, c, 0) != NGX_OK) { + ngx_close_connection(c); + return; + } + + rc = ngx_quic_handshake(c); + + if (rc == NGX_AGAIN) { + cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, + ngx_http_core_module); + + ngx_add_timer(c->read, cscf->client_header_timeout); + + c->ssl->handler = ngx_http_v3_handshake_handler; + return; + } + + ngx_http_v3_handshake_handler(c); + return; } @@ -88,7 +113,7 @@ ngx_http_v3_init_stream(ngx_connection_t *c) ngx_set_connection_log(c, clcf->error_log); } - if (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { + if (c->quic->stream->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { ngx_http_v3_init_uni_stream(c); } else { @@ -97,27 +122,124 @@ ngx_http_v3_init_stream(ngx_connection_t *c) } -ngx_int_t +static void +ngx_http_v3_handshake_handler(ngx_connection_t *c) +{ + if (c->ssl && c->ssl->handshaked) { + + if (ngx_http_v3_init(c) != NGX_OK) { + ngx_http_v3_close_connection(c); + return; + } + + /*TODO read ALPN */ + + ngx_reusable_connection(c, 1); + + c->ssl->handler = ngx_http_v3_handler; + + ngx_http_v3_handler(c); + + return; + } + + if (c->read->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + } + + ngx_http_v3_close_connection(c); +} + + +static void +ngx_http_v3_handler(ngx_connection_t *c) +{ + ngx_connection_t *sc; + ngx_http_v3_session_t *h3c; + + h3c = ngx_http_v3_get_session(c); + + if (c->close) { + c->close = 0; + + if (!ngx_exiting) { + c->ssl->no_wait_shutdown = 1; + ngx_http_v3_close_connection(c); + return; + } + + if (!h3c->goaway) { + h3c->goaway = 1; + + if (!h3c->hq) { + (void) ngx_http_v3_send_goaway(c, h3c->next_request_id); + } + + ngx_quic_reject_streams(c); + } + } + + if (c->read->timedout) { + c->read->timedout = 0; + + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + + ngx_quic_set_app_error(c, NGX_HTTP_V3_ERR_NO_ERROR, + "keepalive timeout"); + ngx_http_v3_close_connection(c); + + return; + } + + while (!ngx_quic_get_error(c)) { + + sc = ngx_quic_accept_stream(c); + if (sc == NULL) { + break; + } + + ngx_http_init_connection(sc); + } + + if (ngx_quic_get_error(c)) { + ngx_http_v3_close_connection(c); + return; + } +} + + +static ngx_int_t ngx_http_v3_init(ngx_connection_t *c) { unsigned int len; const unsigned char *data; + ngx_http_connection_t *hc; ngx_http_v3_session_t *h3c; ngx_http_v3_srv_conf_t *h3scf; + ngx_http_core_srv_conf_t *cscf; ngx_http_core_loc_conf_t *clcf; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init"); + hc = c->data; + if (ngx_http_v3_init_session(c) != NGX_OK) { return NGX_ERROR; } h3c = ngx_http_v3_get_session(c); + h3c->data = hc; + clcf = ngx_http_v3_get_module_loc_conf(c, ngx_http_core_module); - ngx_add_timer(&h3c->keepalive, clcf->keepalive_timeout); + ngx_add_timer(c->read, clcf->keepalive_timeout); + cscf = ngx_http_v3_get_module_srv_conf(c, ngx_http_core_module); h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + h3c->max_literal = cscf->large_client_header_buffers.size; + h3c->max_table_capacity = h3scf->max_table_capacity; + h3c->max_blocked_streams = h3scf->max_blocked_streams; + if (h3scf->enable_hq) { if (!h3scf->enable) { h3c->hq = 1; @@ -148,34 +270,6 @@ ngx_http_v3_init(ngx_connection_t *c) } -void -ngx_http_v3_shutdown(ngx_connection_t *c) -{ - ngx_http_v3_session_t *h3c; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 shutdown"); - - h3c = ngx_http_v3_get_session(c); - - if (h3c == NULL) { - ngx_quic_finalize_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, - "connection shutdown"); - return; - } - - if (!h3c->goaway) { - h3c->goaway = 1; - - if (!h3c->hq) { - (void) ngx_http_v3_send_goaway(c, h3c->next_request_id); - } - - ngx_http_v3_shutdown_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, - "connection shutdown"); - } -} - - static void ngx_http_v3_init_request_stream(ngx_connection_t *c) { @@ -197,7 +291,7 @@ ngx_http_v3_init_request_stream(ngx_connection_t *c) clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module); - n = c->quic->id >> 2; + n = c->quic->stream->id >> 2; if (n >= clcf->keepalive_requests * 2) { ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, @@ -214,7 +308,7 @@ ngx_http_v3_init_request_stream(ngx_connection_t *c) return; } - h3c->next_request_id = c->quic->id + 0x04; + h3c->next_request_id = c->quic->stream->id + 0x04; if (n + 1 == clcf->keepalive_requests || ngx_current_msec - c->start_time > clcf->keepalive_time) @@ -228,8 +322,7 @@ ngx_http_v3_init_request_stream(ngx_connection_t *c) } } - ngx_http_v3_shutdown_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, - "reached maximum number of requests"); + ngx_quic_reject_streams(c->quic->stream->parent); } cln = ngx_pool_cleanup_add(c->pool, 0); @@ -243,8 +336,8 @@ ngx_http_v3_init_request_stream(ngx_connection_t *c) h3c->nrequests++; - if (h3c->keepalive.timer_set) { - ngx_del_timer(&h3c->keepalive); + if (c->quic->stream->parent->read->timer_set) { + ngx_del_timer(c->quic->stream->parent->read); } rev = c->read; @@ -274,14 +367,15 @@ ngx_http_v3_init_request_stream(ngx_connection_t *c) static void ngx_http_v3_wait_request_handler(ngx_event_t *rev) { - size_t size; - ssize_t n; - ngx_buf_t *b; - ngx_connection_t *c; - ngx_pool_cleanup_t *cln; - ngx_http_request_t *r; - ngx_http_connection_t *hc; - ngx_http_core_srv_conf_t *cscf; + size_t size; + ssize_t n; + ngx_buf_t *b; + ngx_connection_t *c; + ngx_pool_cleanup_t *cln; + ngx_http_request_t *r; + ngx_http_connection_t *hc; + ngx_http_core_srv_conf_t *cscf; + ngx_http_v3_parse_headers_t *st; c = rev->data; @@ -370,6 +464,7 @@ ngx_http_v3_wait_request_handler(ngx_event_t *rev) c->log->action = "reading client request"; ngx_reusable_connection(c, 0); + ngx_reusable_connection(c->quic->stream->parent, 0); r = ngx_http_create_request(c); if (r == NULL) { @@ -388,8 +483,15 @@ ngx_http_v3_wait_request_handler(ngx_event_t *rev) r->v3_parse->header_limit = cscf->large_client_header_buffers.size * cscf->large_client_header_buffers.num; + st = &r->v3_parse->headers; + + st->max_literal = cscf->large_client_header_buffers.size; + st->process_insert_count = ngx_http_v3_process_insert_count; + st->process_header = ngx_http_v3_process_header; + st->data = r; + c->data = r; - c->requests = (c->quic->id >> 2) + 1; + c->requests = (c->quic->stream->id >> 2) + 1; cln = ngx_pool_cleanup_add(r->pool, 0); if (cln == NULL) { @@ -414,9 +516,9 @@ ngx_http_v3_reset_stream(ngx_connection_t *c) if (!c->read->eof && !h3c->hq && h3c->known_streams[NGX_HTTP_V3_STREAM_SERVER_DECODER] - && (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0) + && (c->quic->stream->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0) { - (void) ngx_http_v3_send_cancel_stream(c, c->quic->id); + (void) ngx_http_v3_send_cancel_stream(c, c->quic->stream->id); } if (c->timedout) { @@ -436,15 +538,27 @@ ngx_http_v3_cleanup_connection(void *data) { ngx_connection_t *c = data; + ngx_connection_t *pc; ngx_http_v3_session_t *h3c; ngx_http_core_loc_conf_t *clcf; h3c = ngx_http_v3_get_session(c); - if (--h3c->nrequests == 0) { - clcf = ngx_http_v3_get_module_loc_conf(c, ngx_http_core_module); - ngx_add_timer(&h3c->keepalive, clcf->keepalive_timeout); + if (--h3c->nrequests) { + return; + } + + if (h3c->goaway) { + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, + "keepalive shutdown"); + return; } + + clcf = ngx_http_v3_get_module_loc_conf(c, ngx_http_core_module); + + pc = c->quic->stream->parent; + ngx_add_timer(pc->read, clcf->keepalive_timeout); + ngx_reusable_connection(pc, 1); } @@ -484,10 +598,10 @@ ngx_http_v3_process_request(ngx_event_t *rev) return; } - h3c = ngx_http_v3_get_session(c); - st = &r->v3_parse->headers; + h3c = ngx_http_v3_get_session(c); + b = r->header_in; for ( ;; ) { @@ -549,6 +663,10 @@ ngx_http_v3_process_request(ngx_event_t *rev) break; } + if (rc == NGX_ABORT) { + break; + } + r->request_length += b->pos - p; h3c->total_bytes += b->pos - p; @@ -576,24 +694,19 @@ ngx_http_v3_process_request(ngx_event_t *rev) break; } - if (rc == NGX_AGAIN) { - continue; - } - - /* rc == NGX_OK || rc == NGX_DONE */ + if (rc == NGX_DONE) { - h3c->payload_bytes += ngx_http_v3_encode_field_l(NULL, - &st->field_rep.field.name, - &st->field_rep.field.value); + if (st->insert_count > 0) { + if (ngx_http_v3_send_ack_section(c, c->quic->stream->id) + != NGX_OK) + { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + break; + } - if (ngx_http_v3_process_header(r, &st->field_rep.field.name, - &st->field_rep.field.value) - != NGX_OK) - { - break; - } + ngx_http_v3_ack_insert_count(c, st->insert_count); + } - if (rc == NGX_DONE) { if (ngx_http_v3_process_request_header(r) != NGX_OK) { break; } @@ -601,6 +714,8 @@ ngx_http_v3_process_request(ngx_event_t *rev) ngx_http_process_request(r); break; } + + /* rc == NGX_AGAIN */ } ngx_http_run_posted_requests(c); @@ -610,31 +725,83 @@ ngx_http_v3_process_request(ngx_event_t *rev) static ngx_int_t -ngx_http_v3_process_header(ngx_http_request_t *r, ngx_str_t *name, - ngx_str_t *value) +ngx_http_v3_process_header(void *data, ngx_str_t *name, ngx_str_t *value, + ngx_uint_t index, ngx_uint_t dynamic) { + ngx_http_request_t *r = data; + + u_char *p; size_t len; + ngx_str_t namet, valuet; ngx_table_elt_t *h; + ngx_connection_t *c; ngx_http_header_t *hh; + ngx_http_v3_session_t *h3c; ngx_http_core_srv_conf_t *cscf; ngx_http_core_main_conf_t *cmcf; static ngx_str_t cookie = ngx_string("cookie"); + c = r->connection; + h3c = ngx_http_v3_get_session(c); + + if (name == NULL) { + + if (dynamic) { + + if (ngx_http_v3_lookup(c, index, &namet, &valuet) != NGX_OK) { + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; + } + + p = ngx_pstrdup(c->pool, &namet); + if (p == NULL) { + return NGX_ERROR; + } + + namet.data = p; + name = &namet; + + if (value == NULL) { + p = ngx_pstrdup(r->pool, &valuet); + if (p == NULL) { + return NGX_ERROR; + } + + valuet.data = p; + value = &valuet; + } + + } else { + if (ngx_http_v3_lookup_static(c, index, &namet, &valuet) != NGX_OK) + { + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; + } + + name = &namet; + + if (value == NULL) { + value = &valuet; + } + } + } + + h3c->payload_bytes += ngx_http_v3_encode_field_l(NULL, + name->data, name->len, value->data, value->len); + len = name->len + value->len; if (len > r->v3_parse->header_limit) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "client sent too large header"); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE); - return NGX_ERROR; + return NGX_ABORT; } r->v3_parse->header_limit -= len; if (ngx_http_v3_validate_header(r, name, value) != NGX_OK) { ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); - return NGX_ERROR; + return NGX_ABORT; } if (r->invalid_header) { @@ -653,21 +820,19 @@ ngx_http_v3_process_header(ngx_http_request_t *r, ngx_str_t *name, } if (ngx_http_v3_init_pseudo_headers(r) != NGX_OK) { - return NGX_ERROR; + return NGX_ABORT; } if (name->len == cookie.len && ngx_memcmp(name->data, cookie.data, cookie.len) == 0) { if (ngx_http_v3_cookie(r, value) != NGX_OK) { - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_ERROR; } } else { h = ngx_list_push(&r->headers_in.headers); if (h == NULL) { - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_ERROR; } @@ -682,7 +847,7 @@ ngx_http_v3_process_header(ngx_http_request_t *r, ngx_str_t *name, h->lowcase_key, h->key.len); if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { - return NGX_ERROR; + return NGX_ABORT; } } @@ -692,6 +857,32 @@ ngx_http_v3_process_header(ngx_http_request_t *r, ngx_str_t *name, } +static ngx_int_t +ngx_http_v3_process_insert_count(void *data, ngx_uint_t *insert_count) +{ + ngx_http_request_t *r = data; + + ngx_int_t rc; + ngx_uint_t n; + + n = *insert_count; + + rc = ngx_http_v3_decode_insert_count(r->connection, &n); + if (rc != NGX_OK) { + return rc; + } + + rc = ngx_http_v3_check_insert_count(r->connection, n); + if (rc != NGX_OK) { + return rc; + } + + *insert_count = n; + + return NGX_OK; +} + + static ngx_int_t ngx_http_v3_validate_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value) @@ -893,7 +1084,7 @@ ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, failed: ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); - return NGX_ERROR; + return NGX_ABORT; } diff --git a/src/http/v3/ngx_http_v3_table.c b/src/http/v3/ngx_http_v3_table.c index 428e7326b..7cf33c755 100644 --- a/src/http/v3/ngx_http_v3_table.c +++ b/src/http/v3/ngx_http_v3_table.c @@ -284,16 +284,14 @@ ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) ngx_uint_t max, prev_max; ngx_http_v3_field_t **elts; ngx_http_v3_session_t *h3c; - ngx_http_v3_srv_conf_t *h3scf; ngx_http_v3_dynamic_table_t *dt; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 set capacity %ui", capacity); h3c = ngx_http_v3_get_session(c); - h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); - if (capacity > h3scf->max_table_capacity) { + if (capacity > h3c->max_table_capacity) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client exceeded http3_max_table_capacity limit"); return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; @@ -508,7 +506,6 @@ ngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count) { ngx_uint_t max_entries, full_range, max_value, max_wrapped, req_insert_count; - ngx_http_v3_srv_conf_t *h3scf; ngx_http_v3_session_t *h3c; ngx_http_v3_dynamic_table_t *dt; @@ -521,9 +518,7 @@ ngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count) h3c = ngx_http_v3_get_session(c); dt = &h3c->table; - h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); - - max_entries = h3scf->max_table_capacity / 32; + max_entries = h3c->max_table_capacity / 32; full_range = 2 * max_entries; if (*insert_count > full_range) { @@ -563,7 +558,6 @@ ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count) ngx_pool_cleanup_t *cln; ngx_http_v3_block_t *block; ngx_http_v3_session_t *h3c; - ngx_http_v3_srv_conf_t *h3scf; ngx_http_v3_dynamic_table_t *dt; h3c = ngx_http_v3_get_session(c); @@ -605,9 +599,7 @@ ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count) } if (block->queue.prev == NULL) { - h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); - - if (h3c->nblocked == h3scf->max_blocked_streams) { + if (h3c->nblocked == h3c->max_blocked_streams) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client exceeded http3_max_blocked_streams limit"); @@ -618,7 +610,7 @@ ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count) } h3c->nblocked++; - ngx_queue_insert_tail(&h3c->blocked, &block->queue); + ngx_queue_insert_tail(&h3c->queue, &block->queue); } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, @@ -669,8 +661,8 @@ ngx_http_v3_new_entry(ngx_connection_t *c) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 new dynamic entry, blocked:%ui", h3c->nblocked); - while (!ngx_queue_empty(&h3c->blocked)) { - q = ngx_queue_head(&h3c->blocked); + while (!ngx_queue_empty(&h3c->queue)) { + q = ngx_queue_head(&h3c->queue); block = (ngx_http_v3_block_t *) q; bc = block->connection; diff --git a/src/http/v3/ngx_http_v3_table.h b/src/http/v3/ngx_http_v3_table.h index 1c2fb17b9..b7485931c 100644 --- a/src/http/v3/ngx_http_v3_table.h +++ b/src/http/v3/ngx_http_v3_table.h @@ -14,6 +14,29 @@ #include +/* static table indices */ +#define NGX_HTTP_V3_HEADER_AUTHORITY 0 +#define NGX_HTTP_V3_HEADER_PATH_ROOT 1 +#define NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO 4 +#define NGX_HTTP_V3_HEADER_DATE 6 +#define NGX_HTTP_V3_HEADER_LAST_MODIFIED 10 +#define NGX_HTTP_V3_HEADER_LOCATION 12 +#define NGX_HTTP_V3_HEADER_METHOD_GET 17 +#define NGX_HTTP_V3_HEADER_METHOD_HEAD 18 +#define NGX_HTTP_V3_HEADER_METHOD_POST 20 +#define NGX_HTTP_V3_HEADER_METHOD_PUT 21 +#define NGX_HTTP_V3_HEADER_SCHEME_HTTP 22 +#define NGX_HTTP_V3_HEADER_SCHEME_HTTPS 23 +#define NGX_HTTP_V3_HEADER_STATUS_103 24 +#define NGX_HTTP_V3_HEADER_STATUS_200 25 +#define NGX_HTTP_V3_HEADER_ACCEPT_ENCODING 31 +#define NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN 53 +#define NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING 59 +#define NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE 72 +#define NGX_HTTP_V3_HEADER_SERVER 92 +#define NGX_HTTP_V3_HEADER_USER_AGENT 95 + + typedef struct { ngx_str_t name; ngx_str_t value; diff --git a/src/http/v3/ngx_http_v3_uni.c b/src/http/v3/ngx_http_v3_uni.c index 302064b8b..a05e3e706 100644 --- a/src/http/v3/ngx_http_v3_uni.c +++ b/src/http/v3/ngx_http_v3_uni.c @@ -10,24 +10,38 @@ #include +typedef ngx_int_t (*ngx_http_v3_uni_parse_pt)(ngx_connection_t *c, + void *data, ngx_buf_t *b); + + typedef struct { - ngx_http_v3_parse_uni_t parse; - ngx_int_t index; + ngx_http_v3_uni_parse_pt parse; + void *data; + ngx_int_t index; } ngx_http_v3_uni_stream_t; +static ngx_int_t ngx_http_v3_uni_parse_type(ngx_connection_t *c, void *data, + ngx_buf_t *b); +static ngx_int_t ngx_http_v3_parse_unknown(ngx_connection_t *c, void *data, + ngx_buf_t *b); static void ngx_http_v3_close_uni_stream(ngx_connection_t *c); +static ngx_int_t ngx_http_v3_register_uni_stream(ngx_connection_t *c, + uint64_t type); static void ngx_http_v3_uni_read_handler(ngx_event_t *rev); static void ngx_http_v3_uni_dummy_read_handler(ngx_event_t *wev); static void ngx_http_v3_uni_dummy_write_handler(ngx_event_t *wev); +static ngx_int_t ngx_http_v3_cancel_stream(ngx_connection_t *c, + ngx_uint_t stream_id); void ngx_http_v3_init_uni_stream(ngx_connection_t *c) { - uint64_t n; - ngx_http_v3_session_t *h3c; - ngx_http_v3_uni_stream_t *us; + uint64_t n; + ngx_http_v3_session_t *h3c; + ngx_http_v3_uni_stream_t *us; + ngx_http_v3_parse_varlen_int_t *st; h3c = ngx_http_v3_get_session(c); if (h3c->hq) { @@ -41,7 +55,7 @@ ngx_http_v3_init_uni_stream(ngx_connection_t *c) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init uni stream"); - n = c->quic->id >> 2; + n = c->quic->stream->id >> 2; if (n >= NGX_HTTP_V3_MAX_UNI_STREAMS) { ngx_http_v3_finalize_connection(c, @@ -52,8 +66,6 @@ ngx_http_v3_init_uni_stream(ngx_connection_t *c) return; } - ngx_quic_cancelable_stream(c); - us = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_uni_stream_t)); if (us == NULL) { ngx_http_v3_finalize_connection(c, @@ -66,8 +78,20 @@ ngx_http_v3_init_uni_stream(ngx_connection_t *c) us->index = -1; - c->data = us; + st = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_parse_varlen_int_t)); + if (st == NULL) { + ngx_http_v3_finalize_connection(c, + NGX_HTTP_V3_ERR_INTERNAL_ERROR, + "memory allocation error"); + c->data = NULL; + ngx_http_v3_close_uni_stream(c); + return; + } + us->parse = ngx_http_v3_uni_parse_type; + us->data = st; + + c->data = us; c->read->handler = ngx_http_v3_uni_read_handler; c->write->handler = ngx_http_v3_uni_dummy_write_handler; @@ -75,6 +99,107 @@ ngx_http_v3_init_uni_stream(ngx_connection_t *c) } +static ngx_int_t +ngx_http_v3_uni_parse_type(ngx_connection_t *c, void *data, ngx_buf_t *b) +{ + ngx_http_v3_parse_varlen_int_t *st = data; + + ngx_int_t rc; + ngx_http_v3_session_t *h3c; + ngx_http_v3_uni_stream_t *us; + ngx_http_v3_parse_control_t *stc; + ngx_http_v3_parse_encoder_t *ste; + ngx_http_v3_parse_decoder_t *std; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse uni"); + + us = c->data; + + rc = ngx_http_v3_parse_varlen_int(c, st, b); + if (rc != NGX_DONE) { + return rc; + } + + rc = ngx_http_v3_register_uni_stream(c, st->value); + if (rc != NGX_OK) { + return rc; + } + + h3c = ngx_http_v3_get_session(c); + + switch (st->value) { + + case NGX_HTTP_V3_STREAM_CONTROL: + stc = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_parse_control_t)); + if (stc == NULL) { + return NGX_ERROR; + } + + stc->set_param = (ngx_http_v3_set_param_pt) ngx_http_v3_set_param; + stc->data = c; + stc->max_literal = h3c->max_literal; + + us->parse = (ngx_http_v3_uni_parse_pt) ngx_http_v3_parse_control; + us->data = stc; + + break; + + case NGX_HTTP_V3_STREAM_ENCODER: + ste = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_parse_encoder_t)); + if (ste == NULL) { + return NGX_ERROR; + } + + ste->ref_insert = (ngx_http_v3_ref_insert_pt) ngx_http_v3_ref_insert; + ste->insert = (ngx_http_v3_insert_pt) ngx_http_v3_insert; + ste->duplicate = (ngx_http_v3_duplicate_pt) ngx_http_v3_duplicate; + ste->set_capacity = + (ngx_http_v3_set_capacity_pt) ngx_http_v3_set_capacity; + ste->data = c; + ste->max_literal = h3c->max_literal; + + us->parse = (ngx_http_v3_uni_parse_pt) ngx_http_v3_parse_encoder; + us->data = ste; + + break; + + case NGX_HTTP_V3_STREAM_DECODER: + std = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_parse_decoder_t)); + if (std == NULL) { + return NGX_ERROR; + } + + std->ack_section = (ngx_http_v3_ack_section_pt) ngx_http_v3_ack_section; + std->cancel_stream = + (ngx_http_v3_cancel_stream_pt) ngx_http_v3_cancel_stream; + std->inc_insert_count = + (ngx_http_v3_inc_insert_count_pt) ngx_http_v3_inc_insert_count; + std->data = c; + std->max_literal = h3c->max_literal; + + us->parse = (ngx_http_v3_uni_parse_pt) ngx_http_v3_parse_decoder; + us->data = std; + + break; + + default: + us->parse = (ngx_http_v3_uni_parse_pt) ngx_http_v3_parse_unknown; + us->data = NULL; + } + + return us->parse(c, us->data, b); +} + + +static ngx_int_t +ngx_http_v3_parse_unknown(ngx_connection_t *c, void *data, ngx_buf_t *b) +{ + b->pos = b->last; + + return NGX_AGAIN; +} + + static void ngx_http_v3_close_uni_stream(ngx_connection_t *c) { @@ -101,7 +226,7 @@ ngx_http_v3_close_uni_stream(ngx_connection_t *c) } -ngx_int_t +static ngx_int_t ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type) { ngx_int_t index; @@ -224,7 +349,7 @@ ngx_http_v3_uni_read_handler(ngx_event_t *rev) return; } - rc = ngx_http_v3_parse_uni(c, &us->parse, &b); + rc = us->parse(c, us->data, &b); if (rc == NGX_DONE) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, @@ -342,8 +467,6 @@ ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type) goto failed; } - ngx_quic_cancelable_stream(sc); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 create uni stream, type:%ui", type); @@ -393,11 +516,10 @@ failed: ngx_int_t ngx_http_v3_send_settings(ngx_connection_t *c) { - u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 6]; - size_t n; - ngx_connection_t *cc; - ngx_http_v3_session_t *h3c; - ngx_http_v3_srv_conf_t *h3scf; + u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 6]; + size_t n; + ngx_connection_t *cc; + ngx_http_v3_session_t *h3c; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send settings"); @@ -406,26 +528,25 @@ ngx_http_v3_send_settings(ngx_connection_t *c) return NGX_ERROR; } - h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + h3c = ngx_http_v3_get_session(c); n = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY); - n += ngx_http_v3_encode_varlen_int(NULL, h3scf->max_table_capacity); + n += ngx_http_v3_encode_varlen_int(NULL, h3c->max_table_capacity); n += ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_PARAM_BLOCKED_STREAMS); - n += ngx_http_v3_encode_varlen_int(NULL, h3scf->max_blocked_streams); + n += ngx_http_v3_encode_varlen_int(NULL, h3c->max_blocked_streams); p = (u_char *) ngx_http_v3_encode_varlen_int(buf, NGX_HTTP_V3_FRAME_SETTINGS); p = (u_char *) ngx_http_v3_encode_varlen_int(p, n); p = (u_char *) ngx_http_v3_encode_varlen_int(p, NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY); - p = (u_char *) ngx_http_v3_encode_varlen_int(p, h3scf->max_table_capacity); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, h3c->max_table_capacity); p = (u_char *) ngx_http_v3_encode_varlen_int(p, NGX_HTTP_V3_PARAM_BLOCKED_STREAMS); - p = (u_char *) ngx_http_v3_encode_varlen_int(p, h3scf->max_blocked_streams); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, h3c->max_blocked_streams); n = p - buf; - h3c = ngx_http_v3_get_session(c); h3c->total_bytes += n; if (cc->send(cc, buf, n) != (ssize_t) n) { @@ -610,7 +731,7 @@ failed: } -ngx_int_t +static ngx_int_t ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, diff --git a/src/http/v3/ngx_http_v3_uni.h b/src/http/v3/ngx_http_v3_uni.h index 280589469..b424509c6 100644 --- a/src/http/v3/ngx_http_v3_uni.h +++ b/src/http/v3/ngx_http_v3_uni.h @@ -15,9 +15,6 @@ void ngx_http_v3_init_uni_stream(ngx_connection_t *c); -ngx_int_t ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type); - -ngx_int_t ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id); ngx_connection_t *ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type);