diff options
author | Vladimir Homutov <vl@nginx.com> | 2020-03-18 13:02:19 +0300 |
---|---|---|
committer | Vladimir Homutov <vl@nginx.com> | 2020-03-18 13:02:19 +0300 |
commit | 023dbc3cfb73793ac84557ee0d228e63a6cd2308 (patch) | |
tree | cfb12ad3fcefbc05f1cf6570e144160a9f75febd /src | |
parent | 23dc6a68a4c94300896c76e2161d18d11d89d3ee (diff) | |
download | nginx-023dbc3cfb73793ac84557ee0d228e63a6cd2308.tar.gz nginx-023dbc3cfb73793ac84557ee0d228e63a6cd2308.zip |
Style and handlers.
Cleanup in ngx_event_quic.c:
+ reorderded functions, structures
+ added missing prototypes
+ added separate handlers for each frame type
+ numerous indentation/comments/TODO fixes
+ removed non-implemented qc->state and corresponding enum;
this requires deep thinking, stub was unused.
+ streams inside quic connection are now in own structure
Diffstat (limited to 'src')
-rw-r--r-- | src/event/ngx_event_quic.c | 1585 |
1 files changed, 812 insertions, 773 deletions
diff --git a/src/event/ngx_event_quic.c b/src/event/ngx_event_quic.c index 000b8be8b..e6ee6d2a6 100644 --- a/src/event/ngx_event_quic.c +++ b/src/event/ngx_event_quic.c @@ -9,67 +9,38 @@ #include <ngx_event.h> -/* TODO: real states, these are stubs */ -typedef enum { - NGX_QUIC_ST_INITIAL, - NGX_QUIC_ST_HANDSHAKE, - NGX_QUIC_ST_APP_DATA -} ngx_quic_state_t; - - -struct ngx_quic_connection_s { - - ngx_quic_state_t state; - ngx_ssl_t *ssl; - - ngx_quic_frame_t *frames; - - ngx_str_t scid; - ngx_str_t dcid; - ngx_str_t token; - - /* current packet numbers for each namespace */ - ngx_uint_t initial_pn; - ngx_uint_t handshake_pn; - ngx_uint_t appdata_pn; - - ngx_quic_secrets_t secrets; - - /* streams */ - ngx_rbtree_t stree; - ngx_rbtree_node_t stree_sentinel; - ngx_msec_t stream_timeout; - ngx_connection_handler_pt stream_handler; -}; +typedef struct { + ngx_rbtree_node_t node; + ngx_buf_t *b; + ngx_connection_t *c; + ngx_quic_stream_t s; +} ngx_quic_stream_node_t; typedef struct { - ngx_rbtree_node_t node; - ngx_buf_t *b; - ngx_connection_t *c; - ngx_quic_stream_t s; -} ngx_quic_stream_node_t; + ngx_rbtree_t tree; + ngx_rbtree_node_t sentinel; + ngx_msec_t timeout; + ngx_connection_handler_pt handler; +} ngx_quic_streams_t; -static ngx_int_t ngx_quic_input(ngx_connection_t *c, ngx_buf_t *b); -static ngx_int_t ngx_quic_output(ngx_connection_t *c); +struct ngx_quic_connection_s { + ngx_str_t scid; + ngx_str_t dcid; + ngx_str_t token; -static ngx_int_t ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl, - ngx_quic_header_t *pkt); -static void ngx_quic_close_connection(ngx_connection_t *c); + /* current packet numbers for each namespace */ + ngx_uint_t initial_pn; + ngx_uint_t handshake_pn; + ngx_uint_t appdata_pn; -static ngx_quic_stream_node_t *ngx_quic_stream_lookup(ngx_rbtree_t *rbtree, - ngx_uint_t key); -static void ngx_quic_rbtree_insert_stream(ngx_rbtree_node_t *temp, - ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); + ngx_quic_secrets_t secrets; + ngx_ssl_t *ssl; + ngx_quic_frame_t *frames; -static void ngx_quic_handshake_handler(ngx_event_t *rev); -static ngx_int_t ngx_quic_handshake_input(ngx_connection_t *c, - ngx_quic_header_t *pkt); -static ngx_int_t ngx_quic_initial_input(ngx_connection_t *c, - ngx_quic_header_t *pkt); -static ngx_int_t ngx_quic_app_input(ngx_connection_t *c, - ngx_quic_header_t *pkt); + ngx_quic_streams_t streams; +}; #if BORINGSSL_API_VERSION >= 10 @@ -84,6 +55,7 @@ static int ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level, const uint8_t *read_secret, const uint8_t *write_secret, size_t secret_len); #endif + static int ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level, const uint8_t *data, size_t len); static int ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn); @@ -91,6 +63,44 @@ static int ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level, uint8_t alert); +static ngx_int_t ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl, + ngx_quic_header_t *pkt); +static ngx_int_t ngx_quic_init_connection(ngx_connection_t *c); +static void ngx_quic_handshake_handler(ngx_event_t *rev); +static void ngx_quic_close_connection(ngx_connection_t *c); + +static ngx_int_t ngx_quic_input(ngx_connection_t *c, ngx_buf_t *b); +static ngx_int_t ngx_quic_initial_input(ngx_connection_t *c, + ngx_quic_header_t *pkt); +static ngx_int_t ngx_quic_handshake_input(ngx_connection_t *c, + ngx_quic_header_t *pkt); +static ngx_int_t ngx_quic_app_input(ngx_connection_t *c, + ngx_quic_header_t *pkt); +static ngx_int_t ngx_quic_payload_handler(ngx_connection_t *c, + ngx_quic_header_t *pkt); + +static ngx_int_t ngx_quic_handle_ack_frame(ngx_connection_t *c, + ngx_quic_header_t *pkt, ngx_quic_ack_frame_t *f); +static ngx_int_t ngx_quic_handle_crypto_frame(ngx_connection_t *c, + ngx_quic_header_t *pkt, ngx_quic_crypto_frame_t *frame); +static ngx_int_t ngx_quic_handle_stream_frame(ngx_connection_t *c, + ngx_quic_header_t *pkt, ngx_quic_stream_frame_t *frame); + +static void ngx_quic_queue_frame(ngx_quic_connection_t *qc, + ngx_quic_frame_t *frame); + +static ngx_int_t ngx_quic_output(ngx_connection_t *c); +ngx_int_t ngx_quic_frames_send(ngx_connection_t *c, ngx_quic_frame_t *start, + ngx_quic_frame_t *end, size_t total); +static ngx_int_t ngx_quic_send_packet(ngx_connection_t *c, + ngx_quic_connection_t *qc, enum ssl_encryption_level_t level, + ngx_str_t *payload); + + +static void ngx_quic_rbtree_insert_stream(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); +static ngx_quic_stream_node_t *ngx_quic_find_stream(ngx_rbtree_t *rbtree, + ngx_uint_t key); 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, @@ -98,6 +108,7 @@ static ssize_t ngx_quic_stream_send(ngx_connection_t *c, u_char *buf, static ngx_chain_t *ngx_quic_stream_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit); + static SSL_QUIC_METHOD quic_method = { #if BORINGSSL_API_VERSION >= 10 ngx_quic_set_read_secret, @@ -118,6 +129,142 @@ ngx_quic_init_ssl_methods(SSL_CTX* ctx) } +#if BORINGSSL_API_VERSION >= 10 + +static int +ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn, + enum ssl_encryption_level_t level, const SSL_CIPHER *cipher, + const uint8_t *rsecret, size_t secret_len) +{ + ngx_connection_t *c; + + c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); + + ngx_quic_hexdump(c->log, "level:%d read secret", + rsecret, secret_len, level); + + return ngx_quic_set_encryption_secret(c->pool, ssl_conn, level, + rsecret, secret_len, + &c->quic->secrets.client); +} + + +static int +ngx_quic_set_write_secret(ngx_ssl_conn_t *ssl_conn, + enum ssl_encryption_level_t level, const SSL_CIPHER *cipher, + const uint8_t *wsecret, size_t secret_len) +{ + ngx_connection_t *c; + + c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); + + ngx_quic_hexdump(c->log, "level:%d write secret", + wsecret, secret_len, level); + + return ngx_quic_set_encryption_secret(c->pool, ssl_conn, level, + wsecret, secret_len, + &c->quic->secrets.server); +} + +#else + +static int +ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn, + enum ssl_encryption_level_t level, const uint8_t *rsecret, + const uint8_t *wsecret, size_t secret_len) +{ + ngx_int_t rc; + ngx_connection_t *c; + + c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); + + ngx_quic_hexdump(c->log, "level:%d read", rsecret, secret_len, level); + ngx_quic_hexdump(c->log, "level:%d write", wsecret, secret_len, level); + + rc = ngx_quic_set_encryption_secret(c->pool, ssl_conn, level, + rsecret, secret_len, + &c->quic->secrets.client); + if (rc != 1) { + return rc; + } + + return ngx_quic_set_encryption_secret(c->pool, ssl_conn, level, + wsecret, secret_len, + &c->quic->secrets.server); +} + +#endif + + +static int +ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn, + enum ssl_encryption_level_t level, const uint8_t *data, size_t len) +{ + u_char *p; + ngx_quic_frame_t *frame; + ngx_connection_t *c; + ngx_quic_connection_t *qc; + + c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); + qc = c->quic; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, + "ngx_quic_add_handshake_data"); + + frame = ngx_pcalloc(c->pool, sizeof(ngx_quic_frame_t)); + if (frame == NULL) { + return 0; + } + + p = ngx_pnalloc(c->pool, len); + if (p == NULL) { + return 0; + } + + ngx_memcpy(p, data, len); + + frame->level = level; + frame->type = NGX_QUIC_FT_CRYPTO; + frame->u.crypto.len = len; + frame->u.crypto.data = p; + + ngx_sprintf(frame->info, "crypto, generated by SSL len=%ui level=%d", len, level); + + ngx_quic_queue_frame(qc, frame); + + return 1; +} + + +static int +ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn) +{ + ngx_connection_t *c; + + c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "ngx_quic_flush_flight()"); + + return 1; +} + + +static int +ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level, + uint8_t alert) +{ + ngx_connection_t *c; + + c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "ngx_quic_send_alert(), lvl=%d, alert=%d", + (int) level, (int) alert); + + return 1; +} + + void ngx_quic_run(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_msec_t timeout, ngx_connection_handler_pt handler) @@ -144,8 +291,8 @@ ngx_quic_run(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_msec_t timeout, } // we don't need stream handler for initial packet processing - c->quic->stream_handler = handler; - c->quic->stream_timeout = timeout; + c->quic->streams.handler = handler; + c->quic->streams.timeout = timeout; ngx_add_timer(c->read, timeout); @@ -155,13 +302,144 @@ ngx_quic_run(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_msec_t timeout, } +static ngx_int_t +ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl, + ngx_quic_header_t *pkt) +{ + ngx_quic_connection_t *qc; + + if (ngx_buf_size(pkt->raw) < 1200) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "too small UDP datagram"); + return NGX_ERROR; + } + + if (ngx_quic_parse_long_header(pkt) != NGX_OK) { + return NGX_ERROR; + } + + if ((pkt->flags & 0xf0) != NGX_QUIC_PKT_INITIAL) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "invalid initial packet: 0x%xi", pkt->flags); + return NGX_ERROR; + } + + if (ngx_quic_parse_initial_header(pkt) != NGX_OK) { + return NGX_ERROR; + } + + qc = ngx_pcalloc(c->pool, sizeof(ngx_quic_connection_t)); + if (qc == NULL) { + return NGX_ERROR; + } + + ngx_rbtree_init(&qc->streams.tree, &qc->streams.sentinel, + ngx_quic_rbtree_insert_stream); + + c->quic = qc; + qc->ssl = ssl; + + qc->dcid.len = pkt->dcid.len; + qc->dcid.data = ngx_pnalloc(c->pool, pkt->dcid.len); + if (qc->dcid.data == NULL) { + return NGX_ERROR; + } + ngx_memcpy(qc->dcid.data, pkt->dcid.data, qc->dcid.len); + + qc->scid.len = pkt->scid.len; + qc->scid.data = ngx_pnalloc(c->pool, qc->scid.len); + if (qc->scid.data == NULL) { + return NGX_ERROR; + } + ngx_memcpy(qc->scid.data, pkt->scid.data, qc->scid.len); + + qc->token.len = pkt->token.len; + qc->token.data = ngx_pnalloc(c->pool, qc->token.len); + if (qc->token.data == NULL) { + return NGX_ERROR; + } + ngx_memcpy(qc->token.data, pkt->token.data, qc->token.len); + + + if (ngx_quic_set_initial_secret(c->pool, &qc->secrets, &qc->dcid) + != NGX_OK) + { + return NGX_ERROR; + } + + pkt->secret = &qc->secrets.client.in; + pkt->level = ssl_encryption_initial; + + if (ngx_quic_decrypt(c->pool, NULL, pkt) != NGX_OK) { + return NGX_ERROR; + } + + if (ngx_quic_init_connection(c) != NGX_OK) { + return NGX_ERROR; + } + + return ngx_quic_payload_handler(c, pkt); +} + + +static ngx_int_t +ngx_quic_init_connection(ngx_connection_t *c) +{ + int n, sslerr; + ngx_ssl_conn_t *ssl_conn; + ngx_quic_connection_t *qc; + + static const uint8_t params[] = + "\x00\x29" /* parameters length: 41 bytes */ + "\x00\x0e\x00\x01\x05" /* active connection id limit: 5 */ + "\x00\x04\x00\x04\x80\x98\x96\x80" /* initial max data = 10000000 */ + "\x00\x09\x00\x01\x03" /* initial max streams uni: 3 */ + "\x00\x08\x00\x01\x10" /* initial max streams bidi: 16 */ + "\x00\x05\x00\x02\x40\xff" /* initial max stream bidi local: 255 */ + "\x00\x06\x00\x02\x40\xff" /* initial max stream bidi remote: 255 */ + "\x00\x07\x00\x02\x40\xff"; /* initial max stream data uni: 255 */ + + qc = c->quic; + + if (ngx_ssl_create_connection(qc->ssl, c, NGX_SSL_BUFFER) != NGX_OK) { + return NGX_ERROR; + } + + ssl_conn = c->ssl->connection; + + if (SSL_set_quic_transport_params(ssl_conn, params, sizeof(params) - 1) == 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "SSL_set_quic_transport_params() failed"); + return NGX_ERROR; + } + + n = SSL_do_handshake(ssl_conn); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); + + if (n == -1) { + sslerr = SSL_get_error(ssl_conn, n); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", + sslerr); + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "SSL_quic_read_level: %d, SSL_quic_write_level: %d", + (int) SSL_quic_read_level(ssl_conn), + (int) SSL_quic_write_level(ssl_conn)); + + return NGX_OK; +} + + static void ngx_quic_handshake_handler(ngx_event_t *rev) { - ssize_t n; - ngx_connection_t *c; - u_char buf[512]; - ngx_buf_t b; + ssize_t n; + ngx_buf_t b; + ngx_connection_t *c; + + u_char buf[512]; b.start = buf; b.end = buf + 512; @@ -231,14 +509,6 @@ ngx_quic_close_connection(ngx_connection_t *c) } -ngx_connection_t * -ngx_quic_create_uni_stream(ngx_connection_t *c) -{ - /* XXX */ - return NULL; -} - - static ngx_int_t ngx_quic_input(ngx_connection_t *c, ngx_buf_t *b) { @@ -299,317 +569,305 @@ ngx_quic_input(ngx_connection_t *c, ngx_buf_t *b) return NGX_OK; } + static ngx_int_t -ngx_quic_send_packet(ngx_connection_t *c, ngx_quic_connection_t *qc, - enum ssl_encryption_level_t level, ngx_str_t *payload) +ngx_quic_initial_input(ngx_connection_t *c, ngx_quic_header_t *pkt) { - ngx_str_t res; - ngx_quic_header_t pkt; - - pkt.log = c->log; - - static ngx_str_t initial_token = ngx_null_string; - - ngx_memzero(&pkt, sizeof(ngx_quic_header_t)); - ngx_quic_hexdump0(c->log, "payload", payload->data, payload->len); - - pkt.level = level; - pkt.dcid = qc->dcid; - pkt.scid = qc->scid; - - if (level == ssl_encryption_initial) { - pkt.number = &qc->initial_pn; - pkt.flags = NGX_QUIC_PKT_INITIAL; - pkt.secret = &qc->secrets.server.in; - pkt.token = initial_token; + ngx_ssl_conn_t *ssl_conn; + ngx_quic_connection_t *qc; - } else if (level == ssl_encryption_handshake) { - pkt.number = &qc->handshake_pn; - pkt.flags = NGX_QUIC_PKT_HANDSHAKE; - pkt.secret = &qc->secrets.server.hs; + qc = c->quic; + ssl_conn = c->ssl->connection; - } else { - pkt.number = &qc->appdata_pn; - pkt.secret = &qc->secrets.server.ad; + if (ngx_quic_parse_long_header(pkt) != NGX_OK) { + return NGX_ERROR; } - if (ngx_quic_encrypt(c->pool, c->ssl->connection, &pkt, payload, &res) - != NGX_OK) - { + if (ngx_quic_parse_initial_header(pkt) != NGX_OK) { return NGX_ERROR; } - ngx_quic_hexdump0(c->log, "packet to send", res.data, res.len); - - c->send(c, res.data, res.len); // TODO: err handling + pkt->secret = &qc->secrets.client.in; + pkt->level = ssl_encryption_initial; - (*pkt.number)++; + if (ngx_quic_decrypt(c->pool, ssl_conn, pkt) != NGX_OK) { + return NGX_ERROR; + } - return NGX_OK; + return ngx_quic_payload_handler(c, pkt); } -/* pack a group of frames [start; end) into memory p and send as single packet */ -ngx_int_t -ngx_quic_frames_send(ngx_connection_t *c, ngx_quic_frame_t *start, - ngx_quic_frame_t *end, size_t total) +static ngx_int_t +ngx_quic_handshake_input(ngx_connection_t *c, ngx_quic_header_t *pkt) { - ssize_t len; - u_char *p; - ngx_str_t out; - ngx_quic_frame_t *f; + ngx_ssl_conn_t *ssl_conn; + ngx_quic_connection_t *qc; - ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, - "sending frames %p...%p", start, end); + qc = c->quic; + ssl_conn = c->ssl->connection; - p = ngx_pnalloc(c->pool, total); - if (p == NULL) { + /* extract cleartext data into pkt */ + if (ngx_quic_parse_long_header(pkt) != NGX_OK) { return NGX_ERROR; } - out.data = p; + if (pkt->dcid.len != qc->dcid.len) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "unexpected quic dcidl"); + return NGX_ERROR; + } - for (f = start; f != end; f = f->next) { + if (ngx_memcmp(pkt->dcid.data, qc->dcid.data, qc->dcid.len) != 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "unexpected quic dcid"); + return NGX_ERROR; + } - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "frame: %s", f->info); + if (pkt->scid.len != qc->scid.len) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "unexpected quic scidl"); + return NGX_ERROR; + } - len = ngx_quic_create_frame(p, p + total, f); - if (len == -1) { - return NGX_ERROR; - } + if (ngx_memcmp(pkt->scid.data, qc->scid.data, qc->scid.len) != 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "unexpected quic scid"); + return NGX_ERROR; + } - p += len; + if ((pkt->flags & 0xf0) != NGX_QUIC_PKT_HANDSHAKE) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "invalid packet type: 0x%xi", pkt->flags); + return NGX_ERROR; } - out.len = p - out.data; + if (ngx_quic_parse_handshake_header(pkt) != NGX_OK) { + return NGX_ERROR; + } - ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, - "packet ready: %ui bytes at level %d", - out.len, start->level); + pkt->secret = &qc->secrets.client.hs; + pkt->level = ssl_encryption_handshake; - // IOVEC/sendmsg_chain ? - if (ngx_quic_send_packet(c, c->quic, start->level, &out) != NGX_OK) { + if (ngx_quic_decrypt(c->pool, c->ssl->connection, pkt) != NGX_OK) { return NGX_ERROR; } - return NGX_OK; + return ngx_quic_payload_handler(c, pkt); } static ngx_int_t -ngx_quic_output(ngx_connection_t *c) +ngx_quic_app_input(ngx_connection_t *c, ngx_quic_header_t *pkt) { - size_t len; - ngx_uint_t lvl; - ngx_quic_frame_t *f, *start; ngx_quic_connection_t *qc; qc = c->quic; - if (qc->frames == NULL) { - return NGX_OK; + if (qc->secrets.client.ad.key.len == 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "no read keys yet, packet ignored"); + return NGX_DECLINED; } - lvl = qc->frames->level; - start = qc->frames; - f = start; - - do { - len = 0; + if (ngx_quic_parse_short_header(pkt, &qc->dcid) != NGX_OK) { + return NGX_ERROR; + } - do { - /* process same-level group of frames */ + pkt->secret = &qc->secrets.client.ad; + pkt->level = ssl_encryption_application; - len += ngx_quic_frame_len(f);// TODO: handle overflow, max size + if (ngx_quic_decrypt(c->pool, c->ssl->connection, pkt) != NGX_OK) { + return NGX_ERROR; + } - f = f->next; - } while (f && f->level == lvl); + return ngx_quic_payload_handler(c, pkt); +} - if (ngx_quic_frames_send(c, start, f, len) != NGX_OK) { - return NGX_ERROR; - } +static ngx_int_t +ngx_quic_payload_handler(ngx_connection_t *c, ngx_quic_header_t *pkt) +{ + u_char *end, *p; + ssize_t len; + ngx_uint_t ack_this, do_close; + ngx_quic_frame_t frame, *ack_frame; + ngx_quic_connection_t *qc; - if (f == NULL) { - break; - } + qc = c->quic; - lvl = f->level; // TODO: must not decrease (ever, also between calls) - start = f; + p = pkt->payload.data; + end = p + pkt->payload.len; - } while (1); + ack_this = 0; + do_close = 0; - qc->frames = NULL; + while (p < end) { - return NGX_OK; -} + len = ngx_quic_parse_frame(p, end, &frame); + if (len < 0) { + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "failed to parse frame type %xi", frame.type); + return NGX_ERROR; + } + p += len; -#if BORINGSSL_API_VERSION >= 10 + switch (frame.type) { -static int -ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn, - enum ssl_encryption_level_t level, const SSL_CIPHER *cipher, - const uint8_t *rsecret, size_t secret_len) -{ - ngx_connection_t *c; + case NGX_QUIC_FT_ACK: - c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "ACK: { largest=%ui delay=%ui first=%ui count=%ui}", + frame.u.ack.largest, + frame.u.ack.delay, + frame.u.ack.first_range, + frame.u.ack.range_count); - ngx_quic_hexdump(c->log, "level:%d read secret", - rsecret, secret_len, level); + if (ngx_quic_handle_ack_frame(c, pkt, &frame.u.ack) != NGX_OK) { + return NGX_ERROR; + } - return ngx_quic_set_encryption_secret(c->pool, ssl_conn, level, - rsecret, secret_len, - &c->quic->secrets.client); -} + break; + case NGX_QUIC_FT_CRYPTO: -static int -ngx_quic_set_write_secret(ngx_ssl_conn_t *ssl_conn, - enum ssl_encryption_level_t level, const SSL_CIPHER *cipher, - const uint8_t *wsecret, size_t secret_len) -{ - ngx_connection_t *c; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic CRYPTO frame length: %uL off:%uL pp:%p", + frame.u.crypto.len, frame.u.crypto.offset, + frame.u.crypto.data); - c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); + ngx_quic_hexdump0(c->log, "CRYPTO frame contents", + frame.u.crypto.data, frame.u.crypto.len); - ngx_quic_hexdump(c->log, "level:%d write secret", - wsecret, secret_len, level); - return ngx_quic_set_encryption_secret(c->pool, ssl_conn, level, - wsecret, secret_len, - &c->quic->secrets.server); -} + if (ngx_quic_handle_crypto_frame(c, pkt, &frame.u.crypto) + != NGX_OK) + { + return NGX_ERROR; + } -#else + ack_this = 1; + break; -static int -ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn, - enum ssl_encryption_level_t level, const uint8_t *rsecret, - const uint8_t *wsecret, size_t secret_len) -{ - ngx_int_t rc; - ngx_connection_t *c; + case NGX_QUIC_FT_PADDING: + break; - c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); + case NGX_QUIC_FT_PING: + ack_this = 1; + break; - ngx_quic_hexdump(c->log, "level:%d read", rsecret, secret_len, level); - ngx_quic_hexdump(c->log, "level:%d write", wsecret, secret_len, level); + case NGX_QUIC_FT_NEW_CONNECTION_ID: + ack_this = 1; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "NCID: { seq=%ui retire=%ui len=%ui}", + frame.u.ncid.seqnum, + frame.u.ncid.retire, + frame.u.ncid.len); + break; - rc = ngx_quic_set_encryption_secret(c->pool, ssl_conn, level, - rsecret, secret_len, - &c->quic->secrets.client); - if (rc != 1) { - return rc; - } + case NGX_QUIC_FT_CONNECTION_CLOSE: + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "CONN.CLOSE: { %s (0x%xi) type=0x%xi reason='%V'}", + ngx_quic_error_text(frame.u.close.error_code), + frame.u.close.error_code, + frame.u.close.frame_type, + &frame.u.close.reason); - return ngx_quic_set_encryption_secret(c->pool, ssl_conn, level, - wsecret, secret_len, - &c->quic->secrets.server); -} + do_close = 1; + break; -#endif + case NGX_QUIC_FT_STREAM0: + case NGX_QUIC_FT_STREAM1: + case NGX_QUIC_FT_STREAM2: + case NGX_QUIC_FT_STREAM3: + case NGX_QUIC_FT_STREAM4: + case NGX_QUIC_FT_STREAM5: + case NGX_QUIC_FT_STREAM6: + case NGX_QUIC_FT_STREAM7: + ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, + "STREAM frame { 0x%xi id 0x%xi offset 0x%xi len 0x%xi bits:off=%d len=%d fin=%d }", + frame.type, + frame.u.stream.stream_id, + frame.u.stream.offset, + frame.u.stream.length, + frame.u.stream.off, + frame.u.stream.len, + frame.u.stream.fin); -static void -ngx_quic_queue_frame(ngx_quic_connection_t *qc, ngx_quic_frame_t *frame) -{ - ngx_quic_frame_t *f; + ngx_quic_hexdump0(c->log, "STREAM frame contents", + frame.u.stream.data, frame.u.stream.length); - if (qc->frames == NULL) { - qc->frames = frame; - return; - } + if (ngx_quic_handle_stream_frame(c, pkt, &frame.u.stream) + != NGX_OK) + { + return NGX_ERROR; + } - for (f = qc->frames; f->next; f = f->next) { - if (f->next->level > frame->level) { + ack_this = 1; break; + + default: + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "unsupported frame type 0x%xd in packet", frame.type); + return NGX_ERROR; } } - frame->next = f->next; - f->next = frame; -} - - -static int -ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn, - enum ssl_encryption_level_t level, const uint8_t *data, size_t len) -{ - u_char *p; - ngx_quic_frame_t *frame; - ngx_connection_t *c; - ngx_quic_connection_t *qc; - - c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); - qc = c->quic; - - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, - "ngx_quic_add_handshake_data"); - - frame = ngx_pcalloc(c->pool, sizeof(ngx_quic_frame_t)); - if (frame == NULL) { - return 0; + if (p != end) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "trailing garbage in payload: %ui bytes", end - p); + return NGX_ERROR; } - p = ngx_pnalloc(c->pool, len); - if (p == NULL) { - return 0; + if (do_close) { + // TODO: handle stream close } - ngx_memcpy(p, data, len); - - frame->level = level; - frame->type = NGX_QUIC_FT_CRYPTO; - frame->u.crypto.len = len; - frame->u.crypto.data = p; - - ngx_sprintf(frame->info, "crypto, generated by SSL len=%ui level=%d", len, level); - - ngx_quic_queue_frame(qc, frame); - - return 1; -} + if (ack_this == 0) { + /* do not ack packets with ACKs and PADDING */ + return NGX_OK; + } + // packet processed, ACK it now if required + // TODO: if (ack_required) ... - currently just ack each packet -static int -ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn) -{ - ngx_connection_t *c; + ack_frame = ngx_pcalloc(c->pool, sizeof(ngx_quic_frame_t)); + if (ack_frame == NULL) { + return NGX_ERROR; + } - c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); + ack_frame->level = pkt->level; + ack_frame->type = NGX_QUIC_FT_ACK; + ack_frame->u.ack.pn = pkt->pn; - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "ngx_quic_flush_flight()"); + ngx_sprintf(ack_frame->info, "ACK for PN=%d from frame handler level=%d", pkt->pn, pkt->level); + ngx_quic_queue_frame(qc, ack_frame); - return 1; + return ngx_quic_output(c); } -static int -ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level, - uint8_t alert) +static ngx_int_t +ngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt, + ngx_quic_ack_frame_t *f) { - ngx_connection_t *c; - - c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); - - ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, - "ngx_quic_send_alert(), lvl=%d, alert=%d", - (int) level, (int) alert); - - return 1; + /* TODO: handle ACK here */ + return NGX_OK; } - static ngx_int_t ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt, - ngx_quic_frame_t *frame) + ngx_quic_crypto_frame_t *f) { - int sslerr; - ssize_t n; - ngx_ssl_conn_t *ssl_conn; + int sslerr; + ssize_t n; + ngx_ssl_conn_t *ssl_conn; + + if (f->offset != 0x0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "crypto frame with non-zero offset"); + // TODO: add support for crypto frames spanning packets + return NGX_ERROR; + } ssl_conn = c->ssl->connection; @@ -618,9 +876,8 @@ ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt, (int) SSL_quic_read_level(ssl_conn), (int) SSL_quic_write_level(ssl_conn)); - if (!SSL_provide_quic_data(ssl_conn, SSL_quic_read_level(ssl_conn), - frame->u.crypto.data, frame->u.crypto.len)) + f->data, f->len)) { ngx_ssl_error(NGX_LOG_INFO, c->log, 0, "SSL_provide_quic_data() failed"); @@ -654,449 +911,281 @@ ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt, } - static ngx_int_t -ngx_quic_init_connection(ngx_connection_t *c) +ngx_quic_handle_stream_frame(ngx_connection_t *c, + ngx_quic_header_t *pkt, ngx_quic_stream_frame_t *f) { - int n, sslerr; - ngx_ssl_conn_t *ssl_conn; - ngx_quic_connection_t *qc; - - static const uint8_t params[] = - "\x00\x29" /* parameters length: 41 bytes */ - "\x00\x0e\x00\x01\x05" /* active connection id limit: 5 */ - "\x00\x04\x00\x04\x80\x98\x96\x80" /* initial max data = 10000000 */ - "\x00\x09\x00\x01\x03" /* initial max streams uni: 3 */ - "\x00\x08\x00\x01\x10" /* initial max streams bidi: 16 */ - "\x00\x05\x00\x02\x40\xff" /* initial max stream bidi local: 255 */ - "\x00\x06\x00\x02\x40\xff" /* initial max stream bidi remote: 255 */ - "\x00\x07\x00\x02\x40\xff"; /* initial max stream data uni: 255 */ + ngx_buf_t *b; + ngx_log_t *log; + ngx_pool_t *pool; + ngx_event_t *rev, *wev; + ngx_quic_connection_t *qc; + ngx_quic_stream_node_t *sn; qc = c->quic; - if (ngx_ssl_create_connection(qc->ssl, c, NGX_SSL_BUFFER) != NGX_OK) { - return NGX_ERROR; - } + sn = ngx_quic_find_stream(&qc->streams.tree, f->stream_id); - ssl_conn = c->ssl->connection; + if (sn) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "existing stream"); + b = sn->b; - if (SSL_set_quic_transport_params(ssl_conn, params, sizeof(params) - 1) == 0) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "SSL_set_quic_transport_params() failed"); - return NGX_ERROR; - } - - n = SSL_do_handshake(ssl_conn); + if ((size_t) (b->end - b->pos) < f->length) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "no space in stream buffer"); + return NGX_ERROR; + } - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); + ngx_memcpy(b->pos, f->data, f->length); + b->pos += f->length; - if (n == -1) { - sslerr = SSL_get_error(ssl_conn, n); + // TODO: notify - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", - sslerr); + return NGX_OK; } - ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, - "SSL_quic_read_level: %d, SSL_quic_write_level: %d", - (int) SSL_quic_read_level(ssl_conn), - (int) SSL_quic_write_level(ssl_conn)); - - return NGX_OK; -} - - -static ssize_t -ngx_quic_stream_recv(ngx_connection_t *c, u_char *buf, size_t size) -{ - ssize_t len; - ngx_buf_t *b; - ngx_quic_stream_t *qs; - ngx_quic_connection_t *qc; - ngx_quic_stream_node_t *sn; - - qs = c->qs; - qc = qs->parent->quic; - - // XXX: get direct pointer from stream structure? - sn = ngx_quic_stream_lookup(&qc->stree, qs->id); + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "stream is new"); + sn = ngx_pcalloc(c->pool, sizeof(ngx_quic_stream_node_t)); if (sn == NULL) { return NGX_ERROR; } - // XXX: how to return EOF? + sn->c = ngx_get_connection(-1, c->log); // TODO: free on connection termination + if (sn->c == NULL) { + return NGX_ERROR; + } - b = sn->b; + pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, c->log); + if (pool == NULL) { + /* XXX free connection */ + return NGX_ERROR; + } - if (b->last - b->pos == 0) { - c->read->ready = 0; - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic recv() not ready"); - return NGX_AGAIN; // ? + log = ngx_palloc(pool, sizeof(ngx_log_t)); + if (log == NULL) { + /* XXX free pool and connection */ + return NGX_ERROR; } - len = ngx_min(b->last - b->pos, (ssize_t) size); + *log = *c->log; + pool->log = log; - ngx_memcpy(buf, b->pos, len); + sn->c->log = log; + sn->c->pool = pool; - b->pos += len; + sn->c->listening = c->listening; + sn->c->sockaddr = c->sockaddr; + sn->c->local_sockaddr = c->local_sockaddr; - ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic recv: %z of %uz", len, size); + rev = sn->c->read; + wev = sn->c->write; - return len; -} + rev->ready = 1; + rev->log = c->log; + wev->log = c->log; -static ssize_t -ngx_quic_stream_send(ngx_connection_t *c, u_char *buf, size_t size) -{ - u_char *p; - ngx_connection_t *pc; - ngx_quic_frame_t *frame; - ngx_quic_stream_t *qs; - ngx_quic_connection_t *qc; - ngx_quic_stream_node_t *sn; + sn->c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1); - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic send: %uz", size); - - qs = c->qs; - pc = qs->parent; - qc = pc->quic; - - // XXX: get direct pointer from stream structure? - sn = ngx_quic_stream_lookup(&qc->stree, qs->id); - - if (sn == NULL) { + sn->node.key = f->stream_id; + sn->b = ngx_create_temp_buf(pool, 16 * 1024); // XXX enough for everyone + if (sn->b == NULL) { return NGX_ERROR; } + b = sn->b; - frame = ngx_pcalloc(pc->pool, sizeof(ngx_quic_frame_t)); - if (frame == NULL) { - return 0; - } - - p = ngx_pnalloc(pc->pool, size); - if (p == NULL) { - return 0; - } - - ngx_memcpy(p, buf, size); - - frame->level = ssl_encryption_application; - frame->type = NGX_QUIC_FT_STREAM6; /* OFF=1 LEN=1 FIN=0 */ - frame->u.stream.off = 1; - frame->u.stream.len = 1; - frame->u.stream.fin = 0; + ngx_memcpy(b->start, f->data, f->length); + b->last = b->start + f->length; - frame->u.stream.type = frame->type; - frame->u.stream.stream_id = qs->id; - frame->u.stream.offset = c->sent; - frame->u.stream.length = size; - frame->u.stream.data = p; + ngx_rbtree_insert(&qc->streams.tree, &sn->node); - c->sent += size; + sn->s.id = f->stream_id; + sn->s.unidirectional = (sn->s.id & 0x02) ? 1 : 0; + sn->s.parent = c; + sn->c->qs = &sn->s; - ngx_sprintf(frame->info, "stream %xi len=%ui level=%d", - qs->id, size, frame->level); + sn->c->recv = ngx_quic_stream_recv; + sn->c->send = ngx_quic_stream_send; + sn->c->send_chain = ngx_quic_stream_send_chain; - ngx_quic_queue_frame(qc, frame); + qc->streams.handler(sn->c); - return size; + return NGX_OK; } -static ngx_chain_t * -ngx_quic_stream_send_chain(ngx_connection_t *c, ngx_chain_t *in, - off_t limit) +static void +ngx_quic_queue_frame(ngx_quic_connection_t *qc, ngx_quic_frame_t *frame) { - size_t len; - ssize_t n; - ngx_buf_t *b; - - for ( /* void */; in; in = in->next) { - b = in->buf; - - if (!ngx_buf_in_memory(b)) { - continue; - } - - if (ngx_buf_size(b) == 0) { - continue; - } - - len = b->last - b->pos; - - n = ngx_quic_stream_send(c, b->pos, len); - - if (n == NGX_ERROR) { - return NGX_CHAIN_ERROR; - } + ngx_quic_frame_t *f; - if (n == NGX_AGAIN) { - return in; - } + if (qc->frames == NULL) { + qc->frames = frame; + return; + } - if (n != (ssize_t) len) { - b->pos += n; - return in; + for (f = qc->frames; f->next; f = f->next) { + if (f->next->level > frame->level) { + break; } } - return NULL; + frame->next = f->next; + f->next = frame; } -/* process all payload from the current packet and generate ack if required */ static ngx_int_t -ngx_quic_payload_handler(ngx_connection_t *c, ngx_quic_header_t *pkt) +ngx_quic_output(ngx_connection_t *c) { - u_char *end, *p; - ssize_t len; - ngx_buf_t *b; - ngx_log_t *log; - ngx_uint_t ack_this, do_close; - ngx_pool_t *pool; - ngx_event_t *rev, *wev; - ngx_quic_frame_t frame, *ack_frame; - ngx_quic_connection_t *qc; - ngx_quic_stream_node_t *sn; + size_t len; + ngx_uint_t lvl; + ngx_quic_frame_t *f, *start; + ngx_quic_connection_t *qc; qc = c->quic; - p = pkt->payload.data; - end = p + pkt->payload.len; - - ack_this = 0; - do_close = 0; - - while (p < end) { - - len = ngx_quic_parse_frame(p, end, &frame); - if (len < 0) { - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, - "unknown frame type %xi", frame.type); - // XXX: log here - return NGX_ERROR; - } - - p += len; - - switch (frame.type) { - - case NGX_QUIC_FT_ACK: - - // TODO: handle ack - - ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, - "ACK: { largest=%ui delay=%ui first=%ui count=%ui}", - frame.u.ack.largest, - frame.u.ack.delay, - frame.u.ack.first_range, - frame.u.ack.range_count); - - break; - - case NGX_QUIC_FT_CRYPTO: - ngx_quic_hexdump0(c->log, "CRYPTO frame", - frame.u.crypto.data, frame.u.crypto.len); - - ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic CRYPTO frame length: %uL off:%uL pp:%p", - frame.u.crypto.len, frame.u.crypto.offset, - frame.u.crypto.data); + if (qc->frames == NULL) { + return NGX_OK; + } - if (frame.u.crypto.offset != 0x0) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "crypto frame with non-zero offset"); - // TODO: support packet spanning with offsets - return NGX_ERROR; - } + lvl = qc->frames->level; + start = qc->frames; + f = start; - if (ngx_quic_handle_crypto_frame(c, pkt, &frame) != NGX_OK) { - return NGX_ERROR; - } + do { + len = 0; - ack_this = 1; + do { + /* process same-level group of frames */ - continue; + len += ngx_quic_frame_len(f);// TODO: handle overflow, max size - case NGX_QUIC_FT_PADDING: - continue; + f = f->next; + } while (f && f->level == lvl); - case NGX_QUIC_FT_PING: - ack_this = 1; - continue; - case NGX_QUIC_FT_NEW_CONNECTION_ID: - ack_this = 1; - ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, - "NCID: { seq=%ui retire=%ui len=%ui}", - frame.u.ncid.seqnum, - frame.u.ncid.retire, - frame.u.ncid.len); - continue; - - case NGX_QUIC_FT_CONNECTION_CLOSE: - ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, - "CONN.CLOSE: { %s (0x%xi) type=0x%xi reason='%V'}", - ngx_quic_error_text(frame.u.close.error_code), - frame.u.close.error_code, - frame.u.close.frame_type, - &frame.u.close.reason); + if (ngx_quic_frames_send(c, start, f, len) != NGX_OK) { + return NGX_ERROR; + } - do_close = 1; + if (f == NULL) { break; + } - case NGX_QUIC_FT_STREAM0: - case NGX_QUIC_FT_STREAM1: - case NGX_QUIC_FT_STREAM2: - case NGX_QUIC_FT_STREAM3: - case NGX_QUIC_FT_STREAM4: - case NGX_QUIC_FT_STREAM5: - case NGX_QUIC_FT_STREAM6: - case NGX_QUIC_FT_STREAM7: - - ack_this = 1; + lvl = f->level; // TODO: must not decrease (ever, also between calls) + start = f; - ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, - "STREAM frame 0x%xi id 0x%xi offset 0x%xi len 0x%xi bits:off=%d len=%d fin=%d", - frame.type, - frame.u.stream.stream_id, - frame.u.stream.offset, - frame.u.stream.length, - frame.u.stream.off, - frame.u.stream.len, - frame.u.stream.fin); + } while (1); - sn = ngx_quic_stream_lookup(&qc->stree, frame.u.stream.stream_id); - if (sn == NULL) { - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "stream is new"); + qc->frames = NULL; - sn = ngx_pcalloc(c->pool, sizeof(ngx_quic_stream_node_t)); - if (sn == NULL) { - return NGX_ERROR; - } + return NGX_OK; +} - sn->c = ngx_get_connection(-1, c->log); // TODO: free on connection termination - if (sn->c == NULL) { - return NGX_ERROR; - } - pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, c->log); - if (pool == NULL) { - /* XXX free connection */ - return NGX_ERROR; - } +/* pack a group of frames [start; end) into memory p and send as single packet */ +ngx_int_t +ngx_quic_frames_send(ngx_connection_t *c, ngx_quic_frame_t *start, + ngx_quic_frame_t *end, size_t total) +{ + ssize_t len; + u_char *p; + ngx_str_t out; + ngx_quic_frame_t *f; - log = ngx_palloc(pool, sizeof(ngx_log_t)); - if (log == NULL) { - /* XXX free pool and connection */ - return NGX_ERROR; - } + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sending frames %p...%p", start, end); - *log = *c->log; - pool->log = log; + p = ngx_pnalloc(c->pool, total); + if (p == NULL) { + return NGX_ERROR; + } - sn->c->log = log; - sn->c->pool = pool; + out.data = p; - sn->c->listening = c->listening; - sn->c->sockaddr = c->sockaddr; - sn->c->local_sockaddr = c->local_sockaddr; + for (f = start; f != end; f = f->next) { - rev = sn->c->read; - wev = sn->c->write; + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "frame: %s", f->info); - rev->ready = 1; + len = ngx_quic_create_frame(p, p + total, f); + if (len == -1) { + return NGX_ERROR; + } - rev->log = c->log; - wev->log = c->log; + p += len; + } - sn->c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1); + out.len = p - out.data; - sn->node.key = frame.u.stream.stream_id; - sn->b = ngx_create_temp_buf(pool, 16 * 1024); // XXX enough for everyone - if (sn->b == NULL) { - return NGX_ERROR; - } - b = sn->b; + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "packet ready: %ui bytes at level %d", + out.len, start->level); - ngx_memcpy(b->start, frame.u.stream.data, frame.u.stream.length); - b->last = b->start + frame.u.stream.length; + // IOVEC/sendmsg_chain ? + if (ngx_quic_send_packet(c, c->quic, start->level, &out) != NGX_OK) { + return NGX_ERROR; + } - ngx_rbtree_insert(&qc->stree, &sn->node); + return NGX_OK; +} - sn->s.id = frame.u.stream.stream_id; - sn->s.unidirectional = (sn->s.id & 0x02) ? 1 : 0; - sn->s.parent = c; - sn->c->qs = &sn->s; - sn->c->recv = ngx_quic_stream_recv; - sn->c->send = ngx_quic_stream_send; - sn->c->send_chain = ngx_quic_stream_send_chain; +static ngx_int_t +ngx_quic_send_packet(ngx_connection_t *c, ngx_quic_connection_t *qc, + enum ssl_encryption_level_t level, ngx_str_t *payload) +{ + ngx_str_t res; + ngx_quic_header_t pkt; - qc->stream_handler(sn->c); + pkt.log = c->log; - } else { - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "existing stream"); - b = sn->b; + static ngx_str_t initial_token = ngx_null_string; - if ((size_t) (b->end - b->pos) < frame.u.stream.length) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "no space in stream buffer"); - return NGX_ERROR; - } + ngx_memzero(&pkt, sizeof(ngx_quic_header_t)); + ngx_quic_hexdump0(c->log, "payload", payload->data, payload->len); - ngx_memcpy(b->pos, frame.u.stream.data, frame.u.stream.length); - b->pos += frame.u.stream.length; + pkt.level = level; + pkt.dcid = qc->dcid; + pkt.scid = qc->scid; - // TODO: ngx_post_event(&c->read, &ngx_posted_events) ??? - } + if (level == ssl_encryption_initial) { + pkt.number = &qc->initial_pn; + pkt.flags = NGX_QUIC_PKT_INITIAL; + pkt.secret = &qc->secrets.server.in; + pkt.token = initial_token; - ngx_quic_hexdump0(c->log, "STREAM.data", - frame.u.stream.data, frame.u.stream.length); - break; + } else if (level == ssl_encryption_handshake) { + pkt.number = &qc->handshake_pn; + pkt.flags = NGX_QUIC_PKT_HANDSHAKE; + pkt.secret = &qc->secrets.server.hs; - default: - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "unexpected frame type 0x%xd in packet", frame.type); - return NGX_ERROR; - } + } else { + pkt.number = &qc->appdata_pn; + pkt.secret = &qc->secrets.server.ad; } - if (p != end) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "trailing garbage in payload: %ui bytes", end - p); + if (ngx_quic_encrypt(c->pool, c->ssl->connection, &pkt, payload, &res) + != NGX_OK) + { return NGX_ERROR; } - if (do_close) { - // TODO: handle stream close - } - - if (ack_this == 0) { - /* do not ack packets with ACKs and PADDING */ - return NGX_OK; - } + ngx_quic_hexdump0(c->log, "packet to send", res.data, res.len); - // packet processed, ACK it now if required - // TODO: if (ack_required) ... - currently just ack each packet + c->send(c, res.data, res.len); // TODO: err handling - ack_frame = ngx_pcalloc(c->pool, sizeof(ngx_quic_frame_t)); - if (ack_frame == NULL) { - return NGX_ERROR; - } + (*pkt.number)++; - ack_frame->level = pkt->level; - ack_frame->type = NGX_QUIC_FT_ACK; - ack_frame->u.ack.pn = pkt->pn; + return NGX_OK; +} - ngx_sprintf(ack_frame->info, "ACK for PN=%d from frame handler level=%d", pkt->pn, pkt->level); - ngx_quic_queue_frame(qc, ack_frame); - return ngx_quic_output(c); +ngx_connection_t * +ngx_quic_create_uni_stream(ngx_connection_t *c) +{ + /* XXX */ + return NULL; } @@ -1104,8 +1193,8 @@ static void ngx_quic_rbtree_insert_stream(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) { - ngx_rbtree_node_t **p; - ngx_quic_stream_node_t *qn, *qnt; + ngx_rbtree_node_t **p; + ngx_quic_stream_node_t *qn, *qnt; for ( ;; ) { @@ -1145,7 +1234,7 @@ ngx_quic_rbtree_insert_stream(ngx_rbtree_node_t *temp, static ngx_quic_stream_node_t * -ngx_quic_stream_lookup(ngx_rbtree_t *rbtree, ngx_uint_t key) +ngx_quic_find_stream(ngx_rbtree_t *rbtree, ngx_uint_t key) { ngx_rbtree_node_t *node, *sentinel; @@ -1165,193 +1254,143 @@ ngx_quic_stream_lookup(ngx_rbtree_t *rbtree, ngx_uint_t key) } -static ngx_int_t -ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl, - ngx_quic_header_t *pkt) +static ssize_t +ngx_quic_stream_recv(ngx_connection_t *c, u_char *buf, size_t size) { - ngx_quic_connection_t *qc; - - if (ngx_buf_size(pkt->raw) < 1200) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, "too small UDP datagram"); - return NGX_ERROR; - } - - if (ngx_quic_parse_long_header(pkt) != NGX_OK) { - return NGX_ERROR; - } + ssize_t len; + ngx_buf_t *b; + ngx_quic_stream_t *qs; + ngx_quic_connection_t *qc; + ngx_quic_stream_node_t *sn; - if ((pkt->flags & 0xf0) != NGX_QUIC_PKT_INITIAL) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "invalid initial packet: 0x%xi", pkt->flags); - return NGX_ERROR; - } + qs = c->qs; + qc = qs->parent->quic; - if (ngx_quic_parse_initial_header(pkt) != NGX_OK) { - return NGX_ERROR; - } + // XXX: get direct pointer from stream structure? + sn = ngx_quic_find_stream(&qc->streams.tree, qs->id); - qc = ngx_pcalloc(c->pool, sizeof(ngx_quic_connection_t)); - if (qc == NULL) { + if (sn == NULL) { return NGX_ERROR; } - ngx_rbtree_init(&qc->stree, &qc->stree_sentinel, - ngx_quic_rbtree_insert_stream); - - c->quic = qc; - qc->ssl = ssl; - - qc->dcid.len = pkt->dcid.len; - qc->dcid.data = ngx_pnalloc(c->pool, pkt->dcid.len); - if (qc->dcid.data == NULL) { - return NGX_ERROR; - } - ngx_memcpy(qc->dcid.data, pkt->dcid.data, qc->dcid.len); + // XXX: how to return EOF? - qc->scid.len = pkt->scid.len; - qc->scid.data = ngx_pnalloc(c->pool, qc->scid.len); - if (qc->scid.data == NULL) { - return NGX_ERROR; - } - ngx_memcpy(qc->scid.data, pkt->scid.data, qc->scid.len); + b = sn->b; - qc->token.len = pkt->token.len; - qc->token.data = ngx_pnalloc(c->pool, qc->token.len); - if (qc->token.data == NULL) { - return NGX_ERROR; + if (b->last - b->pos == 0) { + c->read->ready = 0; + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic recv() not ready"); + return NGX_AGAIN; // ? } - ngx_memcpy(qc->token.data, pkt->token.data, qc->token.len); + len = ngx_min(b->last - b->pos, (ssize_t) size); - if (ngx_quic_set_initial_secret(c->pool, &qc->secrets, &qc->dcid) - != NGX_OK) - { - return NGX_ERROR; - } - - pkt->secret = &qc->secrets.client.in; - pkt->level = ssl_encryption_initial; + ngx_memcpy(buf, b->pos, len); - if (ngx_quic_decrypt(c->pool, NULL, pkt) != NGX_OK) { - return NGX_ERROR; - } + b->pos += len; - if (ngx_quic_init_connection(c) != NGX_OK) { - return NGX_ERROR; - } + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic recv: %z of %uz", len, size); - return ngx_quic_payload_handler(c, pkt); + return len; } -static ngx_int_t -ngx_quic_initial_input(ngx_connection_t *c, ngx_quic_header_t *pkt) +static ssize_t +ngx_quic_stream_send(ngx_connection_t *c, u_char *buf, size_t size) { - ngx_ssl_conn_t *ssl_conn; - ngx_quic_connection_t *qc; - - qc = c->quic; - ssl_conn = c->ssl->connection; - - if (ngx_quic_parse_long_header(pkt) != NGX_OK) { - return NGX_ERROR; - } - - if (ngx_quic_parse_initial_header(pkt) != NGX_OK) { - return NGX_ERROR; - } - - pkt->secret = &qc->secrets.client.in; - pkt->level = ssl_encryption_initial; - - if (ngx_quic_decrypt(c->pool, ssl_conn, pkt) != NGX_OK) { - return NGX_ERROR; - } - - return ngx_quic_payload_handler(c, pkt); -} + u_char *p; + ngx_connection_t *pc; + ngx_quic_frame_t *frame; + ngx_quic_stream_t *qs; + ngx_quic_connection_t *qc; + ngx_quic_stream_node_t *sn; + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic send: %uz", size); -static ngx_int_t -ngx_quic_handshake_input(ngx_connection_t *c, ngx_quic_header_t *pkt) -{ - ngx_ssl_conn_t *ssl_conn; - ngx_quic_connection_t *qc; + qs = c->qs; + pc = qs->parent; + qc = pc->quic; - qc = c->quic; - ssl_conn = c->ssl->connection; + // XXX: get direct pointer from stream structure? + sn = ngx_quic_find_stream(&qc->streams.tree, qs->id); - /* extract cleartext data into pkt */ - if (ngx_quic_parse_long_header(pkt) != NGX_OK) { + if (sn == NULL) { return NGX_ERROR; } - if (pkt->dcid.len != qc->dcid.len) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, "unexpected quic dcidl"); - return NGX_ERROR; + frame = ngx_pcalloc(pc->pool, sizeof(ngx_quic_frame_t)); + if (frame == NULL) { + return 0; } - if (ngx_memcmp(pkt->dcid.data, qc->dcid.data, qc->dcid.len) != 0) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, "unexpected quic dcid"); - return NGX_ERROR; + p = ngx_pnalloc(pc->pool, size); + if (p == NULL) { + return 0; } - if (pkt->scid.len != qc->scid.len) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, "unexpected quic scidl"); - return NGX_ERROR; - } + ngx_memcpy(p, buf, size); - if (ngx_memcmp(pkt->scid.data, qc->scid.data, qc->scid.len) != 0) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, "unexpected quic scid"); - return NGX_ERROR; - } + frame->level = ssl_encryption_application; + frame->type = NGX_QUIC_FT_STREAM6; /* OFF=1 LEN=1 FIN=0 */ + frame->u.stream.off = 1; + frame->u.stream.len = 1; + frame->u.stream.fin = 0; - if ((pkt->flags & 0xf0) != NGX_QUIC_PKT_HANDSHAKE) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "invalid packet type: 0x%xi", pkt->flags); - return NGX_ERROR; - } + frame->u.stream.type = frame->type; + frame->u.stream.stream_id = qs->id; + frame->u.stream.offset = c->sent; + frame->u.stream.length = size; + frame->u.stream.data = p; - if (ngx_quic_parse_handshake_header(pkt) != NGX_OK) { - return NGX_ERROR; - } + c->sent += size; - pkt->secret = &qc->secrets.client.hs; - pkt->level = ssl_encryption_handshake; + ngx_sprintf(frame->info, "stream %xi len=%ui level=%d", + qs->id, size, frame->level); - if (ngx_quic_decrypt(c->pool, c->ssl->connection, pkt) != NGX_OK) { - return NGX_ERROR; - } + ngx_quic_queue_frame(qc, frame); - return ngx_quic_payload_handler(c, pkt); + return size; } -static ngx_int_t -ngx_quic_app_input(ngx_connection_t *c, ngx_quic_header_t *pkt) +static ngx_chain_t * +ngx_quic_stream_send_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit) { - ngx_quic_connection_t *qc; + size_t len; + ssize_t n; + ngx_buf_t *b; - qc = c->quic; + for ( /* void */; in; in = in->next) { + b = in->buf; - if (qc->secrets.client.ad.key.len == 0) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "no read keys yet, packet ignored"); - return NGX_DECLINED; - } + if (!ngx_buf_in_memory(b)) { + continue; + } - if (ngx_quic_parse_short_header(pkt, &qc->dcid) != NGX_OK) { - return NGX_ERROR; - } + if (ngx_buf_size(b) == 0) { + continue; + } - pkt->secret = &qc->secrets.client.ad; - pkt->level = ssl_encryption_application; + len = b->last - b->pos; - if (ngx_quic_decrypt(c->pool, c->ssl->connection, pkt) != NGX_OK) { - return NGX_ERROR; - } + n = ngx_quic_stream_send(c, b->pos, len); - return ngx_quic_payload_handler(c, pkt); -} + if (n == NGX_ERROR) { + return NGX_CHAIN_ERROR; + } + + if (n == NGX_AGAIN) { + return in; + } + if (n != (ssize_t) len) { + b->pos += n; + return in; + } + } + return NULL; +} |