aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/event/quic/ngx_event_quic.c539
-rw-r--r--src/event/quic/ngx_event_quic.h12
-rw-r--r--src/event/quic/ngx_event_quic_transport.h2
-rw-r--r--src/http/modules/ngx_http_quic_module.c6
-rw-r--r--src/stream/ngx_stream_quic_module.c6
5 files changed, 319 insertions, 246 deletions
diff --git a/src/event/quic/ngx_event_quic.c b/src/event/quic/ngx_event_quic.c
index 4da52a908..52397bcf5 100644
--- a/src/event/quic/ngx_event_quic.c
+++ b/src/event/quic/ngx_event_quic.c
@@ -9,6 +9,7 @@
#include <ngx_event.h>
#include <ngx_event_quic_transport.h>
#include <ngx_event_quic_protection.h>
+#include <ngx_sha1.h>
/* 0-RTT and 1-RTT data exist in the same packet number space,
@@ -113,7 +114,6 @@ typedef struct {
ngx_str_t scid; /* initial client ID */
ngx_str_t dcid; /* server (our own) ID */
ngx_str_t odcid; /* original server ID */
- ngx_str_t token;
struct sockaddr *sockaddr;
socklen_t socklen;
@@ -175,8 +175,6 @@ typedef struct {
unsigned closing:1;
unsigned draining:1;
unsigned key_phase:1;
- unsigned in_retry:1;
- unsigned initialized:1;
unsigned validated:1;
} ngx_quic_connection_t;
@@ -235,10 +233,14 @@ static ngx_int_t ngx_quic_create_server_id(ngx_connection_t *c, u_char *id);
#if (NGX_QUIC_BPF)
static ngx_int_t ngx_quic_bpf_attach_id(ngx_connection_t *c, u_char *id);
#endif
-static ngx_int_t ngx_quic_send_retry(ngx_connection_t *c);
-static ngx_int_t ngx_quic_new_token(ngx_connection_t *c, ngx_str_t *token);
+static ngx_int_t ngx_quic_send_retry(ngx_connection_t *c,
+ ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);
+static ngx_int_t ngx_quic_new_token(ngx_connection_t *c, u_char *key,
+ ngx_str_t *token, ngx_str_t *odcid, time_t expires, ngx_uint_t is_retry);
+static void ngx_quic_address_hash(ngx_connection_t *c, ngx_uint_t no_port,
+ u_char buf[20]);
static ngx_int_t ngx_quic_validate_token(ngx_connection_t *c,
- ngx_quic_header_t *pkt);
+ u_char *key, ngx_quic_header_t *pkt);
static ngx_int_t ngx_quic_init_connection(ngx_connection_t *c);
static ngx_inline size_t ngx_quic_max_udp_payload(ngx_connection_t *c);
static void ngx_quic_input_handler(ngx_event_t *rev);
@@ -253,7 +255,8 @@ static ngx_int_t ngx_quic_input(ngx_connection_t *c, ngx_buf_t *b,
ngx_quic_conf_t *conf);
static ngx_int_t ngx_quic_process_packet(ngx_connection_t *c,
ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);
-static ngx_int_t ngx_quic_init_secrets(ngx_connection_t *c);
+static ngx_int_t ngx_quic_send_early_cc(ngx_connection_t *c,
+ ngx_quic_header_t *inpkt, ngx_uint_t err, const char *reason);
static void ngx_quic_discard_ctx(ngx_connection_t *c,
enum ssl_encryption_level_t level);
static ngx_int_t ngx_quic_check_peer(ngx_quic_connection_t *qc,
@@ -673,7 +676,6 @@ ngx_quic_connstate_dbg(ngx_connection_t *c)
p = ngx_slprintf(p, last, "%s", qc->closing ? " closing" : "");
p = ngx_slprintf(p, last, "%s", qc->draining ? " draining" : "");
p = ngx_slprintf(p, last, "%s", qc->key_phase ? " kp" : "");
- p = ngx_slprintf(p, last, "%s", qc->in_retry ? " retry" : "");
p = ngx_slprintf(p, last, "%s", qc->validated? " valid" : "");
} else {
@@ -1014,12 +1016,16 @@ ngx_quic_run(ngx_connection_t *c, ngx_quic_conf_t *conf)
qc = ngx_quic_get_connection(c);
- ngx_add_timer(c->read, qc->in_retry ? NGX_QUIC_RETRY_TIMEOUT
- : qc->tp.max_idle_timeout);
+ if (qc == NULL) {
+ ngx_quic_close_connection(c, NGX_DONE);
+ return;
+ }
+
+ ngx_add_timer(c->read, qc->tp.max_idle_timeout);
+ ngx_quic_connstate_dbg(c);
c->read->handler = ngx_quic_input_handler;
- ngx_quic_connstate_dbg(c);
return;
}
@@ -1123,8 +1129,8 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf,
qc->congestion.ssthresh = (size_t) -1;
qc->congestion.recovery_start = ngx_current_msec;
- qc->odcid.len = pkt->dcid.len;
- qc->odcid.data = ngx_pstrdup(c->pool, &pkt->dcid);
+ qc->odcid.len = pkt->odcid.len;
+ qc->odcid.data = ngx_pstrdup(c->pool, &pkt->odcid);
if (qc->odcid.data == NULL) {
return NULL;
}
@@ -1144,12 +1150,19 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf,
#endif
qc->tp.initial_scid = qc->dcid;
+ if (pkt->validated && pkt->retried) {
+ qc->tp.retry_scid.len = pkt->dcid.len;
+ qc->tp.retry_scid.data = ngx_pstrdup(c->pool, &pkt->dcid);
+ if (qc->tp.retry_scid.data == NULL) {
+ return NULL;
+ }
+ }
+
qc->scid.len = pkt->scid.len;
- qc->scid.data = ngx_pnalloc(c->pool, qc->scid.len);
+ qc->scid.data = ngx_pstrdup(c->pool, &pkt->scid);
if (qc->scid.data == NULL) {
return NULL;
}
- ngx_memcpy(qc->scid.data, pkt->scid.data, qc->scid.len);
cid = ngx_quic_alloc_client_id(c, qc);
if (cid == NULL) {
@@ -1166,6 +1179,26 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf,
qc->server_seqnum = NGX_QUIC_UNSET_PN;
+ if (ngx_quic_keys_set_initial_secret(c->pool, qc->keys, &pkt->dcid)
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ c->udp = &qc->udp;
+
+ if (ngx_quic_insert_server_id(c, &qc->odcid) == NULL) {
+ return NULL;
+ }
+
+ qc->server_seqnum = 0;
+
+ if (ngx_quic_insert_server_id(c, &qc->dcid) == NULL) {
+ return NULL;
+ }
+
+ qc->validated = pkt->validated;
+
return qc;
}
@@ -1344,27 +1377,41 @@ ngx_quic_bpf_attach_id(ngx_connection_t *c, u_char *id)
static ngx_int_t
-ngx_quic_send_retry(ngx_connection_t *c)
+ngx_quic_send_retry(ngx_connection_t *c, ngx_quic_conf_t *conf,
+ ngx_quic_header_t *inpkt)
{
- ssize_t len;
- ngx_str_t res, token;
- ngx_quic_header_t pkt;
- ngx_quic_connection_t *qc;
- u_char buf[NGX_QUIC_RETRY_BUFFER_SIZE];
+ time_t expires;
+ ssize_t len;
+ ngx_str_t res, token;
+ ngx_quic_header_t pkt;
- qc = ngx_quic_get_connection(c);
+ u_char buf[NGX_QUIC_RETRY_BUFFER_SIZE];
+ u_char dcid[NGX_QUIC_SERVER_CID_LEN];
- if (ngx_quic_new_token(c, &token) != NGX_OK) {
+ expires = ngx_time() + NGX_QUIC_RETRY_LIFETIME;
+
+ if (ngx_quic_new_token(c, conf->token_key, &token, &inpkt->dcid, expires, 1)
+ != NGX_OK)
+ {
return NGX_ERROR;
}
ngx_memzero(&pkt, sizeof(ngx_quic_header_t));
pkt.flags = NGX_QUIC_PKT_FIXED_BIT | NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_RETRY;
- pkt.version = qc->version;
+ pkt.version = inpkt->version;
pkt.log = c->log;
- pkt.odcid = qc->odcid;
- pkt.dcid = qc->scid;
- pkt.scid = qc->dcid;
+
+ pkt.odcid = inpkt->dcid;
+ pkt.dcid = inpkt->scid;
+
+ /* TODO: generate routable dcid */
+ if (RAND_bytes(dcid, NGX_QUIC_SERVER_CID_LEN) != 1) {
+ return NGX_ERROR;
+ }
+
+ pkt.scid.len = NGX_QUIC_SERVER_CID_LEN;
+ pkt.scid.data = dcid;
+
pkt.token = token;
res.data = buf;
@@ -1383,71 +1430,46 @@ ngx_quic_send_retry(ngx_connection_t *c)
return NGX_ERROR;
}
- qc->token = token;
-#if (NGX_QUIC_DRAFT_VERSION < 28)
- qc->tp.original_dcid = qc->odcid;
-#endif
- qc->tp.retry_scid = qc->dcid;
- qc->in_retry = 1;
-
- if (ngx_quic_insert_server_id(c, &qc->dcid) == NULL) {
- return NGX_ERROR;
- }
+ ngx_log_debug(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic retry packet sent to %xV", &pkt.dcid);
- return NGX_OK;
+ /*
+ * quic-transport 17.2.5.1: A server MUST NOT send more than one Retry
+ * packet in response to a single UDP datagram.
+ * NGX_DONE will stop quic_input() from processing further
+ */
+ return NGX_DONE;
}
static ngx_int_t
-ngx_quic_new_token(ngx_connection_t *c, ngx_str_t *token)
+ngx_quic_new_token(ngx_connection_t *c, u_char *key, ngx_str_t *token,
+ ngx_str_t *odcid, time_t exp, ngx_uint_t is_retry)
{
- int len, iv_len;
- u_char *data, *p, *key, *iv;
- ngx_msec_t now;
- EVP_CIPHER_CTX *ctx;
- const EVP_CIPHER *cipher;
- struct sockaddr_in *sin;
-#if (NGX_HAVE_INET6)
- struct sockaddr_in6 *sin6;
-#endif
- ngx_quic_connection_t *qc;
- u_char in[NGX_QUIC_MAX_TOKEN_SIZE];
-
- switch (c->sockaddr->sa_family) {
-
-#if (NGX_HAVE_INET6)
- case AF_INET6:
- sin6 = (struct sockaddr_in6 *) c->sockaddr;
+ int len, iv_len;
+ u_char *p, *iv;
+ EVP_CIPHER_CTX *ctx;
+ const EVP_CIPHER *cipher;
- len = sizeof(struct in6_addr);
- data = sin6->sin6_addr.s6_addr;
+ u_char in[NGX_QUIC_MAX_TOKEN_SIZE];
- break;
-#endif
+ ngx_quic_address_hash(c, !is_retry, in);
-#if (NGX_HAVE_UNIX_DOMAIN)
- case AF_UNIX:
+ p = in + 20;
- len = ngx_min(c->addr_text.len, NGX_QUIC_MAX_TOKEN_SIZE - sizeof(now));
- data = c->addr_text.data;
+ p = ngx_cpymem(p, &exp, sizeof(time_t));
- break;
-#endif
+ *p++ = is_retry ? 1 : 0;
- default: /* AF_INET */
- sin = (struct sockaddr_in *) c->sockaddr;
+ if (odcid) {
+ *p++ = odcid->len;
+ p = ngx_cpymem(p, odcid->data, odcid->len);
- len = sizeof(in_addr_t);
- data = (u_char *) &sin->sin_addr;
-
- break;
+ } else {
+ *p++ = 0;
}
- p = ngx_cpymem(in, data, len);
-
- now = ngx_current_msec;
- len += sizeof(now);
- ngx_memcpy(p, &now, sizeof(now));
+ len = p - in;
cipher = EVP_aes_256_cbc();
iv_len = EVP_CIPHER_iv_length(cipher);
@@ -1463,8 +1485,6 @@ ngx_quic_new_token(ngx_connection_t *c, ngx_str_t *token)
return NGX_ERROR;
}
- qc = ngx_quic_get_connection(c);
- key = qc->conf->token_key;
iv = token->data;
if (RAND_bytes(iv, iv_len) <= 0
@@ -1501,52 +1521,78 @@ ngx_quic_new_token(ngx_connection_t *c, ngx_str_t *token)
}
-static ngx_int_t
-ngx_quic_validate_token(ngx_connection_t *c, ngx_quic_header_t *pkt)
+static void
+ngx_quic_address_hash(ngx_connection_t *c, ngx_uint_t no_port, u_char buf[20])
{
- int len, tlen, iv_len;
- u_char *key, *iv, *p, *data;
- ngx_msec_t msec;
- EVP_CIPHER_CTX *ctx;
- const EVP_CIPHER *cipher;
- struct sockaddr_in *sin;
+ size_t len;
+ u_char *data;
+ ngx_sha1_t sha1;
+ struct sockaddr_in *sin;
#if (NGX_HAVE_INET6)
- struct sockaddr_in6 *sin6;
+ struct sockaddr_in6 *sin6;
#endif
- ngx_quic_connection_t *qc;
- u_char tdec[NGX_QUIC_MAX_TOKEN_SIZE];
- qc = ngx_quic_get_connection(c);
+ len = (size_t) c->socklen;
+ data = (u_char *) c->sockaddr;
- /* Retry token */
+ if (no_port) {
+ switch (c->sockaddr->sa_family) {
- if (qc->token.len) {
- if (pkt->token.len != qc->token.len) {
- goto bad_token;
- }
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) c->sockaddr;
- if (ngx_memcmp(pkt->token.data, qc->token.data, pkt->token.len) != 0) {
- goto bad_token;
- }
+ len = sizeof(struct in6_addr);
+ data = sin6->sin6_addr.s6_addr;
- return NGX_OK;
+ break;
+#endif
+
+ case AF_INET:
+ sin = (struct sockaddr_in *) c->sockaddr;
+
+ len = sizeof(in_addr_t);
+ data = (u_char *) &sin->sin_addr;
+
+ break;
+ }
}
- /* NEW_TOKEN in a previous connection */
+ ngx_sha1_init(&sha1);
+ ngx_sha1_update(&sha1, data, len);
+ ngx_sha1_final(buf, &sha1);
+}
+
+
+static ngx_int_t
+ngx_quic_validate_token(ngx_connection_t *c, u_char *key,
+ ngx_quic_header_t *pkt)
+{
+ int len, tlen, iv_len;
+ u_char *iv, *p;
+ time_t now, exp;
+ size_t total;
+ ngx_str_t odcid;
+ EVP_CIPHER_CTX *ctx;
+ const EVP_CIPHER *cipher;
+
+ u_char addr_hash[20];
+ u_char tdec[NGX_QUIC_MAX_TOKEN_SIZE];
+
+ /* Retry token or NEW_TOKEN in a previous connection */
cipher = EVP_aes_256_cbc();
- key = qc->conf->token_key;
iv = pkt->token.data;
iv_len = EVP_CIPHER_iv_length(cipher);
/* sanity checks */
if (pkt->token.len < (size_t) iv_len + EVP_CIPHER_block_size(cipher)) {
- goto bad_token;
+ goto garbage;
}
if (pkt->token.len > (size_t) iv_len + NGX_QUIC_MAX_TOKEN_SIZE) {
- goto bad_token;
+ goto garbage;
}
ctx = EVP_CIPHER_CTX_new();
@@ -1564,66 +1610,81 @@ ngx_quic_validate_token(ngx_connection_t *c, ngx_quic_header_t *pkt)
if (EVP_DecryptUpdate(ctx, tdec, &len, p, len) != 1) {
EVP_CIPHER_CTX_free(ctx);
- goto bad_token;
+ goto garbage;
}
+ total = len;
if (EVP_DecryptFinal_ex(ctx, tdec + len, &tlen) <= 0) {
EVP_CIPHER_CTX_free(ctx);
- goto bad_token;
+ goto garbage;
}
+ total += tlen;
EVP_CIPHER_CTX_free(ctx);
- switch (c->sockaddr->sa_family) {
-
-#if (NGX_HAVE_INET6)
- case AF_INET6:
- sin6 = (struct sockaddr_in6 *) c->sockaddr;
+ if (total < (20 + sizeof(time_t) + 2)) {
+ goto garbage;
+ }
- len = sizeof(struct in6_addr);
- data = sin6->sin6_addr.s6_addr;
+ p = tdec + 20;
- break;
-#endif
+ ngx_memcpy(&exp, p, sizeof(time_t));
+ p += sizeof(time_t);
-#if (NGX_HAVE_UNIX_DOMAIN)
- case AF_UNIX:
+ pkt->retried = (*p++ == 1);
- len = ngx_min(c->addr_text.len, NGX_QUIC_MAX_TOKEN_SIZE - sizeof(msec));
- data = c->addr_text.data;
-
- break;
-#endif
+ ngx_quic_address_hash(c, !pkt->retried, addr_hash);
- default: /* AF_INET */
- sin = (struct sockaddr_in *) c->sockaddr;
+ if (ngx_memcmp(tdec, addr_hash, 20) != 0) {
+ goto bad_token;
+ }
- len = sizeof(in_addr_t);
- data = (u_char *) &sin->sin_addr;
+ odcid.len = *p++;
+ if (odcid.len) {
+ if (odcid.len > NGX_QUIC_MAX_CID_LEN) {
+ goto bad_token;
+ }
- break;
- }
+ if ((size_t)(tdec + total - p) < odcid.len) {
+ goto bad_token;
+ }
- if (ngx_memcmp(tdec, data, len) != 0) {
- goto bad_token;
+ odcid.data = p;
+ p += odcid.len;
}
- ngx_memcpy(&msec, tdec + len, sizeof(msec));
+ now = ngx_time();
- if (ngx_current_msec - msec > NGX_QUIC_RETRY_LIFETIME) {
+ if (now > exp) {
ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic expired token");
return NGX_DECLINED;
}
+ if (odcid.len) {
+ pkt->odcid.len = odcid.len;
+ pkt->odcid.data = ngx_pstrdup(c->pool, &odcid);
+ if (pkt->odcid.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ } else {
+ pkt->odcid = pkt->dcid;
+ }
+
+ pkt->validated = 1;
+
return NGX_OK;
+garbage:
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic garbage token");
+
+ return NGX_ABORT;
+
bad_token:
ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic invalid token");
- qc->error = NGX_QUIC_ERR_INVALID_TOKEN;
- qc->error_reason = "invalid_token";
-
return NGX_DECLINED;
}
@@ -1817,8 +1878,10 @@ ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc)
qc = ngx_quic_get_connection(c);
if (qc == NULL) {
- ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic close connection early error");
+ if (rc == NGX_ERROR) {
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic close connection early error");
+ }
} else if (ngx_quic_close_quic(c, rc) == NGX_AGAIN) {
return;
@@ -2101,6 +2164,11 @@ ngx_quic_input(ngx_connection_t *c, ngx_buf_t *b, ngx_quic_conf_t *conf)
return NGX_ERROR;
}
+ if (rc == NGX_DONE) {
+ /* stop further processing */
+ return NGX_DECLINED;
+ }
+
if (rc == NGX_OK) {
good = 1;
}
@@ -2216,60 +2284,6 @@ ngx_quic_process_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
return NGX_DECLINED;
}
- if (qc->in_retry) {
-
- c->log->action = "retrying quic connection";
-
- if (pkt->level != ssl_encryption_initial) {
- ngx_log_error(NGX_LOG_INFO, c->log, 0,
- "quic discard late retry packet");
- return NGX_DECLINED;
- }
-
- if (!pkt->token.len) {
- ngx_log_error(NGX_LOG_INFO, c->log, 0,
- "quic discard retry packet without token");
- return NGX_DECLINED;
- }
-
- qc->odcid.len = pkt->dcid.len;
- qc->odcid.data = ngx_pstrdup(c->pool, &pkt->dcid);
- if (qc->odcid.data == NULL) {
- return NGX_ERROR;
- }
-
- ngx_quic_clear_temp_server_ids(c);
-
- qc->dcid.len = NGX_QUIC_SERVER_CID_LEN;
- qc->dcid.data = ngx_pnalloc(c->pool, qc->dcid.len);
- if (qc->dcid.data == NULL) {
- return NGX_ERROR;
- }
-
- if (ngx_quic_create_server_id(c, qc->dcid.data) != NGX_OK) {
- return NGX_ERROR;
- }
-
- qc->server_seqnum = 0;
-
- if (ngx_quic_insert_server_id(c, &qc->dcid) == NULL) {
- return NGX_ERROR;
- }
-
- qc->tp.initial_scid = qc->dcid;
- qc->in_retry = 0;
-
- if (ngx_quic_init_secrets(c) != NGX_OK) {
- return NGX_ERROR;
- }
-
- if (ngx_quic_validate_token(c, pkt) != NGX_OK) {
- return NGX_ERROR;
- }
-
- qc->validated = 1;
- }
-
} else {
if (rc == NGX_ABORT) {
@@ -2277,8 +2291,7 @@ ngx_quic_process_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
}
if (pkt->level == ssl_encryption_initial) {
-
- c->log->action = "creating quic connection";
+ c->log->action = "processing initial packet";
if (pkt->dcid.len < NGX_QUIC_CID_LEN_MIN) {
/* 7.2. Negotiating Connection IDs */
@@ -2288,49 +2301,56 @@ ngx_quic_process_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
return NGX_ERROR;
}
- qc = ngx_quic_new_connection(c, conf, pkt);
- if (qc == NULL) {
- return NGX_ERROR;
- }
-
- c->udp = &qc->udp;
-
- if (ngx_terminate || ngx_exiting) {
- qc->error = NGX_QUIC_ERR_CONNECTION_REFUSED;
- return NGX_ERROR;
- }
+ /* process retry and initialize connection IDs */
if (pkt->token.len) {
- rc = ngx_quic_validate_token(c, pkt);
- if (rc == NGX_OK) {
- qc->validated = 1;
+ rc = ngx_quic_validate_token(c, conf->token_key, pkt);
- } else if (rc == NGX_ERROR) {
+ if (rc == NGX_ERROR) {
+ /* internal error */
return NGX_ERROR;
- } else {
- /* NGX_DECLINED */
- if (conf->retry) {
- return ngx_quic_send_retry(c);
+ } else if (rc == NGX_ABORT) {
+ /* token cannot be decrypted */
+ return ngx_quic_send_early_cc(c, pkt,
+ NGX_QUIC_ERR_INVALID_TOKEN,
+ "cannot decrypt token");
+ } else if (rc == NGX_DECLINED) {
+ /* token is invalid */
+
+ if (pkt->retried) {
+ /* invalid Retry token */
+ return ngx_quic_send_early_cc(c, pkt,
+ NGX_QUIC_ERR_INVALID_TOKEN,
+ "invalid token");
+ } else if (conf->retry) {
+ /* invalid NEW_TOKEN */
+ return ngx_quic_send_retry(c, conf, pkt);
}
}
+ /* NGX_OK */
+
} else if (conf->retry) {
- return ngx_quic_send_retry(c);
- }
+ return ngx_quic_send_retry(c, conf, pkt);
- if (ngx_quic_init_secrets(c) != NGX_OK) {
- return NGX_ERROR;
+ } else {
+ pkt->odcid = pkt->dcid;
}
- if (ngx_quic_insert_server_id(c, &qc->odcid) == NULL) {
+ if (ngx_terminate || ngx_exiting) {
+ if (conf->retry) {
+ return ngx_quic_send_retry(c, conf, pkt);
+ }
+
return NGX_ERROR;
}
- qc->server_seqnum = 0;
+ c->log->action = "creating quic connection";
- if (ngx_quic_insert_server_id(c, &qc->dcid) == NULL) {
+ qc = ngx_quic_new_connection(c, conf, pkt);
+ if (qc == NULL) {
return NGX_ERROR;
}
@@ -2411,19 +2431,76 @@ ngx_quic_process_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
static ngx_int_t
-ngx_quic_init_secrets(ngx_connection_t *c)
+ngx_quic_send_early_cc(ngx_connection_t *c, ngx_quic_header_t *inpkt,
+ ngx_uint_t err, const char *reason)
{
- ngx_quic_connection_t *qc;
+ ssize_t len;
+ ngx_str_t res;
+ ngx_quic_frame_t frame;
+ ngx_quic_header_t pkt;
- qc = ngx_quic_get_connection(c);
+ static u_char src[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
+ static u_char dst[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
+
+ ngx_memzero(&frame, sizeof(ngx_quic_frame_t));
+ ngx_memzero(&pkt, sizeof(ngx_quic_header_t));
+
+ frame.level = inpkt->level;
+ frame.type = NGX_QUIC_FT_CONNECTION_CLOSE;
+ frame.u.close.error_code = err;
+
+ frame.u.close.reason.data = (u_char *) reason;
+ frame.u.close.reason.len = ngx_strlen(reason);
+
+ len = ngx_quic_create_frame(NULL, &frame);
+ if (len > NGX_QUIC_MAX_UDP_PAYLOAD_SIZE) {
+ return NGX_ERROR;
+ }
- if (ngx_quic_keys_set_initial_secret(c->pool, qc->keys, &qc->odcid)
+ ngx_quic_log_frame(c->log, &frame, 1);
+
+ len = ngx_quic_create_frame(src, &frame);
+ if (len == -1) {
+ return NGX_ERROR;
+ }
+
+ pkt.keys = ngx_quic_keys_new(c->pool);
+ if (pkt.keys == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_quic_keys_set_initial_secret(c->pool, pkt.keys, &inpkt->dcid)
!= NGX_OK)
{
return NGX_ERROR;
}
- qc->initialized = 1;
+ pkt.flags = NGX_QUIC_PKT_FIXED_BIT | NGX_QUIC_PKT_LONG
+ | NGX_QUIC_PKT_INITIAL;
+
+ pkt.num_len = 1;
+ /*
+ * pkt.num = 0;
+ * pkt.trunc = 0;
+ */
+
+ pkt.version = inpkt->version;
+ pkt.log = c->log;
+ pkt.level = inpkt->level;
+ pkt.dcid = inpkt->scid;
+ pkt.scid = inpkt->dcid;
+ pkt.payload.data = src;
+ pkt.payload.len = len;
+
+ res.data = dst;
+
+ if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_quic_send(c, res.data, res.len) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
return NGX_OK;
}
@@ -3156,13 +3233,6 @@ ngx_quic_send_cc(ngx_connection_t *c)
return NGX_OK;
}
- if (!qc->initialized) {
- /* try to initialize secrets to send an early error */
- if (ngx_quic_init_secrets(c) != NGX_OK) {
- return NGX_OK;
- }
- }
-
if (qc->closing
&& ngx_current_msec - qc->last_cc < NGX_QUIC_CC_MIN_INTERVAL)
{
@@ -3197,6 +3267,7 @@ ngx_quic_send_cc(ngx_connection_t *c)
static ngx_int_t
ngx_quic_send_new_token(ngx_connection_t *c)
{
+ time_t expires;
ngx_str_t token;
ngx_quic_frame_t *frame;
ngx_quic_connection_t *qc;
@@ -3207,7 +3278,11 @@ ngx_quic_send_new_token(ngx_connection_t *c)
return NGX_OK;
}
- if (ngx_quic_new_token(c, &token) != NGX_OK) {
+ expires = ngx_time() + NGX_QUIC_NEW_TOKEN_LIFETIME;
+
+ if (ngx_quic_new_token(c, qc->conf->token_key, &token, NULL, expires, 0)
+ != NGX_OK)
+ {
return NGX_ERROR;
}
diff --git a/src/event/quic/ngx_event_quic.h b/src/event/quic/ngx_event_quic.h
index 59578feea..0c94707a5 100644
--- a/src/event/quic/ngx_event_quic.h
+++ b/src/event/quic/ngx_event_quic.h
@@ -29,12 +29,12 @@
#define NGX_QUIC_DEFAULT_MAX_ACK_DELAY 25
#define NGX_QUIC_DEFAULT_SRT_KEY_LEN 32
-#define NGX_QUIC_RETRY_TIMEOUT 3000
-#define NGX_QUIC_RETRY_LIFETIME 30000
-#define NGX_QUIC_RETRY_BUFFER_SIZE 128
- /* 1 flags + 4 version + 3 x (1 + 20) s/o/dcid + itag + token(44) */
-#define NGX_QUIC_MAX_TOKEN_SIZE 32
- /* sizeof(struct in6_addr) + sizeof(ngx_msec_t) up to AES-256 block size */
+#define NGX_QUIC_RETRY_LIFETIME 3 /* seconds */
+#define NGX_QUIC_NEW_TOKEN_LIFETIME 600 /* seconds */
+#define NGX_QUIC_RETRY_BUFFER_SIZE 256
+ /* 1 flags + 4 version + 3 x (1 + 20) s/o/dcid + itag + token(64) */
+#define NGX_QUIC_MAX_TOKEN_SIZE 64
+ /* SHA-1(addr)=20 + sizeof(time_t) + retry(1) + odcid.len(1) + odcid */
/* quic-recovery, section 6.2.2, kInitialRtt */
#define NGX_QUIC_INITIAL_RTT 333 /* ms */
diff --git a/src/event/quic/ngx_event_quic_transport.h b/src/event/quic/ngx_event_quic_transport.h
index 1a3fe7766..7f82d949e 100644
--- a/src/event/quic/ngx_event_quic_transport.h
+++ b/src/event/quic/ngx_event_quic_transport.h
@@ -325,6 +325,8 @@ typedef struct {
unsigned key_update:1;
unsigned parsed:1;
unsigned decrypted:1;
+ unsigned validated:1;
+ unsigned retried:1;
} ngx_quic_header_t;
diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c
index 5314af35b..901d1a563 100644
--- a/src/http/modules/ngx_http_quic_module.c
+++ b/src/http/modules/ngx_http_quic_module.c
@@ -346,10 +346,8 @@ ngx_http_quic_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->retry, prev->retry, 0);
- if (conf->retry) {
- if (RAND_bytes(conf->token_key, sizeof(conf->token_key)) <= 0) {
- return NGX_CONF_ERROR;
- }
+ if (RAND_bytes(conf->token_key, sizeof(conf->token_key)) <= 0) {
+ return NGX_CONF_ERROR;
}
ngx_conf_merge_str_value(conf->sr_token_key, prev->sr_token_key, "");
diff --git a/src/stream/ngx_stream_quic_module.c b/src/stream/ngx_stream_quic_module.c
index eaaaba89a..e6466bba4 100644
--- a/src/stream/ngx_stream_quic_module.c
+++ b/src/stream/ngx_stream_quic_module.c
@@ -305,10 +305,8 @@ ngx_stream_quic_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->retry, prev->retry, 0);
- if (conf->retry) {
- if (RAND_bytes(conf->token_key, sizeof(conf->token_key)) <= 0) {
- return NGX_CONF_ERROR;
- }
+ if (RAND_bytes(conf->token_key, sizeof(conf->token_key)) <= 0) {
+ return NGX_CONF_ERROR;
}
ngx_conf_merge_str_value(conf->sr_token_key, prev->sr_token_key, "");