aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/event/ngx_event_quic.c1585
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;
+}