aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Kandaurov <pluknet@nginx.com>2020-02-28 13:09:51 +0300
committerSergey Kandaurov <pluknet@nginx.com>2020-02-28 13:09:51 +0300
commit26ac1c73f0fe90c77cbad84a6b4ef5712e35ba52 (patch)
tree9860cd3f43f9fae5b3d5656b273b4910c504835e
parente92cb24f40b865e3cc5b9f0993e328e4f0642e0f (diff)
downloadnginx-26ac1c73f0fe90c77cbad84a6b4ef5712e35ba52.tar.gz
nginx-26ac1c73f0fe90c77cbad84a6b4ef5712e35ba52.zip
Initial QUIC support in http.
-rw-r--r--auto/modules3
-rw-r--r--src/core/ngx_connection.h7
-rw-r--r--src/core/ngx_core.h36
-rw-r--r--src/event/ngx_event_openssl.c143
-rw-r--r--src/event/ngx_event_openssl.h3
-rw-r--r--src/event/ngx_event_quic.h31
-rw-r--r--src/http/modules/ngx_http_ssl_module.c27
-rw-r--r--src/http/modules/ngx_http_ssl_module.h1
-rw-r--r--src/http/ngx_http.c26
-rw-r--r--src/http/ngx_http_core_module.c18
-rw-r--r--src/http/ngx_http_core_module.h2
-rw-r--r--src/http/ngx_http_request.c495
-rw-r--r--src/http/ngx_http_request.h1
13 files changed, 766 insertions, 27 deletions
diff --git a/auto/modules b/auto/modules
index d78e2823a..480f2b0a7 100644
--- a/auto/modules
+++ b/auto/modules
@@ -1242,7 +1242,8 @@ if [ $USE_OPENSSL = YES ]; then
ngx_module_type=CORE
ngx_module_name=ngx_openssl_module
ngx_module_incs=
- ngx_module_deps=src/event/ngx_event_openssl.h
+ ngx_module_deps="src/event/ngx_event_openssl.h \
+ src/event/ngx_event_quic.h"
ngx_module_srcs="src/event/ngx_event_openssl.c
src/event/ngx_event_openssl_stapling.c"
ngx_module_libs=
diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h
index ad6556d0c..0d7e2166b 100644
--- a/src/core/ngx_connection.h
+++ b/src/core/ngx_connection.h
@@ -147,13 +147,14 @@ struct ngx_connection_s {
socklen_t socklen;
ngx_str_t addr_text;
- ngx_proxy_protocol_t *proxy_protocol;
+ ngx_proxy_protocol_t *proxy_protocol;
#if (NGX_SSL || NGX_COMPAT)
- ngx_ssl_connection_t *ssl;
+ ngx_quic_connection_t *quic;
+ ngx_ssl_connection_t *ssl;
#endif
- ngx_udp_connection_t *udp;
+ ngx_udp_connection_t *udp;
struct sockaddr *local_sockaddr;
socklen_t local_socklen;
diff --git a/src/core/ngx_core.h b/src/core/ngx_core.h
index 7ecdca0cb..549fae084 100644
--- a/src/core/ngx_core.h
+++ b/src/core/ngx_core.h
@@ -12,23 +12,24 @@
#include <ngx_config.h>
-typedef struct ngx_module_s ngx_module_t;
-typedef struct ngx_conf_s ngx_conf_t;
-typedef struct ngx_cycle_s ngx_cycle_t;
-typedef struct ngx_pool_s ngx_pool_t;
-typedef struct ngx_chain_s ngx_chain_t;
-typedef struct ngx_log_s ngx_log_t;
-typedef struct ngx_open_file_s ngx_open_file_t;
-typedef struct ngx_command_s ngx_command_t;
-typedef struct ngx_file_s ngx_file_t;
-typedef struct ngx_event_s ngx_event_t;
-typedef struct ngx_event_aio_s ngx_event_aio_t;
-typedef struct ngx_connection_s ngx_connection_t;
-typedef struct ngx_thread_task_s ngx_thread_task_t;
-typedef struct ngx_ssl_s ngx_ssl_t;
-typedef struct ngx_proxy_protocol_s ngx_proxy_protocol_t;
-typedef struct ngx_ssl_connection_s ngx_ssl_connection_t;
-typedef struct ngx_udp_connection_s ngx_udp_connection_t;
+typedef struct ngx_module_s ngx_module_t;
+typedef struct ngx_conf_s ngx_conf_t;
+typedef struct ngx_cycle_s ngx_cycle_t;
+typedef struct ngx_pool_s ngx_pool_t;
+typedef struct ngx_chain_s ngx_chain_t;
+typedef struct ngx_log_s ngx_log_t;
+typedef struct ngx_open_file_s ngx_open_file_t;
+typedef struct ngx_command_s ngx_command_t;
+typedef struct ngx_file_s ngx_file_t;
+typedef struct ngx_event_s ngx_event_t;
+typedef struct ngx_event_aio_s ngx_event_aio_t;
+typedef struct ngx_connection_s ngx_connection_t;
+typedef struct ngx_thread_task_s ngx_thread_task_t;
+typedef struct ngx_ssl_s ngx_ssl_t;
+typedef struct ngx_proxy_protocol_s ngx_proxy_protocol_t;
+typedef struct ngx_quic_connection_s ngx_quic_connection_t;
+typedef struct ngx_ssl_connection_s ngx_ssl_connection_t;
+typedef struct ngx_udp_connection_s ngx_udp_connection_t;
typedef void (*ngx_event_handler_pt)(ngx_event_t *ev);
typedef void (*ngx_connection_handler_pt)(ngx_connection_t *c);
@@ -82,6 +83,7 @@ typedef void (*ngx_connection_handler_pt)(ngx_connection_t *c);
#include <ngx_resolver.h>
#if (NGX_OPENSSL)
#include <ngx_event_openssl.h>
+#include <ngx_event_quic.h>
#endif
#include <ngx_process_cycle.h>
#include <ngx_conf_file.h>
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
index 91b415caa..baf28ecfd 100644
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -89,6 +89,126 @@ static void *ngx_openssl_create_conf(ngx_cycle_t *cycle);
static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static void ngx_openssl_exit(ngx_cycle_t *cycle);
+#if NGX_OPENSSL_QUIC
+
+static int
+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)
+{
+ size_t *len;
+ uint8_t **rsec, **wsec;
+ ngx_connection_t *c;
+
+ c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
+
+ ngx_ssl_handshake_log(c);
+
+#if (NGX_DEBUG)
+ if (c->log->log_level & NGX_LOG_DEBUG_EVENT) {
+ u_char buf[64];
+ size_t m;
+
+ m = ngx_hex_dump(buf, (u_char *) read_secret, secret_len) - buf;
+ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "set_encryption_secrets: %*s, len: %uz, level:%d",
+ m, buf, secret_len, (int) level);
+
+ m = ngx_hex_dump(buf, (u_char *) write_secret, secret_len) - buf;
+ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "set_encryption_secrets: %*s, len: %uz, level:%d",
+ m, buf, secret_len, (int) level);
+ }
+#endif
+
+ switch (level) {
+
+ case ssl_encryption_handshake:
+ len = &c->quic->handshake_secret_len;
+ rsec = &c->quic->handshake_read_secret;
+ wsec = &c->quic->handshake_write_secret;
+ break;
+
+ case ssl_encryption_application:
+ len = &c->quic->application_secret_len;
+ rsec = &c->quic->application_read_secret;
+ wsec = &c->quic->application_write_secret;
+ break;
+
+ default:
+ return 0;
+ }
+
+ *len = secret_len;
+
+ *rsec = ngx_pnalloc(c->pool, secret_len);
+ if (*rsec == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(*rsec, read_secret, secret_len);
+
+ *wsec = ngx_pnalloc(c->pool, secret_len);
+ if (*wsec == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(*wsec, write_secret, secret_len);
+
+ return 1;
+}
+
+
+static int
+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 buf[512];
+ ngx_int_t m;
+ ngx_connection_t *c;
+
+ c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
+
+ m = ngx_hex_dump(buf, (u_char *) data, ngx_min(len, 256)) - buf;
+ ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic_add_handshake_data: %*s%s, len: %uz, level:%d",
+ m, buf, len < 512 ? "" : "...", len, (int) level);
+
+ if (!(SSL_provide_quic_data(ssl_conn, level, data, len))) {
+ ERR_print_errors_fp(stderr);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int
+quic_flush_flight(ngx_ssl_conn_t *ssl_conn)
+{
+ printf("quic_flush_flight()\n");
+ return 1;
+}
+
+
+static int
+quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level,
+ uint8_t alert)
+{
+ printf("quic_send_alert(), lvl=%d, alert=%d\n", level, alert);
+ return 1;
+}
+
+
+static SSL_QUIC_METHOD quic_method = {
+ quic_set_encryption_secrets,
+ quic_add_handshake_data,
+ quic_flush_flight,
+ quic_send_alert,
+};
+
+#endif
+
static ngx_command_t ngx_openssl_commands[] = {
@@ -1460,6 +1580,29 @@ ngx_ssl_early_data(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_uint_t enable)
ngx_int_t
+ngx_ssl_quic(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_uint_t enable)
+{
+ if (!enable) {
+ return NGX_OK;
+ }
+
+#if NGX_OPENSSL_QUIC
+
+ SSL_CTX_set_quic_method(ssl->ctx, &quic_method);
+printf("%s\n", __func__);
+ return NGX_OK;
+
+#else
+
+ ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
+ "\"ssl_quic\" is not supported on this platform");
+ return NGX_ERROR;
+
+#endif
+}
+
+
+ngx_int_t
ngx_ssl_client_session_cache(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_uint_t enable)
{
if (!enable) {
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
index 61da0c5db..c6124275f 100644
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -14,6 +14,7 @@
#include <openssl/ssl.h>
#include <openssl/err.h>
+#include <openssl/aes.h>
#include <openssl/bn.h>
#include <openssl/conf.h>
#include <openssl/crypto.h>
@@ -22,6 +23,7 @@
#include <openssl/engine.h>
#endif
#include <openssl/evp.h>
+#include <openssl/hkdf.h>
#include <openssl/hmac.h>
#ifndef OPENSSL_NO_OCSP
#include <openssl/ocsp.h>
@@ -189,6 +191,7 @@ ngx_int_t ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file);
ngx_int_t ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name);
ngx_int_t ngx_ssl_early_data(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_uint_t enable);
+ngx_int_t ngx_ssl_quic(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_uint_t enable);
ngx_int_t ngx_ssl_client_session_cache(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_uint_t enable);
ngx_int_t ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,
diff --git a/src/event/ngx_event_quic.h b/src/event/ngx_event_quic.h
new file mode 100644
index 000000000..6b7d32427
--- /dev/null
+++ b/src/event/ngx_event_quic.h
@@ -0,0 +1,31 @@
+
+/*
+ *
+ */
+
+
+#ifndef _NGX_EVENT_QUIC_H_INCLUDED_
+#define _NGX_EVENT_QUIC_H_INCLUDED_
+
+
+struct ngx_quic_connection_s {
+ ngx_str_t scid;
+ ngx_str_t dcid;
+ ngx_str_t token;
+
+ ngx_str_t client_in;
+ ngx_str_t client_in_key;
+ ngx_str_t client_in_iv;
+ ngx_str_t client_in_hp;
+
+ size_t handshake_secret_len;
+ uint8_t *handshake_read_secret;
+ uint8_t *handshake_write_secret;
+
+ size_t application_secret_len;
+ uint8_t *application_read_secret;
+ uint8_t *application_write_secret;
+};
+
+
+#endif /* _NGX_EVENT_QUIC_H_INCLUDED_ */
diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
index 495e628d3..693e45a1c 100644
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -249,6 +249,13 @@ static ngx_command_t ngx_http_ssl_commands[] = {
offsetof(ngx_http_ssl_srv_conf_t, early_data),
NULL },
+ { ngx_string("ssl_quic"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, quic),
+ NULL },
+
ngx_null_command
};
@@ -568,6 +575,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
sscf->enable = NGX_CONF_UNSET;
sscf->prefer_server_ciphers = NGX_CONF_UNSET;
sscf->early_data = NGX_CONF_UNSET;
+ sscf->quic = NGX_CONF_UNSET;
sscf->buffer_size = NGX_CONF_UNSET_SIZE;
sscf->verify = NGX_CONF_UNSET_UINT;
sscf->verify_depth = NGX_CONF_UNSET_UINT;
@@ -612,6 +620,8 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->early_data, prev->early_data, 0);
+ ngx_conf_merge_value(conf->quic, prev->quic, 0);
+
ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,
(NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1
|NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));
@@ -696,6 +706,7 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
}
}
+printf("ngx_ssl_create\n");
if (ngx_ssl_create(&conf->ssl, conf->protocols, conf) != NGX_OK) {
return NGX_CONF_ERROR;
}
@@ -857,6 +868,10 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
return NGX_CONF_ERROR;
}
+ if (ngx_ssl_quic(cf, &conf->ssl, conf->quic) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
return NGX_CONF_OK;
}
@@ -1141,13 +1156,15 @@ ngx_http_ssl_init(ngx_conf_t *cf)
addr = port[p].addrs.elts;
for (a = 0; a < port[p].addrs.nelts; a++) {
+printf("ssl %d http3 %d\n", addr[a].opt.ssl, addr[a].opt.http3);
- if (!addr[a].opt.ssl) {
+ if (!addr[a].opt.ssl && !addr[a].opt.http3) {
continue;
}
cscf = addr[a].default_server;
sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index];
+printf("sscf->protocols %lx\n", sscf->protocols);
if (sscf->certificates == NULL) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
@@ -1156,6 +1173,14 @@ ngx_http_ssl_init(ngx_conf_t *cf)
cscf->file_name, cscf->line);
return NGX_ERROR;
}
+
+ if (addr[a].opt.http3 && !(sscf->protocols & NGX_SSL_TLSv1_3)) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "\"ssl_protocols\" did not enable TLSv1.3 for "
+ "the \"listen ... http3\" directive in %s:%ui",
+ cscf->file_name, cscf->line);
+ return NGX_ERROR;
+ }
}
}
diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h
index 26fdccfe4..310d7c737 100644
--- a/src/http/modules/ngx_http_ssl_module.h
+++ b/src/http/modules/ngx_http_ssl_module.h
@@ -21,6 +21,7 @@ typedef struct {
ngx_flag_t prefer_server_ciphers;
ngx_flag_t early_data;
+ ngx_flag_t quic;
ngx_uint_t protocols;
diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c
index 3e82dc60d..10f88fabb 100644
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -1203,6 +1203,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
#if (NGX_HTTP_V2)
ngx_uint_t http2;
#endif
+#if (NGX_HTTP_SSL)
+ ngx_uint_t http3;
+#endif
/*
* we cannot compare whole sockaddr struct's as kernel
@@ -1238,6 +1241,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
#if (NGX_HTTP_V2)
http2 = lsopt->http2 || addr[i].opt.http2;
#endif
+#if (NGX_HTTP_SSL)
+ http3 = lsopt->http3 || addr[i].opt.http3;
+#endif
if (lsopt->set) {
@@ -1274,6 +1280,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
#if (NGX_HTTP_V2)
addr[i].opt.http2 = http2;
#endif
+#if (NGX_HTTP_SSL)
+ addr[i].opt.http3 = http3;
+#endif
return NGX_OK;
}
@@ -1317,6 +1326,17 @@ ngx_http_add_address(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
#endif
+#if (NGX_HTTP_SSL && !defined NGX_OPENSSL_QUIC)
+
+ if (lsopt->http3) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "nginx was built with OpenSSL that lacks QUIC "
+ "support, HTTP/3 is not enabled for %V",
+ &lsopt->addr_text);
+ }
+
+#endif
+
addr = ngx_array_push(&port->addrs);
if (addr == NULL) {
return NGX_ERROR;
@@ -1807,6 +1827,9 @@ ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport,
#if (NGX_HTTP_V2)
addrs[i].conf.http2 = addr[i].opt.http2;
#endif
+#if (NGX_HTTP_SSL)
+ addrs[i].conf.http3 = addr[i].opt.http3;
+#endif
addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
if (addr[i].hash.buckets == NULL
@@ -1872,6 +1895,9 @@ ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport,
#if (NGX_HTTP_V2)
addrs6[i].conf.http2 = addr[i].opt.http2;
#endif
+#if (NGX_HTTP_SSL)
+ addrs6[i].conf.http3 = addr[i].opt.http3;
+#endif
addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
if (addr[i].hash.buckets == NULL
diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
index b0c8aabfc..5c210bebd 100644
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -3822,11 +3822,6 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
continue;
}
- if (ngx_strcmp(value[n].data, "quic") == 0) {
- lsopt.type = SOCK_DGRAM;
- continue;
- }
-
if (ngx_strcmp(value[n].data, "bind") == 0) {
lsopt.set = 1;
lsopt.bind = 1;
@@ -4004,6 +3999,19 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
#endif
}
+ if (ngx_strcmp(value[n].data, "http3") == 0) {
+#if (NGX_HTTP_SSL)
+ lsopt.http3 = 1;
+ lsopt.type = SOCK_DGRAM;
+ continue;
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the \"http3\" parameter requires "
+ "ngx_http_ssl_module");
+ return NGX_CONF_ERROR;
+#endif
+ }
+
if (ngx_strcmp(value[n].data, "spdy") == 0) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"invalid parameter \"spdy\": "
diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h
index 9f26b5794..25327b2f4 100644
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -75,6 +75,7 @@ typedef struct {
unsigned wildcard:1;
unsigned ssl:1;
unsigned http2:1;
+ unsigned http3:1;
#if (NGX_HAVE_INET6)
unsigned ipv6only:1;
#endif
@@ -238,6 +239,7 @@ struct ngx_http_addr_conf_s {
unsigned ssl:1;
unsigned http2:1;
+ unsigned http3:1;
unsigned proxy_protocol:1;
};
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index f137590fd..54a0da497 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -62,6 +62,8 @@ static u_char *ngx_http_log_error_handler(ngx_http_request_t *r,
#if (NGX_HTTP_SSL)
static void ngx_http_ssl_handshake(ngx_event_t *rev);
static void ngx_http_ssl_handshake_handler(ngx_connection_t *c);
+
+static void ngx_http_quic_handshake(ngx_event_t *rev);
#endif
@@ -328,6 +330,14 @@ ngx_http_init_connection(ngx_connection_t *c)
rev->ready = 1;
}
+#if (NGX_HTTP_SSL)
+ if (hc->addr_conf->http3) {
+ hc->quic = 1;
+ c->log->action = "QUIC handshaking";
+ rev->handler = ngx_http_quic_handshake;
+ }
+#endif
+
#if (NGX_HTTP_V2)
if (hc->addr_conf->http2) {
rev->handler = ngx_http_v2_init;
@@ -647,6 +657,491 @@ ngx_http_alloc_request(ngx_connection_t *c)
#if (NGX_HTTP_SSL)
+static uint64_t
+ngx_quic_parse_int(u_char **pos)
+{
+ u_char *p;
+ uint64_t value;
+ ngx_uint_t len;
+
+ p = *pos;
+ len = 1 << ((*p & 0xc0) >> 6);
+ value = *p++ & 0x3f;
+
+ while (--len) {
+ value = (value << 8) + *p++;
+ }
+
+ *pos = p;
+ return value;
+}
+
+
+static uint64_t
+ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask)
+{
+ u_char *p;
+ uint64_t value;
+
+ p = *pos;
+ value = *p++ ^ *mask++;
+
+ while (--len) {
+ value = (value << 8) + (*p++ ^ *mask++);
+ }
+
+ *pos = p;
+ return value;
+}
+
+
+static void
+ngx_http_quic_handshake(ngx_event_t *rev)
+{
+ int n, sslerr;
+#if (NGX_DEBUG)
+ u_char buf[512];
+ size_t m;
+#endif
+ ngx_buf_t *b;
+ ngx_connection_t *c;
+ ngx_http_connection_t *hc;
+ ngx_quic_connection_t *qc;
+ ngx_http_ssl_srv_conf_t *sscf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic handshake");
+
+ c = rev->data;
+ hc = c->data;
+ b = c->buffer;
+
+ qc = ngx_pcalloc(c->pool, sizeof(ngx_quic_connection_t));
+ if (qc == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ c->quic = qc;
+
+ printf("buffer %p %p:%p:%p:%p \n", b, b->start, b->pos, b->last, b->end);
+
+ if ((b->pos[0] & 0xf0) != 0xc0) {
+ ngx_log_error(NGX_LOG_INFO, rev->log, 0, "invalid initial packet");
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ if (ngx_buf_size(b) < 1200) {
+ ngx_log_error(NGX_LOG_INFO, rev->log, 0, "too small UDP datagram");
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ ngx_int_t flags = *b->pos++;
+ uint32_t version = ngx_http_v2_parse_uint32(b->pos);
+ b->pos += 4;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "quic flags:%xi version:%xD", flags, version);
+
+ if (version != 0xff000017) {
+ ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unsupported quic version");
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ qc->dcid.len = *b->pos++;
+ qc->dcid.data = ngx_pnalloc(c->pool, qc->dcid.len);
+ if (qc->dcid.data == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ ngx_memcpy(qc->dcid.data, b->pos, qc->dcid.len);
+ b->pos += qc->dcid.len;
+
+ qc->scid.len = *b->pos++;
+ qc->scid.data = ngx_pnalloc(c->pool, qc->scid.len);
+ if (qc->scid.data == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ ngx_memcpy(qc->scid.data, b->pos, qc->scid.len);
+ b->pos += qc->scid.len;
+
+ qc->token.len = ngx_quic_parse_int(&b->pos);
+ qc->token.data = ngx_pnalloc(c->pool, qc->token.len);
+ if (qc->token.data == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ ngx_memcpy(qc->token.data, b->pos, qc->token.len);
+ b->pos += qc->token.len;
+
+ uint64_t plen = ngx_quic_parse_int(&b->pos);
+ /* draft-ietf-quic-tls-23#section-5.4.2:
+ * the Packet Number field is assumed to be 4 bytes long
+ * draft-ietf-quic-tls-23#section-5.4.3:
+ * AES-Based header protection samples 16 bytes
+ */
+ u_char *sample = b->pos + 4;
+
+#if (NGX_DEBUG)
+ if (c->log->log_level & NGX_LOG_DEBUG_EVENT) {
+ m = ngx_hex_dump(buf, qc->dcid.data, qc->dcid.len) - buf;
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "quic DCID: %*s, len: %uz", m, buf, qc->dcid.len);
+
+ m = ngx_hex_dump(buf, qc->scid.data, qc->scid.len) - buf;
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "quic SCID: %*s, len: %uz", m, buf, qc->scid.len);
+
+ m = ngx_hex_dump(buf, qc->token.data, qc->token.len) - buf;
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "quic token: %*s, len: %uz", m, buf, qc->token.len);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "quic packet length: %d", plen);
+
+ m = ngx_hex_dump(buf, sample, 16) - buf;
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "quic sample: %*s", m, buf);
+ }
+#endif
+
+// initial secret
+
+ size_t is_len;
+ uint8_t is[SHA256_DIGEST_LENGTH];
+ const EVP_MD *digest;
+ static const uint8_t salt[20] =
+ "\xc3\xee\xf7\x12\xc7\x2e\xbb\x5a\x11\xa7"
+ "\xd2\x43\x2b\xb4\x63\x65\xbe\xf9\xf5\x02";
+
+ digest = EVP_sha256();
+ HKDF_extract(is, &is_len, digest, qc->dcid.data, qc->dcid.len, salt,
+ sizeof(salt));
+
+#if (NGX_DEBUG)
+ if (c->log->log_level & NGX_LOG_DEBUG_EVENT) {
+ m = ngx_hex_dump(buf, (uint8_t *) salt, sizeof(salt)) - buf;
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "quic salt: %*s, len: %uz", m, buf, sizeof(salt));
+
+ m = ngx_hex_dump(buf, is, is_len) - buf;
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "quic initial secret: %*s, len: %uz", m, buf, is_len);
+ }
+#endif
+
+ size_t hkdfl_len;
+ uint8_t hkdfl[20];
+ uint8_t *p;
+
+ /* draft-ietf-quic-tls-23#section-5.2 */
+
+ qc->client_in.len = SHA256_DIGEST_LENGTH;
+ qc->client_in.data = ngx_pnalloc(c->pool, qc->client_in.len);
+ if (qc->client_in.data == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ hkdfl_len = 2 + 1 + sizeof("tls13 client in") - 1 + 1;
+ hkdfl[0] = 0;
+ hkdfl[1] = qc->client_in.len;
+ hkdfl[2] = sizeof("tls13 client in") - 1;
+ p = ngx_cpymem(&hkdfl[3], "tls13 client in",
+ sizeof("tls13 client in") - 1);
+ *p = '\0';
+
+ if (HKDF_expand(qc->client_in.data, qc->client_in.len,
+ digest, is, is_len, hkdfl, hkdfl_len)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_INFO, rev->log, 0,
+ "HKDF_expand(client_in) failed");
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "quic EVP key:%d tag:%d nonce:%d",
+ EVP_AEAD_key_length(EVP_aead_aes_128_gcm()),
+ EVP_AEAD_max_tag_len(EVP_aead_aes_128_gcm()),
+ EVP_AEAD_nonce_length(EVP_aead_aes_128_gcm()));
+
+ /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */
+
+ qc->client_in_key.len = EVP_AEAD_key_length(EVP_aead_aes_128_gcm());
+ qc->client_in_key.data = ngx_pnalloc(c->pool, qc->client_in_key.len);
+ if (qc->client_in_key.data == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ hkdfl_len = 2 + 1 + sizeof("tls13 quic key") - 1 + 1;
+ hkdfl[1] = qc->client_in_key.len;
+ hkdfl[2] = sizeof("tls13 quic key") - 1;
+ p = ngx_cpymem(&hkdfl[3], "tls13 quic key",
+ sizeof("tls13 quic key") - 1);
+ *p = '\0';
+
+ if (HKDF_expand(qc->client_in_key.data, qc->client_in_key.len,
+ digest, qc->client_in.data, qc->client_in.len,
+ hkdfl, hkdfl_len)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_INFO, rev->log, 0,
+ "HKDF_expand(client_in_key) failed");
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ qc->client_in_iv.len = EVP_AEAD_nonce_length(EVP_aead_aes_128_gcm());
+ qc->client_in_iv.data = ngx_pnalloc(c->pool, qc->client_in_iv.len);
+ if (qc->client_in_iv.data == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ hkdfl_len = 2 + 1 + sizeof("tls13 quic iv") - 1 + 1;
+ hkdfl[1] = qc->client_in_iv.len;
+ hkdfl[2] = sizeof("tls13 quic iv") - 1;
+ p = ngx_cpymem(&hkdfl[3], "tls13 quic iv", sizeof("tls13 quic iv") - 1);
+ *p = '\0';
+
+ if (HKDF_expand(qc->client_in_iv.data, qc->client_in_iv.len, digest,
+ qc->client_in.data, qc->client_in.len, hkdfl, hkdfl_len)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_INFO, rev->log, 0,
+ "HKDF_expand(client_in_iv) failed");
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */
+
+ qc->client_in_hp.len = EVP_AEAD_key_length(EVP_aead_aes_128_gcm());
+ qc->client_in_hp.data = ngx_pnalloc(c->pool, qc->client_in_hp.len);
+ if (qc->client_in_hp.data == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ hkdfl_len = 2 + 1 + sizeof("tls13 quic hp") - 1 + 1;
+ hkdfl[1] = qc->client_in_hp.len;
+ hkdfl[2] = sizeof("tls13 quic hp") - 1;
+ p = ngx_cpymem(&hkdfl[3], "tls13 quic hp", sizeof("tls13 quic hp") - 1);
+ *p = '\0';
+
+ if (HKDF_expand(qc->client_in_hp.data, qc->client_in_hp.len, digest,
+ qc->client_in.data, qc->client_in.len, hkdfl, hkdfl_len)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_INFO, rev->log, 0,
+ "HKDF_expand(client_in_hp) failed");
+ ngx_http_close_connection(c);
+ return;
+ }
+
+#if (NGX_DEBUG)
+ if (c->log->log_level & NGX_LOG_DEBUG_EVENT) {
+ m = ngx_hex_dump(buf, qc->client_in.data, qc->client_in.len) - buf;
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "quic client initial secret: %*s, len: %uz",
+ m, buf, qc->client_in.len);
+
+ m = ngx_hex_dump(buf, qc->client_in_key.data, qc->client_in_key.len)
+ - buf;
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "quic key: %*s, len: %uz",
+ m, buf, qc->client_in_key.len);
+
+ m = ngx_hex_dump(buf, qc->client_in_iv.data, qc->client_in_iv.len)
+ - buf;
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "quic iv: %*s, len: %uz", m, buf, qc->client_in_iv.len);
+
+ m = ngx_hex_dump(buf, qc->client_in_hp.data, qc->client_in_hp.len)
+ - buf;
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "quic hp: %*s, len: %uz", m, buf, qc->client_in_hp.len);
+ }
+#endif
+
+// header protection
+
+ EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+ uint8_t mask[16];
+ int outlen;
+
+ if (EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL,
+ qc->client_in_hp.data, NULL)
+ != 1)
+ {
+ EVP_CIPHER_CTX_free(ctx);
+ ngx_ssl_error(NGX_LOG_INFO, rev->log, 0,
+ "EVP_EncryptInit_ex() failed");
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ if (!EVP_EncryptUpdate(ctx, mask, &outlen, sample, 16)) {
+ EVP_CIPHER_CTX_free(ctx);
+ ngx_ssl_error(NGX_LOG_INFO, rev->log, 0,
+ "EVP_EncryptUpdate() failed");
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ EVP_CIPHER_CTX_free(ctx);
+
+ u_char clearflags = flags ^ (mask[0] & 0x0f);
+ ngx_int_t pnl = (clearflags & 0x03) + 1;
+ uint64_t pn = ngx_quic_parse_pn(&b->pos, pnl, &mask[1]);
+
+#if (NGX_DEBUG)
+ if (c->log->log_level & NGX_LOG_DEBUG_EVENT) {
+ m = ngx_hex_dump(buf, sample, 16) - buf;
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "quic sample: %*s", m, buf);
+
+ m = ngx_hex_dump(buf, mask, 5) - buf;
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "quic mask: %*s", m, buf);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "quic packet number: %uL, len: %xi", pn, pnl);
+ }
+#endif
+
+// packet protection
+
+ ngx_str_t ciphertext;
+ ciphertext.data = b->pos;
+ ciphertext.len = plen - pnl;
+
+ ngx_str_t ad;
+ ad.len = b->pos - b->start;
+ ad.data = ngx_pnalloc(c->pool, ad.len);
+ if (ad.data == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ ngx_memcpy(ad.data, b->start, ad.len);
+ ad.data[0] = clearflags;
+ ad.data[ad.len - pnl] = (u_char)pn;
+
+ uint8_t *nonce = ngx_pstrdup(c->pool, &qc->client_in_iv);
+ nonce[11] ^= pn;
+
+#if (NGX_DEBUG)
+ if (c->log->log_level & NGX_LOG_DEBUG_EVENT) {
+ m = ngx_hex_dump(buf, nonce, 12) - buf;
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "quic nonce: %*s, len: %uz", m, buf, 12);
+
+ m = ngx_hex_dump(buf, ad.data, ad.len) - buf;
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "quic ad: %*s, len: %uz", m, buf, ad.len);
+ }
+#endif
+
+ EVP_AEAD_CTX *aead = EVP_AEAD_CTX_new(EVP_aead_aes_128_gcm(),
+ qc->client_in_key.data,
+ qc->client_in_key.len,
+ EVP_AEAD_DEFAULT_TAG_LENGTH);
+ uint8_t cleartext[1600];
+ size_t cleartext_len = sizeof(cleartext);
+
+ if (EVP_AEAD_CTX_open(aead, cleartext, &cleartext_len, sizeof(cleartext),
+ nonce, qc->client_in_iv.len, ciphertext.data,
+ ciphertext.len, ad.data, ad.len)
+ != 1)
+ {
+ EVP_AEAD_CTX_free(aead);
+ ngx_ssl_error(NGX_LOG_INFO, rev->log, 0,
+ "EVP_AEAD_CTX_open() failed");
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ EVP_AEAD_CTX_free(aead);
+
+#if (NGX_DEBUG)
+ if (c->log->log_level & NGX_LOG_DEBUG_EVENT) {
+ m = ngx_hex_dump(buf, cleartext, ngx_min(cleartext_len, 256)) - buf;
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "quic packet: %*s%s, len: %uz",
+ m, buf, m < 512 ? "" : "...", cleartext_len);
+ }
+#endif
+
+ sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);
+
+ if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER)
+ != NGX_OK)
+ {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ n = SSL_do_handshake(c->ssl->connection);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n);
+
+ if (n == -1) {
+ sslerr = SSL_get_error(c->ssl->connection, 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(c->ssl->connection),
+ (int) SSL_quic_write_level(c->ssl->connection));
+
+ if (!SSL_provide_quic_data(c->ssl->connection,
+ SSL_quic_read_level(c->ssl->connection),
+ &cleartext[4], cleartext_len - 4))
+ {
+ ngx_ssl_error(NGX_LOG_INFO, rev->log, 0,
+ "SSL_provide_quic_data() failed");
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ n = SSL_do_handshake(c->ssl->connection);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n);
+
+ if (n == -1) {
+ sslerr = SSL_get_error(c->ssl->connection, n);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d",
+ sslerr);
+
+ if (sslerr == SSL_ERROR_SSL) {
+ ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "SSL_do_handshake() failed");
+ }
+ }
+
+ 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(c->ssl->connection),
+ (int) SSL_quic_write_level(c->ssl->connection));
+
+ ngx_http_close_connection(c);
+ return;
+}
+
+
static void
ngx_http_ssl_handshake(ngx_event_t *rev)
{
diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h
index 70c2d424d..8cc5d6432 100644
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -323,6 +323,7 @@ typedef struct {
ngx_chain_t *free;
unsigned ssl:1;
+ unsigned quic:1;
unsigned proxy_protocol:1;
} ngx_http_connection_t;