From arut at nginx.com Thu Sep 1 09:47:07 2022 From: arut at nginx.com (Roman Arutyunyan) Date: Thu, 1 Sep 2022 13:47:07 +0400 Subject: [PATCH] Core: support for reading PROXY protocol v2 TLVs In-Reply-To: <4b856f1dff939e4eb9c1.1661961135@arut-laptop> References: <4b856f1dff939e4eb9c1.1661961135@arut-laptop> Message-ID: Hi, > On 31 Aug 2022, at 19:52, Roman Arutyunyan wrote: > > # HG changeset patch > # User Roman Arutyunyan > # Date 1661436099 -14400 > # Thu Aug 25 18:01:39 2022 +0400 > # Node ID 4b856f1dff939e4eb9c131e17af061cf2c38cfac > # Parent 069a4813e8d6d7ec662d282a10f5f7062ebd817f > Core: support for reading PROXY protocol v2 TLVs. > > The TLV values are available in HTTP and Stream variables > $proxy_protocol_tlv_0xN, where N is a hexadecimal TLV type number with no > leading zeroes. A small update, thanks to Eran Kornblau. diff --git a/src/core/ngx_proxy_protocol.c b/src/core/ngx_proxy_protocol.c --- a/src/core/ngx_proxy_protocol.c +++ b/src/core/ngx_proxy_protocol.c @@ -446,7 +446,7 @@ ngx_proxy_protocol_v2_read(ngx_connectio } ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, - "PROXY protocol v2 TLV type:0x%0xd len:%uz", + "PROXY protocol v2 TLV type:0x%xd len:%uz", tlv->type, len); t = ngx_list_push(pp_tlv); @@ -462,7 +462,7 @@ ngx_proxy_protocol_v2_read(ngx_connectio return NULL; } - t->key.len = ngx_sprintf(t->key.data, "%0xd", tlv->type) + t->key.len = ngx_sprintf(t->key.data, "%xd", tlv->type) - t->key.data; t->value.data = ngx_pnalloc(c->pool, len); Another possible update is support for arbitrary number of leading zeroes. Not sure we need this right now though. > diff --git a/src/core/ngx_proxy_protocol.c b/src/core/ngx_proxy_protocol.c > --- a/src/core/ngx_proxy_protocol.c > +++ b/src/core/ngx_proxy_protocol.c > @@ -40,6 +40,12 @@ typedef struct { > } ngx_proxy_protocol_inet6_addrs_t; > > > +typedef struct { > + u_char type; > + u_char len[2]; > +} ngx_proxy_protocol_tlv_t; > + > + > static u_char *ngx_proxy_protocol_read_addr(ngx_connection_t *c, u_char *p, > u_char *last, ngx_str_t *addr); > static u_char *ngx_proxy_protocol_read_port(u_char *p, u_char *last, > @@ -273,8 +279,11 @@ ngx_proxy_protocol_v2_read(ngx_connectio > size_t len; > socklen_t socklen; > ngx_uint_t version, command, family, transport; > + ngx_list_t *pp_tlv; > ngx_sockaddr_t src_sockaddr, dst_sockaddr; > + ngx_table_elt_t *t; > ngx_proxy_protocol_t *pp; > + ngx_proxy_protocol_tlv_t *tlv; > ngx_proxy_protocol_header_t *header; > ngx_proxy_protocol_inet_addrs_t *in; > #if (NGX_HAVE_INET6) > @@ -412,8 +421,63 @@ ngx_proxy_protocol_v2_read(ngx_connectio > &pp->src_addr, pp->src_port, &pp->dst_addr, pp->dst_port); > > if (buf < end) { > - ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, > - "PROXY protocol v2 %z bytes of tlv ignored", end - buf); > + pp_tlv = ngx_list_create(c->pool, 1, sizeof(ngx_table_elt_t)); > + if (pp_tlv == NULL) { > + return NULL; > + } > + > + while (buf < end) { > + if ((size_t) (end - buf) < sizeof(ngx_proxy_protocol_tlv_t)) { > + ngx_log_error(NGX_LOG_ERR, c->log, 0, > + "broken PROXY protocol TLV"); > + return NULL; > + } > + > + tlv = (ngx_proxy_protocol_tlv_t *) buf; > + > + buf += sizeof(ngx_proxy_protocol_tlv_t); > + > + len = ngx_proxy_protocol_parse_uint16(tlv->len); > + > + if ((size_t) (end - buf) < len) { > + ngx_log_error(NGX_LOG_ERR, c->log, 0, > + "broken PROXY protocol TLV"); > + return NULL; > + } > + > + ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, > + "PROXY protocol v2 TLV type:0x%0xd len:%uz", > + tlv->type, len); > + > + t = ngx_list_push(pp_tlv); > + if (t == NULL) { > + return NULL; > + } > + > + t->hash = 1; > + t->lowcase_key = NULL; > + > + t->key.data = ngx_pnalloc(c->pool, 2); > + if (t->key.data == NULL) { > + return NULL; > + } > + > + t->key.len = ngx_sprintf(t->key.data, "%0xd", tlv->type) > + - t->key.data; > + > + t->value.data = ngx_pnalloc(c->pool, len); > + if (t->value.data == NULL) { > + return NULL; > + } > + > + ngx_memcpy(t->value.data, buf, len); > + > + t->value.len = len; > + > + buf += len; > + } > + > + pp->tlv = pp_tlv; > } > > c->proxy_protocol = pp; > diff --git a/src/core/ngx_proxy_protocol.h b/src/core/ngx_proxy_protocol.h > --- a/src/core/ngx_proxy_protocol.h > +++ b/src/core/ngx_proxy_protocol.h > @@ -21,6 +21,7 @@ struct ngx_proxy_protocol_s { > ngx_str_t dst_addr; > in_port_t src_port; > in_port_t dst_port; > + ngx_list_t *tlv; > }; > > > diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c > --- a/src/http/ngx_http_variables.c > +++ b/src/http/ngx_http_variables.c > @@ -61,6 +61,8 @@ static ngx_int_t ngx_http_variable_proxy > ngx_http_variable_value_t *v, uintptr_t data); > static ngx_int_t ngx_http_variable_proxy_protocol_port(ngx_http_request_t *r, > ngx_http_variable_value_t *v, uintptr_t data); > +static ngx_int_t ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r, > + ngx_http_variable_value_t *v, uintptr_t data); > static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r, > ngx_http_variable_value_t *v, uintptr_t data); > static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r, > @@ -214,6 +216,10 @@ static ngx_http_variable_t ngx_http_cor > ngx_http_variable_proxy_protocol_port, > offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 }, > > + { ngx_string("proxy_protocol_tlv_0x"), NULL, > + ngx_http_variable_proxy_protocol_tlv, > + 0, NGX_HTTP_VAR_PREFIX, 0 }, > + > { ngx_string("server_addr"), NULL, ngx_http_variable_server_addr, 0, 0, 0 }, > > { ngx_string("server_port"), NULL, ngx_http_variable_server_port, 0, 0, 0 }, > @@ -1387,6 +1393,24 @@ ngx_http_variable_proxy_protocol_port(ng > > > static ngx_int_t > +ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r, > + ngx_http_variable_value_t *v, uintptr_t data) > +{ > + ngx_proxy_protocol_t *pp; > + > + pp = r->connection->proxy_protocol; > + if (pp == NULL || pp->tlv == NULL) { > + v->not_found = 1; > + return NGX_OK; > + } > + > + return ngx_http_variable_unknown_header(r, v, (ngx_str_t *) data, > + &pp->tlv->part, > + sizeof("proxy_protocol_tlv_0x") - 1); > +} > + > + > +static ngx_int_t > ngx_http_variable_server_addr(ngx_http_request_t *r, > ngx_http_variable_value_t *v, uintptr_t data) > { > diff --git a/src/stream/ngx_stream_variables.c b/src/stream/ngx_stream_variables.c > --- a/src/stream/ngx_stream_variables.c > +++ b/src/stream/ngx_stream_variables.c > @@ -23,6 +23,8 @@ static ngx_int_t ngx_stream_variable_pro > ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); > static ngx_int_t ngx_stream_variable_proxy_protocol_port( > ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); > +static ngx_int_t ngx_stream_variable_proxy_protocol_tlv( > + ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); > static ngx_int_t ngx_stream_variable_server_addr(ngx_stream_session_t *s, > ngx_stream_variable_value_t *v, uintptr_t data); > static ngx_int_t ngx_stream_variable_server_port(ngx_stream_session_t *s, > @@ -51,6 +53,10 @@ static ngx_int_t ngx_stream_variable_tim > static ngx_int_t ngx_stream_variable_protocol(ngx_stream_session_t *s, > ngx_stream_variable_value_t *v, uintptr_t data); > > +static ngx_int_t ngx_stream_variable_unknown_header(ngx_stream_session_t *s, > + ngx_stream_variable_value_t *v, ngx_str_t *var, ngx_list_part_t *part, > + size_t prefix); > + > > static ngx_stream_variable_t ngx_stream_core_variables[] = { > > @@ -79,6 +85,10 @@ static ngx_stream_variable_t ngx_stream > ngx_stream_variable_proxy_protocol_port, > offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 }, > > + { ngx_string("proxy_protocol_tlv_0x"), NULL, > + ngx_stream_variable_proxy_protocol_tlv, > + 0, NGX_STREAM_VAR_PREFIX, 0 }, > + > { ngx_string("server_addr"), NULL, > ngx_stream_variable_server_addr, 0, 0, 0 }, > > @@ -622,6 +632,24 @@ ngx_stream_variable_proxy_protocol_port( > > > static ngx_int_t > +ngx_stream_variable_proxy_protocol_tlv(ngx_stream_session_t *s, > + ngx_stream_variable_value_t *v, uintptr_t data) > +{ > + ngx_proxy_protocol_t *pp; > + > + pp = s->connection->proxy_protocol; > + if (pp == NULL || pp->tlv == NULL) { > + v->not_found = 1; > + return NGX_OK; > + } > + > + return ngx_stream_variable_unknown_header(s, v, (ngx_str_t *) data, > + &pp->tlv->part, > + sizeof("proxy_protocol_tlv_0x") - 1); > +} > + > + > +static ngx_int_t > ngx_stream_variable_server_addr(ngx_stream_session_t *s, > ngx_stream_variable_value_t *v, uintptr_t data) > { > @@ -911,6 +939,116 @@ ngx_stream_variable_protocol(ngx_stream_ > } > > > +static ngx_int_t > +ngx_stream_variable_unknown_header(ngx_stream_session_t *s, > + ngx_stream_variable_value_t *v, ngx_str_t *var, > + ngx_list_part_t *part, size_t prefix) > +{ > + u_char *p, ch; > + size_t len; > + ngx_uint_t i, n; > + ngx_table_elt_t *header, *h, **ph; > + > + ph = &h; > +#if (NGX_SUPPRESS_WARN) > + len = 0; > +#endif > + > + header = part->elts; > + > + for (i = 0; /* void */ ; i++) { > + > + if (i >= part->nelts) { > + if (part->next == NULL) { > + break; > + } > + > + part = part->next; > + header = part->elts; > + i = 0; > + } > + > + if (header[i].hash == 0) { > + continue; > + } > + > + if (header[i].key.len != var->len - prefix) { > + continue; > + } > + > + for (n = 0; n < var->len - prefix; n++) { > + ch = header[i].key.data[n]; > + > + if (ch >= 'A' && ch <= 'Z') { > + ch |= 0x20; > + > + } else if (ch == '-') { > + ch = '_'; > + } > + > + if (var->data[n + prefix] != ch) { > + break; > + } > + } > + > + if (n != var->len - prefix) { > + continue; > + } > + > + len += header[i].value.len + 2; > + > + *ph = &header[i]; > + ph = &header[i].next; > + } > + > + *ph = NULL; > + > + if (h == NULL) { > + v->not_found = 1; > + return NGX_OK; > + } > + > + len -= 2; > + > + if (h->next == NULL) { > + > + v->len = h->value.len; > + v->valid = 1; > + v->no_cacheable = 0; > + v->not_found = 0; > + v->data = h->value.data; > + > + return NGX_OK; > + } > + > + p = ngx_pnalloc(s->connection->pool, len); > + if (p == NULL) { > + return NGX_ERROR; > + } > + > + v->len = len; > + v->valid = 1; > + v->no_cacheable = 0; > + v->not_found = 0; > + v->data = p; > + > + for ( ;; ) { > + > + p = ngx_copy(p, h->value.data, h->value.len); > + > + if (h->next == NULL) { > + break; > + } > + > + *p++ = ','; *p++ = ' '; > + > + h = h->next; > + } > + > + return NGX_OK; > +} > + > + > void * > ngx_stream_map_find(ngx_stream_session_t *s, ngx_stream_map_t *map, > ngx_str_t *match) > > _______________________________________________ > nginx-devel mailing list -- nginx-devel at nginx.org > To unsubscribe send an email to nginx-devel-leave at nginx.org ---- Roman Arutyunyan arut at nginx.com -------------- next part -------------- An HTML attachment was scrubbed... URL: From pluknet at nginx.com Thu Sep 1 14:22:09 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Thu, 1 Sep 2022 18:22:09 +0400 Subject: Trying to build nginx with njs module on Windows In-Reply-To: References: Message-ID: <2073F75C-A8FE-479E-B4D1-AC2673185716@nginx.com> > On 31 Aug 2022, at 18:59, Geoff Bache wrote: > > Hi all, > > I'm having trouble building from source with the njs module included (which seems to be the only way to get this module on Windows, right?) > > I am following the advice on > https://nginx.org/en/docs/njs/install.html > > and > http://nginx.org/en/docs/howto_build_on_win32.html > [..] Windows is not supported by njs. For the list of supported platforms see: http://nginx.org/en/docs/njs/#tested_os_and_platforms -- Sergey Kandaurov From geoff.bache at gmail.com Thu Sep 1 15:22:37 2022 From: geoff.bache at gmail.com (Geoff Bache) Date: Thu, 1 Sep 2022 17:22:37 +0200 Subject: Trying to build nginx with njs module on Windows In-Reply-To: <2073F75C-A8FE-479E-B4D1-AC2673185716@nginx.com> References: <2073F75C-A8FE-479E-B4D1-AC2673185716@nginx.com> Message-ID: OK, thanks for the info. /Geoff On Thu, Sep 1, 2022 at 4:22 PM Sergey Kandaurov wrote: > > > On 31 Aug 2022, at 18:59, Geoff Bache wrote: > > > > Hi all, > > > > I'm having trouble building from source with the njs module included > (which seems to be the only way to get this module on Windows, right?) > > > > I am following the advice on > > https://nginx.org/en/docs/njs/install.html > > > > and > > http://nginx.org/en/docs/howto_build_on_win32.html > > [..] > > Windows is not supported by njs. > > For the list of supported platforms see: > http://nginx.org/en/docs/njs/#tested_os_and_platforms > > -- > Sergey Kandaurov > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From mdounin at mdounin.ru Thu Sep 1 16:49:05 2022 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Thu, 01 Sep 2022 19:49:05 +0300 Subject: [PATCH 1 of 3] Win32: removed misleading comment about warnings being disabled Message-ID: <3d162f4c6f62b23d4906.1662050945@1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa> # HG changeset patch # User Maxim Dounin # Date 1662050702 -10800 # Thu Sep 01 19:45:02 2022 +0300 # Node ID 3d162f4c6f62b23d4906d2afaabcd8c2720fb12a # Parent 553e7e669cfd55c9371a03c5c87e626fd9d2f773 Win32: removed misleading comment about warnings being disabled. Warnings being disabled are not only from the "-W4" level since e4590dfd97ff. diff -r 553e7e669cfd -r 3d162f4c6f62 src/os/win32/ngx_win32_config.h --- a/src/os/win32/ngx_win32_config.h Mon Aug 22 02:37:04 2022 +0300 +++ b/src/os/win32/ngx_win32_config.h Thu Sep 01 19:45:02 2022 +0300 @@ -80,8 +80,6 @@ typedef long time_t; #pragma warning(default:4201) -/* disable some "-W4" level warnings */ - /* 'type cast': from function pointer to data pointer */ #pragma warning(disable:4054) From mdounin at mdounin.ru Thu Sep 1 16:49:06 2022 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Thu, 01 Sep 2022 19:49:06 +0300 Subject: [PATCH 2 of 3] Win32: disabled C4306 warnings with MSVC In-Reply-To: <3d162f4c6f62b23d4906.1662050945@1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa> References: <3d162f4c6f62b23d4906.1662050945@1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa> Message-ID: <63a4b5ffd440c526bc96.1662050946@1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa> # HG changeset patch # User Maxim Dounin # Date 1662050722 -10800 # Thu Sep 01 19:45:22 2022 +0300 # Node ID 63a4b5ffd440c526bc96c6879dc1b6b489975d98 # Parent 3d162f4c6f62b23d4906d2afaabcd8c2720fb12a Win32: disabled C4306 warnings with MSVC. Multiple C4306 warnings (conversion from 'type1' to 'type2' of greater size) appear during 64-bit compilation with MSVC 2010 (and older) due to extensively used constructs like "(void *) -1", so they were disabled. In newer MSVC versions C4306 warnings were replaced with C4312 ones, and these are not generated for such trivial type casts. diff -r 3d162f4c6f62 -r 63a4b5ffd440 src/os/win32/ngx_win32_config.h --- a/src/os/win32/ngx_win32_config.h Thu Sep 01 19:45:02 2022 +0300 +++ b/src/os/win32/ngx_win32_config.h Thu Sep 01 19:45:22 2022 +0300 @@ -104,6 +104,9 @@ typedef long time_t; /* array is too small to include a terminating null character */ #pragma warning(disable:4295) +/* conversion from 'type1' to 'type2' of greater size */ +#pragma warning(disable:4306) + #endif From mdounin at mdounin.ru Thu Sep 1 16:49:07 2022 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Thu, 01 Sep 2022 19:49:07 +0300 Subject: [PATCH 3 of 3] Win32: fixed build on Windows with OpenSSL 3.0.x (ticket #2379) In-Reply-To: <3d162f4c6f62b23d4906.1662050945@1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa> References: <3d162f4c6f62b23d4906.1662050945@1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa> Message-ID: # HG changeset patch # User Maxim Dounin # Date 1662050858 -10800 # Thu Sep 01 19:47:38 2022 +0300 # Node ID d73286c43b44f3161ca4de1d9d1cbb070c6da4a7 # Parent 63a4b5ffd440c526bc96c6879dc1b6b489975d98 Win32: fixed build on Windows with OpenSSL 3.0.x (ticket #2379). SSL_sendfile() expects integer file descriptor as an argument, but nginx uses OS file handles (HANDLE) to work with files on Windows, and passing HANDLE instead of an integer correctly results in build failure. Since SSL_sendfile() is not expected to work on Windows anyway, the code is now disabled on Windows with appropriate compile-time checks. diff -r 63a4b5ffd440 -r d73286c43b44 src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Thu Sep 01 19:45:22 2022 +0300 +++ b/src/event/ngx_event_openssl.c Thu Sep 01 19:47:38 2022 +0300 @@ -1770,7 +1770,7 @@ ngx_ssl_handshake(ngx_connection_t *c) #endif #endif -#ifdef BIO_get_ktls_send +#if (defined BIO_get_ktls_send && !NGX_WIN32) if (BIO_get_ktls_send(SSL_get_wbio(c->ssl->connection)) == 1) { ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, @@ -1915,7 +1915,7 @@ ngx_ssl_try_early_data(ngx_connection_t c->read->ready = 1; c->write->ready = 1; -#ifdef BIO_get_ktls_send +#if (defined BIO_get_ktls_send && !NGX_WIN32) if (BIO_get_ktls_send(SSL_get_wbio(c->ssl->connection)) == 1) { ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, @@ -2944,7 +2944,7 @@ ngx_ssl_write_early(ngx_connection_t *c, static ssize_t ngx_ssl_sendfile(ngx_connection_t *c, ngx_buf_t *file, size_t size) { -#ifdef BIO_get_ktls_send +#if (defined BIO_get_ktls_send && !NGX_WIN32) int sslerr, flags; ssize_t n; From pluknet at nginx.com Thu Sep 1 16:57:29 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Thu, 01 Sep 2022 16:57:29 +0000 Subject: [nginx] Version bump. Message-ID: details: https://hg.nginx.org/nginx/rev/6e818b00ee32 branches: changeset: 8062:6e818b00ee32 user: Maxim Dounin date: Tue Aug 30 01:52:51 2022 +0300 description: Version bump. diffstat: src/core/nginx.h | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diffs (14 lines): diff -r 069a4813e8d6 -r 6e818b00ee32 src/core/nginx.h --- a/src/core/nginx.h Tue Jul 19 17:05:27 2022 +0300 +++ b/src/core/nginx.h Tue Aug 30 01:52:51 2022 +0300 @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1023001 -#define NGINX_VERSION "1.23.1" +#define nginx_version 1023002 +#define NGINX_VERSION "1.23.2" #define NGINX_VER "nginx/" NGINX_VERSION #ifdef NGX_BUILD From pluknet at nginx.com Thu Sep 1 16:57:32 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Thu, 01 Sep 2022 16:57:32 +0000 Subject: [nginx] SSL: logging level of "bad record type" errors. Message-ID: details: https://hg.nginx.org/nginx/rev/9cf231508a8d branches: changeset: 8063:9cf231508a8d user: Murilo Andrade date: Tue Aug 09 17:13:46 2022 -0300 description: SSL: logging level of "bad record type" errors. The SSL_R_BAD_RECORD_TYPE ("bad record type") errors are reported by OpenSSL 1.1.1 or newer when using TLSv1.3 if the client sends a record with unknown or unexpected type. These errors are now logged at the "info" level. diffstat: src/event/ngx_event_openssl.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diffs (13 lines): diff -r 6e818b00ee32 -r 9cf231508a8d src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Tue Aug 30 01:52:51 2022 +0300 +++ b/src/event/ngx_event_openssl.c Tue Aug 09 17:13:46 2022 -0300 @@ -3423,6 +3423,9 @@ ngx_ssl_connection_error(ngx_connection_ #ifdef SSL_R_VERSION_TOO_LOW || n == SSL_R_VERSION_TOO_LOW /* 396 */ #endif +#ifdef SSL_R_BAD_RECORD_TYPE + || n == SSL_R_BAD_RECORD_TYPE /* 443 */ +#endif || n == 1000 /* SSL_R_SSLV3_ALERT_CLOSE_NOTIFY */ #ifdef SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE || n == SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE /* 1010 */ From xeioex at nginx.com Fri Sep 2 00:50:35 2022 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 02 Sep 2022 00:50:35 +0000 Subject: [njs] Fixed String.prototype.trimEnd() with unicode string. Message-ID: details: https://hg.nginx.org/njs/rev/dfe0eeff0568 branches: changeset: 1944:dfe0eeff0568 user: Dmitry Volyntsev date: Wed Aug 31 18:35:58 2022 -0700 description: Fixed String.prototype.trimEnd() with unicode string. Previously, when the method was invoked with a string consisting of space characters and at least one of them was a Unicode space separator (code point above 127) it returned invalid string value with non-zero size but zero length. The fix is to update the size of the resulting string appropriately. This closes #569 issue on Github. diffstat: src/njs_string.c | 1 + src/test/njs_unit_test.c | 8 ++++++++ 2 files changed, 9 insertions(+), 0 deletions(-) diffs (29 lines): diff -r 26b8f0c2ef94 -r dfe0eeff0568 src/njs_string.c --- a/src/njs_string.c Wed Aug 31 16:52:16 2022 -0700 +++ b/src/njs_string.c Wed Aug 31 18:35:58 2022 -0700 @@ -2849,6 +2849,7 @@ njs_string_trim(const njs_value_t *value for ( ;; ) { if (start == prev) { + end = prev; break; } diff -r 26b8f0c2ef94 -r dfe0eeff0568 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Wed Aug 31 16:52:16 2022 -0700 +++ b/src/test/njs_unit_test.c Wed Aug 31 18:35:58 2022 -0700 @@ -8450,6 +8450,14 @@ static njs_unit_test_t njs_test[] = { njs_str("' абв '.trimStart().trimEnd()"), njs_str("абв") }, + { njs_str("[" + " String.fromCodePoint(0x2028)," + " String.fromCodePoint(0x20, 0x2028)," + " String.fromCodePoint(0x0009, 0x20, 0x2028)," + " String.fromCodePoint(0xFEFF)," + "].every(v => v.trimEnd() == '')"), + njs_str("true") }, + { njs_str("'\\u2029abc\\uFEFF\\u2028'.trim()"), njs_str("abc") }, From xeioex at nginx.com Fri Sep 2 00:50:37 2022 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 02 Sep 2022 00:50:37 +0000 Subject: [njs] Added error handling for NJS_VMCODE_FUNCTION_COPY instruction. Message-ID: details: https://hg.nginx.org/njs/rev/a37e4df8f082 branches: changeset: 1945:a37e4df8f082 user: Dmitry Volyntsev date: Thu Sep 01 17:49:06 2022 -0700 description: Added error handling for NJS_VMCODE_FUNCTION_COPY instruction. This closes #572 issue on Github. diffstat: src/njs_vmcode.c | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-) diffs (14 lines): diff -r dfe0eeff0568 -r a37e4df8f082 src/njs_vmcode.c --- a/src/njs_vmcode.c Wed Aug 31 18:35:58 2022 -0700 +++ b/src/njs_vmcode.c Thu Sep 01 17:49:06 2022 -0700 @@ -742,6 +742,10 @@ next: fcopy = (njs_vmcode_function_copy_t *) pc; ret = njs_vmcode_function_copy(vm, fcopy->function, fcopy->retval); + if (njs_slow_path(ret != NJS_OK)) { + goto error; + } + break; case NJS_VMCODE_FUNCTION_FRAME: From xeioex at nginx.com Fri Sep 2 01:23:04 2022 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 02 Sep 2022 01:23:04 +0000 Subject: [njs] Fixed typo introduced in a37e4df8f082. Message-ID: details: https://hg.nginx.org/njs/rev/506ba9a639b8 branches: changeset: 1946:506ba9a639b8 user: Dmitry Volyntsev date: Thu Sep 01 18:12:27 2022 -0700 description: Fixed typo introduced in a37e4df8f082. diffstat: src/njs_vmcode.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff -r a37e4df8f082 -r 506ba9a639b8 src/njs_vmcode.c --- a/src/njs_vmcode.c Thu Sep 01 17:49:06 2022 -0700 +++ b/src/njs_vmcode.c Thu Sep 01 18:12:27 2022 -0700 @@ -742,7 +742,7 @@ next: fcopy = (njs_vmcode_function_copy_t *) pc; ret = njs_vmcode_function_copy(vm, fcopy->function, fcopy->retval); - if (njs_slow_path(ret != NJS_OK)) { + if (njs_slow_path(ret == NJS_ERROR)) { goto error; } From alx.manpages at gmail.com Fri Sep 2 20:31:27 2022 From: alx.manpages at gmail.com (Alejandro Colomar) Date: Fri, 2 Sep 2022 22:31:27 +0200 Subject: [WIP/RFC v4 0/5] Support abstract Unix sockets In-Reply-To: <17bdd610-0912-7baf-5231-8bf63f8e7456@gmail.com> References: <20220819142144.13915-1-alx.manpages@gmail.com> <20220823211805.4509-1-alx.manpages@gmail.com> <6940ef35-c3b6-8cc2-2eb6-19471808e93a@gmail.com> <17bdd610-0912-7baf-5231-8bf63f8e7456@gmail.com> Message-ID: Hi Maxim! On 8/24/22 18:00, Maxim Dounin wrote: > I would rather say "it's a common misconception that it's faster > to communicate through Unix sockets". While it Unix sockets can > be beneficial for some microbenchmarks, in most production setups > it makes no difference, yet used to introduce various issues. On 8/25/22 01:14, Alejandro Colomar wrote: > Anyway, if you're not convinced by the feature, let me come back to it > after I do some performance testing in Unit to see how significant are > the numbers. I got some numbers (not with nginx, but with unit; if you want me to try with nginx, I'll do). I think they are relevant enough to come revive this discussion. I set up the following: Unit, running two apps (written in C), one is the backend, and one is the frontend. The user curl(1)s the frontend, which itself calls the backend (through either localhost or an abstract UDS), then responds to the user. The exact code of the apps is here: I used the 'main' branch for the backend, which is a simple Hello-world app. The 'front' branch is a modified version of the backend which calls 1000 times in a loop to the backend and reads the first few bytes of those requests, and then responds to the user. The 'ip' branch is the same as the 'front' one, but calling the backend through localhost. The unit config file is here: $ cat /opt/local/unit/state/conf.json { "listeners": { "*:8080": { "pass": "applications/back" }, "unix:@back": { "pass": "applications/back" }, "unix:@front": { "pass": "applications/front" } }, "applications": { "back": { "type": "external", "executable": "/opt/local/unit/libexec/app_back" }, "front": { "type": "external", "executable": "/opt/local/unit/libexec/app_front" } } } The times, are as follows (I only copied a sample of each, but all of the tests that I run --around 10 each--, only varied +/- 2 seconds): IPv4: $ time curl --output - --abstract-unix-sock front localhost/ >/dev/null 2>&1 real 0m0.122s user 0m0.006s sys 0m0.000s UDS: $ time curl --output - --abstract-unix-sock front localhost/ >/dev/null 2>&1 real 0m0.078s user 0m0.006s sys 0m0.000s I think these numbers are enough to make some users go for UDS instead of localhost if it's possible. Also, I think my test is mostly measuring the latency of the sockets, but throughput should be even more different between UDS and localhost, from what I've read. Of course, if the app is slow, the socket might not be the bottleneck, but from what I've tested, unit is fast enough so that socket speed is still measurable, so fast apps may legitimately want UDS to pair with them. Cheers, Alex -- Alejandro Colomar -------------- next part -------------- A non-text attachment was scrubbed... Name: OpenPGP_signature Type: application/pgp-signature Size: 833 bytes Desc: OpenPGP digital signature URL: From mdounin at mdounin.ru Fri Sep 2 21:11:24 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sat, 3 Sep 2022 00:11:24 +0300 Subject: [WIP/RFC v4 0/5] Support abstract Unix sockets In-Reply-To: References: <20220819142144.13915-1-alx.manpages@gmail.com> <20220823211805.4509-1-alx.manpages@gmail.com> <6940ef35-c3b6-8cc2-2eb6-19471808e93a@gmail.com> <17bdd610-0912-7baf-5231-8bf63f8e7456@gmail.com> Message-ID: Hello! On Fri, Sep 02, 2022 at 10:31:27PM +0200, Alejandro Colomar wrote: > On 8/24/22 18:00, Maxim Dounin wrote: > > I would rather say "it's a common misconception that it's faster > > to communicate through Unix sockets". While it Unix sockets can > > be beneficial for some microbenchmarks, in most production setups > > it makes no difference, yet used to introduce various issues. [...] > I think these numbers are enough to make some users go for UDS instead > of localhost if it's possible. Also, I think my test is mostly > measuring the latency of the sockets, but throughput should be even more > different between UDS and localhost, from what I've read. Of course, if > the app is slow, the socket might not be the bottleneck, but from what > I've tested, unit is fast enough so that socket speed is still > measurable, so fast apps may legitimately want UDS to pair with them. To re-iterate: While Unix sockets can be beneficial for some microbenchmarks, in most production setups it makes no difference, yet used to introduce various issues. Your tests reveal minor difference in yet another microbenchmark, effectively testing connection costs. Connection costs are usually negligible compared to the real work being done by real applications, and yet can be reduced to nearly zero by using keepalive connections. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Fri Sep 2 23:29:49 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sat, 3 Sep 2022 02:29:49 +0300 Subject: [PATCH 2 of 2] Enhanced website navigation with 2-level menu and directive search In-Reply-To: References: Message-ID: Hello! On Tue, Aug 30, 2022 at 12:55:39PM +0000, Liam Crilly via nginx-devel wrote: > # HG changeset patch > # User Liam Crilly > # Date 1659696358 -3600 > # Fri Aug 05 11:45:58 2022 +0100 > # Node ID 8781046c34459c05b4d6f296bbbadc3733d51b17 > # Parent 1087570ec093271d10de8e901122c99003a79b6d > Modified layout of navigation menu. > > This patch modifies the layout of the menu navigation with several changes: > - A two-level heirarchy organises content into sections for nginx, other > nginx projects, and information > - The "projects" of njs and unit are moved up, to a more prominent position > - News is moved lower down, to the "info" section. A third menu level > contains the individual years so that the top of the menu is always the > same, regardless of which page you are on. > - Links to external sites are indicated by a right-arrow character First of all, there are various technical issues with the patch, such as: - The list of changes in the commit log suggests there should be a series of patches instead. - There are issues with the two-level hierarchy you are trying to add, notably language selector is on multiple levels at the "news" page (aka site root). - The patch fails due to corrupted japanese characters. Further, I don't think I like the changes suggested. For a number of reasons, in particular: - There are already hierarchy in the menu, though shown with empty lines instead of different margins. Switching to left margins instead looks questionable (especially given than empty lines are preserved, notably on the "news" page after years list), and adding obvious captions to item groups eats space for no apparent reason. - The "news" page is essentially a site root, and moving it to the bottom of the menu looks wrong. Further, the "info/news" block in the resulting layout on the "news" page (aka site root) looks ugly, and combined with the added captions and moved side projects completely hides trac / twitter / blog links on the typical screen. - The site is about nginx, and moving links to side project above the nginx news page looks completely wrong to me. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Mon Sep 5 00:52:49 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 5 Sep 2022 03:52:49 +0300 Subject: [PATCH] Core: support for reading PROXY protocol v2 TLVs In-Reply-To: <4b856f1dff939e4eb9c1.1661961135@arut-laptop> References: <4b856f1dff939e4eb9c1.1661961135@arut-laptop> Message-ID: Hello! On Wed, Aug 31, 2022 at 07:52:15PM +0400, Roman Arutyunyan wrote: > # HG changeset patch > # User Roman Arutyunyan > # Date 1661436099 -14400 > # Thu Aug 25 18:01:39 2022 +0400 > # Node ID 4b856f1dff939e4eb9c131e17af061cf2c38cfac > # Parent 069a4813e8d6d7ec662d282a10f5f7062ebd817f > Core: support for reading PROXY protocol v2 TLVs. First of all, could you please provide details on the use case? I've seen requests for writing proxy protocol TLVs to upstream servers (see ticket #1639), but not yet seen any meaningful reading requests. > The TLV values are available in HTTP and Stream variables > $proxy_protocol_tlv_0xN, where N is a hexadecimal TLV type number with no > leading zeroes. I can't say I like the "hexadecimal TLV type number with no leading zeroes" approach, especially given that the specification uses leading zeroes in TLV types. With leading zeros might be better, to match specification. Also, it might worth the effort to actually add names for known types instead or in addition to numbers. Another question is PP2_TYPE_SSL, which is itself a complex structure and a list of multiple subtypes. Provided Given the above, not sure if the approach with early parsing and header-like list as in the patch is the good idea. Just preserving TLVs as is and parsing them all during variable evaluation might be easier and more efficient. Also, the idea of merging TLV values with identical types looks wrong to me, especially given that many TLSs are binary. Specification does not seem to define the behaviour here, unfortunately. As far as I understand, HAProxy itself still doesn't implement PPv2 parsing, so there is not reference implementation either. On the other hand, it should be easy enough to check all TLVs for duplicate by using a 256-bit bitmask and reject connections if there are any duplicates. > > diff --git a/src/core/ngx_proxy_protocol.c b/src/core/ngx_proxy_protocol.c > --- a/src/core/ngx_proxy_protocol.c > +++ b/src/core/ngx_proxy_protocol.c > @@ -40,6 +40,12 @@ typedef struct { > } ngx_proxy_protocol_inet6_addrs_t; > > > +typedef struct { > + u_char type; > + u_char len[2]; > +} ngx_proxy_protocol_tlv_t; > + > + > static u_char *ngx_proxy_protocol_read_addr(ngx_connection_t *c, u_char *p, > u_char *last, ngx_str_t *addr); > static u_char *ngx_proxy_protocol_read_port(u_char *p, u_char *last, > @@ -273,8 +279,11 @@ ngx_proxy_protocol_v2_read(ngx_connectio > size_t len; > socklen_t socklen; > ngx_uint_t version, command, family, transport; > + ngx_list_t *pp_tlv; tlvs? See above though, it probably doesn't make sense to parse TLVs here. > ngx_sockaddr_t src_sockaddr, dst_sockaddr; > + ngx_table_elt_t *t; Just in case, most ngx_table_elt_t variables are named "h", as in "header". [...] -- Maxim Dounin http://mdounin.ru/ From eran.kornblau at kaltura.com Mon Sep 5 05:11:41 2022 From: eran.kornblau at kaltura.com (Eran Kornblau) Date: Mon, 5 Sep 2022 05:11:41 +0000 Subject: [PATCH] Core: support for reading PROXY protocol v2 TLVs In-Reply-To: References: <4b856f1dff939e4eb9c1.1661961135@arut-laptop> Message-ID: > > > -----Original Message----- > From: Maxim Dounin > Sent: Monday, 5 September 2022 3:53 > To: nginx-devel at nginx.org > Subject: Re: [PATCH] Core: support for reading PROXY protocol v2 TLVs > > Hello! > > On Wed, Aug 31, 2022 at 07:52:15PM +0400, Roman Arutyunyan wrote: > > > # HG changeset patch > > # User Roman Arutyunyan # Date 1661436099 -14400 > > # Thu Aug 25 18:01:39 2022 +0400 > > # Node ID 4b856f1dff939e4eb9c131e17af061cf2c38cfac > > # Parent 069a4813e8d6d7ec662d282a10f5f7062ebd817f > > Core: support for reading PROXY protocol v2 TLVs. > > First of all, could you please provide details on the use case? > I've seen requests for writing proxy protocol TLVs to upstream servers (see ticket #1639), but not yet seen any meaningful reading requests. > Hi, Jumping in here regarding use cases, we have a couple of use cases for this in our live video system - 1. We support video ingest over Haivision's SRT protocol (a UDP-based transport protocol designed for video streaming). SRT has a concept of 'stream id' (https://github.com/Haivision/srt/blob/master/docs/features/access-control.md) which we're using to identify the incoming video stream (a bit like the stream name/tc url in RTMP). In the implementation, we have separate components for handling the transport layer and the application/video layer. For the transport, we implemented an SRT/TCP gateway (https://github.com/kaltura/nginx-srt-module), while for the application layer, we are using a slightly modified version of Roman's nginx-ts-module. The problem is how to propagate the SRT stream-id to the TCP upstream. Currently, the solution we have for this is a poor man's proxy protocol - we send the stream-id on the TCP socket, delimited by a newline, and our modified version of nginx-ts-module extracts it. 2. Another feature we have is RTMP output, which enables our customers to send their video streams to 3rd parties. We wrote some custom module to translate the internal protocol we use for video transfer back to RTMP, but, we also want to support RTMPS output (RTMP over SSL/TLS). We can extend our module to support SSL, but that is probably not a small task... so, instead, we are using a local nginx 'stream' proxy that acts as a TCP->SSL gateway. The problem is how to pass the host/port of the external RTMP endpoint over the internal TCP connection (we want it to be dynamic - prefer not to use a set of predefined host names in the configuration). The solution we have now is the same as above - we send the URL in the beginning of the TCP stream, and we extract it using some tiny custom module - https://github.com/kaltura/nginx-srt-module/blob/master/src/ngx_stream_preread_str_module.c. In my understanding, with this patch, we should be able to solve both scenarios in a more standard/elegant way. Hope this help, Thanks! Eran > > The TLV values are available in HTTP and Stream variables > > $proxy_protocol_tlv_0xN, where N is a hexadecimal TLV type number with > > no leading zeroes. > > I can't say I like the "hexadecimal TLV type number with no leading zeroes" approach, especially given that the specification uses leading zeroes in TLV types. With leading zeros might be better, to match specification. > > Also, it might worth the effort to actually add names for known types instead or in addition to numbers. > > Another question is PP2_TYPE_SSL, which is itself a complex structure and a list of multiple subtypes. Provided > > Given the above, not sure if the approach with early parsing and header-like list as in the patch is the good idea. Just preserving TLVs as is and parsing them all during variable evaluation might be easier and more efficient. > > Also, the idea of merging TLV values with identical types looks wrong to me, especially given that many TLSs are binary. > Specification does not seem to define the behaviour here, unfortunately. As far as I understand, HAProxy itself still doesn't implement PPv2 parsing, so there is not reference implementation either. On the other hand, it should be easy enough to check all TLVs for duplicate by using a 256-bit bitmask and reject connections if there are any duplicates. > > > > > diff --git a/src/core/ngx_proxy_protocol.c > > b/src/core/ngx_proxy_protocol.c > > --- a/src/core/ngx_proxy_protocol.c > > +++ b/src/core/ngx_proxy_protocol.c > > @@ -40,6 +40,12 @@ typedef struct { > > } ngx_proxy_protocol_inet6_addrs_t; > > > > > > +typedef struct { > > + u_char type; > > + u_char len[2]; > > +} ngx_proxy_protocol_tlv_t; > > + > > + > > static u_char *ngx_proxy_protocol_read_addr(ngx_connection_t *c, u_char *p, > > u_char *last, ngx_str_t *addr); > > static u_char *ngx_proxy_protocol_read_port(u_char *p, u_char *last, > > @@ -273,8 +279,11 @@ ngx_proxy_protocol_v2_read(ngx_connectio > > size_t len; > > socklen_t socklen; > > ngx_uint_t version, command, family, transport; > > + ngx_list_t *pp_tlv; > > tlvs? > > See above though, it probably doesn't make sense to parse TLVs here. > > > ngx_sockaddr_t src_sockaddr, dst_sockaddr; > > + ngx_table_elt_t *t; > > Just in case, most ngx_table_elt_t variables are named "h", as in "header". > > [...] > > -- > Maxim Dounin > https://eur02.safelinks.protection.outlook.com/?url=http%3A%2F%2Fmdounin.ru%2F&data=05%7C01%7C%7C71b5ceb7d09644384d1708da8ed932e4%7C0c503748de3f4e2597e26819d53a42b6%7C1%7C0%7C637979360772824220%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=%2FrPlS2oVVwuMtoNTJv3iobQsr2sgBWQHb71bmLYEd6Y%3D&reserved=0 > _______________________________________________ > nginx-devel mailing list -- nginx-devel at nginx.org To unsubscribe send an email to nginx-devel-leave at nginx.org > From arut at nginx.com Mon Sep 5 13:23:18 2022 From: arut at nginx.com (Roman Arutyunyan) Date: Mon, 5 Sep 2022 17:23:18 +0400 Subject: [PATCH] Core: support for reading PROXY protocol v2 TLVs In-Reply-To: References: <4b856f1dff939e4eb9c1.1661961135@arut-laptop> Message-ID: <20220905132318.s27wgtof6wuqde7x@N00W24XTQX> Hi, On Mon, Sep 05, 2022 at 03:52:49AM +0300, Maxim Dounin wrote: > Hello! > > On Wed, Aug 31, 2022 at 07:52:15PM +0400, Roman Arutyunyan wrote: > > > # HG changeset patch > > # User Roman Arutyunyan > > # Date 1661436099 -14400 > > # Thu Aug 25 18:01:39 2022 +0400 > > # Node ID 4b856f1dff939e4eb9c131e17af061cf2c38cfac > > # Parent 069a4813e8d6d7ec662d282a10f5f7062ebd817f > > Core: support for reading PROXY protocol v2 TLVs. > > First of all, could you please provide details on the use case? > I've seen requests for writing proxy protocol TLVs to upstream > servers (see ticket #1639), but not yet seen any meaningful > reading requests. The known cases are these: - https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-target-groups.html#proxy-protocol - https://docs.microsoft.com/en-us/azure/private-link/private-link-service-overview#getting-connection-information-using-tcp-proxy-v2 - https://cloud.google.com/vpc/docs/configure-private-service-connect-producer#proxy-protocol The data may need further parsing, but it can be done in njs or perl. > > The TLV values are available in HTTP and Stream variables > > $proxy_protocol_tlv_0xN, where N is a hexadecimal TLV type number with no > > leading zeroes. > > I can't say I like the "hexadecimal TLV type number with no > leading zeroes" approach, especially given that the specification > uses leading zeroes in TLV types. With leading zeros might be > better, to match specification. > > Also, it might worth the effort to actually add names for known > types instead or in addition to numbers. This is indeed a good idea and we have such plans as a further extenion of this work. One of the problems is however that the abovementioned TLV variables are specified in internal documents of AWS/Azure/GCP which are not standards. They can be changed anytime, while we have to maintain those variables in nginx. Also, raw variables give more flexibility in supporting less known TLVs. > Another question is PP2_TYPE_SSL, which is itself a complex > structure and a list of multiple subtypes. This is an obvious one. However we had exactly zero requests for this. > Provided > Given the above, not sure if the approach with early parsing and > header-like list as in the patch is the good idea. Just > preserving TLVs as is and parsing them all during variable > evaluation might be easier and more efficient. In this case, if we have two variables, say $proxy_protocol_tlv_ssl_{sni, alpn}, we'll parse the entire TLV block twice - once per variable evaluation. > Also, the idea of merging TLV values with identical types looks > wrong to me, especially given that many TLSs are binary. > Specification does not seem to define the behaviour here, > unfortunately. As far as I understand, HAProxy itself still > doesn't implement PPv2 parsing, so there is not reference > implementation either. On the other hand, it should be easy > enough to check all TLVs for duplicate by using a 256-bit bitmask > and reject connections if there are any duplicates. This can be added, thanks. > > diff --git a/src/core/ngx_proxy_protocol.c b/src/core/ngx_proxy_protocol.c > > --- a/src/core/ngx_proxy_protocol.c > > +++ b/src/core/ngx_proxy_protocol.c > > @@ -40,6 +40,12 @@ typedef struct { > > } ngx_proxy_protocol_inet6_addrs_t; > > > > > > +typedef struct { > > + u_char type; > > + u_char len[2]; > > +} ngx_proxy_protocol_tlv_t; > > + > > + > > static u_char *ngx_proxy_protocol_read_addr(ngx_connection_t *c, u_char *p, > > u_char *last, ngx_str_t *addr); > > static u_char *ngx_proxy_protocol_read_port(u_char *p, u_char *last, > > @@ -273,8 +279,11 @@ ngx_proxy_protocol_v2_read(ngx_connectio > > size_t len; > > socklen_t socklen; > > ngx_uint_t version, command, family, transport; > > + ngx_list_t *pp_tlv; > > tlvs? > > See above though, it probably doesn't make sense to parse TLVs > here. > > > ngx_sockaddr_t src_sockaddr, dst_sockaddr; > > + ngx_table_elt_t *t; > > Just in case, most ngx_table_elt_t variables are named "h", as in > "header". > > [...] > > -- > Maxim Dounin > http://mdounin.ru/ > _______________________________________________ > nginx-devel mailing list -- nginx-devel at nginx.org > To unsubscribe send an email to nginx-devel-leave at nginx.org From mdounin at mdounin.ru Mon Sep 5 15:58:38 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 5 Sep 2022 18:58:38 +0300 Subject: [PATCH] Core: support for reading PROXY protocol v2 TLVs In-Reply-To: <20220905132318.s27wgtof6wuqde7x@N00W24XTQX> References: <4b856f1dff939e4eb9c1.1661961135@arut-laptop> <20220905132318.s27wgtof6wuqde7x@N00W24XTQX> Message-ID: Hello! On Mon, Sep 05, 2022 at 05:23:18PM +0400, Roman Arutyunyan wrote: > Hi, > > On Mon, Sep 05, 2022 at 03:52:49AM +0300, Maxim Dounin wrote: > > Hello! > > > > On Wed, Aug 31, 2022 at 07:52:15PM +0400, Roman Arutyunyan wrote: > > > > > # HG changeset patch > > > # User Roman Arutyunyan > > > # Date 1661436099 -14400 > > > # Thu Aug 25 18:01:39 2022 +0400 > > > # Node ID 4b856f1dff939e4eb9c131e17af061cf2c38cfac > > > # Parent 069a4813e8d6d7ec662d282a10f5f7062ebd817f > > > Core: support for reading PROXY protocol v2 TLVs. > > > > First of all, could you please provide details on the use case? > > I've seen requests for writing proxy protocol TLVs to upstream > > servers (see ticket #1639), but not yet seen any meaningful > > reading requests. > > The known cases are these: > > - https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-target-groups.html#proxy-protocol > - https://docs.microsoft.com/en-us/azure/private-link/private-link-service-overview#getting-connection-information-using-tcp-proxy-v2 > - https://cloud.google.com/vpc/docs/configure-private-service-connect-producer#proxy-protocol > > The data may need further parsing, but it can be done in njs or perl. Thanks for the details. So, basically, it's about vendor-specific endpoint IDs. > > > The TLV values are available in HTTP and Stream variables > > > $proxy_protocol_tlv_0xN, where N is a hexadecimal TLV type number with no > > > leading zeroes. > > > > I can't say I like the "hexadecimal TLV type number with no > > leading zeroes" approach, especially given that the specification > > uses leading zeroes in TLV types. With leading zeros might be > > better, to match specification. > > > > Also, it might worth the effort to actually add names for known > > types instead or in addition to numbers. > > This is indeed a good idea and we have such plans as a further extenion of this > work. One of the problems is however that the abovementioned TLV variables > are specified in internal documents of AWS/Azure/GCP which are not standards. > They can be changed anytime, while we have to maintain those variables in > nginx. Also, raw variables give more flexibility in supporting less known TLVs. Of course I'm not suggesting to ditch raw variables, at least not for unknown/non-standard values. But for known/standard values it should be easy enough to provide alternative names for easier use, probably with type-specific parsing. With on-demand parsing it would be trivial to support both $proxy_protocol_tlv_alpn and $proxy_protocol_tlv_0x01. Further, it will be trivial to support $proxy_protocol_tlv_aws_vpc_id while still providing $proxy_protocol_tlv_0xea for raw data. > > Another question is PP2_TYPE_SSL, which is itself a complex > > structure and a list of multiple subtypes. > > This is an obvious one. However we had exactly zero requests for this. See the ticket mentioned above, it seems to be the main reason why people want to see proxy protocol v2 to backends. > > Provided > > Given the above, not sure if the approach with early parsing and > > header-like list as in the patch is the good idea. Just > > preserving TLVs as is and parsing them all during variable > > evaluation might be easier and more efficient. > > In this case, if we have two variables, say $proxy_protocol_tlv_ssl_{sni, alpn}, > we'll parse the entire TLV block twice - once per variable evaluation. Assuming you mean $proxy_protocol_{authority, alpn} (as these aren't SSL subtypes), I actually see no difference in on-demand parsing of the TLV block and looking for a header in the pre-created list of headers. Further, parsing the block for each variable evaluation might be actually faster due to better locality, and should simplify adding alternative names. And a single TLV block certainly will be more optimal in terms of memory usage due to no additional allocations. Not to mention the typical case when TLV variables aren't used at all. > > Also, the idea of merging TLV values with identical types looks > > wrong to me, especially given that many TLSs are binary. > > Specification does not seem to define the behaviour here, > > unfortunately. As far as I understand, HAProxy itself still > > doesn't implement PPv2 parsing, so there is not reference > > implementation either. On the other hand, it should be easy > > enough to check all TLVs for duplicate by using a 256-bit bitmask > > and reject connections if there are any duplicates. > > This can be added, thanks. Not sure it's actually needed though, especially given that proxy protocol is only expected to be accepted from trusted sources anyway. It might be good enough to just assume there is only one value with a given type. -- Maxim Dounin http://mdounin.ru/ From pluknet at nginx.com Mon Sep 5 18:44:06 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Mon, 5 Sep 2022 22:44:06 +0400 Subject: [PATCH 3 of 3] Win32: fixed build on Windows with OpenSSL 3.0.x (ticket #2379) In-Reply-To: References: <3d162f4c6f62b23d4906.1662050945@1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa> Message-ID: > On 1 Sep 2022, at 20:49, Maxim Dounin wrote: > > # HG changeset patch > # User Maxim Dounin > # Date 1662050858 -10800 > # Thu Sep 01 19:47:38 2022 +0300 > # Node ID d73286c43b44f3161ca4de1d9d1cbb070c6da4a7 > # Parent 63a4b5ffd440c526bc96c6879dc1b6b489975d98 > Win32: fixed build on Windows with OpenSSL 3.0.x (ticket #2379). BTW, win32 build on Windows XP with OpenSSL 3.0.x is currently broken for another reason: due to a missing InterlockedOr64 implementation. See the related fix, expected to appear in upcoming OpenSSL 3.0.6: ce3951fc30c7bc7c3dbacba19d87c79d9af9da0d Now I have to configure OpenSSL with "no-threads" to pass to this error. > > SSL_sendfile() expects integer file descriptor as an argument, but nginx > uses OS file handles (HANDLE) to work with files on Windows, and passing > HANDLE instead of an integer correctly results in build failure. Since > SSL_sendfile() is not expected to work on Windows anyway, the code is now > disabled on Windows with appropriate compile-time checks. > > diff -r 63a4b5ffd440 -r d73286c43b44 src/event/ngx_event_openssl.c > --- a/src/event/ngx_event_openssl.c Thu Sep 01 19:45:22 2022 +0300 > +++ b/src/event/ngx_event_openssl.c Thu Sep 01 19:47:38 2022 +0300 > @@ -1770,7 +1770,7 @@ ngx_ssl_handshake(ngx_connection_t *c) > #endif > #endif > > -#ifdef BIO_get_ktls_send > +#if (defined BIO_get_ktls_send && !NGX_WIN32) > > if (BIO_get_ktls_send(SSL_get_wbio(c->ssl->connection)) == 1) { > ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, > @@ -1915,7 +1915,7 @@ ngx_ssl_try_early_data(ngx_connection_t > c->read->ready = 1; > c->write->ready = 1; > > -#ifdef BIO_get_ktls_send > +#if (defined BIO_get_ktls_send && !NGX_WIN32) > > if (BIO_get_ktls_send(SSL_get_wbio(c->ssl->connection)) == 1) { > ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, > @@ -2944,7 +2944,7 @@ ngx_ssl_write_early(ngx_connection_t *c, > static ssize_t > ngx_ssl_sendfile(ngx_connection_t *c, ngx_buf_t *file, size_t size) > { > -#ifdef BIO_get_ktls_send > +#if (defined BIO_get_ktls_send && !NGX_WIN32) > > int sslerr, flags; > ssize_t n; > This could be simplified if replaced #ifdef with #if. BIO_get_ktls_send is documented to be a macro (and so tested here). When OpenSSL isn't configured with KTLS, the macro is explanded to 0. Replacement allows optimize ngx_ssl_sendfile() at compile time, as well. I see that it's convention in nginx to test external macros using #ifdef. In certain cases we use an exception there if it does or even does not make sense, such as when testing SSL_CTRL_SET_ECDH_AUTO (though that's rather a typo there). Using #if BIO_get_ktls_send looks reasonable to me. Another way (though, a less obvious for the reader) is to replace #if/ifdef BIO_get_ktls_send with a more convenient #ifndef OPENSSL_NO_KTLS. This macro is set when KTLS isn't supported and not configured for OpenSSL. As per INSTALL.md in the root of OpenSSL distribution, the enable-ktls option "is forced off on systems that do not support the Kernel TLS data-path". This makes no matter how OpenSSL is configured, with or without this option, if it's claimed in OpenSSL to be unsupported by platform. I tested to configure enable-ktls on win32: that's appeared to be true. Unfortunately, OPENSSL_NO_KTLS is used to be documented (even for runtime BIO_get_ktls_send() checks) only in sources, such as in apps/s_server.c. -- Sergey Kandaurov From mdounin at mdounin.ru Tue Sep 6 03:49:30 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 6 Sep 2022 06:49:30 +0300 Subject: [PATCH 3 of 3] Win32: fixed build on Windows with OpenSSL 3.0.x (ticket #2379) In-Reply-To: References: <3d162f4c6f62b23d4906.1662050945@1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa> Message-ID: Hello! On Mon, Sep 05, 2022 at 10:44:06PM +0400, Sergey Kandaurov wrote: > > On 1 Sep 2022, at 20:49, Maxim Dounin wrote: > > > > # HG changeset patch > > # User Maxim Dounin > > # Date 1662050858 -10800 > > # Thu Sep 01 19:47:38 2022 +0300 > > # Node ID d73286c43b44f3161ca4de1d9d1cbb070c6da4a7 > > # Parent 63a4b5ffd440c526bc96c6879dc1b6b489975d98 > > Win32: fixed build on Windows with OpenSSL 3.0.x (ticket #2379). > > BTW, win32 build on Windows XP with OpenSSL 3.0.x is currently broken > for another reason: due to a missing InterlockedOr64 implementation. > See the related fix, expected to appear in upcoming OpenSSL 3.0.6: > ce3951fc30c7bc7c3dbacba19d87c79d9af9da0d Well, that's exactly the reason why I've fixed 64-bit build with MSVC 2010, the 2nd patch in this series. But see ticket's comment:1, the patch committed into OpenSSL is incomplete and only fixes MSVC 2008 and older, but won't fix Windows XP builds in newer compilers. > Now I have to configure OpenSSL with "no-threads" to pass to this error. We actually use "no-threads" in Unix builds, see b329c0ab1a48. On win32 we do use threads (for cache loader and cache manager), but this shouldn't require threads support in OpenSSL. That is, it might be a good idea to use "no-threads" in win32 builds too, especially given that this was the default before OpenSSL 1.1.0. Patch below. > > SSL_sendfile() expects integer file descriptor as an argument, but nginx > > uses OS file handles (HANDLE) to work with files on Windows, and passing > > HANDLE instead of an integer correctly results in build failure. Since > > SSL_sendfile() is not expected to work on Windows anyway, the code is now > > disabled on Windows with appropriate compile-time checks. > > > > diff -r 63a4b5ffd440 -r d73286c43b44 src/event/ngx_event_openssl.c > > --- a/src/event/ngx_event_openssl.c Thu Sep 01 19:45:22 2022 +0300 > > +++ b/src/event/ngx_event_openssl.c Thu Sep 01 19:47:38 2022 +0300 > > @@ -1770,7 +1770,7 @@ ngx_ssl_handshake(ngx_connection_t *c) > > #endif > > #endif > > > > -#ifdef BIO_get_ktls_send > > +#if (defined BIO_get_ktls_send && !NGX_WIN32) > > > > if (BIO_get_ktls_send(SSL_get_wbio(c->ssl->connection)) == 1) { > > ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, > > @@ -1915,7 +1915,7 @@ ngx_ssl_try_early_data(ngx_connection_t > > c->read->ready = 1; > > c->write->ready = 1; > > > > -#ifdef BIO_get_ktls_send > > +#if (defined BIO_get_ktls_send && !NGX_WIN32) > > > > if (BIO_get_ktls_send(SSL_get_wbio(c->ssl->connection)) == 1) { > > ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, > > @@ -2944,7 +2944,7 @@ ngx_ssl_write_early(ngx_connection_t *c, > > static ssize_t > > ngx_ssl_sendfile(ngx_connection_t *c, ngx_buf_t *file, size_t size) > > { > > -#ifdef BIO_get_ktls_send > > +#if (defined BIO_get_ktls_send && !NGX_WIN32) > > > > int sslerr, flags; > > ssize_t n; > > > > This could be simplified if replaced #ifdef with #if. > BIO_get_ktls_send is documented to be a macro (and so tested here). > When OpenSSL isn't configured with KTLS, the macro is explanded to 0. > Replacement allows optimize ngx_ssl_sendfile() at compile time, as well. While BIO_get_ktls_send is documented to be a macro, it's not required to expand to "(0)" if KTLS is not supported. Further, it's not going to actually work for positive tests, since non-numeric macro values will evaluate to 0, effectively disabling SSL_sendfile() for all builds. > I see that it's convention in nginx to test external macros using #ifdef. > In certain cases we use an exception there if it does or even does not > make sense, such as when testing SSL_CTRL_SET_ECDH_AUTO (though that's > rather a typo there). Using #if BIO_get_ktls_send looks reasonable to me. Sure, "#if SSL_CTRL_SET_ECDH_AUTO" looks like a typo (patch below), but it used to work, since SSL_CTRL_SET_ECDH_AUTO is a non-zero numeric constant when defined. This is not the case with BIO_get_ktls_send. > Another way (though, a less obvious for the reader) is to replace > #if/ifdef BIO_get_ktls_send with a more convenient #ifndef OPENSSL_NO_KTLS. > This macro is set when KTLS isn't supported and not configured for OpenSSL. > As per INSTALL.md in the root of OpenSSL distribution, the enable-ktls option > "is forced off on systems that do not support the Kernel TLS data-path". > This makes no matter how OpenSSL is configured, with or without this option, > if it's claimed in OpenSSL to be unsupported by platform. > I tested to configure enable-ktls on win32: that's appeared to be true. > Unfortunately, OPENSSL_NO_KTLS is used to be documented (even for runtime > BIO_get_ktls_send() checks) only in sources, such as in apps/s_server.c. This is not going to work for older OpenSSL versions without KTLS support. At most, you can replace the !NGX_WIN32 part with !defined OPENSSL_NO_KTLS, but I don't think it is a good change for this win32-specific issue. # HG changeset patch # User Maxim Dounin # Date 1662432499 -10800 # Tue Sep 06 05:48:19 2022 +0300 # Node ID de9fe82b0c7789474bb6284f13ebd3f903b129b0 # Parent 9cf231508a8dbc2492e0d1cd06d7b1258eb5f435 SSL: fixed incorrect usage of #if instead of #ifdef. In 2014ed60f17f, "#if SSL_CTRL_SET_ECDH_AUTO" test was incorrectly used instead of "#ifdef SSL_CTRL_SET_ECDH_AUTO". There is no practical difference, since SSL_CTRL_SET_ECDH_AUTO evaluates to a non-zero numeric value when defined, but anyway it's better to correctly test if the value is defined. diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -1426,7 +1426,7 @@ ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_s SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_ECDH_USE); -#if SSL_CTRL_SET_ECDH_AUTO +#ifdef SSL_CTRL_SET_ECDH_AUTO /* not needed in OpenSSL 1.1.0+ */ SSL_CTX_set_ecdh_auto(ssl->ctx, 1); #endif # HG changeset patch # User Maxim Dounin # Date 1662434808 -10800 # Tue Sep 06 06:26:48 2022 +0300 # Node ID 5e493208769dd754253f567405c05b3cf5f05c8a # Parent 2642d97294f0e09c1fd67e434aae759768805617 Win32: disabled threads support in OpenSSL builds. Threads are disabled during UNIX builds (see b329c0ab1a48), and also not needed for Windows builds. This used to be the default before OpenSSL 1.1.0. diff -r 2642d97294f0 -r 5e493208769d auto/lib/openssl/makefile.msvc --- a/auto/lib/openssl/makefile.msvc Tue Sep 06 05:48:19 2022 +0300 +++ b/auto/lib/openssl/makefile.msvc Tue Sep 06 06:26:48 2022 +0300 @@ -6,7 +6,7 @@ all: cd $(OPENSSL) - perl Configure VC-WIN32 no-shared \ + perl Configure VC-WIN32 no-shared no-threads \ --prefix="%cd%/openssl" \ --openssldir="%cd%/openssl/ssl" \ $(OPENSSL_OPT) -- Maxim Dounin http://mdounin.ru/ From l.crilly at f5.com Tue Sep 6 08:12:28 2022 From: l.crilly at f5.com (Liam Crilly) Date: Tue, 6 Sep 2022 08:12:28 +0000 Subject: [PATCH 0 of 2] Enhanced website navigation with 2-level menu and directive search In-Reply-To: References: Message-ID: Hi Maxim, Thank you for your considered response to these patches. I am collecting my thoughts and may respond further, or submit fresh patches based on your feedback. Cheers, Liam. From pluknet at nginx.com Tue Sep 6 11:35:20 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Tue, 6 Sep 2022 15:35:20 +0400 Subject: [PATCH 3 of 3] Win32: fixed build on Windows with OpenSSL 3.0.x (ticket #2379) In-Reply-To: References: <3d162f4c6f62b23d4906.1662050945@1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa> Message-ID: <1D2B1D0D-ECF2-4A91-AEB9-944CC878C828@nginx.com> > On 6 Sep 2022, at 07:49, Maxim Dounin wrote: > > Hello! > > On Mon, Sep 05, 2022 at 10:44:06PM +0400, Sergey Kandaurov wrote: > >>> On 1 Sep 2022, at 20:49, Maxim Dounin wrote: >>> >>> # HG changeset patch >>> # User Maxim Dounin >>> # Date 1662050858 -10800 >>> # Thu Sep 01 19:47:38 2022 +0300 >>> # Node ID d73286c43b44f3161ca4de1d9d1cbb070c6da4a7 >>> # Parent 63a4b5ffd440c526bc96c6879dc1b6b489975d98 >>> Win32: fixed build on Windows with OpenSSL 3.0.x (ticket #2379). >> >> BTW, win32 build on Windows XP with OpenSSL 3.0.x is currently broken >> for another reason: due to a missing InterlockedOr64 implementation. >> See the related fix, expected to appear in upcoming OpenSSL 3.0.6: >> ce3951fc30c7bc7c3dbacba19d87c79d9af9da0d > > Well, that's exactly the reason why I've fixed 64-bit build with > MSVC 2010, the 2nd patch in this series. But see ticket's > comment:1, the patch committed into OpenSSL is incomplete and only > fixes MSVC 2008 and older, but won't fix Windows XP builds in newer > compilers. So, the checks for _MSC_VER in the committed fix seem to be not enough if older WIN32_WINNT requested. > >> Now I have to configure OpenSSL with "no-threads" to pass to this error. > > We actually use "no-threads" in Unix builds, see b329c0ab1a48. On > win32 we do use threads (for cache loader and cache manager), but > this shouldn't require threads support in OpenSSL. That is, it > might be a good idea to use "no-threads" in win32 builds too, > especially given that this was the default before OpenSSL 1.1.0. > Patch below. At least, all tests passed. > >>> SSL_sendfile() expects integer file descriptor as an argument, but nginx >>> uses OS file handles (HANDLE) to work with files on Windows, and passing >>> HANDLE instead of an integer correctly results in build failure. Since >>> SSL_sendfile() is not expected to work on Windows anyway, the code is now >>> disabled on Windows with appropriate compile-time checks. >>> >>> diff -r 63a4b5ffd440 -r d73286c43b44 src/event/ngx_event_openssl.c >>> --- a/src/event/ngx_event_openssl.c Thu Sep 01 19:45:22 2022 +0300 >>> +++ b/src/event/ngx_event_openssl.c Thu Sep 01 19:47:38 2022 +0300 >>> @@ -1770,7 +1770,7 @@ ngx_ssl_handshake(ngx_connection_t *c) >>> #endif >>> #endif >>> >>> -#ifdef BIO_get_ktls_send >>> +#if (defined BIO_get_ktls_send && !NGX_WIN32) >>> >>> if (BIO_get_ktls_send(SSL_get_wbio(c->ssl->connection)) == 1) { >>> ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, >>> @@ -1915,7 +1915,7 @@ ngx_ssl_try_early_data(ngx_connection_t >>> c->read->ready = 1; >>> c->write->ready = 1; >>> >>> -#ifdef BIO_get_ktls_send >>> +#if (defined BIO_get_ktls_send && !NGX_WIN32) >>> >>> if (BIO_get_ktls_send(SSL_get_wbio(c->ssl->connection)) == 1) { >>> ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, >>> @@ -2944,7 +2944,7 @@ ngx_ssl_write_early(ngx_connection_t *c, >>> static ssize_t >>> ngx_ssl_sendfile(ngx_connection_t *c, ngx_buf_t *file, size_t size) >>> { >>> -#ifdef BIO_get_ktls_send >>> +#if (defined BIO_get_ktls_send && !NGX_WIN32) >>> >>> int sslerr, flags; >>> ssize_t n; >>> >> >> This could be simplified if replaced #ifdef with #if. >> BIO_get_ktls_send is documented to be a macro (and so tested here). >> When OpenSSL isn't configured with KTLS, the macro is explanded to 0. >> Replacement allows optimize ngx_ssl_sendfile() at compile time, as well. > > While BIO_get_ktls_send is documented to be a macro, it's not > required to expand to "(0)" if KTLS is not supported. Well, this is sort of documented in SSL_write.pod: SSL_sendfile() is available only when Kernel TLS is enabled, which can be checked by calling BIO_get_ktls_send(). Then in BIO_ctrl.pod: BIO_get_ktls_send() returns 1 if the BIO is using the Kernel TLS data-path for sending. Otherwise, it returns zero. > > Further, it's not going to actually work for positive tests, since > non-numeric macro values will evaluate to 0, effectively disabling > SSL_sendfile() for all builds. Missed that point, thnx. > >> I see that it's convention in nginx to test external macros using #ifdef. >> In certain cases we use an exception there if it does or even does not >> make sense, such as when testing SSL_CTRL_SET_ECDH_AUTO (though that's >> rather a typo there). Using #if BIO_get_ktls_send looks reasonable to me. > > Sure, "#if SSL_CTRL_SET_ECDH_AUTO" looks like a typo (patch > below), but it used to work, since SSL_CTRL_SET_ECDH_AUTO is a > non-zero numeric constant when defined. This is not the case with > BIO_get_ktls_send. > >> Another way (though, a less obvious for the reader) is to replace >> #if/ifdef BIO_get_ktls_send with a more convenient #ifndef OPENSSL_NO_KTLS. >> This macro is set when KTLS isn't supported and not configured for OpenSSL. >> As per INSTALL.md in the root of OpenSSL distribution, the enable-ktls option >> "is forced off on systems that do not support the Kernel TLS data-path". >> This makes no matter how OpenSSL is configured, with or without this option, >> if it's claimed in OpenSSL to be unsupported by platform. >> I tested to configure enable-ktls on win32: that's appeared to be true. >> Unfortunately, OPENSSL_NO_KTLS is used to be documented (even for runtime >> BIO_get_ktls_send() checks) only in sources, such as in apps/s_server.c. > > This is not going to work for older OpenSSL versions without KTLS > support. At most, you can replace the !NGX_WIN32 part with > !defined OPENSSL_NO_KTLS, but I don't think it is a good change > for this win32-specific issue. Agreed. So, your proposed change is the only way. > > # HG changeset patch > # User Maxim Dounin > # Date 1662432499 -10800 > # Tue Sep 06 05:48:19 2022 +0300 > # Node ID de9fe82b0c7789474bb6284f13ebd3f903b129b0 > # Parent 9cf231508a8dbc2492e0d1cd06d7b1258eb5f435 > SSL: fixed incorrect usage of #if instead of #ifdef. > > In 2014ed60f17f, "#if SSL_CTRL_SET_ECDH_AUTO" test was incorrectly used > instead of "#ifdef SSL_CTRL_SET_ECDH_AUTO". There is no practical > difference, since SSL_CTRL_SET_ECDH_AUTO evaluates to a non-zero numeric > value when defined, but anyway it's better to correctly test if the value > is defined. > > diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c > --- a/src/event/ngx_event_openssl.c > +++ b/src/event/ngx_event_openssl.c > @@ -1426,7 +1426,7 @@ ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_s > > SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_ECDH_USE); > > -#if SSL_CTRL_SET_ECDH_AUTO > +#ifdef SSL_CTRL_SET_ECDH_AUTO > /* not needed in OpenSSL 1.1.0+ */ > SSL_CTX_set_ecdh_auto(ssl->ctx, 1); > #endif > Looks good. > # HG changeset patch > # User Maxim Dounin > # Date 1662434808 -10800 > # Tue Sep 06 06:26:48 2022 +0300 > # Node ID 5e493208769dd754253f567405c05b3cf5f05c8a > # Parent 2642d97294f0e09c1fd67e434aae759768805617 > Win32: disabled threads support in OpenSSL builds. > > Threads are disabled during UNIX builds (see b329c0ab1a48), and also not > needed for Windows builds. > > This used to be the default before OpenSSL 1.1.0. > > diff -r 2642d97294f0 -r 5e493208769d auto/lib/openssl/makefile.msvc > --- a/auto/lib/openssl/makefile.msvc Tue Sep 06 05:48:19 2022 +0300 > +++ b/auto/lib/openssl/makefile.msvc Tue Sep 06 06:26:48 2022 +0300 > @@ -6,7 +6,7 @@ > all: > cd $(OPENSSL) > > - perl Configure VC-WIN32 no-shared \ > + perl Configure VC-WIN32 no-shared no-threads \ > --prefix="%cd%/openssl" \ > --openssldir="%cd%/openssl/ssl" \ > $(OPENSSL_OPT) > > Looks good. Does it make sense to apply this to makefile.bcc for consistency? -- Sergey Kandaurov From pluknet at nginx.com Tue Sep 6 11:39:32 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Tue, 6 Sep 2022 15:39:32 +0400 Subject: [PATCH 1 of 3] Win32: removed misleading comment about warnings being disabled In-Reply-To: <3d162f4c6f62b23d4906.1662050945@1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa> <63a4b5ffd440c526bc96.1662050946@1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa> Message-ID: <20220906113932.cx5wcxwaijxdy5se@Y9MQ9X2QVV> On Thu, Sep 01, 2022 at 07:49:05PM +0300, Maxim Dounin wrote: > # HG changeset patch > # User Maxim Dounin > # Date 1662050702 -10800 > # Thu Sep 01 19:45:02 2022 +0300 > # Node ID 3d162f4c6f62b23d4906d2afaabcd8c2720fb12a > # Parent 553e7e669cfd55c9371a03c5c87e626fd9d2f773 > Win32: removed misleading comment about warnings being disabled. On Thu, Sep 01, 2022 at 07:49:06PM +0300, Maxim Dounin wrote: > # HG changeset patch > # User Maxim Dounin > # Date 1662050722 -10800 > # Thu Sep 01 19:45:22 2022 +0300 > # Node ID 63a4b5ffd440c526bc96c6879dc1b6b489975d98 > # Parent 3d162f4c6f62b23d4906d2afaabcd8c2720fb12a > Win32: disabled C4306 warnings with MSVC. Looks good. From pluknet at nginx.com Tue Sep 6 11:49:19 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Tue, 6 Sep 2022 15:49:19 +0400 Subject: [PATCH] Events: fixed style and wrong error handling in the iocp module In-Reply-To: <3296b3825fa9b8cd4d0a.1661476661@vm-bsd.mdounin.ru> References: <3296b3825fa9b8cd4d0a.1661476661@vm-bsd.mdounin.ru> Message-ID: <325F34E0-6A06-4BE8-81EE-841D1DFC50ED@nginx.com> > On 26 Aug 2022, at 05:17, Maxim Dounin wrote: > > # HG changeset patch > # User Maxim Dounin > # Date 1661476617 -10800 > # Fri Aug 26 04:16:57 2022 +0300 > # Node ID 3296b3825fa9b8cd4d0aafb39bb5ea1fac97c6db > # Parent 069a4813e8d6d7ec662d282a10f5f7062ebd817f > Events: fixed style and wrong error handling in the iocp module. > > diff --git a/src/event/modules/ngx_iocp_module.c b/src/event/modules/ngx_iocp_module.c > --- a/src/event/modules/ngx_iocp_module.c > +++ b/src/event/modules/ngx_iocp_module.c > @@ -231,9 +231,8 @@ ngx_iocp_del_connection(ngx_connection_t > } > > > -static > -ngx_int_t ngx_iocp_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, > - ngx_uint_t flags) > +static ngx_int_t > +ngx_iocp_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) > { > int rc; > u_int key; > @@ -356,7 +355,7 @@ ngx_iocp_create_conf(ngx_cycle_t *cycle) > > cf = ngx_palloc(cycle->pool, sizeof(ngx_iocp_conf_t)); > if (cf == NULL) { > - return NGX_CONF_ERROR; > + return NULL; > } > > cf->threads = NGX_CONF_UNSET; Looks good. -- Sergey Kandaurov From pluknet at nginx.com Tue Sep 6 13:52:51 2022 From: pluknet at nginx.com (=?iso-8859-1?q?Sergey_Kandaurov?=) Date: Tue, 06 Sep 2022 17:52:51 +0400 Subject: [PATCH] Tests: fixed try_run() to remove temporary directory on failure Message-ID: <7eb87eaf06f4b9161cfe.1662472371@enoparse.local> # HG changeset patch # User Sergey Kandaurov # Date 1662472340 -14400 # Tue Sep 06 17:52:20 2022 +0400 # Node ID 7eb87eaf06f4b9161cfe298f195947dbddedd01d # Parent 78fe648d54a79822a72f3a5f8cc79651b3b1f5a7 Tests: fixed try_run() to remove temporary directory on failure. Keeping the file handle pointing to the "stderr" file prevented removal of the temporary directory on win32 if execution aborted in run(). The fix is to move safe file operations surrounding run() out of the eval block such that the state of file handles is consistent. Fixes a1874249496d. diff -r 78fe648d54a7 -r 7eb87eaf06f4 lib/Test/Nginx.pm --- a/lib/Test/Nginx.pm Tue Aug 30 17:24:16 2022 +0400 +++ b/lib/Test/Nginx.pm Tue Sep 06 17:52:20 2022 +0400 @@ -290,8 +290,9 @@ sub has_daemon($) { sub try_run($$) { my ($self, $message) = @_; + open OLDERR, ">&", \*STDERR; + eval { - open OLDERR, ">&", \*STDERR; open NEWERR, ">", $self->{_testdir} . '/stderr' or die "Can't open stderr: $!"; close STDERR; @@ -299,10 +300,10 @@ sub try_run($$) { close NEWERR; $self->run(); + }; - close STDERR; - open STDERR, ">&", \*OLDERR; - }; + close STDERR; + open STDERR, ">&", \*OLDERR; return $self unless $@; From xeioex at nginx.com Tue Sep 6 17:36:52 2022 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 06 Sep 2022 17:36:52 +0000 Subject: [njs] WebCrypto: fixed dangling pointer warning by gcc-12. Message-ID: details: https://hg.nginx.org/njs/rev/c597cd200724 branches: changeset: 1947:c597cd200724 user: Dmitry Volyntsev date: Tue Sep 06 10:09:08 2022 -0700 description: WebCrypto: fixed dangling pointer warning by gcc-12. diffstat: external/njs_webcrypto_module.c | 22 +++++++++++++--------- 1 files changed, 13 insertions(+), 9 deletions(-) diffs (82 lines): diff -r 506ba9a639b8 -r c597cd200724 external/njs_webcrypto_module.c --- a/external/njs_webcrypto_module.c Thu Sep 01 18:12:27 2022 -0700 +++ b/external/njs_webcrypto_module.c Tue Sep 06 10:09:08 2022 -0700 @@ -121,7 +121,7 @@ static njs_int_t njs_ext_get_random_valu static void njs_webcrypto_cleanup_pkey(void *data); static njs_webcrypto_key_format_t njs_key_format(njs_vm_t *vm, - njs_value_t *value, njs_str_t *format); + njs_value_t *value); static njs_int_t njs_key_usage(njs_vm_t *vm, njs_value_t *value, unsigned *mask); static njs_webcrypto_algorithm_t *njs_key_algorithm(njs_vm_t *vm, @@ -1649,7 +1649,7 @@ njs_ext_import_key(njs_vm_t *vm, njs_val unsigned usage; EVP_PKEY *pkey; njs_int_t ret; - njs_str_t key_data, format; + njs_str_t key_data; njs_value_t value, *options; const u_char *start; #if (OPENSSL_VERSION_NUMBER < 0x30000000L) @@ -1669,9 +1669,8 @@ njs_ext_import_key(njs_vm_t *vm, njs_val pkey = NULL; - fmt = njs_key_format(vm, njs_arg(args, nargs, 1), &format); + fmt = njs_key_format(vm, njs_arg(args, nargs, 1)); if (njs_slow_path(fmt == NJS_KEY_FORMAT_UNKNOWN)) { - njs_type_error(vm, "unknown key format: \"%V\"", &format); goto fail; } @@ -1746,7 +1745,7 @@ njs_ext_import_key(njs_vm_t *vm, njs_val break; default: - njs_internal_error(vm, "not implemented key format: \"%V\"", &format); + njs_internal_error(vm, "not implemented key format: \"jwk\""); goto fail; } @@ -2484,9 +2483,10 @@ njs_webcrypto_cleanup_pkey(void *data) static njs_webcrypto_key_format_t -njs_key_format(njs_vm_t *vm, njs_value_t *value, njs_str_t *format) +njs_key_format(njs_vm_t *vm, njs_value_t *value) { njs_int_t ret; + njs_str_t format; njs_uint_t fmt; njs_value_t string; @@ -2502,21 +2502,25 @@ njs_key_format(njs_vm_t *vm, njs_value_t ret = njs_value_to_string(vm, &string, value); if (njs_slow_path(ret != NJS_OK)) { - return NJS_ERROR; + goto fail; } - njs_string_get(&string, format); + njs_string_get(&string, &format); fmt = 0; while (fmt < sizeof(formats) / sizeof(formats[0])) { - if (njs_strstr_eq(format, &formats[fmt].name)) { + if (njs_strstr_eq(&format, &formats[fmt].name)) { return formats[fmt].value; } fmt++; } +fail: + + njs_type_error(vm, "unknown key format: \"%V\"", &format); + return NJS_KEY_FORMAT_UNKNOWN; } From mdounin at mdounin.ru Wed Sep 7 00:11:24 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 7 Sep 2022 03:11:24 +0300 Subject: [PATCH 3 of 3] Win32: fixed build on Windows with OpenSSL 3.0.x (ticket #2379) In-Reply-To: <1D2B1D0D-ECF2-4A91-AEB9-944CC878C828@nginx.com> References: <3d162f4c6f62b23d4906.1662050945@1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa> <1D2B1D0D-ECF2-4A91-AEB9-944CC878C828@nginx.com> Message-ID: Hello! On Tue, Sep 06, 2022 at 03:35:20PM +0400, Sergey Kandaurov wrote: > > On 6 Sep 2022, at 07:49, Maxim Dounin wrote: > > > > Hello! > > > > On Mon, Sep 05, 2022 at 10:44:06PM +0400, Sergey Kandaurov wrote: > > > >>> On 1 Sep 2022, at 20:49, Maxim Dounin wrote: > >>> > >>> # HG changeset patch > >>> # User Maxim Dounin > >>> # Date 1662050858 -10800 > >>> # Thu Sep 01 19:47:38 2022 +0300 > >>> # Node ID d73286c43b44f3161ca4de1d9d1cbb070c6da4a7 > >>> # Parent 63a4b5ffd440c526bc96c6879dc1b6b489975d98 > >>> Win32: fixed build on Windows with OpenSSL 3.0.x (ticket #2379). > >> > >> BTW, win32 build on Windows XP with OpenSSL 3.0.x is currently broken > >> for another reason: due to a missing InterlockedOr64 implementation. > >> See the related fix, expected to appear in upcoming OpenSSL 3.0.6: > >> ce3951fc30c7bc7c3dbacba19d87c79d9af9da0d > > > > Well, that's exactly the reason why I've fixed 64-bit build with > > MSVC 2010, the 2nd patch in this series. But see ticket's > > comment:1, the patch committed into OpenSSL is incomplete and only > > fixes MSVC 2008 and older, but won't fix Windows XP builds in newer > > compilers. > > So, the checks for _MSC_VER in the committed fix > seem to be not enough if older WIN32_WINNT requested. Yes, exactly. Either way, this should be irrelevant to nginx builds with "no-threads". [...] > >>> -#ifdef BIO_get_ktls_send > >>> +#if (defined BIO_get_ktls_send && !NGX_WIN32) > >>> > >>> int sslerr, flags; > >>> ssize_t n; > >>> > >> > >> This could be simplified if replaced #ifdef with #if. > >> BIO_get_ktls_send is documented to be a macro (and so tested here). > >> When OpenSSL isn't configured with KTLS, the macro is explanded to 0. > >> Replacement allows optimize ngx_ssl_sendfile() at compile time, as well. > > > > While BIO_get_ktls_send is documented to be a macro, it's not > > required to expand to "(0)" if KTLS is not supported. > > Well, this is sort of documented in SSL_write.pod: > SSL_sendfile() is available only when > Kernel TLS is enabled, which can be checked by calling BIO_get_ktls_send(). > > Then in BIO_ctrl.pod: > BIO_get_ktls_send() returns 1 if the BIO is using the Kernel TLS data-path for > sending. Otherwise, it returns zero. It still not required to expand to "(0)", and can instead expand to a function which returns 0, making it completely indistinguishable from the case when KTLS can be supported. Either way, this is irrelevant, given that #if also breaks positive tests. > > Further, it's not going to actually work for positive tests, since > > non-numeric macro values will evaluate to 0, effectively disabling > > SSL_sendfile() for all builds. > > Missed that point, thnx. [...] > > # HG changeset patch > > # User Maxim Dounin > > # Date 1662434808 -10800 > > # Tue Sep 06 06:26:48 2022 +0300 > > # Node ID 5e493208769dd754253f567405c05b3cf5f05c8a > > # Parent 2642d97294f0e09c1fd67e434aae759768805617 > > Win32: disabled threads support in OpenSSL builds. > > > > Threads are disabled during UNIX builds (see b329c0ab1a48), and also not > > needed for Windows builds. > > > > This used to be the default before OpenSSL 1.1.0. > > > > diff -r 2642d97294f0 -r 5e493208769d auto/lib/openssl/makefile.msvc > > --- a/auto/lib/openssl/makefile.msvc Tue Sep 06 05:48:19 2022 +0300 > > +++ b/auto/lib/openssl/makefile.msvc Tue Sep 06 06:26:48 2022 +0300 > > @@ -6,7 +6,7 @@ > > all: > > cd $(OPENSSL) > > > > - perl Configure VC-WIN32 no-shared \ > > + perl Configure VC-WIN32 no-shared no-threads \ > > --prefix="%cd%/openssl" \ > > --openssldir="%cd%/openssl/ssl" \ > > $(OPENSSL_OPT) > > > > > > Looks good. > Does it make sense to apply this to makefile.bcc for consistency? I've considered it, but decided to don't touch makefile.bcc, given that OpenSSL 1.1.0 removed "the aged BC-32 config and all its supporting scripts" (quote from CHANGES), so "no-threads" for BCC is the default for all existing OpenSSL versions which do support building with BCC. Pushed to http://mdounin.ru/hg/nginx, thanks. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Wed Sep 7 00:12:11 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 7 Sep 2022 03:12:11 +0300 Subject: [PATCH] Events: fixed style and wrong error handling in the iocp module In-Reply-To: <325F34E0-6A06-4BE8-81EE-841D1DFC50ED@nginx.com> References: <3296b3825fa9b8cd4d0a.1661476661@vm-bsd.mdounin.ru> <325F34E0-6A06-4BE8-81EE-841D1DFC50ED@nginx.com> Message-ID: Hello! On Tue, Sep 06, 2022 at 03:49:19PM +0400, Sergey Kandaurov wrote: > > > On 26 Aug 2022, at 05:17, Maxim Dounin wrote: > > > > # HG changeset patch > > # User Maxim Dounin > > # Date 1661476617 -10800 > > # Fri Aug 26 04:16:57 2022 +0300 > > # Node ID 3296b3825fa9b8cd4d0aafb39bb5ea1fac97c6db > > # Parent 069a4813e8d6d7ec662d282a10f5f7062ebd817f > > Events: fixed style and wrong error handling in the iocp module. > > > > diff --git a/src/event/modules/ngx_iocp_module.c b/src/event/modules/ngx_iocp_module.c > > --- a/src/event/modules/ngx_iocp_module.c > > +++ b/src/event/modules/ngx_iocp_module.c > > @@ -231,9 +231,8 @@ ngx_iocp_del_connection(ngx_connection_t > > } > > > > > > -static > > -ngx_int_t ngx_iocp_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, > > - ngx_uint_t flags) > > +static ngx_int_t > > +ngx_iocp_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) > > { > > int rc; > > u_int key; > > @@ -356,7 +355,7 @@ ngx_iocp_create_conf(ngx_cycle_t *cycle) > > > > cf = ngx_palloc(cycle->pool, sizeof(ngx_iocp_conf_t)); > > if (cf == NULL) { > > - return NGX_CONF_ERROR; > > + return NULL; > > } > > > > cf->threads = NGX_CONF_UNSET; > > Looks good. Pushed to http://mdounin.ru/hg/nginx, thanks. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Wed Sep 7 01:46:35 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 7 Sep 2022 04:46:35 +0300 Subject: [PATCH] Tests: fixed try_run() to remove temporary directory on failure In-Reply-To: <7eb87eaf06f4b9161cfe.1662472371@enoparse.local> References: <7eb87eaf06f4b9161cfe.1662472371@enoparse.local> Message-ID: Hello! On Tue, Sep 06, 2022 at 05:52:51PM +0400, Sergey Kandaurov wrote: > # HG changeset patch > # User Sergey Kandaurov > # Date 1662472340 -14400 > # Tue Sep 06 17:52:20 2022 +0400 > # Node ID 7eb87eaf06f4b9161cfe298f195947dbddedd01d > # Parent 78fe648d54a79822a72f3a5f8cc79651b3b1f5a7 > Tests: fixed try_run() to remove temporary directory on failure. > > Keeping the file handle pointing to the "stderr" file prevented removal > of the temporary directory on win32 if execution aborted in run(). > The fix is to move safe file operations surrounding run() out of the > eval block such that the state of file handles is consistent. > Fixes a1874249496d. I believe it might need some additional emphasis that this is about Windows only. For example: Tests: fixed try_run() to remove temporary directory on win32. > > diff -r 78fe648d54a7 -r 7eb87eaf06f4 lib/Test/Nginx.pm > --- a/lib/Test/Nginx.pm Tue Aug 30 17:24:16 2022 +0400 > +++ b/lib/Test/Nginx.pm Tue Sep 06 17:52:20 2022 +0400 > @@ -290,8 +290,9 @@ sub has_daemon($) { > sub try_run($$) { > my ($self, $message) = @_; > > + open OLDERR, ">&", \*STDERR; > + > eval { > - open OLDERR, ">&", \*STDERR; > open NEWERR, ">", $self->{_testdir} . '/stderr' > or die "Can't open stderr: $!"; > close STDERR; > @@ -299,10 +300,10 @@ sub try_run($$) { > close NEWERR; > > $self->run(); > + }; > > - close STDERR; > - open STDERR, ">&", \*OLDERR; > - }; > + close STDERR; > + open STDERR, ">&", \*OLDERR; > > return $self unless $@; Wouldn't it be better to move all stderr handling out of the eval? diff --git a/lib/Test/Nginx.pm b/lib/Test/Nginx.pm --- a/lib/Test/Nginx.pm +++ b/lib/Test/Nginx.pm @@ -291,14 +291,13 @@ sub try_run($$) { my ($self, $message) = @_; open OLDERR, ">&", \*STDERR; + open NEWERR, ">", $self->{_testdir} . '/stderr' + or die "Can't open stderr: $!"; + close STDERR; + open STDERR, ">&", \*NEWERR; + close NEWERR; eval { - open NEWERR, ">", $self->{_testdir} . '/stderr' - or die "Can't open stderr: $!"; - close STDERR; - open STDERR, ">&", \*NEWERR; - close NEWERR; - $self->run(); }; Alternatively, it might be the right time to backout a1874249496d completely and rely on "-e ..." instead, given that it was introduced in 1.19.5 and already present in all supported versions. -- Maxim Dounin http://mdounin.ru/ From v.zhestikov at f5.com Wed Sep 7 02:17:37 2022 From: v.zhestikov at f5.com (Vadim Zhestikov) Date: Wed, 07 Sep 2022 02:17:37 +0000 Subject: [njs] Fixed Object.freeze() with fast arrays. Message-ID: details: https://hg.nginx.org/njs/rev/d40481363bdf branches: changeset: 1948:d40481363bdf user: Vadim Zhestikov date: Tue Sep 06 11:13:31 2022 -0700 description: Fixed Object.freeze() with fast arrays. Previously, Object.freeze() failed to freeze the 'length' property of a fast array. diffstat: src/njs_object.c | 13 ++++++++++++- src/test/njs_unit_test.c | 6 ++++++ 2 files changed, 18 insertions(+), 1 deletions(-) diffs (50 lines): diff -r c597cd200724 -r d40481363bdf src/njs_object.c --- a/src/njs_object.c Tue Sep 06 10:09:08 2022 -0700 +++ b/src/njs_object.c Tue Sep 06 11:13:31 2022 -0700 @@ -1553,7 +1553,9 @@ static njs_int_t njs_object_set_integrity_level(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t level) { + uint32_t length; njs_int_t ret; + njs_array_t *array; njs_value_t *value; njs_lvlhsh_t *hash; njs_object_t *object; @@ -1576,10 +1578,19 @@ njs_object_set_integrity_level(njs_vm_t } if (njs_is_fast_array(value)) { - ret = njs_array_convert_to_slow_array(vm, njs_array(value)); + array = njs_array(value); + length = array->length; + + ret = njs_array_convert_to_slow_array(vm, array); if (ret != NJS_OK) { return ret; } + + ret = njs_array_length_redefine(vm, value, length); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + } object = njs_object(value); diff -r c597cd200724 -r d40481363bdf src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Tue Sep 06 10:09:08 2022 -0700 +++ b/src/test/njs_unit_test.c Tue Sep 06 11:13:31 2022 -0700 @@ -4615,6 +4615,12 @@ static njs_unit_test_t njs_test[] = { njs_str("Object.prototype[1] = 1; Object.prototype.length = 2; Array.prototype.pop.call({0:0})"), njs_str("1") }, + { njs_str("var a = []; Object.freeze(a); Object.getOwnPropertyDescriptor(a, 'length').writable"), + njs_str("false") }, + + { njs_str("var o = Object.freeze([0,1,2]); o.length=3"), + njs_str("TypeError: Cannot assign to read-only property \"length\" of array") }, + { njs_str("var o = Object.freeze({0: 0, 1: 1, length: 2}); Array.prototype.pop.call(o)"), njs_str("TypeError: Cannot delete property \"1\" of object") }, From v.zhestikov at f5.com Wed Sep 7 02:17:39 2022 From: v.zhestikov at f5.com (Vadim Zhestikov) Date: Wed, 07 Sep 2022 02:17:39 +0000 Subject: [njs] Fixed Object.defineProperty() with fast arrays. Message-ID: details: https://hg.nginx.org/njs/rev/a2db32044812 branches: changeset: 1949:a2db32044812 user: Vadim Zhestikov date: Tue Sep 06 18:44:47 2022 -0700 description: Fixed Object.defineProperty() with fast arrays. Previously, Object.defineProperty() failed to properly set attributes for properties of fast array. diffstat: src/njs_array.c | 29 ++++++---- src/njs_array.h | 2 +- src/njs_json.c | 123 ++++++++++++++++++++++++++-------------------- src/njs_object.c | 3 +- src/njs_object_prop.c | 40 ++++++++++++-- src/njs_value.c | 11 +-- src/test/njs_unit_test.c | 28 +++++++--- 7 files changed, 148 insertions(+), 88 deletions(-) diffs (464 lines): diff -r d40481363bdf -r a2db32044812 src/njs_array.c --- a/src/njs_array.c Tue Sep 06 11:13:31 2022 -0700 +++ b/src/njs_array.c Tue Sep 06 18:44:47 2022 -0700 @@ -100,7 +100,7 @@ njs_array_alloc(njs_vm_t *vm, njs_bool_t array->length = 0; njs_set_array(&value, array); - ret = njs_array_length_redefine(vm, &value, length); + ret = njs_array_length_redefine(vm, &value, length, 1); if (njs_slow_path(ret != NJS_OK)) { return NULL; } @@ -173,7 +173,7 @@ njs_array_convert_to_slow_array(njs_vm_t njs_int_t -njs_array_length_redefine(njs_vm_t *vm, njs_value_t *value, uint32_t length) +njs_array_length_redefine(njs_vm_t *vm, njs_value_t *value, uint32_t length, int writable) { njs_object_prop_t *prop; @@ -192,6 +192,7 @@ njs_array_length_redefine(njs_vm_t *vm, return NJS_ERROR; } + prop->writable = writable; prop->enumerable = 0; prop->configurable = 0; @@ -209,12 +210,7 @@ njs_array_length_set(njs_vm_t *vm, njs_v int64_t prev_length; uint32_t i, length; njs_int_t ret; - njs_array_t *array, *keys; - - array = njs_object_proto_lookup(njs_object(value), NJS_ARRAY, njs_array_t); - if (njs_slow_path(array == NULL)) { - return NJS_DECLINED; - } + njs_array_t *keys; ret = njs_value_to_number(vm, setval, &num); if (njs_slow_path(ret != NJS_OK)) { @@ -256,7 +252,7 @@ njs_array_length_set(njs_vm_t *vm, njs_v } } - ret = njs_array_length_redefine(vm, value, length); + ret = njs_array_length_redefine(vm, value, length, prev->writable); if (njs_slow_path(ret != NJS_OK)) { return ret; } @@ -1604,7 +1600,7 @@ static int njs_array_indices_handler(const void *first, const void *second, void *ctx) { double num1, num2; - int64_t diff; + int64_t diff, cmp_res; njs_str_t str1, str2; const njs_value_t *val1, *val2; @@ -1635,8 +1631,19 @@ njs_array_indices_handler(const void *fi njs_string_get(val1, &str1); njs_string_get(val2, &str2); - return strncmp((const char *) str1.start, (const char *) str2.start, + cmp_res = strncmp((const char *) str1.start, (const char *) str2.start, njs_min(str1.length, str2.length)); + if (cmp_res == 0) { + if (str1.length < str2.length) { + return -1; + } else if (str1.length > str2.length) { + return 1; + } else { + return 0; + } + } + + return cmp_res; } diff -r d40481363bdf -r a2db32044812 src/njs_array.h --- a/src/njs_array.h Tue Sep 06 11:13:31 2022 -0700 +++ b/src/njs_array.h Tue Sep 06 18:44:47 2022 -0700 @@ -25,7 +25,7 @@ void njs_array_destroy(njs_vm_t *vm, njs njs_int_t njs_array_add(njs_vm_t *vm, njs_array_t *array, njs_value_t *value); njs_int_t njs_array_convert_to_slow_array(njs_vm_t *vm, njs_array_t *array); njs_int_t njs_array_length_redefine(njs_vm_t *vm, njs_value_t *value, - uint32_t length); + uint32_t length, int writable); njs_int_t njs_array_length_set(njs_vm_t *vm, njs_value_t *value, njs_object_prop_t *prev, njs_value_t *setval); njs_array_t *njs_array_keys(njs_vm_t *vm, njs_value_t *array, njs_bool_t all); diff -r d40481363bdf -r a2db32044812 src/njs_json.c --- a/src/njs_json.c Tue Sep 06 11:13:31 2022 -0700 +++ b/src/njs_json.c Tue Sep 06 18:44:47 2022 -0700 @@ -989,8 +989,8 @@ njs_json_push_stringify_state(njs_vm_t * if (njs_is_array(&stringify->replacer)) { state->keys = njs_array(&stringify->replacer); - } else if (state->array && !state->fast_array) { - state->keys = njs_array_keys(vm, value, 0); + } else if (state->array) { + state->keys = njs_array_keys(vm, value, 1); if (njs_slow_path(state->keys == NULL)) { return NULL; } @@ -1727,7 +1727,7 @@ njs_dump_terminal(njs_json_stringify_t * break; case NJS_INVALID: - njs_chb_append_literal(chain, ""); + /* Fall through. */ break; case NJS_OBJECT_VALUE: @@ -1916,7 +1916,7 @@ njs_dump_empty(njs_json_stringify_t *str double key, prev; int64_t diff; - if (!state->array || state->fast_array) { + if (!state->array) { return 0; } @@ -1926,7 +1926,12 @@ njs_dump_empty(njs_json_stringify_t *str } else { key = state->length; - prev = (state->index > 0) ? njs_key_to_index(state->key) : -1; + if (state->key == NULL) { + prev = -1; + + } else { + prev = (state->index > 0) ? njs_key_to_index(state->key) : -1; + } } if (isnan(key)) { @@ -1937,11 +1942,19 @@ njs_dump_empty(njs_json_stringify_t *str if (diff > 1) { if (sep_position == 0 && state->keys->length) { - njs_chb_append_literal(chain, ","); - njs_json_stringify_indent(stringify, chain, 1); + if (prev != -1) { + njs_chb_append_literal(chain, ","); + njs_json_stringify_indent(stringify, chain, 1); + } } - njs_chb_sprintf(chain, 64, "<%L empty items>", diff - 1); + if (diff - 1 == 1) { + njs_chb_sprintf(chain, 64, ""); + + } else { + njs_chb_sprintf(chain, 64, "<%L empty items>", diff - 1); + } + state->written = 1; if (sep_position == 1 && state->keys->length) { @@ -2019,7 +2032,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ njs_json_stringify_indent(stringify, &chain, 1); } - if (njs_json_stringify_done(state, state->fast_array)) { + if (njs_json_stringify_done(state, 0)) { njs_dump_empty(stringify, state, &chain, 0); njs_json_stringify_indent(stringify, &chain, 0); @@ -2033,57 +2046,59 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_ continue; } - if (state->fast_array) { - if (state->written) { - njs_chb_append_literal(&chain, ","); - njs_json_stringify_indent(stringify, &chain, 1); - } - - state->written = 1; - - val = &njs_array_start(&state->value)[state->index++]; - - } else { - njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0); - - key = &state->keys->start[state->index++]; - state->key = key; - - ret = njs_property_query(vm, &pq, &state->value, key); - if (njs_slow_path(ret != NJS_OK)) { - if (ret == NJS_DECLINED) { + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0); + + key = &state->keys->start[state->index++]; + + if(state->array) { + if (key->type == NJS_STRING) { + njs_string_get(key, &str); + if (str.length == 6 && memcmp(str.start, "length", 6) == 0) { continue; } - - goto exception; } - - prop = pq.lhq.value; - - if (prop->type == NJS_WHITEOUT || !prop->enumerable) { + } + + state->key = key; + + ret = njs_property_query(vm, &pq, &state->value, key); + if (njs_slow_path(ret != NJS_OK)) { + if (ret == NJS_DECLINED) { + continue; + } + + goto exception; + } + + prop = pq.lhq.value; + + if (prop->type == NJS_WHITEOUT || !prop->enumerable) { + if (!state->array) { continue; } - - if (state->written) { - njs_chb_append_literal(&chain, ","); - njs_json_stringify_indent(stringify, &chain, 1); + } + + if (state->written) { + njs_chb_append_literal(&chain, ","); + njs_json_stringify_indent(stringify, &chain, 1); + } + + state->written = 1; + + njs_dump_empty(stringify, state, &chain, 1); + + if (!state->array || isnan(njs_key_to_index(key))) { + njs_key_string_get(vm, key, &pq.lhq.key); + njs_chb_append(&chain, pq.lhq.key.start, pq.lhq.key.length); + njs_chb_append_literal(&chain, ":"); + if (stringify->space.length != 0) { + njs_chb_append_literal(&chain, " "); } - - state->written = 1; - - njs_dump_empty(stringify, state, &chain, 1); - - if (!state->array || isnan(njs_key_to_index(key))) { - njs_key_string_get(vm, key, &pq.lhq.key); - njs_chb_append(&chain, pq.lhq.key.start, pq.lhq.key.length); - njs_chb_append_literal(&chain, ":"); - if (stringify->space.length != 0) { - njs_chb_append_literal(&chain, " "); - } - } - - val = &prop->value; - + } + + val = &prop->value; + + if (!state->fast_array) { if (prop->type == NJS_PROPERTY_HANDLER) { pq.scratch = *prop; prop = &pq.scratch; diff -r d40481363bdf -r a2db32044812 src/njs_object.c --- a/src/njs_object.c Tue Sep 06 11:13:31 2022 -0700 +++ b/src/njs_object.c Tue Sep 06 18:44:47 2022 -0700 @@ -1586,11 +1586,10 @@ njs_object_set_integrity_level(njs_vm_t return ret; } - ret = njs_array_length_redefine(vm, value, length); + ret = njs_array_length_redefine(vm, value, length, 1); if (njs_slow_path(ret != NJS_OK)) { return ret; } - } object = njs_object(value); diff -r d40481363bdf -r a2db32044812 src/njs_object_prop.c --- a/src/njs_object_prop.c Tue Sep 06 11:13:31 2022 -0700 +++ b/src/njs_object_prop.c Tue Sep 06 18:44:47 2022 -0700 @@ -153,6 +153,21 @@ njs_object_prop_define(njs_vm_t *vm, njs } } + if (njs_slow_path(njs_is_fast_array(object))) { + array = njs_array(object); + length = array->length; + + ret = njs_array_convert_to_slow_array(vm, array); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + ret = njs_array_length_redefine(vm, object, length, 1); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + } + again: njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 1); @@ -290,7 +305,7 @@ set_prop: return ret; } - ret = njs_array_length_redefine(vm, object, length); + ret = njs_array_length_redefine(vm, object, length, 1); if (njs_slow_path(ret != NJS_OK)) { return ret; } @@ -440,11 +455,24 @@ done: } } else { - if (njs_slow_path(pq.lhq.key_hash == NJS_LENGTH_HASH)) { - if (njs_strstr_eq(&pq.lhq.key, &length_key)) { - ret = njs_array_length_set(vm, object, prev, &prop->value); - if (ret != NJS_DECLINED) { - return ret; + if (njs_is_array(object)) { + if (njs_slow_path(pq.lhq.key_hash == NJS_LENGTH_HASH)) { + + if (njs_strstr_eq(&pq.lhq.key, &length_key)) { + + if (prev->configurable != 1 && + prev->writable != 1 && + !njs_values_strict_equal(&prev->value, &prop->value)) + { + njs_type_error(vm, "Cannot redefine property: \"length\""); + return NJS_ERROR; + } + + if (prop->writable != NJS_ATTRIBUTE_UNSET) { + prev->writable = prop->writable; + } + + return njs_array_length_set(vm, object, prev, &prop->value); } } } diff -r d40481363bdf -r a2db32044812 src/njs_value.c --- a/src/njs_value.c Tue Sep 06 11:13:31 2022 -0700 +++ b/src/njs_value.c Tue Sep 06 18:44:47 2022 -0700 @@ -790,7 +790,7 @@ njs_array_property_query(njs_vm_t *vm, n } if ((index + 1) > length) { - ret = njs_array_length_redefine(vm, &value, index + 1); + ret = njs_array_length_redefine(vm, &value, index + 1, 1); if (njs_slow_path(ret != NJS_OK)) { return ret; } @@ -1223,11 +1223,10 @@ slow_path: if (pq.own) { switch (prop->type) { case NJS_PROPERTY: - if (njs_slow_path(pq.lhq.key_hash == NJS_LENGTH_HASH)) { - if (njs_strstr_eq(&pq.lhq.key, &length_key)) { - ret = njs_array_length_set(vm, value, prop, setval); - if (ret != NJS_DECLINED) { - return ret; + if (njs_is_array(value)) { + if (njs_slow_path(pq.lhq.key_hash == NJS_LENGTH_HASH)) { + if (njs_strstr_eq(&pq.lhq.key, &length_key)) { + return njs_array_length_set(vm, value, prop, setval); } } } diff -r d40481363bdf -r a2db32044812 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Tue Sep 06 11:13:31 2022 -0700 +++ b/src/test/njs_unit_test.c Tue Sep 06 18:44:47 2022 -0700 @@ -4455,6 +4455,18 @@ static njs_unit_test_t njs_test[] = "Object.defineProperty(a, 'length', {writable:true})"), njs_str("TypeError: Cannot redefine property: \"length\"") }, + { njs_str ("var a =[0,1,2]; Object.defineProperty(a, 100, {value:100});" + "njs.dump(a);"), + njs_str("[0,1,2,<97 empty items>,100]") }, + + { njs_str("var a =[0,1,2]; Object.defineProperty(a, 3, {value:30});" + "njs.dump(Object.getOwnPropertyDescriptor(a,3));"), + njs_str("{value:30,writable:false,enumerable:false,configurable:false}") }, + + { njs_str("var a =[0,1,2]; Object.defineProperty(a, 3, {value:30});" + "a[3]=33;"), + njs_str("TypeError: Cannot assign to read-only property \"3\" of array") }, + { njs_str("[1, 2, 3, 4, 5].copyWithin(0, 3)"), njs_str("4,5,3,4,5") }, @@ -7032,11 +7044,11 @@ static njs_unit_test_t njs_test[] = { njs_str("var a = [,,undefined,undefined,,undefined];" "a.sort(function(x, y) { return x - y }); njs.dump(a)"), - njs_str("[undefined,undefined,undefined,,,]") }, + njs_str("[undefined,undefined,undefined,<3 empty items>]") }, { njs_str("var a = [1,,undefined,8,undefined,,undefined,,2];" "a.sort(function(x, y) { return x - y }); njs.dump(a)"), - njs_str("[1,2,8,undefined,undefined,undefined,,,]") }, + njs_str("[1,2,8,undefined,undefined,undefined,<3 empty items>]") }, { njs_str("var a = [1,,];" "a.sort(function(x, y) { return x - y })"), @@ -12473,8 +12485,8 @@ static njs_unit_test_t njs_test[] = njs_str("0") }, { njs_str("var x = [0, 1, 2]; x[4294967294] = 4294967294; x.length = 2;" - "njs.dump([x,x.length,Array.prototype,Array.prototype.length])"), - njs_str("[[0,1],2,[],0]") }, + "njs.dump([x,x.length,Array.prototype.length])"), + njs_str("[[0,1],2,0]") }, #if 0 /* TODO: length 2**53-1. */ { njs_str("var x = Array(2**20), y = Array(2**12).fill(x);" @@ -14713,7 +14725,7 @@ static njs_unit_test_t njs_test[] = njs_str("a,b") }, { njs_str("Object.getOwnPropertyNames(Object.defineProperty([], 'b', {}))"), - njs_str("b,length") }, + njs_str("length,b") }, { njs_str("Object.getOwnPropertyNames(Object.defineProperty(new String(), 'b', {}))"), njs_str("b,length") }, @@ -18014,11 +18026,11 @@ static njs_unit_test_t njs_test[] = { njs_str("var a = ['a',,'c']; a.length = 2**31;" "njs.dump(a)"), - njs_str("['a',<1 empty items>,'c',<2147483645 empty items>]") }, + njs_str("['a',,'c',<2147483645 empty items>]") }, { njs_str("var a = [,'b','c']; a.length = 2**31;" "njs.dump(a)"), - njs_str("[<1 empty items>,'b','c',<2147483645 empty items>]") }, + njs_str("[,'b','c',<2147483645 empty items>]") }, #if (!NJS_HAVE_MEMORY_SANITIZER) /* False-positive in MSAN? */ { njs_str("var a = []; a[2**31] = 'Z'; a[0] = 'A'; njs.dump(a)"), @@ -18035,7 +18047,7 @@ static njs_unit_test_t njs_test[] = njs_str("[1,'[Getter]',3]") }, { njs_str("var a = [1,2,3];Object.defineProperty(a, '1', {enumerable:false});njs.dump(a)"), - njs_str("[1,<1 empty items>,3]") }, + njs_str("[1,2,3]") }, { njs_str("njs.dump(-0)"), njs_str("-0") }, From pluknet at nginx.com Wed Sep 7 12:34:32 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Wed, 07 Sep 2022 12:34:32 +0000 Subject: [nginx] Events: fixed style and wrong error handling in the iocp module. Message-ID: details: https://hg.nginx.org/nginx/rev/5f5a34e83ca2 branches: changeset: 8064:5f5a34e83ca2 user: Maxim Dounin date: Wed Sep 07 00:43:51 2022 +0300 description: Events: fixed style and wrong error handling in the iocp module. diffstat: src/event/modules/ngx_iocp_module.c | 7 +++---- 1 files changed, 3 insertions(+), 4 deletions(-) diffs (24 lines): diff -r 9cf231508a8d -r 5f5a34e83ca2 src/event/modules/ngx_iocp_module.c --- a/src/event/modules/ngx_iocp_module.c Tue Aug 09 17:13:46 2022 -0300 +++ b/src/event/modules/ngx_iocp_module.c Wed Sep 07 00:43:51 2022 +0300 @@ -231,9 +231,8 @@ ngx_iocp_del_connection(ngx_connection_t } -static -ngx_int_t ngx_iocp_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, - ngx_uint_t flags) +static ngx_int_t +ngx_iocp_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) { int rc; u_int key; @@ -356,7 +355,7 @@ ngx_iocp_create_conf(ngx_cycle_t *cycle) cf = ngx_palloc(cycle->pool, sizeof(ngx_iocp_conf_t)); if (cf == NULL) { - return NGX_CONF_ERROR; + return NULL; } cf->threads = NGX_CONF_UNSET; From pluknet at nginx.com Wed Sep 7 12:34:35 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Wed, 07 Sep 2022 12:34:35 +0000 Subject: [nginx] SSL: fixed incorrect usage of #if instead of #ifdef. Message-ID: details: https://hg.nginx.org/nginx/rev/0ce2d7a520be branches: changeset: 8065:0ce2d7a520be user: Maxim Dounin date: Wed Sep 07 00:44:10 2022 +0300 description: SSL: fixed incorrect usage of #if instead of #ifdef. In 2014ed60f17f, "#if SSL_CTRL_SET_ECDH_AUTO" test was incorrectly used instead of "#ifdef SSL_CTRL_SET_ECDH_AUTO". There is no practical difference, since SSL_CTRL_SET_ECDH_AUTO evaluates to a non-zero numeric value when defined, but anyway it's better to correctly test if the value is defined. diffstat: src/event/ngx_event_openssl.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff -r 5f5a34e83ca2 -r 0ce2d7a520be src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Wed Sep 07 00:43:51 2022 +0300 +++ b/src/event/ngx_event_openssl.c Wed Sep 07 00:44:10 2022 +0300 @@ -1426,7 +1426,7 @@ ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_s SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_ECDH_USE); -#if SSL_CTRL_SET_ECDH_AUTO +#ifdef SSL_CTRL_SET_ECDH_AUTO /* not needed in OpenSSL 1.1.0+ */ SSL_CTX_set_ecdh_auto(ssl->ctx, 1); #endif From pluknet at nginx.com Wed Sep 7 12:34:37 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Wed, 07 Sep 2022 12:34:37 +0000 Subject: [nginx] Win32: removed misleading comment about warnings being disabled. Message-ID: details: https://hg.nginx.org/nginx/rev/d1aa8cc3e387 branches: changeset: 8066:d1aa8cc3e387 user: Maxim Dounin date: Wed Sep 07 00:47:02 2022 +0300 description: Win32: removed misleading comment about warnings being disabled. Warnings being disabled are not only from the "-W4" level since e4590dfd97ff. diffstat: src/os/win32/ngx_win32_config.h | 2 -- 1 files changed, 0 insertions(+), 2 deletions(-) diffs (12 lines): diff -r 0ce2d7a520be -r d1aa8cc3e387 src/os/win32/ngx_win32_config.h --- a/src/os/win32/ngx_win32_config.h Wed Sep 07 00:44:10 2022 +0300 +++ b/src/os/win32/ngx_win32_config.h Wed Sep 07 00:47:02 2022 +0300 @@ -80,8 +80,6 @@ typedef long time_t; #pragma warning(default:4201) -/* disable some "-W4" level warnings */ - /* 'type cast': from function pointer to data pointer */ #pragma warning(disable:4054) From pluknet at nginx.com Wed Sep 7 12:34:40 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Wed, 07 Sep 2022 12:34:40 +0000 Subject: [nginx] Win32: disabled C4306 warnings with MSVC. Message-ID: details: https://hg.nginx.org/nginx/rev/b347fe705ff2 branches: changeset: 8067:b347fe705ff2 user: Maxim Dounin date: Wed Sep 07 00:47:07 2022 +0300 description: Win32: disabled C4306 warnings with MSVC. Multiple C4306 warnings (conversion from 'type1' to 'type2' of greater size) appear during 64-bit compilation with MSVC 2010 (and older) due to extensively used constructs like "(void *) -1", so they were disabled. In newer MSVC versions C4306 warnings were replaced with C4312 ones, and these are not generated for such trivial type casts. diffstat: src/os/win32/ngx_win32_config.h | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diffs (13 lines): diff -r d1aa8cc3e387 -r b347fe705ff2 src/os/win32/ngx_win32_config.h --- a/src/os/win32/ngx_win32_config.h Wed Sep 07 00:47:02 2022 +0300 +++ b/src/os/win32/ngx_win32_config.h Wed Sep 07 00:47:07 2022 +0300 @@ -104,6 +104,9 @@ typedef long time_t; /* array is too small to include a terminating null character */ #pragma warning(disable:4295) +/* conversion from 'type1' to 'type2' of greater size */ +#pragma warning(disable:4306) + #endif From pluknet at nginx.com Wed Sep 7 12:34:46 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Wed, 07 Sep 2022 12:34:46 +0000 Subject: [nginx] Win32: disabled threads support in OpenSSL builds. Message-ID: details: https://hg.nginx.org/nginx/rev/a423e314c22f branches: changeset: 8069:a423e314c22f user: Maxim Dounin date: Wed Sep 07 00:47:31 2022 +0300 description: Win32: disabled threads support in OpenSSL builds. Threads are disabled during UNIX builds (see b329c0ab1a48), and also not needed for Windows builds. This used to be the default before OpenSSL 1.1.0. diffstat: auto/lib/openssl/makefile.msvc | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff -r 0546ab9351c8 -r a423e314c22f auto/lib/openssl/makefile.msvc --- a/auto/lib/openssl/makefile.msvc Wed Sep 07 00:47:17 2022 +0300 +++ b/auto/lib/openssl/makefile.msvc Wed Sep 07 00:47:31 2022 +0300 @@ -6,7 +6,7 @@ all: cd $(OPENSSL) - perl Configure VC-WIN32 no-shared \ + perl Configure VC-WIN32 no-shared no-threads \ --prefix="%cd%/openssl" \ --openssldir="%cd%/openssl/ssl" \ $(OPENSSL_OPT) From pluknet at nginx.com Wed Sep 7 12:34:43 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Wed, 07 Sep 2022 12:34:43 +0000 Subject: [nginx] Win32: fixed build on Windows with OpenSSL 3.0.x (ticket #2379). Message-ID: details: https://hg.nginx.org/nginx/rev/0546ab9351c8 branches: changeset: 8068:0546ab9351c8 user: Maxim Dounin date: Wed Sep 07 00:47:17 2022 +0300 description: Win32: fixed build on Windows with OpenSSL 3.0.x (ticket #2379). SSL_sendfile() expects integer file descriptor as an argument, but nginx uses OS file handles (HANDLE) to work with files on Windows, and passing HANDLE instead of an integer correctly results in build failure. Since SSL_sendfile() is not expected to work on Windows anyway, the code is now disabled on Windows with appropriate compile-time checks. diffstat: src/event/ngx_event_openssl.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diffs (30 lines): diff -r b347fe705ff2 -r 0546ab9351c8 src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Wed Sep 07 00:47:07 2022 +0300 +++ b/src/event/ngx_event_openssl.c Wed Sep 07 00:47:17 2022 +0300 @@ -1769,7 +1769,7 @@ ngx_ssl_handshake(ngx_connection_t *c) #endif #endif -#ifdef BIO_get_ktls_send +#if (defined BIO_get_ktls_send && !NGX_WIN32) if (BIO_get_ktls_send(SSL_get_wbio(c->ssl->connection)) == 1) { ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, @@ -1914,7 +1914,7 @@ ngx_ssl_try_early_data(ngx_connection_t c->read->ready = 1; c->write->ready = 1; -#ifdef BIO_get_ktls_send +#if (defined BIO_get_ktls_send && !NGX_WIN32) if (BIO_get_ktls_send(SSL_get_wbio(c->ssl->connection)) == 1) { ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, @@ -2943,7 +2943,7 @@ ngx_ssl_write_early(ngx_connection_t *c, static ssize_t ngx_ssl_sendfile(ngx_connection_t *c, ngx_buf_t *file, size_t size) { -#ifdef BIO_get_ktls_send +#if (defined BIO_get_ktls_send && !NGX_WIN32) int sslerr, flags; ssize_t n; From pluknet at nginx.com Wed Sep 7 15:33:23 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Wed, 7 Sep 2022 19:33:23 +0400 Subject: [PATCH 3 of 3] Win32: fixed build on Windows with OpenSSL 3.0.x (ticket #2379) In-Reply-To: References: <3d162f4c6f62b23d4906.1662050945@1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa> Message-ID: <361A1976-02B8-470D-8AC0-2211399ECE3F@nginx.com> > On 6 Sep 2022, at 07:49, Maxim Dounin wrote: > > Hello! > > On Mon, Sep 05, 2022 at 10:44:06PM +0400, Sergey Kandaurov wrote: > >> I see that it's convention in nginx to test external macros using #ifdef. >> In certain cases we use an exception there if it does or even does not >> make sense, such as when testing SSL_CTRL_SET_ECDH_AUTO (though that's >> rather a typo there). Using #if BIO_get_ktls_send looks reasonable to me. > > Sure, "#if SSL_CTRL_SET_ECDH_AUTO" looks like a typo (patch > below), but it used to work, since SSL_CTRL_SET_ECDH_AUTO is a > non-zero numeric constant when defined. This is not the case with > BIO_get_ktls_send. > > [..] > # HG changeset patch > # User Maxim Dounin > # Date 1662432499 -10800 > # Tue Sep 06 05:48:19 2022 +0300 > # Node ID de9fe82b0c7789474bb6284f13ebd3f903b129b0 > # Parent 9cf231508a8dbc2492e0d1cd06d7b1258eb5f435 > SSL: fixed incorrect usage of #if instead of #ifdef. > > In 2014ed60f17f, "#if SSL_CTRL_SET_ECDH_AUTO" test was incorrectly used > instead of "#ifdef SSL_CTRL_SET_ECDH_AUTO". There is no practical > difference, since SSL_CTRL_SET_ECDH_AUTO evaluates to a non-zero numeric > value when defined, but anyway it's better to correctly test if the value > is defined. > > diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c > --- a/src/event/ngx_event_openssl.c > +++ b/src/event/ngx_event_openssl.c > @@ -1426,7 +1426,7 @@ ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_s > > SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_ECDH_USE); > > -#if SSL_CTRL_SET_ECDH_AUTO > +#ifdef SSL_CTRL_SET_ECDH_AUTO > /* not needed in OpenSSL 1.1.0+ */ > SSL_CTX_set_ecdh_auto(ssl->ctx, 1); > #endif > Just noted that BoringSSL uses stubs, which makes GCC unhappy. // SSL_CTX_set_ecdh_auto returns one. #define SSL_CTX_set_ecdh_auto(ctx, onoff) 1 #define SSL_CTRL_SET_ECDH_AUTO doesnt_exist Patch to address this below: # HG changeset patch # User Sergey Kandaurov # Date 1662564729 -14400 # Wed Sep 07 19:32:09 2022 +0400 # Node ID e7997671018932c6cc97e1debf95b515e4d39e3c # Parent a423e314c22fe99fe9faf28f033c266426993105 SSL: silenced GCC warnings when building with BoringSSL. BoringSSL uses macro stub for SSL_CTX_set_ecdh_auto that expands to 1, which triggers -Wunused-value "statement with no effect" warnings. diff -r a423e314c22f -r e79976710189 src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Wed Sep 07 00:47:31 2022 +0300 +++ b/src/event/ngx_event_openssl.c Wed Sep 07 19:32:09 2022 +0400 @@ -1428,7 +1428,7 @@ ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_s #ifdef SSL_CTRL_SET_ECDH_AUTO /* not needed in OpenSSL 1.1.0+ */ - SSL_CTX_set_ecdh_auto(ssl->ctx, 1); + (void) SSL_CTX_set_ecdh_auto(ssl->ctx, 1); #endif if (ngx_strcmp(name->data, "auto") == 0) { -- Sergey Kandaurov From mdounin at mdounin.ru Wed Sep 7 21:13:10 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 8 Sep 2022 00:13:10 +0300 Subject: [PATCH 3 of 3] Win32: fixed build on Windows with OpenSSL 3.0.x (ticket #2379) In-Reply-To: <361A1976-02B8-470D-8AC0-2211399ECE3F@nginx.com> References: <3d162f4c6f62b23d4906.1662050945@1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa> <361A1976-02B8-470D-8AC0-2211399ECE3F@nginx.com> Message-ID: Hello! On Wed, Sep 07, 2022 at 07:33:23PM +0400, Sergey Kandaurov wrote: > > On 6 Sep 2022, at 07:49, Maxim Dounin wrote: > > > > On Mon, Sep 05, 2022 at 10:44:06PM +0400, Sergey Kandaurov wrote: > > > >> I see that it's convention in nginx to test external macros using #ifdef. > >> In certain cases we use an exception there if it does or even does not > >> make sense, such as when testing SSL_CTRL_SET_ECDH_AUTO (though that's > >> rather a typo there). Using #if BIO_get_ktls_send looks reasonable to me. > > > > Sure, "#if SSL_CTRL_SET_ECDH_AUTO" looks like a typo (patch > > below), but it used to work, since SSL_CTRL_SET_ECDH_AUTO is a > > non-zero numeric constant when defined. This is not the case with > > BIO_get_ktls_send. > > > > [..] > > # HG changeset patch > > # User Maxim Dounin > > # Date 1662432499 -10800 > > # Tue Sep 06 05:48:19 2022 +0300 > > # Node ID de9fe82b0c7789474bb6284f13ebd3f903b129b0 > > # Parent 9cf231508a8dbc2492e0d1cd06d7b1258eb5f435 > > SSL: fixed incorrect usage of #if instead of #ifdef. > > > > In 2014ed60f17f, "#if SSL_CTRL_SET_ECDH_AUTO" test was incorrectly used > > instead of "#ifdef SSL_CTRL_SET_ECDH_AUTO". There is no practical > > difference, since SSL_CTRL_SET_ECDH_AUTO evaluates to a non-zero numeric > > value when defined, but anyway it's better to correctly test if the value > > is defined. > > > > diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c > > --- a/src/event/ngx_event_openssl.c > > +++ b/src/event/ngx_event_openssl.c > > @@ -1426,7 +1426,7 @@ ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_s > > > > SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_ECDH_USE); > > > > -#if SSL_CTRL_SET_ECDH_AUTO > > +#ifdef SSL_CTRL_SET_ECDH_AUTO > > /* not needed in OpenSSL 1.1.0+ */ > > SSL_CTX_set_ecdh_auto(ssl->ctx, 1); > > #endif > > > > Just noted that BoringSSL uses stubs, which makes GCC unhappy. > > // SSL_CTX_set_ecdh_auto returns one. > #define SSL_CTX_set_ecdh_auto(ctx, onoff) 1 > #define SSL_CTRL_SET_ECDH_AUTO doesnt_exist > > Patch to address this below: > > # HG changeset patch > # User Sergey Kandaurov > # Date 1662564729 -14400 > # Wed Sep 07 19:32:09 2022 +0400 > # Node ID e7997671018932c6cc97e1debf95b515e4d39e3c > # Parent a423e314c22fe99fe9faf28f033c266426993105 > SSL: silenced GCC warnings when building with BoringSSL. > > BoringSSL uses macro stub for SSL_CTX_set_ecdh_auto that expands to 1, > which triggers -Wunused-value "statement with no effect" warnings. > > diff -r a423e314c22f -r e79976710189 src/event/ngx_event_openssl.c > --- a/src/event/ngx_event_openssl.c Wed Sep 07 00:47:31 2022 +0300 > +++ b/src/event/ngx_event_openssl.c Wed Sep 07 19:32:09 2022 +0400 > @@ -1428,7 +1428,7 @@ ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_s > > #ifdef SSL_CTRL_SET_ECDH_AUTO > /* not needed in OpenSSL 1.1.0+ */ > - SSL_CTX_set_ecdh_auto(ssl->ctx, 1); > + (void) SSL_CTX_set_ecdh_auto(ssl->ctx, 1); > #endif > > if (ngx_strcmp(name->data, "auto") == 0) { Looks good. -- Maxim Dounin http://mdounin.ru/ From arut at nginx.com Thu Sep 8 09:06:27 2022 From: arut at nginx.com (=?iso-8859-1?q?Roman_Arutyunyan?=) Date: Thu, 08 Sep 2022 13:06:27 +0400 Subject: [PATCH 00 of 10] QUIC connection reuse In-Reply-To: References: Message-ID: - fixed logging in patch #1 - reworked patch #3: managing c->reusable state moved to transport layer - minor changes and rebases in further patches From arut at nginx.com Thu Sep 8 09:06:28 2022 From: arut at nginx.com (=?iso-8859-1?q?Roman_Arutyunyan?=) Date: Thu, 08 Sep 2022 13:06:28 +0400 Subject: [PATCH 01 of 10] QUIC: treat qc->error == -1 as a missing error In-Reply-To: References: Message-ID: <48645c5397a3f178286b.1662627988@arut-laptop> # HG changeset patch # User Roman Arutyunyan # Date 1662539835 -14400 # Wed Sep 07 12:37:15 2022 +0400 # Branch quic # Node ID 48645c5397a3f178286bcd108748c140adccc8a5 # Parent f9d7930d0eedae28defd0803cb95dc8ab68e56b3 QUIC: treat qc->error == -1 as a missing error. Previously, zero was used for this purpose. However, NGX_QUIC_ERR_NO_ERROR is zero too. As a result, NGX_QUIC_ERR_NO_ERROR was changed to NGX_QUIC_ERR_INTERNAL_ERROR when closing a QUIC connection. diff --git a/src/event/quic/ngx_event_quic.c b/src/event/quic/ngx_event_quic.c --- a/src/event/quic/ngx_event_quic.c +++ b/src/event/quic/ngx_event_quic.c @@ -73,7 +73,7 @@ ngx_quic_connstate_dbg(ngx_connection_t if (qc) { - if (qc->error) { + if (qc->error != (ngx_uint_t) -1) { p = ngx_slprintf(p, last, "%s", qc->error_app ? " app" : ""); p = ngx_slprintf(p, last, " error:%ui", qc->error); @@ -523,7 +523,7 @@ ngx_quic_close_connection(ngx_connection qc->error = NGX_QUIC_ERR_NO_ERROR; } else { - if (qc->error == 0 && !qc->error_app) { + if (qc->error == (ngx_uint_t) -1 && !qc->error_app) { qc->error = NGX_QUIC_ERR_INTERNAL_ERROR; } @@ -939,7 +939,7 @@ ngx_quic_handle_payload(ngx_connection_t qc = ngx_quic_get_connection(c); - qc->error = 0; + qc->error = (ngx_uint_t) -1; qc->error_reason = 0; c->log->action = "decrypting packet"; From arut at nginx.com Thu Sep 8 09:06:29 2022 From: arut at nginx.com (=?iso-8859-1?q?Roman_Arutyunyan?=) Date: Thu, 08 Sep 2022 13:06:29 +0400 Subject: [PATCH 02 of 10] QUIC: made ngx_quic_finalize_connecion() more graceful In-Reply-To: References: Message-ID: # HG changeset patch # User Roman Arutyunyan # Date 1661167731 -14400 # Mon Aug 22 15:28:51 2022 +0400 # Branch quic # Node ID a3be42d42e7e6e6a941adb458cab201ff2cce8ce # Parent 48645c5397a3f178286bcd108748c140adccc8a5 QUIC: made ngx_quic_finalize_connecion() more graceful. Previously, ngx_quic_finalize_connection() closed the connection with NGX_ERROR code, which resulted in immediate connection closure. Now the code is NGX_OK, which provides a more graceful shutdown with a timeout. diff --git a/src/event/quic/ngx_event_quic.c b/src/event/quic/ngx_event_quic.c --- a/src/event/quic/ngx_event_quic.c +++ b/src/event/quic/ngx_event_quic.c @@ -414,6 +414,7 @@ ngx_quic_input_handler(ngx_event_t *rev) } if (c->close) { + qc->error = NGX_QUIC_ERR_NO_ERROR; qc->error_reason = "graceful shutdown"; ngx_quic_close_connection(c, NGX_OK); return; @@ -506,31 +507,26 @@ ngx_quic_close_connection(ngx_connection qc->error_level = c->ssl ? SSL_quic_read_level(c->ssl->connection) : ssl_encryption_initial; + if (qc->error == (ngx_uint_t) -1) { + qc->error = NGX_QUIC_ERR_INTERNAL_ERROR; + qc->error_app = 0; + } + + ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic close immediate term:%d drain:%d " + "%serror:%ui \"%s\"", + rc == NGX_ERROR ? 1 : 0, qc->draining, + qc->error_app ? "app " : "", qc->error, + qc->error_reason ? qc->error_reason : ""); + if (rc == NGX_OK) { - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic close immediate drain:%d", - qc->draining); - qc->close.log = c->log; qc->close.data = c; qc->close.handler = ngx_quic_close_timer_handler; qc->close.cancelable = 1; ctx = ngx_quic_get_send_ctx(qc, qc->error_level); - ngx_add_timer(&qc->close, 3 * ngx_quic_pto(c, ctx)); - - qc->error = NGX_QUIC_ERR_NO_ERROR; - - } else { - if (qc->error == (ngx_uint_t) -1 && !qc->error_app) { - qc->error = NGX_QUIC_ERR_INTERNAL_ERROR; - } - - ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic close immediate due to %serror: %ui %s", - qc->error_app ? "app " : "", qc->error, - qc->error_reason ? qc->error_reason : ""); } (void) ngx_quic_send_cc(c); @@ -617,7 +613,7 @@ ngx_quic_finalize_connection(ngx_connect qc->error_app = 1; qc->error_ftype = 0; - ngx_quic_close_connection(c, NGX_ERROR); + ngx_quic_close_connection(c, NGX_OK); } From arut at nginx.com Thu Sep 8 09:06:30 2022 From: arut at nginx.com (=?iso-8859-1?q?Roman_Arutyunyan?=) Date: Thu, 08 Sep 2022 13:06:30 +0400 Subject: [PATCH 03 of 10] QUIC: post close event for connection close In-Reply-To: References: Message-ID: # HG changeset patch # User Roman Arutyunyan # Date 1662564313 -14400 # Wed Sep 07 19:25:13 2022 +0400 # Branch quic # Node ID b4662fc66f1e8388f54d988777b741964892cec2 # Parent a3be42d42e7e6e6a941adb458cab201ff2cce8ce QUIC: post close event for connection close. Previously, close event was used only for close timeout, while read event was used for posting connection close. diff --git a/src/event/quic/ngx_event_quic.c b/src/event/quic/ngx_event_quic.c --- a/src/event/quic/ngx_event_quic.c +++ b/src/event/quic/ngx_event_quic.c @@ -15,8 +15,7 @@ static ngx_quic_connection_t *ngx_quic_n static ngx_int_t ngx_quic_handle_stateless_reset(ngx_connection_t *c, ngx_quic_header_t *pkt); static void ngx_quic_input_handler(ngx_event_t *rev); - -static void ngx_quic_close_timer_handler(ngx_event_t *ev); +static void ngx_quic_close_handler(ngx_event_t *ev); static ngx_int_t ngx_quic_handle_datagram(ngx_connection_t *c, ngx_buf_t *b, ngx_quic_conf_t *conf); @@ -283,6 +282,11 @@ ngx_quic_new_connection(ngx_connection_t qc->push.handler = ngx_quic_push_handler; qc->push.cancelable = 1; + qc->close.log = c->log; + qc->close.data = c; + qc->close.handler = ngx_quic_close_handler; + qc->close.cancelable = 1; + qc->path_validation.log = c->log; qc->path_validation.data = c; qc->path_validation.handler = ngx_quic_path_validation_handler; @@ -420,19 +424,11 @@ ngx_quic_input_handler(ngx_event_t *rev) return; } - if (!rev->ready) { - if (qc->closing) { - ngx_quic_close_connection(c, NGX_OK); - - } else if (qc->shutdown) { - ngx_quic_shutdown_quic(c); - } - + b = c->udp->buffer; + if (b == NULL) { return; } - b = c->udp->buffer; - rc = ngx_quic_handle_datagram(c, b, NULL); if (rc == NGX_ERROR) { @@ -520,11 +516,6 @@ ngx_quic_close_connection(ngx_connection qc->error_reason ? qc->error_reason : ""); if (rc == NGX_OK) { - qc->close.log = c->log; - qc->close.data = c; - qc->close.handler = ngx_quic_close_timer_handler; - qc->close.cancelable = 1; - ctx = ngx_quic_get_send_ctx(qc, qc->error_level); ngx_add_timer(&qc->close, 3 * ngx_quic_pto(c, ctx)); } @@ -570,6 +561,10 @@ ngx_quic_close_connection(ngx_connection return; } + if (qc->close.posted) { + ngx_delete_posted_event(&qc->close); + } + ngx_quic_close_sockets(c); ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic close completed"); @@ -633,14 +628,22 @@ ngx_quic_shutdown_connection(ngx_connect static void -ngx_quic_close_timer_handler(ngx_event_t *ev) +ngx_quic_close_handler(ngx_event_t *ev) { - ngx_connection_t *c; + ngx_connection_t *c; + ngx_quic_connection_t *qc; - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "quic close timer"); + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "quic close handler"); c = ev->data; - ngx_quic_close_connection(c, NGX_DONE); + qc = ngx_quic_get_connection(c); + + if (qc->closing) { + ngx_quic_close_connection(c, NGX_OK); + + } else if (qc->shutdown) { + ngx_quic_shutdown_quic(c); + } } diff --git a/src/event/quic/ngx_event_quic_streams.c b/src/event/quic/ngx_event_quic_streams.c --- a/src/event/quic/ngx_event_quic_streams.c +++ b/src/event/quic/ngx_event_quic_streams.c @@ -1031,7 +1031,7 @@ ngx_quic_close_stream(ngx_quic_stream_t if (qc->closing) { /* schedule handler call to continue ngx_quic_close_connection() */ - ngx_post_event(pc->read, &ngx_posted_events); + ngx_post_event(&qc->close, &ngx_posted_events); return NGX_OK; } @@ -1057,7 +1057,7 @@ ngx_quic_close_stream(ngx_quic_stream_t } if (qc->shutdown) { - ngx_post_event(pc->read, &ngx_posted_events); + ngx_post_event(&qc->close, &ngx_posted_events); } return NGX_OK; From arut at nginx.com Thu Sep 8 09:06:31 2022 From: arut at nginx.com (=?iso-8859-1?q?Roman_Arutyunyan?=) Date: Thu, 08 Sep 2022 13:06:31 +0400 Subject: [PATCH 04 of 10] QUIC: reusable mode for main connection In-Reply-To: References: Message-ID: <0a5de8e68cb8b238a1fb.1662627991@arut-laptop> # HG changeset patch # User Roman Arutyunyan # Date 1662478181 -14400 # Tue Sep 06 19:29:41 2022 +0400 # Branch quic # Node ID 0a5de8e68cb8b238a1fb82da93ce583c0fa1a6a1 # Parent b4662fc66f1e8388f54d988777b741964892cec2 QUIC: reusable mode for main connection. The connection is automatically switched to this mode by transport layer when there are no non-cancelable streams. Currently, cancelable streams are HTTP/3 encoder/decoder/control streams. diff --git a/src/event/quic/ngx_event_quic.c b/src/event/quic/ngx_event_quic.c --- a/src/event/quic/ngx_event_quic.c +++ b/src/event/quic/ngx_event_quic.c @@ -341,6 +341,8 @@ ngx_quic_new_connection(ngx_connection_t return NULL; } + ngx_reusable_connection(c, 1); + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic connection created"); @@ -420,7 +422,7 @@ ngx_quic_input_handler(ngx_event_t *rev) if (c->close) { qc->error = NGX_QUIC_ERR_NO_ERROR; qc->error_reason = "graceful shutdown"; - ngx_quic_close_connection(c, NGX_OK); + ngx_quic_close_connection(c, NGX_ERROR); return; } @@ -603,12 +605,17 @@ ngx_quic_finalize_connection(ngx_connect ngx_quic_connection_t *qc; qc = ngx_quic_get_connection(c); + + if (qc->closing) { + return; + } + qc->error = err; qc->error_reason = reason; qc->error_app = 1; qc->error_ftype = 0; - ngx_quic_close_connection(c, NGX_OK); + ngx_post_event(&qc->close, &ngx_posted_events); } @@ -630,20 +637,13 @@ ngx_quic_shutdown_connection(ngx_connect static void ngx_quic_close_handler(ngx_event_t *ev) { - ngx_connection_t *c; - ngx_quic_connection_t *qc; + ngx_connection_t *c; ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "quic close handler"); c = ev->data; - qc = ngx_quic_get_connection(c); - if (qc->closing) { - ngx_quic_close_connection(c, NGX_OK); - - } else if (qc->shutdown) { - ngx_quic_shutdown_quic(c); - } + ngx_quic_close_connection(c, NGX_OK); } @@ -1428,31 +1428,10 @@ ngx_quic_push_handler(ngx_event_t *ev) void ngx_quic_shutdown_quic(ngx_connection_t *c) { - ngx_rbtree_t *tree; - ngx_rbtree_node_t *node; - ngx_quic_stream_t *qs; ngx_quic_connection_t *qc; - qc = ngx_quic_get_connection(c); - - if (qc->closing) { - return; + if (c->reusable) { + qc = ngx_quic_get_connection(c); + ngx_quic_finalize_connection(c, qc->shutdown_code, qc->shutdown_reason); } - - tree = &qc->streams.tree; - - if (tree->root != tree->sentinel) { - for (node = ngx_rbtree_min(tree->root, tree->sentinel); - node; - node = ngx_rbtree_next(tree, node)) - { - qs = (ngx_quic_stream_t *) node; - - if (!qs->cancelable) { - return; - } - } - } - - ngx_quic_finalize_connection(c, qc->shutdown_code, qc->shutdown_reason); } diff --git a/src/event/quic/ngx_event_quic.h b/src/event/quic/ngx_event_quic.h --- a/src/event/quic/ngx_event_quic.h +++ b/src/event/quic/ngx_event_quic.h @@ -113,6 +113,7 @@ void ngx_quic_shutdown_connection(ngx_co const char *reason); ngx_int_t ngx_quic_reset_stream(ngx_connection_t *c, ngx_uint_t err); ngx_int_t ngx_quic_shutdown_stream(ngx_connection_t *c, int how); +void ngx_quic_cancelable_stream(ngx_connection_t *c); ngx_int_t ngx_quic_handle_read_event(ngx_event_t *rev, ngx_uint_t flags); ngx_int_t ngx_quic_handle_write_event(ngx_event_t *wev, size_t lowat); ngx_int_t ngx_quic_get_packet_dcid(ngx_log_t *log, u_char *data, size_t len, diff --git a/src/event/quic/ngx_event_quic_streams.c b/src/event/quic/ngx_event_quic_streams.c --- a/src/event/quic/ngx_event_quic_streams.c +++ b/src/event/quic/ngx_event_quic_streams.c @@ -33,6 +33,7 @@ static ngx_chain_t *ngx_quic_stream_send static ngx_int_t ngx_quic_stream_flush(ngx_quic_stream_t *qs); static void ngx_quic_stream_cleanup_handler(void *data); static ngx_int_t ngx_quic_close_stream(ngx_quic_stream_t *qs); +static ngx_int_t ngx_quic_can_shutdown(ngx_connection_t *c); static ngx_int_t ngx_quic_control_flow(ngx_quic_stream_t *qs, uint64_t last); static ngx_int_t ngx_quic_update_flow(ngx_quic_stream_t *qs, uint64_t last); static ngx_int_t ngx_quic_update_max_stream_data(ngx_quic_stream_t *qs); @@ -51,6 +52,10 @@ ngx_quic_open_stream(ngx_connection_t *c pc = c->quic ? c->quic->parent : c; qc = ngx_quic_get_connection(pc); + if (qc->shutdown || qc->closing) { + return NULL; + } + if (bidi) { if (qc->streams.server_streams_bidi >= qc->streams.server_max_streams_bidi) @@ -161,13 +166,10 @@ ngx_quic_close_streams(ngx_connection_t ngx_pool_t *pool; ngx_queue_t *q; ngx_rbtree_t *tree; + ngx_connection_t *sc; ngx_rbtree_node_t *node; ngx_quic_stream_t *qs; -#if (NGX_DEBUG) - ngx_uint_t ns; -#endif - while (!ngx_queue_empty(&qc->streams.uninitialized)) { q = ngx_queue_head(&qc->streams.uninitialized); ngx_queue_remove(q); @@ -185,34 +187,34 @@ ngx_quic_close_streams(ngx_connection_t return NGX_OK; } -#if (NGX_DEBUG) - ns = 0; -#endif - node = ngx_rbtree_min(tree->root, tree->sentinel); while (node) { qs = (ngx_quic_stream_t *) node; node = ngx_rbtree_next(tree, node); + sc = qs->connection; qs->recv_state = NGX_QUIC_STREAM_RECV_RESET_RECVD; qs->send_state = NGX_QUIC_STREAM_SEND_RESET_SENT; - if (qs->connection == NULL) { + if (sc == NULL) { ngx_quic_close_stream(qs); continue; } - ngx_quic_set_event(qs->connection->read); - ngx_quic_set_event(qs->connection->write); + ngx_quic_set_event(sc->read); + ngx_quic_set_event(sc->write); -#if (NGX_DEBUG) - ns++; -#endif + sc->close = 1; + sc->read->handler(sc->read); } - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic connection has %ui active streams", ns); + if (tree->root == tree->sentinel) { + return NGX_OK; + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic connection has active streams"); return NGX_AGAIN; } @@ -587,6 +589,7 @@ ngx_quic_create_stream(ngx_connection_t { ngx_log_t *log; ngx_pool_t *pool; + ngx_uint_t reusable; ngx_queue_t *q; ngx_connection_t *sc; ngx_quic_stream_t *qs; @@ -639,10 +642,14 @@ ngx_quic_create_stream(ngx_connection_t *log = *c->log; pool->log = log; + reusable = c->reusable; + ngx_reusable_connection(c, 0); + sc = ngx_get_connection(c->fd, log); if (sc == NULL) { ngx_destroy_pool(pool); ngx_queue_insert_tail(&qc->streams.free, &qs->queue); + ngx_reusable_connection(c, reusable); return NULL; } @@ -712,6 +719,7 @@ ngx_quic_create_stream(ngx_connection_t ngx_close_connection(sc); ngx_destroy_pool(pool); ngx_queue_insert_tail(&qc->streams.free, &qs->queue); + ngx_reusable_connection(c, reusable); return NULL; } @@ -724,6 +732,31 @@ ngx_quic_create_stream(ngx_connection_t } +void +ngx_quic_cancelable_stream(ngx_connection_t *c) +{ + ngx_connection_t *pc; + ngx_quic_stream_t *qs; + ngx_quic_connection_t *qc; + + qs = c->quic; + pc = qs->parent; + qc = ngx_quic_get_connection(pc); + + if (!qs->cancelable) { + qs->cancelable = 1; + + if (ngx_quic_can_shutdown(pc) == NGX_OK) { + ngx_reusable_connection(pc, 1); + + if (qc->shutdown) { + ngx_quic_shutdown_quic(pc); + } + } + } +} + + static void ngx_quic_empty_handler(ngx_event_t *ev) { @@ -1056,14 +1089,47 @@ ngx_quic_close_stream(ngx_quic_stream_t ngx_quic_queue_frame(qc, frame); } + if (!pc->reusable && ngx_quic_can_shutdown(pc) == NGX_OK) { + ngx_reusable_connection(pc, 1); + } + if (qc->shutdown) { - ngx_post_event(&qc->close, &ngx_posted_events); + ngx_quic_shutdown_quic(pc); } return NGX_OK; } +static ngx_int_t +ngx_quic_can_shutdown(ngx_connection_t *c) +{ + ngx_rbtree_t *tree; + ngx_rbtree_node_t *node; + ngx_quic_stream_t *qs; + ngx_quic_connection_t *qc; + + qc = ngx_quic_get_connection(c); + + tree = &qc->streams.tree; + + if (tree->root != tree->sentinel) { + for (node = ngx_rbtree_min(tree->root, tree->sentinel); + node; + node = ngx_rbtree_next(tree, node)) + { + qs = (ngx_quic_stream_t *) node; + + if (!qs->cancelable) { + return NGX_DECLINED; + } + } + } + + return NGX_OK; +} + + ngx_int_t ngx_quic_handle_stream_frame(ngx_connection_t *c, ngx_quic_header_t *pkt, ngx_quic_frame_t *frame) diff --git a/src/http/v3/ngx_http_v3_uni.c b/src/http/v3/ngx_http_v3_uni.c --- a/src/http/v3/ngx_http_v3_uni.c +++ b/src/http/v3/ngx_http_v3_uni.c @@ -52,7 +52,7 @@ ngx_http_v3_init_uni_stream(ngx_connecti return; } - c->quic->cancelable = 1; + ngx_quic_cancelable_stream(c); us = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_uni_stream_t)); if (us == NULL) { @@ -182,6 +182,11 @@ ngx_http_v3_uni_read_handler(ngx_event_t ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read handler"); + if (c->close) { + ngx_http_v3_close_uni_stream(c); + return; + } + ngx_memzero(&b, sizeof(ngx_buf_t)); while (rev->ready) { @@ -262,6 +267,11 @@ ngx_http_v3_uni_dummy_read_handler(ngx_e ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 dummy read handler"); + if (c->close) { + ngx_http_v3_close_uni_stream(c); + return; + } + if (rev->ready) { if (c->recv(c, &ch, 1) != 0) { ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, NULL); @@ -404,7 +414,7 @@ ngx_http_v3_get_uni_stream(ngx_connectio goto failed; } - sc->quic->cancelable = 1; + ngx_quic_cancelable_stream(sc); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 create uni stream, type:%ui", type); From arut at nginx.com Thu Sep 8 09:06:32 2022 From: arut at nginx.com (=?iso-8859-1?q?Roman_Arutyunyan?=) Date: Thu, 08 Sep 2022 13:06:32 +0400 Subject: [PATCH 05 of 10] QUIC: defer stream removal until all its data is acked In-Reply-To: References: Message-ID: # HG changeset patch # User Roman Arutyunyan # Date 1661168003 -14400 # Mon Aug 22 15:33:23 2022 +0400 # Branch quic # Node ID b83d00cbc660b426e4f43ac698345ebbd13215e1 # Parent 0a5de8e68cb8b238a1fb82da93ce583c0fa1a6a1 QUIC: defer stream removal until all its data is acked. Previously, stream was kept alive until all its data is sent. This resulted in disabling retransmission of final part of stream when QUIC connection was closed right after closing stream connection. diff --git a/src/event/quic/ngx_event_quic.h b/src/event/quic/ngx_event_quic.h --- a/src/event/quic/ngx_event_quic.h +++ b/src/event/quic/ngx_event_quic.h @@ -85,6 +85,7 @@ struct ngx_quic_stream_s { ngx_connection_t *parent; ngx_connection_t *connection; uint64_t id; + uint64_t sent; uint64_t acked; uint64_t send_max_data; uint64_t send_offset; @@ -98,7 +99,8 @@ struct ngx_quic_stream_s { ngx_quic_buffer_t recv; ngx_quic_stream_send_state_e send_state; ngx_quic_stream_recv_state_e recv_state; - ngx_uint_t cancelable; /* unsigned cancelable:1; */ + unsigned cancelable:1; + unsigned fin_acked:1; }; diff --git a/src/event/quic/ngx_event_quic_ack.c b/src/event/quic/ngx_event_quic_ack.c --- a/src/event/quic/ngx_event_quic_ack.c +++ b/src/event/quic/ngx_event_quic_ack.c @@ -253,6 +253,7 @@ ngx_quic_handle_ack_frame_range(ngx_conn break; case NGX_QUIC_FT_STREAM: + case NGX_QUIC_FT_RESET_STREAM: ngx_quic_handle_stream_ack(c, f); break; } diff --git a/src/event/quic/ngx_event_quic_streams.c b/src/event/quic/ngx_event_quic_streams.c --- a/src/event/quic/ngx_event_quic_streams.c +++ b/src/event/quic/ngx_event_quic_streams.c @@ -887,7 +887,7 @@ ngx_quic_stream_send_chain(ngx_connectio qs->send_state = NGX_QUIC_STREAM_SEND_SEND; - flow = qs->acked + qc->conf->stream_buffer_size - c->sent; + flow = qs->acked + qc->conf->stream_buffer_size - qs->sent; if (flow == 0) { wev->ready = 0; @@ -900,13 +900,14 @@ ngx_quic_stream_send_chain(ngx_connectio n = qs->send.size; - in = ngx_quic_write_buffer(pc, &qs->send, in, limit, c->sent); + in = ngx_quic_write_buffer(pc, &qs->send, in, limit, qs->sent); if (in == NGX_CHAIN_ERROR) { return NGX_CHAIN_ERROR; } n = qs->send.size - n; c->sent += n; + qs->sent += n; qc->streams.sent += n; if (flow == n) { @@ -1045,9 +1046,12 @@ ngx_quic_close_stream(ngx_quic_stream_t if (!qc->closing) { /* make sure everything is sent and final size is received */ - if (qs->recv_state == NGX_QUIC_STREAM_RECV_RECV - || qs->send_state == NGX_QUIC_STREAM_SEND_READY - || qs->send_state == NGX_QUIC_STREAM_SEND_SEND) + if (qs->recv_state == NGX_QUIC_STREAM_RECV_RECV) { + return NGX_OK; + } + + if (qs->send_state != NGX_QUIC_STREAM_SEND_DATA_RECVD + && qs->send_state != NGX_QUIC_STREAM_SEND_RESET_RECVD) { return NGX_OK; } @@ -1488,36 +1492,70 @@ ngx_quic_handle_max_streams_frame(ngx_co void ngx_quic_handle_stream_ack(ngx_connection_t *c, ngx_quic_frame_t *f) { - uint64_t sent, unacked; + uint64_t acked; ngx_quic_stream_t *qs; ngx_quic_connection_t *qc; qc = ngx_quic_get_connection(c); - qs = ngx_quic_find_stream(&qc->streams.tree, f->u.stream.stream_id); - if (qs == NULL) { + switch (f->type) { + + case NGX_QUIC_FT_RESET_STREAM: + + qs = ngx_quic_find_stream(&qc->streams.tree, f->u.reset_stream.id); + if (qs == NULL) { + return; + } + + qs->send_state = NGX_QUIC_STREAM_SEND_RESET_RECVD; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic stream id:0x%xL ack reset final_size:%uL", + qs->id, f->u.reset_stream.final_size); + + break; + + case NGX_QUIC_FT_STREAM: + + qs = ngx_quic_find_stream(&qc->streams.tree, f->u.stream.stream_id); + if (qs == NULL) { + return; + } + + acked = qs->acked; + qs->acked += f->u.stream.length; + + if (f->u.stream.fin) { + qs->fin_acked = 1; + } + + if (qs->send_state == NGX_QUIC_STREAM_SEND_DATA_SENT + && qs->acked == qs->sent && qs->fin_acked) + { + qs->send_state = NGX_QUIC_STREAM_SEND_DATA_RECVD; + } + + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic stream id:0x%xL ack len:%uL fin:%d unacked:%uL", + qs->id, f->u.stream.length, f->u.stream.fin, + qs->sent - qs->acked); + + if (qs->connection + && qs->sent - acked == qc->conf->stream_buffer_size + && f->u.stream.length > 0) + { + ngx_quic_set_event(qs->connection->write); + } + + break; + + default: return; } if (qs->connection == NULL) { - qs->acked += f->u.stream.length; - return; + ngx_quic_close_stream(qs); } - - sent = qs->connection->sent; - unacked = sent - qs->acked; - qs->acked += f->u.stream.length; - - ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic stream id:0x%xL ack len:%uL acked:%uL unacked:%uL", - qs->id, f->u.stream.length, qs->acked, sent - qs->acked); - - if (unacked != qc->conf->stream_buffer_size) { - /* not blocked on buffer size */ - return; - } - - ngx_quic_set_event(qs->connection->write); } From arut at nginx.com Thu Sep 8 09:06:33 2022 From: arut at nginx.com (=?iso-8859-1?q?Roman_Arutyunyan?=) Date: Thu, 08 Sep 2022 13:06:33 +0400 Subject: [PATCH 06 of 10] QUIC: do not send MAX_STREAMS in shutdown state In-Reply-To: References: Message-ID: <28437cb91bd8624b30a4.1662627993@arut-laptop> # HG changeset patch # User Roman Arutyunyan # Date 1662541976 -14400 # Wed Sep 07 13:12:56 2022 +0400 # Branch quic # Node ID 28437cb91bd8624b30a4c841852504110b0f3f7d # Parent b83d00cbc660b426e4f43ac698345ebbd13215e1 QUIC: do not send MAX_STREAMS in shutdown state. No more streams are expected from client. diff --git a/src/event/quic/ngx_event_quic_streams.c b/src/event/quic/ngx_event_quic_streams.c --- a/src/event/quic/ngx_event_quic_streams.c +++ b/src/event/quic/ngx_event_quic_streams.c @@ -1072,6 +1072,15 @@ ngx_quic_close_stream(ngx_quic_stream_t return NGX_OK; } + if (!pc->reusable && ngx_quic_can_shutdown(pc) == NGX_OK) { + ngx_reusable_connection(pc, 1); + } + + if (qc->shutdown) { + ngx_quic_shutdown_quic(pc); + return NGX_OK; + } + if ((qs->id & NGX_QUIC_STREAM_SERVER_INITIATED) == 0) { frame = ngx_quic_alloc_frame(pc); if (frame == NULL) { @@ -1093,14 +1102,6 @@ ngx_quic_close_stream(ngx_quic_stream_t ngx_quic_queue_frame(qc, frame); } - if (!pc->reusable && ngx_quic_can_shutdown(pc) == NGX_OK) { - ngx_reusable_connection(pc, 1); - } - - if (qc->shutdown) { - ngx_quic_shutdown_quic(pc); - } - return NGX_OK; } From arut at nginx.com Thu Sep 8 09:06:34 2022 From: arut at nginx.com (=?iso-8859-1?q?Roman_Arutyunyan?=) Date: Thu, 08 Sep 2022 13:06:34 +0400 Subject: [PATCH 07 of 10] HTTP/3: unified hq code with regular HTTP/3 code In-Reply-To: References: Message-ID: <1dd6fabfdcb5b52af495.1662627994@arut-laptop> # HG changeset patch # User Roman Arutyunyan # Date 1662626426 -14400 # Thu Sep 08 12:40:26 2022 +0400 # Branch quic # Node ID 1dd6fabfdcb5b52af495f9d8fc00f64ae36a537c # Parent 28437cb91bd8624b30a4c841852504110b0f3f7d HTTP/3: unified hq code with regular HTTP/3 code. The change removes hq-specific request handler. Now hq requests are handled by the HTTP/3 request handler. This brings missing keepalive timeout feature to hq. diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c --- a/src/http/v3/ngx_http_v3.c +++ b/src/http/v3/ngx_http_v3.c @@ -17,10 +17,13 @@ static void ngx_http_v3_cleanup_session( ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c) { - ngx_connection_t *pc; - ngx_pool_cleanup_t *cln; - ngx_http_connection_t *hc; - ngx_http_v3_session_t *h3c; + ngx_connection_t *pc; + ngx_pool_cleanup_t *cln; + ngx_http_connection_t *hc; + ngx_http_v3_session_t *h3c; +#if (NGX_HTTP_V3_HQ) + ngx_http_v3_srv_conf_t *h3scf; +#endif pc = c->quic->parent; hc = pc->data; @@ -39,6 +42,13 @@ ngx_http_v3_init_session(ngx_connection_ h3c->max_push_id = (uint64_t) -1; h3c->goaway_push_id = (uint64_t) -1; +#if (NGX_HTTP_V3_HQ) + h3scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module); + if (h3scf->hq) { + h3c->hq = 1; + } +#endif + ngx_queue_init(&h3c->blocked); ngx_queue_init(&h3c->pushing); @@ -61,6 +71,12 @@ ngx_http_v3_init_session(ngx_connection_ hc->v3_session = h3c; +#if (NGX_HTTP_V3_HQ) + if (h3c->hq) { + return NGX_OK; + } +#endif + return ngx_http_v3_send_settings(c); failed: diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -145,7 +145,10 @@ struct ngx_http_v3_session_s { off_t total_bytes; off_t payload_bytes; - ngx_uint_t goaway; /* unsigned goaway:1; */ + unsigned goaway:1; +#if (NGX_HTTP_V3_HQ) + unsigned hq:1; +#endif ngx_connection_t *known_streams[NGX_HTTP_V3_MAX_KNOWN_STREAM]; }; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -10,9 +10,6 @@ #include -#if (NGX_HTTP_V3_HQ) -static void ngx_http_v3_init_hq_stream(ngx_connection_t *c); -#endif static void ngx_http_v3_init_request_stream(ngx_connection_t *c); static void ngx_http_v3_wait_request_handler(ngx_event_t *rev); static void ngx_http_v3_cleanup_request(void *data); @@ -86,13 +83,6 @@ ngx_http_v3_init(ngx_connection_t *c) ngx_set_connection_log(c, clcf->error_log); } -#if (NGX_HTTP_V3_HQ) - if (h3scf->hq) { - ngx_http_v3_init_hq_stream(c); - return; - } -#endif - if (ngx_http_v3_init_session(c) != NGX_OK) { ngx_http_close_connection(c); return; @@ -107,83 +97,12 @@ ngx_http_v3_init(ngx_connection_t *c) } -#if (NGX_HTTP_V3_HQ) - -static void -ngx_http_v3_init_hq_stream(ngx_connection_t *c) -{ - uint64_t n; - ngx_event_t *rev; - ngx_http_connection_t *hc; - ngx_http_core_loc_conf_t *clcf; - ngx_http_core_srv_conf_t *cscf; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init hq stream"); - -#if (NGX_STAT_STUB) - (void) ngx_atomic_fetch_add(ngx_stat_active, 1); -#endif - - hc = c->data; - - /* Use HTTP/3 General Protocol Error Code 0x101 for finalization */ - - if (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { - ngx_quic_finalize_connection(c->quic->parent, - NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR, - "unexpected uni stream"); - ngx_http_close_connection(c); - return; - } - - clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module); - - n = c->quic->id >> 2; - - if (n >= clcf->keepalive_requests) { - ngx_quic_finalize_connection(c->quic->parent, - NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR, - "reached maximum number of requests"); - ngx_http_close_connection(c); - return; - } - - if (ngx_current_msec - c->quic->parent->start_time - > clcf->keepalive_time) - { - ngx_quic_finalize_connection(c->quic->parent, - NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR, - "reached maximum time for requests"); - ngx_http_close_connection(c); - return; - } - - rev = c->read; - - if (rev->ready) { - rev->handler(rev); - return; - } - - cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); - - ngx_add_timer(rev, cscf->client_header_timeout); - ngx_reusable_connection(c, 1); - - if (ngx_handle_read_event(rev, 0) != NGX_OK) { - ngx_http_close_connection(c); - return; - } -} - -#endif - - static void ngx_http_v3_init_request_stream(ngx_connection_t *c) { uint64_t n; ngx_event_t *rev; + ngx_connection_t *pc; ngx_http_connection_t *hc; ngx_http_v3_session_t *h3c; ngx_http_core_loc_conf_t *clcf; @@ -216,15 +135,21 @@ ngx_http_v3_init_request_stream(ngx_conn return; } + pc = c->quic->parent; + if (n + 1 == clcf->keepalive_requests - || ngx_current_msec - c->quic->parent->start_time - > clcf->keepalive_time) + || ngx_current_msec - pc->start_time > clcf->keepalive_time) { h3c->goaway = 1; - if (ngx_http_v3_send_goaway(c, (n + 1) << 2) != NGX_OK) { - ngx_http_close_connection(c); - return; +#if (NGX_HTTP_V3_HQ) + if (!h3c->hq) +#endif + { + if (ngx_http_v3_send_goaway(c, (n + 1) << 2) != NGX_OK) { + ngx_http_close_connection(c); + return; + } } ngx_http_v3_shutdown_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, @@ -232,8 +157,14 @@ ngx_http_v3_init_request_stream(ngx_conn } rev = c->read; - rev->handler = ngx_http_v3_wait_request_handler; - c->write->handler = ngx_http_empty_handler; + +#if (NGX_HTTP_V3_HQ) + if (!h3c->hq) +#endif + { + rev->handler = ngx_http_v3_wait_request_handler; + c->write->handler = ngx_http_empty_handler; + } if (rev->ready) { rev->handler(rev); @@ -261,8 +192,8 @@ ngx_http_v3_wait_request_handler(ngx_eve ngx_connection_t *c; ngx_pool_cleanup_t *cln; ngx_http_request_t *r; + ngx_http_v3_session_t *h3c; ngx_http_connection_t *hc; - ngx_http_v3_session_t *h3c; ngx_http_core_srv_conf_t *cscf; c = rev->data; @@ -401,13 +332,10 @@ ngx_http_v3_reset_connection(ngx_connect h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + if (h3scf->max_table_capacity > 0 && !c->read->eof #if (NGX_HTTP_V3_HQ) - if (h3scf->hq) { - return; - } + && !h3scf->hq #endif - - if (h3scf->max_table_capacity > 0 && !c->read->eof && (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0) { (void) ngx_http_v3_send_cancel_stream(c, c->quic->id); diff --git a/src/http/v3/ngx_http_v3_uni.c b/src/http/v3/ngx_http_v3_uni.c --- a/src/http/v3/ngx_http_v3_uni.c +++ b/src/http/v3/ngx_http_v3_uni.c @@ -37,8 +37,23 @@ void ngx_http_v3_init_uni_stream(ngx_connection_t *c) { uint64_t n; +#if (NGX_HTTP_V3_HQ) + ngx_http_v3_session_t *h3c; +#endif ngx_http_v3_uni_stream_t *us; +#if (NGX_HTTP_V3_HQ) + h3c = ngx_http_v3_get_session(c); + if (h3c->hq) { + ngx_http_v3_finalize_connection(c, + NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, + "uni stream in hq mode"); + c->data = NULL; + ngx_http_v3_close_uni_stream(c); + return; + } +#endif + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init uni stream"); n = c->quic->id >> 2; From arut at nginx.com Thu Sep 8 09:06:35 2022 From: arut at nginx.com (=?iso-8859-1?q?Roman_Arutyunyan?=) Date: Thu, 08 Sep 2022 13:06:35 +0400 Subject: [PATCH 08 of 10] QUIC: idle mode for main connection In-Reply-To: References: Message-ID: # HG changeset patch # User Roman Arutyunyan # Date 1662627133 -14400 # Thu Sep 08 12:52:13 2022 +0400 # Branch quic # Node ID e0634a484d9a2d82d43f565d64a0a22e989ac1cb # Parent 1dd6fabfdcb5b52af495f9d8fc00f64ae36a537c QUIC: idle mode for main connection. Now main QUIC connection for HTTP/3 always has c->idle flag set. This allows the connection to receive worker shutdown notification. It is passed to application level via a new conf->shutdown() callback. The HTTP/3 shutdown callback sends GOAWAY to client and gracefully shuts down the QUIC connection. diff --git a/src/event/quic/ngx_event_quic.c b/src/event/quic/ngx_event_quic.c --- a/src/event/quic/ngx_event_quic.c +++ b/src/event/quic/ngx_event_quic.c @@ -341,6 +341,7 @@ ngx_quic_new_connection(ngx_connection_t return NULL; } + c->idle = 1; ngx_reusable_connection(c, 1); ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, @@ -420,9 +421,9 @@ ngx_quic_input_handler(ngx_event_t *rev) } if (c->close) { - qc->error = NGX_QUIC_ERR_NO_ERROR; - qc->error_reason = "graceful shutdown"; - ngx_quic_close_connection(c, NGX_ERROR); + if (qc->conf->shutdown) { + qc->conf->shutdown(c); + } return; } diff --git a/src/event/quic/ngx_event_quic.h b/src/event/quic/ngx_event_quic.h --- a/src/event/quic/ngx_event_quic.h +++ b/src/event/quic/ngx_event_quic.h @@ -28,6 +28,9 @@ #define NGX_QUIC_STREAM_UNIDIRECTIONAL 0x02 +typedef void (*ngx_quic_shutdown_pt)(ngx_connection_t *c); + + typedef enum { NGX_QUIC_STREAM_SEND_READY = 0, NGX_QUIC_STREAM_SEND_SEND, @@ -74,6 +77,8 @@ typedef struct { ngx_int_t stream_reject_code_uni; ngx_int_t stream_reject_code_bidi; + ngx_quic_shutdown_pt shutdown; + u_char av_token_key[NGX_QUIC_AV_KEY_LEN]; u_char sr_token_key[NGX_QUIC_SR_KEY_LEN]; } ngx_quic_conf_t; diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -141,6 +141,7 @@ struct ngx_http_v3_session_s { uint64_t next_push_id; uint64_t max_push_id; uint64_t goaway_push_id; + uint64_t next_request_id; off_t total_bytes; off_t payload_bytes; @@ -158,6 +159,7 @@ void ngx_http_v3_init(ngx_connection_t * void ngx_http_v3_reset_connection(ngx_connection_t *c); ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c); ngx_int_t ngx_http_v3_check_flood(ngx_connection_t *c); +void ngx_http_v3_shutdown(ngx_connection_t *c); ngx_int_t ngx_http_v3_read_request_body(ngx_http_request_t *r); ngx_int_t ngx_http_v3_read_unbuffered_request_body(ngx_http_request_t *r); diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -249,6 +249,8 @@ ngx_http_v3_create_srv_conf(ngx_conf_t * h3scf->quic.stream_reject_code_bidi = NGX_HTTP_V3_ERR_REQUEST_REJECTED; h3scf->quic.active_connection_id_limit = NGX_CONF_UNSET_UINT; + h3scf->quic.shutdown = ngx_http_v3_shutdown; + return h3scf; } diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -97,6 +97,37 @@ ngx_http_v3_init(ngx_connection_t *c) } +void +ngx_http_v3_shutdown(ngx_connection_t *c) +{ + ngx_http_v3_session_t *h3c; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 shutdown"); + + h3c = ngx_http_v3_get_session(c); + + if (h3c == NULL) { + ngx_quic_finalize_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, + "connection shutdown"); + return; + } + + if (!h3c->goaway) { + h3c->goaway = 1; + +#if (NGX_HTTP_V3_HQ) + if (!h3c->hq) +#endif + { + (void) ngx_http_v3_send_goaway(c, h3c->next_request_id); + } + + ngx_http_v3_shutdown_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, + "connection shutdown"); + } +} + + static void ngx_http_v3_init_request_stream(ngx_connection_t *c) { @@ -137,6 +168,8 @@ ngx_http_v3_init_request_stream(ngx_conn pc = c->quic->parent; + h3c->next_request_id = c->quic->id + 0x04; + if (n + 1 == clcf->keepalive_requests || ngx_current_msec - pc->start_time > clcf->keepalive_time) { @@ -146,7 +179,7 @@ ngx_http_v3_init_request_stream(ngx_conn if (!h3c->hq) #endif { - if (ngx_http_v3_send_goaway(c, (n + 1) << 2) != NGX_OK) { + if (ngx_http_v3_send_goaway(c, h3c->next_request_id) != NGX_OK) { ngx_http_close_connection(c); return; } From arut at nginx.com Thu Sep 8 09:06:36 2022 From: arut at nginx.com (=?iso-8859-1?q?Roman_Arutyunyan?=) Date: Thu, 08 Sep 2022 13:06:36 +0400 Subject: [PATCH 09 of 10] HTTP/3: renamed functions In-Reply-To: References: Message-ID: <861d6897151fe6773898.1662627996@arut-laptop> # HG changeset patch # User Roman Arutyunyan # Date 1661162943 -14400 # Mon Aug 22 14:09:03 2022 +0400 # Branch quic # Node ID 861d6897151fe6773898db6cfdb36f56403302c5 # Parent e0634a484d9a2d82d43f565d64a0a22e989ac1cb HTTP/3: renamed functions. ngx_http_v3_init() is renamed ngx_http_v3_init_stream(). ngx_http_v3_reset_connection() is renamed to ngx_http_v3_reset_stream(). diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -326,7 +326,7 @@ ngx_http_init_connection(ngx_connection_ #if (NGX_HTTP_V3) if (hc->addr_conf->http3) { - ngx_http_v3_init(c); + ngx_http_v3_init_stream(c); return; } #endif @@ -3786,7 +3786,7 @@ ngx_http_close_connection(ngx_connection #if (NGX_HTTP_V3) if (c->quic) { - ngx_http_v3_reset_connection(c); + ngx_http_v3_reset_stream(c); } #endif diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -155,8 +155,8 @@ struct ngx_http_v3_session_s { }; -void ngx_http_v3_init(ngx_connection_t *c); -void ngx_http_v3_reset_connection(ngx_connection_t *c); +void ngx_http_v3_init_stream(ngx_connection_t *c); +void ngx_http_v3_reset_stream(ngx_connection_t *c); ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c); ngx_int_t ngx_http_v3_check_flood(ngx_connection_t *c); void ngx_http_v3_shutdown(ngx_connection_t *c); diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -55,7 +55,7 @@ static const struct { void -ngx_http_v3_init(ngx_connection_t *c) +ngx_http_v3_init_stream(ngx_connection_t *c) { ngx_http_connection_t *hc, *phc; ngx_http_v3_srv_conf_t *h3scf; @@ -359,7 +359,7 @@ ngx_http_v3_wait_request_handler(ngx_eve void -ngx_http_v3_reset_connection(ngx_connection_t *c) +ngx_http_v3_reset_stream(ngx_connection_t *c) { ngx_http_v3_srv_conf_t *h3scf; From arut at nginx.com Thu Sep 8 09:06:37 2022 From: arut at nginx.com (=?iso-8859-1?q?Roman_Arutyunyan?=) Date: Thu, 08 Sep 2022 13:06:37 +0400 Subject: [PATCH 10 of 10] QUIC: application init() callback In-Reply-To: References: Message-ID: <8e58a27b320807aae001.1662627997@arut-laptop> # HG changeset patch # User Roman Arutyunyan # Date 1662627905 -14400 # Thu Sep 08 13:05:05 2022 +0400 # Branch quic # Node ID 8e58a27b320807aae00194b82e2c997287e3ad42 # Parent 861d6897151fe6773898db6cfdb36f56403302c5 QUIC: application init() callback. It's called after handshake completion or prior to the first early data stream creation. The callback should initialize application-level data before creating streams. HTTP/3 callback implementation sets keepalive timer and sends SETTINGS. Also, this allows to limit max handshake time in ngx_http_v3_init_stream(). diff --git a/src/event/quic/ngx_event_quic.h b/src/event/quic/ngx_event_quic.h --- a/src/event/quic/ngx_event_quic.h +++ b/src/event/quic/ngx_event_quic.h @@ -28,6 +28,7 @@ #define NGX_QUIC_STREAM_UNIDIRECTIONAL 0x02 +typedef ngx_int_t (*ngx_quic_init_pt)(ngx_connection_t *c); typedef void (*ngx_quic_shutdown_pt)(ngx_connection_t *c); @@ -77,6 +78,7 @@ typedef struct { ngx_int_t stream_reject_code_uni; ngx_int_t stream_reject_code_bidi; + ngx_quic_init_pt init; ngx_quic_shutdown_pt shutdown; u_char av_token_key[NGX_QUIC_AV_KEY_LEN]; diff --git a/src/event/quic/ngx_event_quic_streams.c b/src/event/quic/ngx_event_quic_streams.c --- a/src/event/quic/ngx_event_quic_streams.c +++ b/src/event/quic/ngx_event_quic_streams.c @@ -21,6 +21,7 @@ static ngx_quic_stream_t *ngx_quic_get_s static ngx_int_t ngx_quic_reject_stream(ngx_connection_t *c, uint64_t id); static void ngx_quic_init_stream_handler(ngx_event_t *ev); static void ngx_quic_init_streams_handler(ngx_connection_t *c); +static ngx_int_t ngx_quic_do_init_streams(ngx_connection_t *c); static ngx_quic_stream_t *ngx_quic_create_stream(ngx_connection_t *c, uint64_t id); static void ngx_quic_empty_handler(ngx_event_t *ev); @@ -555,15 +556,22 @@ ngx_quic_init_streams(ngx_connection_t * return NGX_OK; } - ngx_quic_init_streams_handler(c); - - return NGX_OK; + return ngx_quic_do_init_streams(c); } static void ngx_quic_init_streams_handler(ngx_connection_t *c) { + if (ngx_quic_do_init_streams(c) != NGX_OK) { + ngx_quic_close_connection(c, NGX_ERROR); + } +} + + +static ngx_int_t +ngx_quic_do_init_streams(ngx_connection_t *c) +{ ngx_queue_t *q; ngx_quic_stream_t *qs; ngx_quic_connection_t *qc; @@ -572,6 +580,12 @@ ngx_quic_init_streams_handler(ngx_connec qc = ngx_quic_get_connection(c); + if (qc->conf->init) { + if (qc->conf->init(c) != NGX_OK) { + return NGX_ERROR; + } + } + for (q = ngx_queue_head(&qc->streams.uninitialized); q != ngx_queue_sentinel(&qc->streams.uninitialized); q = ngx_queue_next(q)) @@ -581,6 +595,8 @@ ngx_quic_init_streams_handler(ngx_connec } qc->streams.initialized = 1; + + return NGX_OK; } diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c --- a/src/http/v3/ngx_http_v3.c +++ b/src/http/v3/ngx_http_v3.c @@ -17,7 +17,6 @@ static void ngx_http_v3_cleanup_session( ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c) { - ngx_connection_t *pc; ngx_pool_cleanup_t *cln; ngx_http_connection_t *hc; ngx_http_v3_session_t *h3c; @@ -25,16 +24,11 @@ ngx_http_v3_init_session(ngx_connection_ ngx_http_v3_srv_conf_t *h3scf; #endif - pc = c->quic->parent; - hc = pc->data; - - if (hc->v3_session) { - return NGX_OK; - } + hc = c->data; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init session"); - h3c = ngx_pcalloc(pc->pool, sizeof(ngx_http_v3_session_t)); + h3c = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_session_t)); if (h3c == NULL) { goto failed; } @@ -52,16 +46,16 @@ ngx_http_v3_init_session(ngx_connection_ ngx_queue_init(&h3c->blocked); ngx_queue_init(&h3c->pushing); - h3c->keepalive.log = pc->log; - h3c->keepalive.data = pc; + h3c->keepalive.log = c->log; + h3c->keepalive.data = c; h3c->keepalive.handler = ngx_http_v3_keepalive_handler; h3c->keepalive.cancelable = 1; - h3c->table.send_insert_count.log = pc->log; - h3c->table.send_insert_count.data = pc; + h3c->table.send_insert_count.log = c->log; + h3c->table.send_insert_count.data = c; h3c->table.send_insert_count.handler = ngx_http_v3_inc_insert_count_handler; - cln = ngx_pool_cleanup_add(pc->pool, 0); + cln = ngx_pool_cleanup_add(c->pool, 0); if (cln == NULL) { goto failed; } @@ -71,13 +65,7 @@ ngx_http_v3_init_session(ngx_connection_ hc->v3_session = h3c; -#if (NGX_HTTP_V3_HQ) - if (h3c->hq) { - return NGX_OK; - } -#endif - - return ngx_http_v3_send_settings(c); + return NGX_OK; failed: diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -159,6 +159,7 @@ void ngx_http_v3_init_stream(ngx_connect void ngx_http_v3_reset_stream(ngx_connection_t *c); ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c); ngx_int_t ngx_http_v3_check_flood(ngx_connection_t *c); +ngx_int_t ngx_http_v3_init(ngx_connection_t *c); void ngx_http_v3_shutdown(ngx_connection_t *c); ngx_int_t ngx_http_v3_read_request_body(ngx_http_request_t *r); diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c --- a/src/http/v3/ngx_http_v3_module.c +++ b/src/http/v3/ngx_http_v3_module.c @@ -249,6 +249,7 @@ ngx_http_v3_create_srv_conf(ngx_conf_t * h3scf->quic.stream_reject_code_bidi = NGX_HTTP_V3_ERR_REQUEST_REJECTED; h3scf->quic.active_connection_id_limit = NGX_CONF_UNSET_UINT; + h3scf->quic.init = ngx_http_v3_init; h3scf->quic.shutdown = ngx_http_v3_shutdown; return h3scf; diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -57,18 +57,29 @@ static const struct { void ngx_http_v3_init_stream(ngx_connection_t *c) { + ngx_http_v3_session_t *h3c; ngx_http_connection_t *hc, *phc; ngx_http_v3_srv_conf_t *h3scf; ngx_http_core_loc_conf_t *clcf; + ngx_http_core_srv_conf_t *cscf; hc = c->data; hc->ssl = 1; clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module); + cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); h3scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module); if (c->quic == NULL) { + if (ngx_http_v3_init_session(c) != NGX_OK) { + ngx_http_close_connection(c); + return; + } + + h3c = hc->v3_session; + ngx_add_timer(&h3c->keepalive, cscf->client_header_timeout); + h3scf->quic.timeout = clcf->keepalive_timeout; ngx_quic_run(c, &h3scf->quic); return; @@ -83,11 +94,6 @@ ngx_http_v3_init_stream(ngx_connection_t ngx_set_connection_log(c, clcf->error_log); } - if (ngx_http_v3_init_session(c) != NGX_OK) { - ngx_http_close_connection(c); - return; - } - if (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { ngx_http_v3_init_uni_stream(c); @@ -97,6 +103,28 @@ ngx_http_v3_init_stream(ngx_connection_t } +ngx_int_t +ngx_http_v3_init(ngx_connection_t *c) +{ + ngx_http_v3_session_t *h3c; + ngx_http_core_loc_conf_t *clcf; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init"); + + h3c = ngx_http_v3_get_session(c); + clcf = ngx_http_v3_get_module_loc_conf(c, ngx_http_core_module); + ngx_add_timer(&h3c->keepalive, clcf->keepalive_timeout); + +#if (NGX_HTTP_V3_HQ) + if (h3c->hq) { + return NGX_OK; + } +#endif + + return ngx_http_v3_send_settings(c); +} + + void ngx_http_v3_shutdown(ngx_connection_t *c) { From pluknet at nginx.com Thu Sep 8 11:31:20 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Thu, 08 Sep 2022 11:31:20 +0000 Subject: [nginx] SSL: silenced GCC warnings when building with BoringSSL. Message-ID: details: https://hg.nginx.org/nginx/rev/ba5cf8f73a2d branches: changeset: 8070:ba5cf8f73a2d user: Sergey Kandaurov date: Thu Sep 08 13:53:49 2022 +0400 description: SSL: silenced GCC warnings when building with BoringSSL. BoringSSL uses macro stub for SSL_CTX_set_ecdh_auto that expands to 1, which triggers -Wunused-value "statement with no effect" warnings. diffstat: src/event/ngx_event_openssl.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff -r a423e314c22f -r ba5cf8f73a2d src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Wed Sep 07 00:47:31 2022 +0300 +++ b/src/event/ngx_event_openssl.c Thu Sep 08 13:53:49 2022 +0400 @@ -1428,7 +1428,7 @@ ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_s #ifdef SSL_CTRL_SET_ECDH_AUTO /* not needed in OpenSSL 1.1.0+ */ - SSL_CTX_set_ecdh_auto(ssl->ctx, 1); + (void) SSL_CTX_set_ecdh_auto(ssl->ctx, 1); #endif if (ngx_strcmp(name->data, "auto") == 0) { From alx.manpages at gmail.com Thu Sep 8 12:06:28 2022 From: alx.manpages at gmail.com (Alejandro Colomar) Date: Thu, 8 Sep 2022 14:06:28 +0200 Subject: [nginx] SSL: silenced GCC warnings when building with BoringSSL. In-Reply-To: References: Message-ID: Hi Sergey, On 9/8/22 13:31, Sergey Kandaurov wrote: > details: https://hg.nginx.org/nginx/rev/ba5cf8f73a2d > branches: > changeset: 8070:ba5cf8f73a2d > user: Sergey Kandaurov > date: Thu Sep 08 13:53:49 2022 +0400 > description: > SSL: silenced GCC warnings when building with BoringSSL. > > BoringSSL uses macro stub for SSL_CTX_set_ecdh_auto that expands to 1, > which triggers -Wunused-value "statement with no effect" warnings. I think this workaround is incorrect, and the problem is in the buildsystem. See gcc(1): -I dir -iquote dir -isystem dir -idirafter dir ... You can use -I to override a system header file, substituting your own version, since these directories are searched before the standard system header file directories. However, you should not use this option to add directories that contain vendor‐ supplied system header files; use -isystem for that. The -isystem and -idirafter options also mark the directory as a system directory, so that it gets the same special treatment that is applied to the standard system directories. ... Basically, -isystem works as -I, but disables warnings caused by system headers. With that flag, I don't get any warnings in the following simple reproducer (a bit weird is that clang doesn't get a warning even with -I): $ tree . ├── include │   └── one.h └── main.c 1 directory, 2 files $ $ cat include/one.h #define ONE() 1 $ $ cat main.c #include int main(void) { ONE(); return 0; } $ $ gcc -Wall -Wextra -isystem./include main.c $ $ clang -Weverything -isystem./include main.c $ $ gcc -Wall -Wextra -I./include main.c In file included from main.c:1: main.c: In function ‘main’: ./include/one.h:1:16: warning: statement with no effect [-Wunused-value] 1 | #define ONE() 1 | ^ main.c:6:9: note: in expansion of macro ‘ONE’ 6 | ONE(); | ^~~ $ $ clang -Weverything -I./include main.c $ Of course, this is considering that you normally don't want to get warnings from dubious system headers, which normally should be the case in user applications, but you may legitimately doubt the correctness of some dependencies, and may want to see the warnings... Cheers, Alex > > diffstat: > > src/event/ngx_event_openssl.c | 2 +- > 1 files changed, 1 insertions(+), 1 deletions(-) > > diffs (12 lines): > > diff -r a423e314c22f -r ba5cf8f73a2d src/event/ngx_event_openssl.c > --- a/src/event/ngx_event_openssl.c Wed Sep 07 00:47:31 2022 +0300 > +++ b/src/event/ngx_event_openssl.c Thu Sep 08 13:53:49 2022 +0400 > @@ -1428,7 +1428,7 @@ ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_s > > #ifdef SSL_CTRL_SET_ECDH_AUTO > /* not needed in OpenSSL 1.1.0+ */ > - SSL_CTX_set_ecdh_auto(ssl->ctx, 1); > + (void) SSL_CTX_set_ecdh_auto(ssl->ctx, 1); > #endif > > if (ngx_strcmp(name->data, "auto") == 0) { > _______________________________________________ > nginx-devel mailing list -- nginx-devel at nginx.org > To unsubscribe send an email to nginx-devel-leave at nginx.org -- -------------- next part -------------- A non-text attachment was scrubbed... Name: OpenPGP_signature Type: application/pgp-signature Size: 833 bytes Desc: OpenPGP digital signature URL: From noamc at qwilt.com Thu Sep 8 13:58:24 2022 From: noamc at qwilt.com (Noam Cvikel) Date: Thu, 8 Sep 2022 16:58:24 +0300 Subject: nginx KTLS and HTTP/2 performance degradation In-Reply-To: References: <2d37d96e-ff1b-7113-434f-8d04751bb85e@stoev.eu> Message-ID: <91cc4352cabbf021d97fa9621344fc07@mail.gmail.com> Hello! Late to the party. We've seen the same results over here when using sendfile with HTTP/2. You can increase it from 8k to 16 frames using http2_chunk_size but that still won't be good performance when dealing with files that aren't tiny. Really glad I found this thread, and I appreciate the clarification Maxim. One thing I ponder though, shouldn't it be beneficial to have a directive to toggle Sendfile on/off specifically for HTTP/2? It would be nice to allow a location to Sendfile over HTTP/1.1 by still respond to HTTP/2 requests. Best, Noam Cvikel Qwilt | Delivery Software Engineer | noamc at qwilt.com -----Original Message----- From: nginx-devel [mailto:nginx-devel-bounces at nginx.org] On Behalf Of Maxim Dounin Sent: Thursday, December 2, 2021 15:07 To: nginx-devel at nginx.org Subject: Re: nginx KTLS and HTTP/2 performance degradation Hello! On Thu, Dec 02, 2021 at 02:05:52PM +0200, Lyuben Stoev wrote: > Hello, > I have tested the nginx with the patch > https://hg.nginx.org/nginx/rev/65946a191197 (SSL: SSL_sendfile() > support with kernel TLS.) following the nginx blog article > https://www.nginx.com/blog/improving-nginx-performance-with-kernel-tls > / And it sort of works, but I have bad performance when making HTTP/2 > requests. If I made a HTTP/1.1 request there is 30-35% increase in > performance as the Nginx blog article stated, but when I changed the > request to use HTTP/2 the request was 40% slower than an ordinary > nginx without KTLS enabled. Does anyone have such perfomance > degradation with nginx KTLS and HTTP/2? I am using generic setup - > Ubuntu 20.04.3 LTS and kernels 5.8.0-63-generic (the same results are with > 5.4.0-91-generic). > The nginx vritual host is the same as in the Nginx blog article with > exception of adding http2 to the listen! OpenSSL 3.0.0 and nginx > 1.21.4 are used. > The KTLS seems to work, because the strace and debug logs show it. > Just the sstrange thing is when using HTTP2, the sendfile syscalls look: > write(39, "\0 \0\0\0\0\0\0\1", 9) = 9 > sendfile(39, 131, [1418218] => [1426410], 8192) = 8192 > write(39, "\0 \0\0\0\0\0\0\1", 9) = 9 > sendfile(39, 131, [1426410] => [1434602], 8192) = 8192 > write(39, "\0 \0\0\0\0\0\0\1", 9) = 9 > sendfile(39, 131, [1434602] => [1442794], 8192) = 8192 > write(39, "\0 \0\0\0\0\0\0\1", 9) = 9 > sendfile(39, 131, [1442794] => [1450986], 8192) = 8192 > write(39, "\0 \0\0\0\0\0\0\1", 9) = 9 > sendfile(39, 131, [1450986] => [1459178], 8192) = 8192 > write(39, "\0 \0\0\0\0\0\0\1", 9) = 9 > sendfile(39, 131, [1459178] => [1467370], 8192) = 8192 > > It is always 8K and there are thousands of sendfile syscalls.... That's expected, because of HTTP/2 framing. Unfortunately, HTTP/2 isn't designed to work with sendfile(), and sending large files over HTTP/2 require a lot of sendfile() syscalls. In general, for HTTP/2 it is better to keep sendfile() disabled. -- Maxim Dounin http://mdounin.ru/ _______________________________________________ nginx-devel mailing list nginx-devel at nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel From mdounin at mdounin.ru Thu Sep 8 14:20:20 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 8 Sep 2022 17:20:20 +0300 Subject: [nginx] SSL: silenced GCC warnings when building with BoringSSL. In-Reply-To: References: Message-ID: Hello! On Thu, Sep 08, 2022 at 02:06:28PM +0200, Alejandro Colomar wrote: > Hi Sergey, > > On 9/8/22 13:31, Sergey Kandaurov wrote: > > details: https://hg.nginx.org/nginx/rev/ba5cf8f73a2d > > branches: > > changeset: 8070:ba5cf8f73a2d > > user: Sergey Kandaurov > > date: Thu Sep 08 13:53:49 2022 +0400 > > description: > > SSL: silenced GCC warnings when building with BoringSSL. > > > > BoringSSL uses macro stub for SSL_CTX_set_ecdh_auto that expands to 1, > > which triggers -Wunused-value "statement with no effect" warnings. > > I think this workaround is incorrect, and the problem is in the buildsystem. > > See gcc(1): > > -I dir > -iquote dir > -isystem dir > -idirafter dir > ... > > You can use -I to override a system header file, > substituting your own version, since these > directories are searched before the standard system > header file directories. However, you should not use > this option to add directories that contain vendor‐ > supplied system header files; use -isystem for that. > > The -isystem and -idirafter options also mark the > directory as a system directory, so that it gets the > same special treatment that is applied to the > standard system directories. > > ... > > > Basically, -isystem works as -I, but disables warnings caused by system > headers. [...] > Of course, this is considering that you normally don't want to get > warnings from dubious system headers, which normally should be the case > in user applications, but you may legitimately doubt the correctness of > some dependencies, and may want to see the warnings... In no particular order: - The "-isystem" flag is GCC-specific and not portable. - SSL library headers are not required to be in the system headers, and can be instead provided by user via --with-cc-opt / --with-ld-opt (and, in case of BoringSSL, this is usually the case). - Disabling warnings for system headers is at most a workaround, and a bad one. If there are warnings, these should be addressed (or silenced, or fixed in the compiler), and not ignored because they happen to come from the system headers. In particular, this ensures that the application code won't silently blow up if a particular system implemented something slightly differently than others, and the application do not take this into account. Disabling warnings for system headers only make sense if there is a bug in system headers you cannot reasonably fix or silence from the application. In this particular case, the warning is somewhat valid: the return value of the SSL_CTX_set_ecdh_auto() call is ignored by nginx. The only issue is that for some reason GCC isn't smart enough to understand that this is a function(-like) call, and the computed value isn't the only potential result. Casting the returned value to "(void)" explicitly tells GCC that we are not going to use the result. Another option would be to add proper error checking, but this is rather unneeded, given that errors here might only happen due to incorrect library version being used at runtime, only potentially affects ancient library versions, and we cannot do anything reasonable with this anyway. -- Maxim Dounin http://mdounin.ru/ From xeioex at nginx.com Thu Sep 8 17:43:12 2022 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 08 Sep 2022 17:43:12 +0000 Subject: [njs] Improved njs_mp_free() to aling with free() semantics. Message-ID: details: https://hg.nginx.org/njs/rev/4689935e5d36 branches: changeset: 1950:4689935e5d36 user: Dmitry Volyntsev date: Thu Sep 08 10:03:25 2022 -0700 description: Improved njs_mp_free() to aling with free() semantics. Previously, njs_mp_free() issued an assertion when p == NULL, while NULL argument is explicitly allowed for free(). diffstat: src/njs_mp.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff -r a2db32044812 -r 4689935e5d36 src/njs_mp.c --- a/src/njs_mp.c Tue Sep 06 18:44:47 2022 -0700 +++ b/src/njs_mp.c Thu Sep 08 10:03:25 2022 -0700 @@ -682,7 +682,7 @@ njs_mp_free(njs_mp_t *mp, void *p) } } else { - njs_assert_msg(0, "freed pointer is out of mp: %p\n", p); + njs_assert_msg(p == NULL, "freed pointer is out of mp: %p\n", p); } } From mdounin at mdounin.ru Fri Sep 9 03:12:06 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Fri, 9 Sep 2022 06:12:06 +0300 Subject: nginx KTLS and HTTP/2 performance degradation In-Reply-To: <91cc4352cabbf021d97fa9621344fc07@mail.gmail.com> References: <2d37d96e-ff1b-7113-434f-8d04751bb85e@stoev.eu> <91cc4352cabbf021d97fa9621344fc07@mail.gmail.com> Message-ID: Hello! On Thu, Sep 08, 2022 at 04:58:24PM +0300, Noam Cvikel wrote: > Late to the party. We've seen the same results over here when using sendfile > with HTTP/2. You can increase it from 8k to 16 frames using http2_chunk_size > but that still won't be good performance when dealing with files that aren't > tiny. > > Really glad I found this thread, and I appreciate the clarification Maxim. > One thing I ponder though, shouldn't it be beneficial to have a directive to > toggle Sendfile on/off specifically for HTTP/2? > It would be nice to allow a location to Sendfile over HTTP/1.1 by still > respond to HTTP/2 requests. As long as you have HTTP/2 enabled, it hardly make sense to optimize things for anything but HTTP/2, since most of the clients will use HTTP/2 anyway. If you really do care, a readily available solution might be to rewrite to a different location with sendfile enabled. For example: server { listen 443 ssl http2; location / { if ($server_protocol != 'HTTP/2.0') { rewrite ^(.*) /sendfile$1 last; } sendfile off; } location /sendfile/ { alias html/; sendfile on; } } I don't think it worth the effort though. Alternatively, a dedicated HTTP/1.x-only server for large downloads with sendfile enabled might be the way to go. This will ensure that kernel TLS and SSL_sendfile() can be effectively used for all clients. -- Maxim Dounin http://mdounin.ru/ From lucas.ollie.zhang at gmail.com Fri Sep 9 15:06:05 2022 From: lucas.ollie.zhang at gmail.com (lucas zhang) Date: Fri, 9 Sep 2022 23:06:05 +0800 Subject: [PATCH] Core: fixed potential memory leak in ngx_core_module_create_conf() Message-ID: # HG changeset patch # User Tong Zhang # Date 1662734441 -28800 # Fri Sep 09 22:40:41 2022 +0800 # Node ID a1a31f97ae70621282ad04302acdb6c9def306f7 # Parent ba5cf8f73a2d0a3615565bf9545f3d65216a0530 Core: fixed potential memory leak in ngx_core_module_create_conf(). When "ngx_array_init" failed and returns a NULL, the object held by "ccf" may be leaked. diff -r ba5cf8f73a2d -r a1a31f97ae70 src/core/nginx.c --- a/src/core/nginx.c Thu Sep 08 13:53:49 2022 +0400 +++ b/src/core/nginx.c Fri Sep 09 22:40:41 2022 +0800 @@ -1076,6 +1076,7 @@ if (ngx_array_init(&ccf->env, cycle->pool, 1, sizeof(ngx_str_t)) != NGX_OK) { + ngx_pfree(cycle->pool, ccf); return NULL; } -------------- next part -------------- An HTML attachment was scrubbed... URL: From mdounin at mdounin.ru Fri Sep 9 15:16:13 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Fri, 9 Sep 2022 18:16:13 +0300 Subject: [PATCH] Core: fixed potential memory leak in ngx_core_module_create_conf() In-Reply-To: References: Message-ID: Hello! On Fri, Sep 09, 2022 at 11:06:05PM +0800, lucas zhang wrote: > # HG changeset patch > # User Tong Zhang > # Date 1662734441 -28800 > # Fri Sep 09 22:40:41 2022 +0800 > # Node ID a1a31f97ae70621282ad04302acdb6c9def306f7 > # Parent ba5cf8f73a2d0a3615565bf9545f3d65216a0530 > Core: fixed potential memory leak in ngx_core_module_create_conf(). > > When "ngx_array_init" failed and returns a NULL, the object held by > "ccf" may be leaked. > > diff -r ba5cf8f73a2d -r a1a31f97ae70 src/core/nginx.c > --- a/src/core/nginx.c Thu Sep 08 13:53:49 2022 +0400 > +++ b/src/core/nginx.c Fri Sep 09 22:40:41 2022 +0800 > @@ -1076,6 +1076,7 @@ > if (ngx_array_init(&ccf->env, cycle->pool, 1, sizeof(ngx_str_t)) > != NGX_OK) > { > + ngx_pfree(cycle->pool, ccf); > return NULL; > } Pool allocations are freed automatically when the pool is destroyed, see here for details: https://nginx.org/en/docs/dev/development_guide.html#pool Thanks. -- Maxim Dounin http://mdounin.ru/ From arut at nginx.com Fri Sep 9 15:46:58 2022 From: arut at nginx.com (Roman Arutyunyan) Date: Fri, 9 Sep 2022 19:46:58 +0400 Subject: [PATCH] Core: support for reading PROXY protocol v2 TLVs In-Reply-To: References: <4b856f1dff939e4eb9c1.1661961135@arut-laptop> <20220905132318.s27wgtof6wuqde7x@N00W24XTQX> Message-ID: <20220909154658.fpnpndo2opnnzywx@N00W24XTQX> Hi, On Mon, Sep 05, 2022 at 06:58:38PM +0300, Maxim Dounin wrote: > Hello! > > On Mon, Sep 05, 2022 at 05:23:18PM +0400, Roman Arutyunyan wrote: > > > Hi, > > > > On Mon, Sep 05, 2022 at 03:52:49AM +0300, Maxim Dounin wrote: > > > Hello! > > > > > > On Wed, Aug 31, 2022 at 07:52:15PM +0400, Roman Arutyunyan wrote: > > > > > > > # HG changeset patch > > > > # User Roman Arutyunyan > > > > # Date 1661436099 -14400 > > > > # Thu Aug 25 18:01:39 2022 +0400 > > > > # Node ID 4b856f1dff939e4eb9c131e17af061cf2c38cfac > > > > # Parent 069a4813e8d6d7ec662d282a10f5f7062ebd817f > > > > Core: support for reading PROXY protocol v2 TLVs. > > > > > > First of all, could you please provide details on the use case? > > > I've seen requests for writing proxy protocol TLVs to upstream > > > servers (see ticket #1639), but not yet seen any meaningful > > > reading requests. > > > > The known cases are these: > > > > - https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-target-groups.html#proxy-protocol > > - https://docs.microsoft.com/en-us/azure/private-link/private-link-service-overview#getting-connection-information-using-tcp-proxy-v2 > > - https://cloud.google.com/vpc/docs/configure-private-service-connect-producer#proxy-protocol > > > > The data may need further parsing, but it can be done in njs or perl. > > Thanks for the details. So, basically, it's about vendor-specific > endpoint IDs. > > > > > The TLV values are available in HTTP and Stream variables > > > > $proxy_protocol_tlv_0xN, where N is a hexadecimal TLV type number with no > > > > leading zeroes. > > > > > > I can't say I like the "hexadecimal TLV type number with no > > > leading zeroes" approach, especially given that the specification > > > uses leading zeroes in TLV types. With leading zeros might be > > > better, to match specification. With on-demand approach this is no longer an issue. > > > Also, it might worth the effort to actually add names for known > > > types instead or in addition to numbers. > > > > This is indeed a good idea and we have such plans as a further extenion of this > > work. One of the problems is however that the abovementioned TLV variables > > are specified in internal documents of AWS/Azure/GCP which are not standards. > > They can be changed anytime, while we have to maintain those variables in > > nginx. Also, raw variables give more flexibility in supporting less known TLVs. > > Of course I'm not suggesting to ditch raw variables, at least not > for unknown/non-standard values. But for known/standard values it > should be easy enough to provide alternative names for easier use, > probably with type-specific parsing. > > With on-demand parsing it would be trivial to support both > $proxy_protocol_tlv_alpn and $proxy_protocol_tlv_0x01. Further, > it will be trivial to support $proxy_protocol_tlv_aws_vpc_id while > still providing $proxy_protocol_tlv_0xea for raw data. > > > > Another question is PP2_TYPE_SSL, which is itself a complex > > > structure and a list of multiple subtypes. > > > > This is an obvious one. However we had exactly zero requests for this. > > See the ticket mentioned above, it seems to be the main reason why > people want to see proxy protocol v2 to backends. > > > > Provided > > > Given the above, not sure if the approach with early parsing and > > > header-like list as in the patch is the good idea. Just > > > preserving TLVs as is and parsing them all during variable > > > evaluation might be easier and more efficient. > > > > In this case, if we have two variables, say $proxy_protocol_tlv_ssl_{sni, alpn}, > > we'll parse the entire TLV block twice - once per variable evaluation. > > Assuming you mean $proxy_protocol_{authority, alpn} (as these > aren't SSL subtypes), I actually see no difference in on-demand > parsing of the TLV block and looking for a header in the > pre-created list of headers. Further, parsing the block for each > variable evaluation might be actually faster due to better > locality, and should simplify adding alternative names. > > And a single TLV block certainly will be more optimal in terms of > memory usage due to no additional allocations. Not to mention the > typical case when TLV variables aren't used at all. Indeed on-demand parsing is quite easy and does not seems to be less efficient. > > > Also, the idea of merging TLV values with identical types looks > > > wrong to me, especially given that many TLSs are binary. > > > Specification does not seem to define the behaviour here, > > > unfortunately. As far as I understand, HAProxy itself still > > > doesn't implement PPv2 parsing, so there is not reference > > > implementation either. On the other hand, it should be easy > > > enough to check all TLVs for duplicate by using a 256-bit bitmask > > > and reject connections if there are any duplicates. > > > > This can be added, thanks. > > Not sure it's actually needed though, especially given that proxy > protocol is only expected to be accepted from trusted sources > anyway. It might be good enough to just assume there is only one > value with a given type. Two patches attached: - TLV on-demand parsing + raw variables - standard named variables -- Roman Arutyunyan -------------- next part -------------- # HG changeset patch # User Roman Arutyunyan # Date 1662729947 -14400 # Fri Sep 09 17:25:47 2022 +0400 # Node ID 80714f1b0f597ce5e530e7274457450db4587fc9 # Parent 069a4813e8d6d7ec662d282a10f5f7062ebd817f Core: support for reading PROXY protocol v2 TLVs. The TLV values are available in HTTP and Stream variables $proxy_protocol_tlv_0xN, where N is a hexadecimal TLV type number. diff --git a/src/core/ngx_proxy_protocol.c b/src/core/ngx_proxy_protocol.c --- a/src/core/ngx_proxy_protocol.c +++ b/src/core/ngx_proxy_protocol.c @@ -40,6 +40,12 @@ typedef struct { } ngx_proxy_protocol_inet6_addrs_t; +typedef struct { + u_char type; + u_char len[2]; +} ngx_proxy_protocol_tlv_t; + + static u_char *ngx_proxy_protocol_read_addr(ngx_connection_t *c, u_char *p, u_char *last, ngx_str_t *addr); static u_char *ngx_proxy_protocol_read_port(u_char *p, u_char *last, @@ -412,11 +418,73 @@ ngx_proxy_protocol_v2_read(ngx_connectio &pp->src_addr, pp->src_port, &pp->dst_addr, pp->dst_port); if (buf < end) { - ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, - "PROXY protocol v2 %z bytes of tlv ignored", end - buf); + pp->tlvs.data = ngx_pnalloc(c->pool, end - buf); + if (pp->tlvs.data == NULL) { + return NULL; + } + + ngx_memcpy(pp->tlvs.data, buf, end - buf); + pp->tlvs.len = end - buf; } c->proxy_protocol = pp; return end; } + + +ngx_int_t +ngx_proxy_protocol_lookup_tlv(ngx_connection_t *c, ngx_str_t *tlvs, + ngx_uint_t type, ngx_str_t *value) +{ + u_char *p; + size_t n, len; + ngx_proxy_protocol_tlv_t *tlv; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 lookup tlv:%02xd", type); + + if (tlvs == NULL) { + if (c->proxy_protocol == NULL) { + return NGX_DECLINED; + } + + tlvs = &c->proxy_protocol->tlvs; + } + + p = tlvs->data; + n = tlvs->len; + + while (n) { + if (n < sizeof(ngx_proxy_protocol_tlv_t)) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, "broken PROXY protocol TLV"); + return NGX_ERROR; + } + + tlv = (ngx_proxy_protocol_tlv_t *) p; + len = ngx_proxy_protocol_parse_uint16(tlv->len); + + p += sizeof(ngx_proxy_protocol_tlv_t); + n -= sizeof(ngx_proxy_protocol_tlv_t); + + if (n < len) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, "broken PROXY protocol TLV"); + return NGX_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 tlv:0x%02xd len:%uz", + tlv->type, len); + + if (tlv->type == type) { + value->data = p; + value->len = len; + return NGX_OK; + } + + p += len; + n -= len; + } + + return NGX_DECLINED; +} diff --git a/src/core/ngx_proxy_protocol.h b/src/core/ngx_proxy_protocol.h --- a/src/core/ngx_proxy_protocol.h +++ b/src/core/ngx_proxy_protocol.h @@ -21,6 +21,7 @@ struct ngx_proxy_protocol_s { ngx_str_t dst_addr; in_port_t src_port; in_port_t dst_port; + ngx_str_t tlvs; }; @@ -28,6 +29,8 @@ u_char *ngx_proxy_protocol_read(ngx_conn u_char *last); u_char *ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, u_char *last); +ngx_int_t ngx_proxy_protocol_lookup_tlv(ngx_connection_t *c, ngx_str_t *tlvs, + ngx_uint_t type, ngx_str_t *value); #endif /* _NGX_PROXY_PROTOCOL_H_INCLUDED_ */ diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -61,6 +61,8 @@ static ngx_int_t ngx_http_variable_proxy ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_proxy_protocol_port(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_proxy_protocol_tlv_0x(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r, @@ -214,6 +216,10 @@ static ngx_http_variable_t ngx_http_cor ngx_http_variable_proxy_protocol_port, offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 }, + { ngx_string("proxy_protocol_tlv_0x"), NULL, + ngx_http_variable_proxy_protocol_tlv_0x, + 0, NGX_HTTP_VAR_PREFIX, 0 }, + { ngx_string("server_addr"), NULL, ngx_http_variable_server_addr, 0, 0, 0 }, { ngx_string("server_port"), NULL, ngx_http_variable_server_port, 0, 0, 0 }, @@ -1387,6 +1393,46 @@ ngx_http_variable_proxy_protocol_port(ng static ngx_int_t +ngx_http_variable_proxy_protocol_tlv_0x(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_str_t *name = (ngx_str_t *) data; + + size_t n; + u_char *p; + ngx_str_t s; + ngx_int_t rc, type; + + n = name->len - (sizeof("proxy_protocol_tlv_0x") - 1); + p = name->data + sizeof("proxy_protocol_tlv_0x") - 1; + + type = ngx_hextoi(p, n); + if (type == NGX_ERROR) { + return NGX_ERROR; + } + + rc = ngx_proxy_protocol_lookup_tlv(r->connection, NULL, type, &s); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_DECLINED) { + v->not_found = 1; + return NGX_OK; + } + + v->len = s.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = s.data; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { diff --git a/src/stream/ngx_stream_variables.c b/src/stream/ngx_stream_variables.c --- a/src/stream/ngx_stream_variables.c +++ b/src/stream/ngx_stream_variables.c @@ -23,6 +23,8 @@ static ngx_int_t ngx_stream_variable_pro ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_variable_proxy_protocol_port( ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_proxy_protocol_tlv_0x( + ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_variable_server_addr(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_variable_server_port(ngx_stream_session_t *s, @@ -79,6 +81,10 @@ static ngx_stream_variable_t ngx_stream ngx_stream_variable_proxy_protocol_port, offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 }, + { ngx_string("proxy_protocol_tlv_0x"), NULL, + ngx_stream_variable_proxy_protocol_tlv_0x, + 0, NGX_STREAM_VAR_PREFIX, 0 }, + { ngx_string("server_addr"), NULL, ngx_stream_variable_server_addr, 0, 0, 0 }, @@ -622,6 +628,46 @@ ngx_stream_variable_proxy_protocol_port( static ngx_int_t +ngx_stream_variable_proxy_protocol_tlv_0x(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_str_t *name = (ngx_str_t *) data; + + size_t n; + u_char *p; + ngx_str_t str; + ngx_int_t rc, type; + + n = name->len - (sizeof("proxy_protocol_tlv_0x") - 1); + p = name->data + sizeof("proxy_protocol_tlv_0x") - 1; + + type = ngx_hextoi(p, n); + if (type == NGX_ERROR) { + return NGX_ERROR; + } + + rc = ngx_proxy_protocol_lookup_tlv(s->connection, NULL, type, &str); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_DECLINED) { + v->not_found = 1; + return NGX_OK; + } + + v->len = str.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = str.data; + + return NGX_OK; +} + + +static ngx_int_t ngx_stream_variable_server_addr(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data) { -------------- next part -------------- # HG changeset patch # User Roman Arutyunyan # Date 1662718130 -14400 # Fri Sep 09 14:08:50 2022 +0400 # Node ID 832f6c96b26c3009640072e3f4b1f0bf43e644d0 # Parent 80714f1b0f597ce5e530e7274457450db4587fc9 Standard PROXY protocol v2 TLV variables. diff --git a/src/core/ngx_proxy_protocol.h b/src/core/ngx_proxy_protocol.h --- a/src/core/ngx_proxy_protocol.h +++ b/src/core/ngx_proxy_protocol.h @@ -13,7 +13,18 @@ #include -#define NGX_PROXY_PROTOCOL_MAX_HEADER 107 +#define NGX_PROXY_PROTOCOL_MAX_HEADER 107 + +#define NGX_PROXY_PROTOCOL_TLV_ALPN 0x01 +#define NGX_PROXY_PROTOCOL_TLV_AUTHORITY 0x02 +#define NGX_PROXY_PROTOCOL_TLV_UNIQUE_ID 0x05 +#define NGX_PROXY_PROTOCOL_TLV_SSL 0x20 +#define NGX_PROXY_PROTOCOL_TLV_SSL_VERSION 0x21 +#define NGX_PROXY_PROTOCOL_TLV_SSL_CN 0x22 +#define NGX_PROXY_PROTOCOL_TLV_SSL_CIPHER 0x23 +#define NGX_PROXY_PROTOCOL_TLV_SSL_SIG_ALG 0x24 +#define NGX_PROXY_PROTOCOL_TLV_SSL_KEY_ALG 0x25 +#define NGX_PROXY_PROTOCOL_TLV_NETNS 0x30 struct ngx_proxy_protocol_s { @@ -25,6 +36,12 @@ struct ngx_proxy_protocol_s { }; +typedef struct { + u_char client; + u_char verify[4]; +} ngx_proxy_protocol_tlv_ssl_t; + + u_char *ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, u_char *last); u_char *ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -63,6 +63,10 @@ static ngx_int_t ngx_http_variable_proxy ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_proxy_protocol_tlv_0x(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_proxy_protocol_tlv_ssl( + ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r, @@ -220,6 +224,42 @@ static ngx_http_variable_t ngx_http_cor ngx_http_variable_proxy_protocol_tlv_0x, 0, NGX_HTTP_VAR_PREFIX, 0 }, + { ngx_string("proxy_protocol_tlv_alpn"), NULL, + ngx_http_variable_proxy_protocol_tlv, + NGX_PROXY_PROTOCOL_TLV_ALPN, 0, 0 }, + + { ngx_string("proxy_protocol_tlv_authority"), NULL, + ngx_http_variable_proxy_protocol_tlv, + NGX_PROXY_PROTOCOL_TLV_AUTHORITY, 0, 0 }, + + { ngx_string("proxy_protocol_tlv_unique_id"), NULL, + ngx_http_variable_proxy_protocol_tlv, + NGX_PROXY_PROTOCOL_TLV_UNIQUE_ID, 0, 0 }, + + { ngx_string("proxy_protocol_tlv_ssl_version"), NULL, + ngx_http_variable_proxy_protocol_tlv_ssl, + NGX_PROXY_PROTOCOL_TLV_SSL_VERSION, 0, 0 }, + + { ngx_string("proxy_protocol_tlv_ssl_cn"), NULL, + ngx_http_variable_proxy_protocol_tlv_ssl, + NGX_PROXY_PROTOCOL_TLV_SSL_CN, 0, 0 }, + + { ngx_string("proxy_protocol_tlv_ssl_cipher"), NULL, + ngx_http_variable_proxy_protocol_tlv_ssl, + NGX_PROXY_PROTOCOL_TLV_SSL_CIPHER, 0, 0 }, + + { ngx_string("proxy_protocol_tlv_ssl_sig_alg"), NULL, + ngx_http_variable_proxy_protocol_tlv_ssl, + NGX_PROXY_PROTOCOL_TLV_SSL_SIG_ALG, 0, 0 }, + + { ngx_string("proxy_protocol_tlv_ssl_key_alg"), NULL, + ngx_http_variable_proxy_protocol_tlv_ssl, + NGX_PROXY_PROTOCOL_TLV_SSL_KEY_ALG, 0, 0 }, + + { ngx_string("proxy_protocol_tlv_netns"), NULL, + ngx_http_variable_proxy_protocol_tlv, + NGX_PROXY_PROTOCOL_TLV_NETNS, 0, 0 }, + { ngx_string("server_addr"), NULL, ngx_http_variable_server_addr, 0, 0, 0 }, { ngx_string("server_port"), NULL, ngx_http_variable_server_port, 0, 0, 0 }, @@ -1400,8 +1440,7 @@ ngx_http_variable_proxy_protocol_tlv_0x( size_t n; u_char *p; - ngx_str_t s; - ngx_int_t rc, type; + ngx_int_t type; n = name->len - (sizeof("proxy_protocol_tlv_0x") - 1); p = name->data + sizeof("proxy_protocol_tlv_0x") - 1; @@ -1411,7 +1450,58 @@ ngx_http_variable_proxy_protocol_tlv_0x( return NGX_ERROR; } - rc = ngx_proxy_protocol_lookup_tlv(r->connection, NULL, type, &s); + return ngx_http_variable_proxy_protocol_tlv(r, v, type); +} + + +static ngx_int_t +ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_str_t s; + ngx_int_t rc; + + rc = ngx_proxy_protocol_lookup_tlv(r->connection, NULL, data, &s); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_DECLINED) { + v->not_found = 1; + return NGX_OK; + } + + v->len = s.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = s.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_proxy_protocol_tlv_ssl(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_str_t s, ssl; + ngx_int_t rc; + + rc = ngx_proxy_protocol_lookup_tlv(r->connection, NULL, + NGX_PROXY_PROTOCOL_TLV_SSL, &ssl); + + if (rc == NGX_OK) { + if (ssl.len < sizeof(ngx_proxy_protocol_tlv_ssl_t)) { + return NGX_ERROR; + } + + ssl.data += sizeof(ngx_proxy_protocol_tlv_ssl_t); + ssl.len -= sizeof(ngx_proxy_protocol_tlv_ssl_t); + + rc = ngx_proxy_protocol_lookup_tlv(r->connection, &ssl, data, &s); + } if (rc == NGX_ERROR) { return NGX_ERROR; diff --git a/src/stream/ngx_stream_variables.c b/src/stream/ngx_stream_variables.c --- a/src/stream/ngx_stream_variables.c +++ b/src/stream/ngx_stream_variables.c @@ -25,6 +25,10 @@ static ngx_int_t ngx_stream_variable_pro ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_variable_proxy_protocol_tlv_0x( ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_proxy_protocol_tlv( + ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_proxy_protocol_tlv_ssl( + ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_variable_server_addr(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_variable_server_port(ngx_stream_session_t *s, @@ -85,6 +89,42 @@ static ngx_stream_variable_t ngx_stream ngx_stream_variable_proxy_protocol_tlv_0x, 0, NGX_STREAM_VAR_PREFIX, 0 }, + { ngx_string("proxy_protocol_tlv_alpn"), NULL, + ngx_stream_variable_proxy_protocol_tlv, + NGX_PROXY_PROTOCOL_TLV_ALPN, 0, 0 }, + + { ngx_string("proxy_protocol_tlv_authority"), NULL, + ngx_stream_variable_proxy_protocol_tlv, + NGX_PROXY_PROTOCOL_TLV_AUTHORITY, 0, 0 }, + + { ngx_string("proxy_protocol_tlv_unique_id"), NULL, + ngx_stream_variable_proxy_protocol_tlv, + NGX_PROXY_PROTOCOL_TLV_UNIQUE_ID, 0, 0 }, + + { ngx_string("proxy_protocol_tlv_ssl_version"), NULL, + ngx_stream_variable_proxy_protocol_tlv_ssl, + NGX_PROXY_PROTOCOL_TLV_SSL_VERSION, 0, 0 }, + + { ngx_string("proxy_protocol_tlv_ssl_cn"), NULL, + ngx_stream_variable_proxy_protocol_tlv_ssl, + NGX_PROXY_PROTOCOL_TLV_SSL_CN, 0, 0 }, + + { ngx_string("proxy_protocol_tlv_ssl_cipher"), NULL, + ngx_stream_variable_proxy_protocol_tlv_ssl, + NGX_PROXY_PROTOCOL_TLV_SSL_CIPHER, 0, 0 }, + + { ngx_string("proxy_protocol_tlv_ssl_sig_alg"), NULL, + ngx_stream_variable_proxy_protocol_tlv_ssl, + NGX_PROXY_PROTOCOL_TLV_SSL_SIG_ALG, 0, 0 }, + + { ngx_string("proxy_protocol_tlv_ssl_key_alg"), NULL, + ngx_stream_variable_proxy_protocol_tlv_ssl, + NGX_PROXY_PROTOCOL_TLV_SSL_KEY_ALG, 0, 0 }, + + { ngx_string("proxy_protocol_tlv_netns"), NULL, + ngx_stream_variable_proxy_protocol_tlv, + NGX_PROXY_PROTOCOL_TLV_NETNS, 0, 0 }, + { ngx_string("server_addr"), NULL, ngx_stream_variable_server_addr, 0, 0, 0 }, @@ -635,8 +675,7 @@ ngx_stream_variable_proxy_protocol_tlv_0 size_t n; u_char *p; - ngx_str_t str; - ngx_int_t rc, type; + ngx_int_t type; n = name->len - (sizeof("proxy_protocol_tlv_0x") - 1); p = name->data + sizeof("proxy_protocol_tlv_0x") - 1; @@ -646,7 +685,58 @@ ngx_stream_variable_proxy_protocol_tlv_0 return NGX_ERROR; } - rc = ngx_proxy_protocol_lookup_tlv(s->connection, NULL, type, &str); + return ngx_stream_variable_proxy_protocol_tlv(s, v, type); +} + + +static ngx_int_t +ngx_stream_variable_proxy_protocol_tlv(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_str_t str; + ngx_int_t rc; + + rc = ngx_proxy_protocol_lookup_tlv(s->connection, NULL, data, &str); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_DECLINED) { + v->not_found = 1; + return NGX_OK; + } + + v->len = str.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = str.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_proxy_protocol_tlv_ssl(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_str_t str, ssl; + ngx_int_t rc; + + rc = ngx_proxy_protocol_lookup_tlv(s->connection, NULL, + NGX_PROXY_PROTOCOL_TLV_SSL, &ssl); + + if (rc == NGX_OK) { + if (ssl.len < sizeof(ngx_proxy_protocol_tlv_ssl_t)) { + return NGX_ERROR; + } + + ssl.data += sizeof(ngx_proxy_protocol_tlv_ssl_t); + ssl.len -= sizeof(ngx_proxy_protocol_tlv_ssl_t); + + rc = ngx_proxy_protocol_lookup_tlv(s->connection, &ssl, data, &str); + } if (rc == NGX_ERROR) { return NGX_ERROR; From zhangtong2017 at whu.edu.cn Fri Sep 9 14:48:14 2022 From: zhangtong2017 at whu.edu.cn (=?UTF-8?B?5byg5qGQ?=) Date: Fri, 9 Sep 2022 22:48:14 +0800 (GMT+08:00) Subject: [PATCH] Core: fixed potential memory leak in ngx_core_module_create_conf() Message-ID: <7ecb8554.143c3.18322b9054a.Coremail.zhangtong2017@whu.edu.cn> # HG changeset patch # User Tong Zhang # Date 1662734441 -28800 # Fri Sep 09 22:40:41 2022 +0800 # Node ID a1a31f97ae70621282ad04302acdb6c9def306f7 # Parent ba5cf8f73a2d0a3615565bf9545f3d65216a0530 Core: fixed potential memory leak in ngx_core_module_create_conf(). When "ngx_array_init" failed and returns a NULL, the object held by "ccf" may be leaked. diff -r ba5cf8f73a2d -r a1a31f97ae70 src/core/nginx.c --- a/src/core/nginx.c Thu Sep 08 13:53:49 2022 +0400 +++ b/src/core/nginx.c Fri Sep 09 22:40:41 2022 +0800 @@ -1076,6 +1076,7 @@ if (ngx_array_init(&ccf->env, cycle->pool, 1, sizeof(ngx_str_t)) != NGX_OK) { + ngx_pfree(cycle->pool, ccf); return NULL; } From zhangtong2017 at whu.edu.cn Fri Sep 9 15:01:09 2022 From: zhangtong2017 at whu.edu.cn (=?UTF-8?B?5byg5qGQ?=) Date: Fri, 9 Sep 2022 23:01:09 +0800 (GMT+08:00) Subject: [PATCH] Core: fixed potential memory leak in ngx_core_module_create_conf() Message-ID: <371315d3.1440f.18322c4d9f4.Coremail.zhangtong2017@whu.edu.cn> # HG changeset patch # User Tong Zhang # Date 1662734441 -28800 # Fri Sep 09 22:40:41 2022 +0800 # Node ID a1a31f97ae70621282ad04302acdb6c9def306f7 # Parent ba5cf8f73a2d0a3615565bf9545f3d65216a0530 Core: fixed potential memory leak in ngx_core_module_create_conf(). When "ngx_array_init" failed and returns a NULL, the object held by "ccf" may be leaked. diff -r ba5cf8f73a2d -r a1a31f97ae70 src/core/nginx.c --- a/src/core/nginx.c Thu Sep 08 13:53:49 2022 +0400 +++ b/src/core/nginx.c Fri Sep 09 22:40:41 2022 +0800 @@ -1076,6 +1076,7 @@ if (ngx_array_init(&ccf->env, cycle->pool, 1, sizeof(ngx_str_t)) != NGX_OK) { + ngx_pfree(cycle->pool, ccf); return NULL; } From arut at nginx.com Mon Sep 12 08:43:39 2022 From: arut at nginx.com (Roman Arutyunyan) Date: Mon, 12 Sep 2022 12:43:39 +0400 Subject: [PATCH] Core: fixed potential memory leak in ngx_core_module_create_conf() In-Reply-To: <371315d3.1440f.18322c4d9f4.Coremail.zhangtong2017@whu.edu.cn> References: <371315d3.1440f.18322c4d9f4.Coremail.zhangtong2017@whu.edu.cn> Message-ID: <20220912084339.xopig6hd6czi3hfy@N00W24XTQX> Hi, On Fri, Sep 09, 2022 at 11:01:09PM +0800, 张桐 wrote: > # HG changeset patch > # User Tong Zhang > # Date 1662734441 -28800 > # Fri Sep 09 22:40:41 2022 +0800 > # Node ID a1a31f97ae70621282ad04302acdb6c9def306f7 > # Parent ba5cf8f73a2d0a3615565bf9545f3d65216a0530 > Core: fixed potential memory leak in ngx_core_module_create_conf(). > > When "ngx_array_init" failed and returns a NULL, the object held by "ccf" may be leaked. > > diff -r ba5cf8f73a2d -r a1a31f97ae70 src/core/nginx.c > --- a/src/core/nginx.c Thu Sep 08 13:53:49 2022 +0400 > +++ b/src/core/nginx.c Fri Sep 09 22:40:41 2022 +0800 > @@ -1076,6 +1076,7 @@ > if (ngx_array_init(&ccf->env, cycle->pool, 1, sizeof(ngx_str_t)) > != NGX_OK) > { > + ngx_pfree(cycle->pool, ccf); > return NULL; > } The ccf object is allocated from cycle->pool and will be freed automatically when the pool is destroyed, which will quickly happen after this error. Also, ngx_pfree() only works for large objects. For cycle pools 'large' means bigger than 16K. An ngx_core_conf_t object is smaller than that, which means that nothing will be freed anyway. If you're interested in getting more information about nginx pools, visit nginx development guide: https://nginx.org/en/docs/dev/development_guide.html#pool -- Roman Arutyunyan From mdounin at mdounin.ru Mon Sep 12 21:30:17 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 13 Sep 2022 00:30:17 +0300 Subject: [PATCH] Core: support for reading PROXY protocol v2 TLVs In-Reply-To: <20220909154658.fpnpndo2opnnzywx@N00W24XTQX> References: <4b856f1dff939e4eb9c1.1661961135@arut-laptop> <20220905132318.s27wgtof6wuqde7x@N00W24XTQX> <20220909154658.fpnpndo2opnnzywx@N00W24XTQX> Message-ID: Hello! On Fri, Sep 09, 2022 at 07:46:58PM +0400, Roman Arutyunyan wrote: > On Mon, Sep 05, 2022 at 06:58:38PM +0300, Maxim Dounin wrote: > > Hello! > > > > On Mon, Sep 05, 2022 at 05:23:18PM +0400, Roman Arutyunyan wrote: > > > > > Hi, > > > > > > On Mon, Sep 05, 2022 at 03:52:49AM +0300, Maxim Dounin wrote: > > > > Hello! > > > > > > > > On Wed, Aug 31, 2022 at 07:52:15PM +0400, Roman Arutyunyan wrote: > > > > > > > > > # HG changeset patch > > > > > # User Roman Arutyunyan > > > > > # Date 1661436099 -14400 > > > > > # Thu Aug 25 18:01:39 2022 +0400 > > > > > # Node ID 4b856f1dff939e4eb9c131e17af061cf2c38cfac > > > > > # Parent 069a4813e8d6d7ec662d282a10f5f7062ebd817f > > > > > Core: support for reading PROXY protocol v2 TLVs. > > > > > > > > First of all, could you please provide details on the use case? > > > > I've seen requests for writing proxy protocol TLVs to upstream > > > > servers (see ticket #1639), but not yet seen any meaningful > > > > reading requests. > > > > > > The known cases are these: > > > > > > - https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-target-groups.html#proxy-protocol > > > - https://docs.microsoft.com/en-us/azure/private-link/private-link-service-overview#getting-connection-information-using-tcp-proxy-v2 > > > - https://cloud.google.com/vpc/docs/configure-private-service-connect-producer#proxy-protocol > > > > > > The data may need further parsing, but it can be done in njs or perl. > > > > Thanks for the details. So, basically, it's about vendor-specific > > endpoint IDs. > > > > > > > The TLV values are available in HTTP and Stream variables > > > > > $proxy_protocol_tlv_0xN, where N is a hexadecimal TLV type number with no > > > > > leading zeroes. > > > > > > > > I can't say I like the "hexadecimal TLV type number with no > > > > leading zeroes" approach, especially given that the specification > > > > uses leading zeroes in TLV types. With leading zeros might be > > > > better, to match specification. > > With on-demand approach this is no longer an issue. > > > > > Also, it might worth the effort to actually add names for known > > > > types instead or in addition to numbers. > > > > > > This is indeed a good idea and we have such plans as a further extenion of this > > > work. One of the problems is however that the abovementioned TLV variables > > > are specified in internal documents of AWS/Azure/GCP which are not standards. > > > They can be changed anytime, while we have to maintain those variables in > > > nginx. Also, raw variables give more flexibility in supporting less known TLVs. > > > > Of course I'm not suggesting to ditch raw variables, at least not > > for unknown/non-standard values. But for known/standard values it > > should be easy enough to provide alternative names for easier use, > > probably with type-specific parsing. > > > > With on-demand parsing it would be trivial to support both > > $proxy_protocol_tlv_alpn and $proxy_protocol_tlv_0x01. Further, > > it will be trivial to support $proxy_protocol_tlv_aws_vpc_id while > > still providing $proxy_protocol_tlv_0xea for raw data. [...] > # HG changeset patch > # User Roman Arutyunyan > # Date 1662718130 -14400 > # Fri Sep 09 14:08:50 2022 +0400 > # Node ID 832f6c96b26c3009640072e3f4b1f0bf43e644d0 > # Parent 80714f1b0f597ce5e530e7274457450db4587fc9 > Standard PROXY protocol v2 TLV variables. > > diff --git a/src/core/ngx_proxy_protocol.h b/src/core/ngx_proxy_protocol.h > --- a/src/core/ngx_proxy_protocol.h > +++ b/src/core/ngx_proxy_protocol.h > @@ -13,7 +13,18 @@ > #include > > > -#define NGX_PROXY_PROTOCOL_MAX_HEADER 107 > +#define NGX_PROXY_PROTOCOL_MAX_HEADER 107 > + > +#define NGX_PROXY_PROTOCOL_TLV_ALPN 0x01 > +#define NGX_PROXY_PROTOCOL_TLV_AUTHORITY 0x02 > +#define NGX_PROXY_PROTOCOL_TLV_UNIQUE_ID 0x05 > +#define NGX_PROXY_PROTOCOL_TLV_SSL 0x20 > +#define NGX_PROXY_PROTOCOL_TLV_SSL_VERSION 0x21 > +#define NGX_PROXY_PROTOCOL_TLV_SSL_CN 0x22 > +#define NGX_PROXY_PROTOCOL_TLV_SSL_CIPHER 0x23 > +#define NGX_PROXY_PROTOCOL_TLV_SSL_SIG_ALG 0x24 > +#define NGX_PROXY_PROTOCOL_TLV_SSL_KEY_ALG 0x25 > +#define NGX_PROXY_PROTOCOL_TLV_NETNS 0x30 > > > struct ngx_proxy_protocol_s { > @@ -25,6 +36,12 @@ struct ngx_proxy_protocol_s { > }; > > > +typedef struct { > + u_char client; > + u_char verify[4]; Note that these two are completely ignored. > +} ngx_proxy_protocol_tlv_ssl_t; > + > + > u_char *ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, > u_char *last); > u_char *ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, > diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c > --- a/src/http/ngx_http_variables.c > +++ b/src/http/ngx_http_variables.c > @@ -63,6 +63,10 @@ static ngx_int_t ngx_http_variable_proxy > ngx_http_variable_value_t *v, uintptr_t data); > static ngx_int_t ngx_http_variable_proxy_protocol_tlv_0x(ngx_http_request_t *r, > ngx_http_variable_value_t *v, uintptr_t data); > +static ngx_int_t ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r, > + ngx_http_variable_value_t *v, uintptr_t data); > +static ngx_int_t ngx_http_variable_proxy_protocol_tlv_ssl( > + ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); > static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r, > ngx_http_variable_value_t *v, uintptr_t data); > static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r, > @@ -220,6 +224,42 @@ static ngx_http_variable_t ngx_http_cor > ngx_http_variable_proxy_protocol_tlv_0x, > 0, NGX_HTTP_VAR_PREFIX, 0 }, > > + { ngx_string("proxy_protocol_tlv_alpn"), NULL, > + ngx_http_variable_proxy_protocol_tlv, > + NGX_PROXY_PROTOCOL_TLV_ALPN, 0, 0 }, > + > + { ngx_string("proxy_protocol_tlv_authority"), NULL, > + ngx_http_variable_proxy_protocol_tlv, > + NGX_PROXY_PROTOCOL_TLV_AUTHORITY, 0, 0 }, > + > + { ngx_string("proxy_protocol_tlv_unique_id"), NULL, > + ngx_http_variable_proxy_protocol_tlv, > + NGX_PROXY_PROTOCOL_TLV_UNIQUE_ID, 0, 0 }, > + > + { ngx_string("proxy_protocol_tlv_ssl_version"), NULL, > + ngx_http_variable_proxy_protocol_tlv_ssl, > + NGX_PROXY_PROTOCOL_TLV_SSL_VERSION, 0, 0 }, > + > + { ngx_string("proxy_protocol_tlv_ssl_cn"), NULL, > + ngx_http_variable_proxy_protocol_tlv_ssl, > + NGX_PROXY_PROTOCOL_TLV_SSL_CN, 0, 0 }, > + > + { ngx_string("proxy_protocol_tlv_ssl_cipher"), NULL, > + ngx_http_variable_proxy_protocol_tlv_ssl, > + NGX_PROXY_PROTOCOL_TLV_SSL_CIPHER, 0, 0 }, > + > + { ngx_string("proxy_protocol_tlv_ssl_sig_alg"), NULL, > + ngx_http_variable_proxy_protocol_tlv_ssl, > + NGX_PROXY_PROTOCOL_TLV_SSL_SIG_ALG, 0, 0 }, > + > + { ngx_string("proxy_protocol_tlv_ssl_key_alg"), NULL, > + ngx_http_variable_proxy_protocol_tlv_ssl, > + NGX_PROXY_PROTOCOL_TLV_SSL_KEY_ALG, 0, 0 }, > + > + { ngx_string("proxy_protocol_tlv_netns"), NULL, > + ngx_http_variable_proxy_protocol_tlv, > + NGX_PROXY_PROTOCOL_TLV_NETNS, 0, 0 }, > + Further, these provide no interface to support arbitrary SSL TLVs (that is, something like "$proxy_protocol_tlv_ssl_0x10"). Overall, I tend to think it would be much easier / less intrusive to keep all the TLV handling logic in the src/core/ngx_proxy_protocol.c, including both arbitrary TLVs with hexadecimal type and known named TLVs, and only provide a single prefix variable in stream / http modules. That is, use "$proxy_protocol_tlv_" prefix variable and call a function to obtain TLV with the rest of the variable name. This might imply slightly more complex/less efficient name parsing logic for known TLVs, but I don't think it will be noticeable. On the other hand, it will reduce clutter in the variables hash, and therefore will save some resources in configurations where these variables are not used. [...] -- Maxim Dounin http://mdounin.ru/ From xeioex at nginx.com Tue Sep 13 00:57:16 2022 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 13 Sep 2022 00:57:16 +0000 Subject: [njs] Parser: fixed async token as a property name of an object. Message-ID: details: https://hg.nginx.org/njs/rev/86d181bb72e4 branches: changeset: 1951:86d181bb72e4 user: Dmitry Volyntsev date: Mon Sep 12 17:56:44 2022 -0700 description: Parser: fixed async token as a property name of an object. This closes #575 issue on Github. diffstat: src/njs_parser.c | 4 ++++ src/test/njs_unit_test.c | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diffs (28 lines): diff -r 4689935e5d36 -r 86d181bb72e4 src/njs_parser.c --- a/src/njs_parser.c Thu Sep 08 10:03:25 2022 -0700 +++ b/src/njs_parser.c Mon Sep 12 17:56:44 2022 -0700 @@ -1983,6 +1983,10 @@ njs_parser_property_definition(njs_parse njs_parser_computed_property_async_after); } + if (token->type == NJS_TOKEN_COLON) { + return njs_parser_property_name(parser, current, 2); + } + if (!njs_lexer_token_is_identifier_name(token)) { return njs_parser_failed(parser); } diff -r 4689935e5d36 -r 86d181bb72e4 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Thu Sep 08 10:03:25 2022 -0700 +++ b/src/test/njs_unit_test.c Mon Sep 12 17:56:44 2022 -0700 @@ -3883,8 +3883,8 @@ static njs_unit_test_t njs_test[] = "[f.length, delete f.length, f.length, delete f.length]"), njs_str("2,true,0,true") }, - { njs_str("njs.dump({break:1,3:2,'a':4,\"b\":2,true:1,null:0})"), - njs_str("{break:1,3:2,a:4,b:2,true:1,null:0}") }, + { njs_str("njs.dump({break:1,3:2,'a':4,\"b\":2,true:1,null:0,async:2})"), + njs_str("{break:1,3:2,a:4,b:2,true:1,null:0,async:2}") }, { njs_str("var o1 = {a:1,b:2}, o2 = {c:3}; o1.a + o2.c"), njs_str("4") }, From thresh at nginx.com Tue Sep 13 12:18:46 2022 From: thresh at nginx.com (=?iso-8859-1?q?Konstantin_Pavlov?=) Date: Tue, 13 Sep 2022 16:18:46 +0400 Subject: [PATCH] Linux packages: removed Debian 10 'buster' due to EOL Message-ID: <3907d2d6e9e23b59549c.1663071526@QGCD7XG9R9> # HG changeset patch # User Konstantin Pavlov # Date 1663071405 -14400 # Tue Sep 13 16:16:45 2022 +0400 # Node ID 3907d2d6e9e23b59549ce83829cee0d2affbd8de # Parent 05284b3a363fdac0b7ce7ec9428cf521e0101767 Linux packages: removed Debian 10 'buster' due to EOL. diff -r 05284b3a363f -r 3907d2d6e9e2 xml/en/linux_packages.xml --- a/xml/en/linux_packages.xml Tue Sep 13 13:14:33 2022 +0100 +++ b/xml/en/linux_packages.xml Tue Sep 13 16:16:45 2022 +0400 @@ -7,7 +7,7 @@
+ rev="78">
@@ -55,11 +55,6 @@ versions: -10.x “buster” -x86_64, i386, aarch64/arm64 - - - 11.x “bullseye” x86_64, aarch64/arm64 diff -r 05284b3a363f -r 3907d2d6e9e2 xml/ru/linux_packages.xml --- a/xml/ru/linux_packages.xml Tue Sep 13 13:14:33 2022 +0100 +++ b/xml/ru/linux_packages.xml Tue Sep 13 16:16:45 2022 +0400 @@ -7,7 +7,7 @@
+ rev="76">
@@ -55,11 +55,6 @@ -10.x “buster” -x86_64, i386, aarch64/arm64 - - - 11.x “bullseye” x86_64, aarch64/arm64 From pluknet at nginx.com Tue Sep 13 12:52:18 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Tue, 13 Sep 2022 16:52:18 +0400 Subject: [PATCH] Linux packages: removed Debian 10 'buster' due to EOL In-Reply-To: <3907d2d6e9e23b59549c.1663071526@QGCD7XG9R9> References: <3907d2d6e9e23b59549c.1663071526@QGCD7XG9R9> Message-ID: <2A2C9044-7A3E-4153-8A20-A9E296CB9954@nginx.com> > On 13 Sep 2022, at 16:18, Konstantin Pavlov wrote: > > # HG changeset patch > # User Konstantin Pavlov > # Date 1663071405 -14400 > # Tue Sep 13 16:16:45 2022 +0400 > # Node ID 3907d2d6e9e23b59549ce83829cee0d2affbd8de > # Parent 05284b3a363fdac0b7ce7ec9428cf521e0101767 > Linux packages: removed Debian 10 'buster' due to EOL. > > diff -r 05284b3a363f -r 3907d2d6e9e2 xml/en/linux_packages.xml > --- a/xml/en/linux_packages.xml Tue Sep 13 13:14:33 2022 +0100 > +++ b/xml/en/linux_packages.xml Tue Sep 13 16:16:45 2022 +0400 > @@ -7,7 +7,7 @@ >
link="/en/linux_packages.html" > lang="en" > - rev="77"> > + rev="78"> > >
> > @@ -55,11 +55,6 @@ versions: > > > > -10.x “buster” > -x86_64, i386, aarch64/arm64 > - > - > - > 11.x “bullseye” > x86_64, aarch64/arm64 > > diff -r 05284b3a363f -r 3907d2d6e9e2 xml/ru/linux_packages.xml > --- a/xml/ru/linux_packages.xml Tue Sep 13 13:14:33 2022 +0100 > +++ b/xml/ru/linux_packages.xml Tue Sep 13 16:16:45 2022 +0400 > @@ -7,7 +7,7 @@ >
link="/ru/linux_packages.html" > lang="ru" > - rev="77"> > + rev="76"> Counter incremented backwards, otherwise looks good. > >
> > @@ -55,11 +55,6 @@ > > > > -10.x “buster” > -x86_64, i386, aarch64/arm64 > - > - > - > 11.x “bullseye” > x86_64, aarch64/arm64 > > _______________________________________________ > nginx-devel mailing list -- nginx-devel at nginx.org > To unsubscribe send an email to nginx-devel-leave at nginx.org -- Sergey Kandaurov From thresh at nginx.com Tue Sep 13 12:58:04 2022 From: thresh at nginx.com (Konstantin Pavlov) Date: Tue, 13 Sep 2022 16:58:04 +0400 Subject: [PATCH] Linux packages: removed Debian 10 'buster' due to EOL In-Reply-To: <2A2C9044-7A3E-4153-8A20-A9E296CB9954@nginx.com> References: <3907d2d6e9e23b59549c.1663071526@QGCD7XG9R9> <2A2C9044-7A3E-4153-8A20-A9E296CB9954@nginx.com> Message-ID: <4a26fad8-d8e0-efb9-f5cd-1e0c36ad4e69@nginx.com> Hi On 13/09/2022 4:52 PM, Sergey Kandaurov wrote: >> On 13 Sep 2022, at 16:18, Konstantin Pavlov wrote: >> >> # HG changeset patch >> # User Konstantin Pavlov >> # Date 1663071405 -14400 >> # Tue Sep 13 16:16:45 2022 +0400 >> # Node ID 3907d2d6e9e23b59549ce83829cee0d2affbd8de >> # Parent 05284b3a363fdac0b7ce7ec9428cf521e0101767 >> Linux packages: removed Debian 10 'buster' due to EOL. >> >> diff -r 05284b3a363f -r 3907d2d6e9e2 xml/en/linux_packages.xml >> --- a/xml/en/linux_packages.xml Tue Sep 13 13:14:33 2022 +0100 >> +++ b/xml/en/linux_packages.xml Tue Sep 13 16:16:45 2022 +0400 >> @@ -7,7 +7,7 @@ >>
> link="/en/linux_packages.html" >> lang="en" >> - rev="77"> >> + rev="78"> >> >>
>> >> @@ -55,11 +55,6 @@ versions: >> >> >> >> -10.x “buster” >> -x86_64, i386, aarch64/arm64 >> - >> - >> - >> 11.x “bullseye” >> x86_64, aarch64/arm64 >> >> diff -r 05284b3a363f -r 3907d2d6e9e2 xml/ru/linux_packages.xml >> --- a/xml/ru/linux_packages.xml Tue Sep 13 13:14:33 2022 +0100 >> +++ b/xml/ru/linux_packages.xml Tue Sep 13 16:16:45 2022 +0400 >> @@ -7,7 +7,7 @@ >>
> link="/ru/linux_packages.html" >> lang="ru" >> - rev="77"> >> + rev="76"> > Counter incremented backwards, > otherwise looks good. Oh right - thanks! Pushed. From arut at nginx.com Tue Sep 13 15:03:04 2022 From: arut at nginx.com (Roman Arutyunyan) Date: Tue, 13 Sep 2022 19:03:04 +0400 Subject: [PATCH] Core: support for reading PROXY protocol v2 TLVs In-Reply-To: References: <4b856f1dff939e4eb9c1.1661961135@arut-laptop> <20220905132318.s27wgtof6wuqde7x@N00W24XTQX> <20220909154658.fpnpndo2opnnzywx@N00W24XTQX> Message-ID: <20220913150304.k2fjjdxgesgzbilu@N00W24XTQX> Hi, On Tue, Sep 13, 2022 at 12:30:17AM +0300, Maxim Dounin wrote: > Hello! > > On Fri, Sep 09, 2022 at 07:46:58PM +0400, Roman Arutyunyan wrote: > > > On Mon, Sep 05, 2022 at 06:58:38PM +0300, Maxim Dounin wrote: > > > Hello! > > > > > > On Mon, Sep 05, 2022 at 05:23:18PM +0400, Roman Arutyunyan wrote: > > > > > > > Hi, > > > > > > > > On Mon, Sep 05, 2022 at 03:52:49AM +0300, Maxim Dounin wrote: > > > > > Hello! > > > > > > > > > > On Wed, Aug 31, 2022 at 07:52:15PM +0400, Roman Arutyunyan wrote: > > > > > > > > > > > # HG changeset patch > > > > > > # User Roman Arutyunyan > > > > > > # Date 1661436099 -14400 > > > > > > # Thu Aug 25 18:01:39 2022 +0400 > > > > > > # Node ID 4b856f1dff939e4eb9c131e17af061cf2c38cfac > > > > > > # Parent 069a4813e8d6d7ec662d282a10f5f7062ebd817f > > > > > > Core: support for reading PROXY protocol v2 TLVs. > > > > > > > > > > First of all, could you please provide details on the use case? > > > > > I've seen requests for writing proxy protocol TLVs to upstream > > > > > servers (see ticket #1639), but not yet seen any meaningful > > > > > reading requests. > > > > > > > > The known cases are these: > > > > > > > > - https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-target-groups.html#proxy-protocol > > > > - https://docs.microsoft.com/en-us/azure/private-link/private-link-service-overview#getting-connection-information-using-tcp-proxy-v2 > > > > - https://cloud.google.com/vpc/docs/configure-private-service-connect-producer#proxy-protocol > > > > > > > > The data may need further parsing, but it can be done in njs or perl. > > > > > > Thanks for the details. So, basically, it's about vendor-specific > > > endpoint IDs. > > > > > > > > > The TLV values are available in HTTP and Stream variables > > > > > > $proxy_protocol_tlv_0xN, where N is a hexadecimal TLV type number with no > > > > > > leading zeroes. > > > > > > > > > > I can't say I like the "hexadecimal TLV type number with no > > > > > leading zeroes" approach, especially given that the specification > > > > > uses leading zeroes in TLV types. With leading zeros might be > > > > > better, to match specification. > > > > With on-demand approach this is no longer an issue. > > > > > > > Also, it might worth the effort to actually add names for known > > > > > types instead or in addition to numbers. > > > > > > > > This is indeed a good idea and we have such plans as a further extenion of this > > > > work. One of the problems is however that the abovementioned TLV variables > > > > are specified in internal documents of AWS/Azure/GCP which are not standards. > > > > They can be changed anytime, while we have to maintain those variables in > > > > nginx. Also, raw variables give more flexibility in supporting less known TLVs. > > > > > > Of course I'm not suggesting to ditch raw variables, at least not > > > for unknown/non-standard values. But for known/standard values it > > > should be easy enough to provide alternative names for easier use, > > > probably with type-specific parsing. > > > > > > With on-demand parsing it would be trivial to support both > > > $proxy_protocol_tlv_alpn and $proxy_protocol_tlv_0x01. Further, > > > it will be trivial to support $proxy_protocol_tlv_aws_vpc_id while > > > still providing $proxy_protocol_tlv_0xea for raw data. > > [...] > > > # HG changeset patch > > # User Roman Arutyunyan > > # Date 1662718130 -14400 > > # Fri Sep 09 14:08:50 2022 +0400 > > # Node ID 832f6c96b26c3009640072e3f4b1f0bf43e644d0 > > # Parent 80714f1b0f597ce5e530e7274457450db4587fc9 > > Standard PROXY protocol v2 TLV variables. > > > > diff --git a/src/core/ngx_proxy_protocol.h b/src/core/ngx_proxy_protocol.h > > --- a/src/core/ngx_proxy_protocol.h > > +++ b/src/core/ngx_proxy_protocol.h > > @@ -13,7 +13,18 @@ > > #include > > > > > > -#define NGX_PROXY_PROTOCOL_MAX_HEADER 107 > > +#define NGX_PROXY_PROTOCOL_MAX_HEADER 107 > > + > > +#define NGX_PROXY_PROTOCOL_TLV_ALPN 0x01 > > +#define NGX_PROXY_PROTOCOL_TLV_AUTHORITY 0x02 > > +#define NGX_PROXY_PROTOCOL_TLV_UNIQUE_ID 0x05 > > +#define NGX_PROXY_PROTOCOL_TLV_SSL 0x20 > > +#define NGX_PROXY_PROTOCOL_TLV_SSL_VERSION 0x21 > > +#define NGX_PROXY_PROTOCOL_TLV_SSL_CN 0x22 > > +#define NGX_PROXY_PROTOCOL_TLV_SSL_CIPHER 0x23 > > +#define NGX_PROXY_PROTOCOL_TLV_SSL_SIG_ALG 0x24 > > +#define NGX_PROXY_PROTOCOL_TLV_SSL_KEY_ALG 0x25 > > +#define NGX_PROXY_PROTOCOL_TLV_NETNS 0x30 > > > > > > struct ngx_proxy_protocol_s { > > @@ -25,6 +36,12 @@ struct ngx_proxy_protocol_s { > > }; > > > > > > +typedef struct { > > + u_char client; > > + u_char verify[4]; > > Note that these two are completely ignored. Yes, currently they are. I thought about adding variables for them too, but then decided to postpone this. Do you think these have enough value to be added to this patch? > > +} ngx_proxy_protocol_tlv_ssl_t; > > + > > + > > u_char *ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, > > u_char *last); > > u_char *ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, > > diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c > > --- a/src/http/ngx_http_variables.c > > +++ b/src/http/ngx_http_variables.c > > @@ -63,6 +63,10 @@ static ngx_int_t ngx_http_variable_proxy > > ngx_http_variable_value_t *v, uintptr_t data); > > static ngx_int_t ngx_http_variable_proxy_protocol_tlv_0x(ngx_http_request_t *r, > > ngx_http_variable_value_t *v, uintptr_t data); > > +static ngx_int_t ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r, > > + ngx_http_variable_value_t *v, uintptr_t data); > > +static ngx_int_t ngx_http_variable_proxy_protocol_tlv_ssl( > > + ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); > > static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r, > > ngx_http_variable_value_t *v, uintptr_t data); > > static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r, > > @@ -220,6 +224,42 @@ static ngx_http_variable_t ngx_http_cor > > ngx_http_variable_proxy_protocol_tlv_0x, > > 0, NGX_HTTP_VAR_PREFIX, 0 }, > > > > + { ngx_string("proxy_protocol_tlv_alpn"), NULL, > > + ngx_http_variable_proxy_protocol_tlv, > > + NGX_PROXY_PROTOCOL_TLV_ALPN, 0, 0 }, > > + > > + { ngx_string("proxy_protocol_tlv_authority"), NULL, > > + ngx_http_variable_proxy_protocol_tlv, > > + NGX_PROXY_PROTOCOL_TLV_AUTHORITY, 0, 0 }, > > + > > + { ngx_string("proxy_protocol_tlv_unique_id"), NULL, > > + ngx_http_variable_proxy_protocol_tlv, > > + NGX_PROXY_PROTOCOL_TLV_UNIQUE_ID, 0, 0 }, > > + > > + { ngx_string("proxy_protocol_tlv_ssl_version"), NULL, > > + ngx_http_variable_proxy_protocol_tlv_ssl, > > + NGX_PROXY_PROTOCOL_TLV_SSL_VERSION, 0, 0 }, > > + > > + { ngx_string("proxy_protocol_tlv_ssl_cn"), NULL, > > + ngx_http_variable_proxy_protocol_tlv_ssl, > > + NGX_PROXY_PROTOCOL_TLV_SSL_CN, 0, 0 }, > > + > > + { ngx_string("proxy_protocol_tlv_ssl_cipher"), NULL, > > + ngx_http_variable_proxy_protocol_tlv_ssl, > > + NGX_PROXY_PROTOCOL_TLV_SSL_CIPHER, 0, 0 }, > > + > > + { ngx_string("proxy_protocol_tlv_ssl_sig_alg"), NULL, > > + ngx_http_variable_proxy_protocol_tlv_ssl, > > + NGX_PROXY_PROTOCOL_TLV_SSL_SIG_ALG, 0, 0 }, > > + > > + { ngx_string("proxy_protocol_tlv_ssl_key_alg"), NULL, > > + ngx_http_variable_proxy_protocol_tlv_ssl, > > + NGX_PROXY_PROTOCOL_TLV_SSL_KEY_ALG, 0, 0 }, > > + > > + { ngx_string("proxy_protocol_tlv_netns"), NULL, > > + ngx_http_variable_proxy_protocol_tlv, > > + NGX_PROXY_PROTOCOL_TLV_NETNS, 0, 0 }, > > + > > Further, these provide no interface to support arbitrary SSL TLVs > (that is, something like "$proxy_protocol_tlv_ssl_0x10"). > > Overall, I tend to think it would be much easier / less intrusive > to keep all the TLV handling logic in the > src/core/ngx_proxy_protocol.c, including both arbitrary TLVs with > hexadecimal type and known named TLVs, and only provide a single > prefix variable in stream / http modules. > > That is, use "$proxy_protocol_tlv_" prefix variable and call a > function to obtain TLV with the rest of the variable name. > > This might imply slightly more complex/less efficient name parsing > logic for known TLVs, but I don't think it will be noticeable. On > the other hand, it will reduce clutter in the variables hash, and > therefore will save some resources in configurations where these > variables are not used. OK. Stream/HTTP part now looks simpler. -- Roman Arutyunyan -------------- next part -------------- # HG changeset patch # User Roman Arutyunyan # Date 1663080928 -14400 # Tue Sep 13 18:55:28 2022 +0400 # Node ID 0736b29bfafac445fb8cf57312c3b7a2c3247a60 # Parent ba5cf8f73a2d0a3615565bf9545f3d65216a0530 PROXY protocol v2 TLV variables. The variables have prefix $proxy_protocol_tlv_ and are accessible by name and by type. Examples are: $proxy_protocol_tlv_0x01, $proxy_protocol_tlv_alpn. diff --git a/src/core/ngx_proxy_protocol.c b/src/core/ngx_proxy_protocol.c --- a/src/core/ngx_proxy_protocol.c +++ b/src/core/ngx_proxy_protocol.c @@ -40,6 +40,24 @@ typedef struct { } ngx_proxy_protocol_inet6_addrs_t; +typedef struct { + u_char type; + u_char len[2]; +} ngx_proxy_protocol_tlv_t; + + +typedef struct { + u_char client; + u_char verify[4]; +} ngx_proxy_protocol_tlv_ssl_t; + + +typedef struct { + ngx_str_t name; + ngx_uint_t type; +} ngx_proxy_protocol_tlv_entry_t; + + static u_char *ngx_proxy_protocol_read_addr(ngx_connection_t *c, u_char *p, u_char *last, ngx_str_t *addr); static u_char *ngx_proxy_protocol_read_port(u_char *p, u_char *last, @@ -48,6 +66,26 @@ static u_char *ngx_proxy_protocol_v2_rea u_char *last); +static ngx_proxy_protocol_tlv_entry_t ngx_proxy_protocol_tlv_entries[] = { + { ngx_string("alpn"), 0x01 }, + { ngx_string("authority"), 0x02 }, + { ngx_string("unique_id"), 0x05 }, + { ngx_string("ssl"), 0x20 }, + { ngx_string("netns"), 0x30 }, + { ngx_null_string, 0x00 } +}; + + +static ngx_proxy_protocol_tlv_entry_t ngx_proxy_protocol_tlv_ssl_entries[] = { + { ngx_string("version"), 0x21 }, + { ngx_string("cn"), 0x22 }, + { ngx_string("cipher"), 0x23 }, + { ngx_string("sig_alg"), 0x24 }, + { ngx_string("key_alg"), 0x25 }, + { ngx_null_string, 0x00 } +}; + + u_char * ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, u_char *last) { @@ -412,11 +450,128 @@ ngx_proxy_protocol_v2_read(ngx_connectio &pp->src_addr, pp->src_port, &pp->dst_addr, pp->dst_port); if (buf < end) { - ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, - "PROXY protocol v2 %z bytes of tlv ignored", end - buf); + pp->tlvs.data = ngx_pnalloc(c->pool, end - buf); + if (pp->tlvs.data == NULL) { + return NULL; + } + + ngx_memcpy(pp->tlvs.data, buf, end - buf); + pp->tlvs.len = end - buf; } c->proxy_protocol = pp; return end; } + + +ngx_int_t +ngx_proxy_protocol_lookup_tlv(ngx_connection_t *c, ngx_str_t *tlvs, + ngx_uint_t type, ngx_str_t *value) +{ + u_char *p; + size_t n, len; + ngx_proxy_protocol_tlv_t *tlv; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 lookup tlv:%02xi", type); + + p = tlvs->data; + n = tlvs->len; + + while (n) { + if (n < sizeof(ngx_proxy_protocol_tlv_t)) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, "broken PROXY protocol TLV"); + return NGX_ERROR; + } + + tlv = (ngx_proxy_protocol_tlv_t *) p; + len = ngx_proxy_protocol_parse_uint16(tlv->len); + + p += sizeof(ngx_proxy_protocol_tlv_t); + n -= sizeof(ngx_proxy_protocol_tlv_t); + + if (n < len) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, "broken PROXY protocol TLV"); + return NGX_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 tlv:0x%02xd len:%uz", tlv->type, len); + + if (tlv->type == type) { + value->data = p; + value->len = len; + return NGX_OK; + } + + p += len; + n -= len; + } + + return NGX_DECLINED; +} + + +ngx_int_t +ngx_proxy_protocol_get_tlv(ngx_connection_t *c, ngx_str_t *name, + ngx_str_t *value) +{ + u_char *p; + size_t n; + ngx_str_t ssl, *tlvs; + ngx_int_t rc, type; + ngx_proxy_protocol_tlv_entry_t *te; + + if (c->proxy_protocol == NULL) { + return NGX_DECLINED; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 get tlv \"%V\"", name); + + te = ngx_proxy_protocol_tlv_entries; + tlvs = &c->proxy_protocol->tlvs; + + p = name->data; + n = name->len; + + if (n >= 4 && p[0] == 's' && p[1] == 's' && p[2] == 'l' && p[3] == '_') { + + rc = ngx_proxy_protocol_lookup_tlv(c, tlvs, 0x20, &ssl); + if (rc != NGX_OK) { + return rc; + } + + if (ssl.len < sizeof(ngx_proxy_protocol_tlv_ssl_t)) { + return NGX_ERROR; + } + + ssl.data += sizeof(ngx_proxy_protocol_tlv_ssl_t); + ssl.len -= sizeof(ngx_proxy_protocol_tlv_ssl_t); + + te = ngx_proxy_protocol_tlv_ssl_entries; + tlvs = &ssl; + + p += 4; + n -= 4; + } + + if (n >= 2 && p[0] == '0' && p[1] == 'x') { + + type = ngx_hextoi(p + 2, n - 2); + if (type == NGX_ERROR) { + return NGX_ERROR; + } + + return ngx_proxy_protocol_lookup_tlv(c, tlvs, type, value); + } + + for ( /* void */ ; te->type; te++) { + if (te->name.len == n && ngx_strncmp(te->name.data, p, n) == 0) { + return ngx_proxy_protocol_lookup_tlv(c, tlvs, te->type, value); + } + } + + return NGX_DECLINED; +} diff --git a/src/core/ngx_proxy_protocol.h b/src/core/ngx_proxy_protocol.h --- a/src/core/ngx_proxy_protocol.h +++ b/src/core/ngx_proxy_protocol.h @@ -21,6 +21,7 @@ struct ngx_proxy_protocol_s { ngx_str_t dst_addr; in_port_t src_port; in_port_t dst_port; + ngx_str_t tlvs; }; @@ -28,6 +29,10 @@ u_char *ngx_proxy_protocol_read(ngx_conn u_char *last); u_char *ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, u_char *last); +ngx_int_t ngx_proxy_protocol_lookup_tlv(ngx_connection_t *c, ngx_str_t *tlvs, + ngx_uint_t type, ngx_str_t *value); +ngx_int_t ngx_proxy_protocol_get_tlv(ngx_connection_t *c, ngx_str_t *name, + ngx_str_t *value); #endif /* _NGX_PROXY_PROTOCOL_H_INCLUDED_ */ diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -61,6 +61,8 @@ static ngx_int_t ngx_http_variable_proxy ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_proxy_protocol_port(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r, @@ -214,6 +216,10 @@ static ngx_http_variable_t ngx_http_cor ngx_http_variable_proxy_protocol_port, offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 }, + { ngx_string("proxy_protocol_tlv_"), NULL, + ngx_http_variable_proxy_protocol_tlv, + 0, NGX_HTTP_VAR_PREFIX, 0 }, + { ngx_string("server_addr"), NULL, ngx_http_variable_server_addr, 0, 0, 0 }, { ngx_string("server_port"), NULL, ngx_http_variable_server_port, 0, 0, 0 }, @@ -1387,6 +1393,39 @@ ngx_http_variable_proxy_protocol_port(ng static ngx_int_t +ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_str_t *name = (ngx_str_t *) data; + + ngx_int_t rc; + ngx_str_t tlv, value; + + tlv.len = name->len - (sizeof("proxy_protocol_tlv_") - 1); + tlv.data = name->data + sizeof("proxy_protocol_tlv_") - 1; + + rc = ngx_proxy_protocol_get_tlv(r->connection, &tlv, &value); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_DECLINED) { + v->not_found = 1; + return NGX_OK; + } + + v->len = value.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = value.data; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { diff --git a/src/stream/ngx_stream_variables.c b/src/stream/ngx_stream_variables.c --- a/src/stream/ngx_stream_variables.c +++ b/src/stream/ngx_stream_variables.c @@ -23,6 +23,8 @@ static ngx_int_t ngx_stream_variable_pro ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_variable_proxy_protocol_port( ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_proxy_protocol_tlv( + ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_variable_server_addr(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_variable_server_port(ngx_stream_session_t *s, @@ -79,6 +81,10 @@ static ngx_stream_variable_t ngx_stream ngx_stream_variable_proxy_protocol_port, offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 }, + { ngx_string("proxy_protocol_tlv_"), NULL, + ngx_stream_variable_proxy_protocol_tlv, + 0, NGX_STREAM_VAR_PREFIX, 0 }, + { ngx_string("server_addr"), NULL, ngx_stream_variable_server_addr, 0, 0, 0 }, @@ -622,6 +628,39 @@ ngx_stream_variable_proxy_protocol_port( static ngx_int_t +ngx_stream_variable_proxy_protocol_tlv(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_str_t *name = (ngx_str_t *) data; + + ngx_int_t rc; + ngx_str_t tlv, value; + + tlv.len = name->len - (sizeof("proxy_protocol_tlv_") - 1); + tlv.data = name->data + sizeof("proxy_protocol_tlv_") - 1; + + rc = ngx_proxy_protocol_get_tlv(s->connection, &tlv, &value); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_DECLINED) { + v->not_found = 1; + return NGX_OK; + } + + v->len = value.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = value.data; + + return NGX_OK; +} + + +static ngx_int_t ngx_stream_variable_server_addr(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data) { From v.zhestikov at f5.com Wed Sep 14 04:22:12 2022 From: v.zhestikov at f5.com (Vadim Zhestikov) Date: Wed, 14 Sep 2022 04:22:12 +0000 Subject: [njs] Modules: common code for js_import is moved to shared library. Message-ID: details: https://hg.nginx.org/njs/rev/05efe34376ab branches: changeset: 1952:05efe34376ab user: Vadim Zhestikov date: Tue Sep 13 21:13:17 2022 -0700 description: Modules: common code for js_import is moved to shared library. diffstat: nginx/ngx_http_js_module.c | 160 +++++------------------------------------- nginx/ngx_js.c | 107 ++++++++++++++++++++++++++++ nginx/ngx_js.h | 18 ++++ nginx/ngx_stream_js_module.c | 153 ++++------------------------------------ 4 files changed, 164 insertions(+), 274 deletions(-) diffs (574 lines): diff -r 86d181bb72e4 -r 05efe34376ab nginx/ngx_http_js_module.c --- a/nginx/ngx_http_js_module.c Mon Sep 12 17:56:44 2022 -0700 +++ b/nginx/ngx_http_js_module.c Tue Sep 13 21:13:17 2022 -0700 @@ -12,15 +12,8 @@ #include "ngx_js.h" -#define NJS_HEADER_SEMICOLON 0x1 -#define NJS_HEADER_SINGLE 0x2 -#define NJS_HEADER_ARRAY 0x4 - - typedef struct { - njs_vm_t *vm; - ngx_array_t *imports; - ngx_array_t *paths; + NGX_JS_COMMON_CONF; ngx_str_t content; ngx_str_t header_filter; @@ -42,12 +35,9 @@ typedef struct { } ngx_http_js_loc_conf_t; -typedef struct { - ngx_str_t name; - ngx_str_t path; - u_char *file; - ngx_uint_t line; -} ngx_http_js_import_t; +#define NJS_HEADER_SEMICOLON 0x1 +#define NJS_HEADER_SINGLE 0x2 +#define NJS_HEADER_ARRAY 0x4 typedef struct { @@ -263,8 +253,6 @@ static void ngx_http_js_handle_event(ngx njs_vm_event_t vm_event, njs_value_t *args, njs_uint_t nargs); static ngx_int_t ngx_http_js_init(ngx_conf_t *cf); -static char *ngx_http_js_import(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf); static char *ngx_http_js_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_js_var(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_js_content(ngx_conf_t *cf, ngx_command_t *cmd, @@ -301,7 +289,7 @@ static ngx_command_t ngx_http_js_comman { ngx_string("js_import"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE13, - ngx_http_js_import, + ngx_js_import, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, @@ -4182,10 +4170,10 @@ static ngx_int_t ngx_http_js_merge_vm(ngx_conf_t *cf, ngx_http_js_loc_conf_t *conf, ngx_http_js_loc_conf_t *prev) { - ngx_str_t *path, *s; - ngx_uint_t i; - ngx_array_t *imports, *paths; - ngx_http_js_import_t *import, *pi; + ngx_str_t *path, *s; + ngx_uint_t i; + ngx_array_t *imports, *paths; + ngx_js_named_path_t *import, *pi; if (prev->imports != NGX_CONF_UNSET_PTR && prev->vm == NULL) { if (ngx_http_js_init_conf_vm(cf, prev) != NGX_OK) { @@ -4210,7 +4198,7 @@ ngx_http_js_merge_vm(ngx_conf_t *cf, ngx } else { imports = ngx_array_create(cf->pool, 4, - sizeof(ngx_http_js_import_t)); + sizeof(ngx_js_named_path_t)); if (imports == NULL) { return NGX_ERROR; } @@ -4288,17 +4276,17 @@ ngx_http_js_merge_vm(ngx_conf_t *cf, ngx static ngx_int_t ngx_http_js_init_conf_vm(ngx_conf_t *cf, ngx_http_js_loc_conf_t *conf) { - size_t size; - u_char *start, *end, *p; - ngx_str_t *m, file; - njs_int_t rc; - njs_str_t text, path; - ngx_uint_t i; - njs_value_t *value; - njs_vm_opt_t options; - ngx_pool_cleanup_t *cln; - njs_opaque_value_t lvalue, exception; - ngx_http_js_import_t *import; + size_t size; + u_char *start, *end, *p; + ngx_str_t *m, file; + njs_int_t rc; + njs_str_t text, path; + ngx_uint_t i; + njs_value_t *value; + njs_vm_opt_t options; + ngx_pool_cleanup_t *cln; + njs_opaque_value_t lvalue, exception; + ngx_js_named_path_t *import; static const njs_str_t line_number_key = njs_str("lineNumber"); static const njs_str_t file_name_key = njs_str("fileName"); @@ -4466,112 +4454,6 @@ ngx_http_js_init(ngx_conf_t *cf) static char * -ngx_http_js_import(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - ngx_http_js_loc_conf_t *jlcf = conf; - - u_char *p, *end, c; - ngx_int_t from; - ngx_str_t *value, name, path; - ngx_http_js_import_t *import; - - value = cf->args->elts; - from = (cf->args->nelts == 4); - - if (from) { - if (ngx_strcmp(value[2].data, "from") != 0) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid parameter \"%V\"", &value[2]); - return NGX_CONF_ERROR; - } - } - - name = value[1]; - path = (from ? value[3] : value[1]); - - if (!from) { - end = name.data + name.len; - - for (p = end - 1; p >= name.data; p--) { - if (*p == '/') { - break; - } - } - - name.data = p + 1; - name.len = end - p - 1; - - if (name.len < 3 - || ngx_memcmp(&name.data[name.len - 3], ".js", 3) != 0) - { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "cannot extract export name from file path " - "\"%V\", use extended \"from\" syntax", &path); - return NGX_CONF_ERROR; - } - - name.len -= 3; - } - - if (name.len == 0) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty export name"); - return NGX_CONF_ERROR; - } - - p = name.data; - end = name.data + name.len; - - while (p < end) { - c = ngx_tolower(*p); - - if (*p != '_' && (c < 'a' || c > 'z')) { - if (p == name.data) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "cannot start " - "with \"%c\" in export name \"%V\"", *p, - &name); - return NGX_CONF_ERROR; - } - - if (*p < '0' || *p > '9') { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid character " - "\"%c\" in export name \"%V\"", *p, - &name); - return NGX_CONF_ERROR; - } - } - - p++; - } - - if (ngx_strchr(path.data, '\'') != NULL) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid character \"'\" " - "in file path \"%V\"", &path); - return NGX_CONF_ERROR; - } - - if (jlcf->imports == NGX_CONF_UNSET_PTR) { - jlcf->imports = ngx_array_create(cf->pool, 4, - sizeof(ngx_http_js_import_t)); - if (jlcf->imports == NULL) { - return NGX_CONF_ERROR; - } - } - - import = ngx_array_push(jlcf->imports); - if (import == NULL) { - return NGX_CONF_ERROR; - } - - import->name = name; - import->path = path; - import->file = cf->conf_file->file.name.data; - import->line = cf->conf_file->line; - - return NGX_CONF_OK; -} - - -static char * ngx_http_js_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_str_t *value, *fname; diff -r 86d181bb72e4 -r 05efe34376ab nginx/ngx_js.c --- a/nginx/ngx_js.c Mon Sep 12 17:56:44 2022 -0700 +++ b/nginx/ngx_js.c Tue Sep 13 21:13:17 2022 -0700 @@ -386,3 +386,110 @@ ngx_js_logger(njs_vm_t *vm, njs_external c->log->handler = handler; } + + +char * +ngx_js_import(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_js_conf_t *jscf = conf; + + u_char *p, *end, c; + ngx_int_t from; + ngx_str_t *value, name, path; + ngx_js_named_path_t *import; + + value = cf->args->elts; + from = (cf->args->nelts == 4); + + if (from) { + if (ngx_strcmp(value[2].data, "from") != 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } + } + + name = value[1]; + path = (from ? value[3] : value[1]); + + if (!from) { + end = name.data + name.len; + + for (p = end - 1; p >= name.data; p--) { + if (*p == '/') { + break; + } + } + + name.data = p + 1; + name.len = end - p - 1; + + if (name.len < 3 + || ngx_memcmp(&name.data[name.len - 3], ".js", 3) != 0) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "cannot extract export name from file path " + "\"%V\", use extended \"from\" syntax", &path); + return NGX_CONF_ERROR; + } + + name.len -= 3; + } + + if (name.len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty export name"); + return NGX_CONF_ERROR; + } + + p = name.data; + end = name.data + name.len; + + while (p < end) { + c = ngx_tolower(*p); + + if (*p != '_' && (c < 'a' || c > 'z')) { + if (p == name.data) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "cannot start " + "with \"%c\" in export name \"%V\"", *p, + &name); + return NGX_CONF_ERROR; + } + + if (*p < '0' || *p > '9') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid character " + "\"%c\" in export name \"%V\"", *p, + &name); + return NGX_CONF_ERROR; + } + } + + p++; + } + + if (ngx_strchr(path.data, '\'') != NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid character \"'\" " + "in file path \"%V\"", &path); + return NGX_CONF_ERROR; + } + + if (jscf->imports == NGX_CONF_UNSET_PTR) { + jscf->imports = ngx_array_create(cf->pool, 4, + sizeof(ngx_js_named_path_t)); + if (jscf->imports == NULL) { + return NGX_CONF_ERROR; + } + } + + import = ngx_array_push(jscf->imports); + if (import == NULL) { + return NGX_CONF_ERROR; + } + + import->name = name; + import->path = path; + import->file = cf->conf_file->file.name.data; + import->line = cf->conf_file->line; + + return NGX_CONF_OK; +} + diff -r 86d181bb72e4 -r 05efe34376ab nginx/ngx_js.h --- a/nginx/ngx_js.h Mon Sep 12 17:56:44 2022 -0700 +++ b/nginx/ngx_js.h Tue Sep 13 21:13:17 2022 -0700 @@ -38,6 +38,23 @@ typedef ngx_flag_t (*ngx_external_size_p njs_external_ptr_t e); typedef ngx_ssl_t *(*ngx_external_ssl_pt)(njs_vm_t *vm, njs_external_ptr_t e); +typedef struct { + ngx_str_t name; + ngx_str_t path; + u_char *file; + ngx_uint_t line; +} ngx_js_named_path_t; + + +#define NGX_JS_COMMON_CONF \ + njs_vm_t *vm; \ + ngx_array_t *imports; \ + ngx_array_t *paths \ + +typedef struct { + NGX_JS_COMMON_CONF; +} ngx_js_conf_t; + #define ngx_external_connection(vm, e) \ (*((ngx_connection_t **) ((u_char *) (e) + njs_vm_meta(vm, 0)))) @@ -75,6 +92,7 @@ njs_int_t ngx_js_ext_log(njs_vm_t *vm, n njs_index_t level); void ngx_js_logger(njs_vm_t *vm, njs_external_ptr_t external, njs_log_level_t level, const u_char *start, size_t length); +char * ngx_js_import(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); njs_int_t ngx_js_ext_string(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); diff -r 86d181bb72e4 -r 05efe34376ab nginx/ngx_stream_js_module.c --- a/nginx/ngx_stream_js_module.c Mon Sep 12 17:56:44 2022 -0700 +++ b/nginx/ngx_stream_js_module.c Tue Sep 13 21:13:17 2022 -0700 @@ -13,17 +13,7 @@ typedef struct { - ngx_str_t name; - ngx_str_t path; - u_char *file; - ngx_uint_t line; -} ngx_stream_js_import_t; - - -typedef struct { - njs_vm_t *vm; - ngx_array_t *imports; - ngx_array_t *paths; + NGX_JS_COMMON_CONF; ngx_str_t access; ngx_str_t preread; @@ -137,8 +127,6 @@ static size_t ngx_stream_js_max_response static void ngx_stream_js_handle_event(ngx_stream_session_t *s, njs_vm_event_t vm_event, njs_value_t *args, njs_uint_t nargs); -static char *ngx_stream_js_import(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf); static char *ngx_stream_js_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_stream_js_var(ngx_conf_t *cf, ngx_command_t *cmd, @@ -176,7 +164,7 @@ static ngx_command_t ngx_stream_js_comm { ngx_string("js_import"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE13, - ngx_stream_js_import, + ngx_js_import, NGX_STREAM_SRV_CONF_OFFSET, 0, NULL }, @@ -1654,10 +1642,10 @@ static ngx_int_t ngx_stream_js_merge_vm(ngx_conf_t *cf, ngx_stream_js_srv_conf_t *conf, ngx_stream_js_srv_conf_t *prev) { - ngx_str_t *path, *s; - ngx_uint_t i; - ngx_array_t *imports, *paths; - ngx_stream_js_import_t *import, *pi; + ngx_str_t *path, *s; + ngx_uint_t i; + ngx_array_t *imports, *paths; + ngx_js_named_path_t *import, *pi; if (prev->imports != NGX_CONF_UNSET_PTR && prev->vm == NULL) { if (ngx_stream_js_init_conf_vm(cf, prev) != NGX_OK) { @@ -1682,7 +1670,7 @@ ngx_stream_js_merge_vm(ngx_conf_t *cf, n } else { imports = ngx_array_create(cf->pool, 4, - sizeof(ngx_stream_js_import_t)); + sizeof(ngx_js_named_path_t)); if (imports == NULL) { return NGX_ERROR; } @@ -1760,17 +1748,17 @@ ngx_stream_js_merge_vm(ngx_conf_t *cf, n static ngx_int_t ngx_stream_js_init_conf_vm(ngx_conf_t *cf, ngx_stream_js_srv_conf_t *conf) { - size_t size; - u_char *start, *end, *p; - ngx_str_t *m, file; - njs_int_t rc; - njs_str_t text, path; - ngx_uint_t i; - njs_value_t *value; - njs_vm_opt_t options; - ngx_pool_cleanup_t *cln; - njs_opaque_value_t lvalue, exception; - ngx_stream_js_import_t *import; + size_t size; + u_char *start, *end, *p; + ngx_str_t *m, file; + njs_int_t rc; + njs_str_t text, path; + ngx_uint_t i; + njs_value_t *value; + njs_vm_opt_t options; + ngx_pool_cleanup_t *cln; + njs_opaque_value_t lvalue, exception; + ngx_js_named_path_t *import; static const njs_str_t line_number_key = njs_str("lineNumber"); static const njs_str_t file_name_key = njs_str("fileName"); @@ -1933,111 +1921,6 @@ ngx_stream_js_init_conf_vm(ngx_conf_t *c static char * -ngx_stream_js_import(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - ngx_stream_js_srv_conf_t *jscf = conf; - - u_char *p, *end, c; - ngx_int_t from; - ngx_str_t *value, name, path; - ngx_stream_js_import_t *import; - - value = cf->args->elts; - from = (cf->args->nelts == 4); - - if (from) { - if (ngx_strcmp(value[2].data, "from") != 0) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid parameter \"%V\"", &value[2]); - return NGX_CONF_ERROR; - } - } - - name = value[1]; - path = (from ? value[3] : value[1]); - - if (!from) { - end = name.data + name.len; - - for (p = end - 1; p >= name.data; p--) { - if (*p == '/') { - break; - } - } - - name.data = p + 1; - name.len = end - p - 1; - - if (name.len < 3 - || ngx_memcmp(&name.data[name.len - 3], ".js", 3) != 0) - { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "cannot extract export name from file path " - "\"%V\", use extended \"from\" syntax", &path); - return NGX_CONF_ERROR; - } - - name.len -= 3; - } - - if (name.len == 0) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty \"name\" parameter"); - return NGX_CONF_ERROR; - } - - p = name.data; - end = name.data + name.len; - - while (p < end) { - c = ngx_tolower(*p); - - if (*p != '_' && (c < 'a' || c > 'z')) { - if (p == name.data) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "cannot start " - "with \"%c\" in export name \"%V\"", *p, - &name); - return NGX_CONF_ERROR; - } - - if (*p < '0' || *p > '9') { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid character " - "\"%c\" in export name \"%V\"", *p, &name); - return NGX_CONF_ERROR; - } - } - - p++; - } - - if (ngx_strchr(path.data, '\'') != NULL) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid character \"'\" " - "in file path \"%V\"", &path); - return NGX_CONF_ERROR; - } - - if (jscf->imports == NGX_CONF_UNSET_PTR) { - jscf->imports = ngx_array_create(cf->pool, 4, - sizeof(ngx_stream_js_import_t)); - if (jscf->imports == NULL) { - return NGX_CONF_ERROR; - } - } - - import = ngx_array_push(jscf->imports); - if (import == NULL) { - return NGX_CONF_ERROR; - } - - import->name = name; - import->path = path; - import->file = cf->conf_file->file.name.data; - import->line = cf->conf_file->line; - - return NGX_CONF_OK; -} - - -static char * ngx_stream_js_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_str_t *value, *fname; From 12274594 at qq.com Wed Sep 14 04:22:53 2022 From: 12274594 at qq.com (=?utf-8?B?MTIyNzQ1OTQ=?=) Date: Wed, 14 Sep 2022 12:22:53 +0800 Subject: AutoReply: [njs] Modules: common code for js_import is moved to shared library. In-Reply-To: Message-ID: An HTML attachment was scrubbed... URL: From pluknet at nginx.com Thu Sep 15 05:36:31 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Thu, 15 Sep 2022 09:36:31 +0400 Subject: [PATCH 01 of 11] SSL: disabled saving tickets to session cache In-Reply-To: <2cd8fbeb4edc5a99b725.1661482868@vm-bsd.mdounin.ru> References: <2cd8fbeb4edc5a99b725.1661482868@vm-bsd.mdounin.ru> Message-ID: > On 26 Aug 2022, at 07:01, Maxim Dounin wrote: > > # HG changeset patch > # User Maxim Dounin > # Date 1661481945 -10800 > # Fri Aug 26 05:45:45 2022 +0300 > # Node ID 2cd8fbeb4edc5a99b725585edc02a16a8a0c503e > # Parent 069a4813e8d6d7ec662d282a10f5f7062ebd817f > SSL: disabled saving tickets to session cache. > > OpenSSL for TLSv1.3 tries to save tickets into session cache "because some > applications just want to know about the creation of a session". To avoid > trashing session cache with useless data, we do not save such sessions now. > For the record, BoringSSL doesn't seem to call new_session_cb for TLSv1.3 at all, so there is no way to resume sessions with SSL_OP_NO_TICKET set. In contrary, OpenSSL emits stateful tickets in this case, which contain dummy session id used then as a session cache lookup key on server (much like session ids in TLSv1.2) OTOH, without SSL_OP_NO_TICKET set, OpenSSL emits self-containing tickets with enough info to resume session, so nothing to lookup in session cache. The latter makes impractical storing something in session cache, except to use the callback for things like tracking "the creation of a session". Namely, OpenSSL puts session (i.e. something that SSL_get_session returns) and supplementary info to session ticket message as the ticket value. With these thoughts in mind, I think log could be clarified to emphasize: - it's not tickets that are stored in cache - with SSL_OP_NO_TICKET set TLSv1.3 session are still saved to lookup by id. > diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c > --- a/src/event/ngx_event_openssl.c > +++ b/src/event/ngx_event_openssl.c > @@ -3815,6 +3815,22 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > ngx_ssl_session_cache_t *cache; > u_char buf[NGX_SSL_MAX_SESSION_SIZE]; > > +#ifdef TLS1_3_VERSION > + > + /* > + * OpenSSL for TLSv1.3 tries to save tickets into session cache > + * "because some applications just want to know about the creation > + * of a session"; do not cache such sessions > + */ > + > + if (SSL_version(ssl_conn) == TLS1_3_VERSION > + && (SSL_get_options(ssl_conn) & SSL_OP_NO_TICKET) == 0) > + { > + return 0; > + } > + > +#endif > + > len = i2d_SSL_SESSION(sess, NULL); > > /* do not cache too big session */ > Looks good. BTW, looking through this code I see that casting NGX_SSL_MAX_SESSION_SIZE, as seen below the above comment, should be safe to remove, as it stopped using offsetof since 5ffd76a9ccf3. # HG changeset patch # User Sergey Kandaurov # Date 1663213568 -14400 # Thu Sep 15 07:46:08 2022 +0400 # Node ID 45f239ff9ad31d33b58822eff8686657e0862e44 # Parent bcdd372dd558c6bcef981704b788b3b224173347 SSL: removed cast not needed after 5ffd76a9ccf3. diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -3842,7 +3842,7 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ /* do not cache too big session */ - if (len > (int) NGX_SSL_MAX_SESSION_SIZE) { + if (len > NGX_SSL_MAX_SESSION_SIZE) { return 0; } -- Sergey Kandaurov From pluknet at nginx.com Thu Sep 15 05:37:17 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Thu, 15 Sep 2022 09:37:17 +0400 Subject: [PATCH 02 of 11] SSL: reduced logging of session cache failures (ticket #621) In-Reply-To: <5b137f110e84af974ef2.1661482869@vm-bsd.mdounin.ru> References: <5b137f110e84af974ef2.1661482869@vm-bsd.mdounin.ru> Message-ID: <42A8BF68-4BA9-41F4-A3BE-8E0B40CD2234@nginx.com> > On 26 Aug 2022, at 07:01, Maxim Dounin wrote: > > # HG changeset patch > # User Maxim Dounin > # Date 1661481947 -10800 > # Fri Aug 26 05:45:47 2022 +0300 > # Node ID 5b137f110e84af974ef2b9efcf35bec2d883c187 > # Parent 2cd8fbeb4edc5a99b725585edc02a16a8a0c503e > SSL: reduced logging of session cache failures (ticket #621). > > Session cache allocations might fail as long as the new session is different > in size from the one least recently used (and freed when the first allocation > fails). In particular, it might not be possible to allocate space for > sessions with client certificates, since they are noticeably bigger than > normal sessions. > > To ensure such allocation failures won't clutter logs, logging level changed > to "warn", and logging is now limited to at most one warning per second. > > diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c > --- a/src/event/ngx_event_openssl.c > +++ b/src/event/ngx_event_openssl.c > @@ -3949,8 +3949,11 @@ failed: > > ngx_shmtx_unlock(&shpool->mutex); > > - ngx_log_error(NGX_LOG_ALERT, c->log, 0, > - "could not allocate new session%s", shpool->log_ctx); > + if (cache->fail_time != ngx_time()) { > + cache->fail_time = ngx_time(); > + ngx_log_error(NGX_LOG_WARN, c->log, 0, > + "could not allocate new session%s", shpool->log_ctx); > + } > This makes three ngx_time() calls in this function in total. A good reason to cache value in a local variable. > return 0; > } > diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h > --- a/src/event/ngx_event_openssl.h > +++ b/src/event/ngx_event_openssl.h > @@ -150,6 +150,7 @@ typedef struct { > ngx_rbtree_t session_rbtree; > ngx_rbtree_node_t sentinel; > ngx_queue_t expire_queue; > + time_t fail_time; > } ngx_ssl_session_cache_t; > Missed initialization to something sensible (zero?). -- Sergey Kandaurov From pluknet at nginx.com Thu Sep 15 05:40:32 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Thu, 15 Sep 2022 09:40:32 +0400 Subject: [PATCH 04 of 11] SSL: explicit session id length checking In-Reply-To: References: Message-ID: > On 26 Aug 2022, at 07:01, Maxim Dounin wrote: > > # HG changeset patch > # User Maxim Dounin > # Date 1661481949 -10800 > # Fri Aug 26 05:45:49 2022 +0300 > # Node ID f4ae0f4ee928cf20346530e96f1431314ecd0171 > # Parent 86d827338fdd13ea899d618b0bcb2be23469cbac > SSL: explicit session id length checking. > > Session ids are not expected to be longer than 32 bytes, but this is > theoretically possible with TLSv1.3, where session ids are essentially > arbitrary and sent as session tickets. Since on 64-bit platforms we > use fixed 32-byte buffer for session ids, added an explicit length check > to make sure the buffer is large enough. > I don't follow how session ids could be "essentially arbitrary" (except a library bug that justifies such safety belt). For TLSv1.3, this callback is used to update session cache as part of constructing NewSessionTicket. It's called after generating dummy session ids, which, and regardless of protocol version, are capped to SSL3_SSL_SESSION_ID_LENGTH (32). > diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c > --- a/src/event/ngx_event_openssl.c > +++ b/src/event/ngx_event_openssl.c > @@ -3842,6 +3842,14 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > p = buf; > i2d_SSL_SESSION(sess, &p); > > + session_id = (u_char *) SSL_SESSION_get_id(sess, &session_id_length); > + > + /* do not cache sessions with too long session id */ > + > + if (session_id_length > 32) { > + return 0; > + } > + The check can be moved above the cpu expensive i2d_SSL_SESSION call. Or rather move i2d_SSL_SESSION closer to corresponding ngx_memcpy(). (but see my reply on the next patch) > c = ngx_ssl_get_connection(ssl_conn); > > ssl_ctx = c->ssl->session_ctx; > @@ -3886,8 +3894,6 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > } > } > > - session_id = (u_char *) SSL_SESSION_get_id(sess, &session_id_length); > - > #if (NGX_PTR_SIZE == 8) > > id = sess_id->sess_id; > -- Sergey Kandaurov From pluknet at nginx.com Thu Sep 15 05:41:36 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Thu, 15 Sep 2022 09:41:36 +0400 Subject: [PATCH 05 of 11] SSL: single allocation in session cache on 32-bit platforms In-Reply-To: References: Message-ID: <336FD698-C706-4B4C-B24F-25E7BD9113E2@nginx.com> > On 26 Aug 2022, at 07:01, Maxim Dounin wrote: > > # HG changeset patch > # User Maxim Dounin > # Date 1661481950 -10800 > # Fri Aug 26 05:45:50 2022 +0300 > # Node ID e88baee178eed529c6170678e373f5e2e0883c37 > # Parent f4ae0f4ee928cf20346530e96f1431314ecd0171 > SSL: single allocation in session cache on 32-bit platforms. > > Given the present typical SSL session sizes, on 32-bit platforms it is > now beneficial to store all data in a single allocation, since rbtree > node + session id + ASN1 representation of a session takes 256 bytes of > shared memory (36 + 32 + 150 = about 218 bytes plus SNI server name). > > Storing all data in a single allocation is beneficial for SNI names up to > about 40 characters long and makes it possible to store about 4000 sessions > in one megabyte (instead of about 3000 sessions now). This also slightly > simplifies the code. > > diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c > --- a/src/event/ngx_event_openssl.c > +++ b/src/event/ngx_event_openssl.c > @@ -3789,9 +3789,9 @@ ngx_ssl_session_cache_init(ngx_shm_zone_ > * Typical length of the external ASN1 representation of a session > * is about 150 bytes plus SNI server name. > * > - * On 32-bit platforms we allocate separately an rbtree node, > - * a session id, and an ASN1 representation, they take accordingly > - * 64, 32, and 256 bytes. > + * On 32-bit platforms we allocate an rbtree node, a session id, and > + * an ASN1 representation in a single allocation, it typically takes > + * 256 bytes. > * > * On 64-bit platforms we allocate separately an rbtree node + session_id, > * and an ASN1 representation, they take accordingly 128 and 256 bytes. > @@ -3804,7 +3804,8 @@ static int > ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess) > { > int len; > - u_char *p, *id, *cached_sess, *session_id; > + u_char *p, *session_id; > + size_t n; > uint32_t hash; > SSL_CTX *ssl_ctx; > unsigned int session_id_length; > @@ -3863,23 +3864,13 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > /* drop one or two expired sessions */ > ngx_ssl_expire_sessions(cache, shpool, 1); > > - cached_sess = ngx_slab_alloc_locked(shpool, len); > - > - if (cached_sess == NULL) { > - > - /* drop the oldest non-expired session and try once more */ > - > - ngx_ssl_expire_sessions(cache, shpool, 0); > - > - cached_sess = ngx_slab_alloc_locked(shpool, len); > - > - if (cached_sess == NULL) { > - sess_id = NULL; > - goto failed; > - } > - } > - > - sess_id = ngx_slab_alloc_locked(shpool, sizeof(ngx_ssl_sess_id_t)); > +#if (NGX_PTR_SIZE == 8) > + n = sizeof(ngx_ssl_sess_id_t); > +#else > + n = offsetof(ngx_ssl_sess_id_t, session) + len; > +#endif > + > + sess_id = ngx_slab_alloc_locked(shpool, n); > > if (sess_id == NULL) { > > @@ -3887,7 +3878,7 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > > ngx_ssl_expire_sessions(cache, shpool, 0); > > - sess_id = ngx_slab_alloc_locked(shpool, sizeof(ngx_ssl_sess_id_t)); > + sess_id = ngx_slab_alloc_locked(shpool, n); > > if (sess_id == NULL) { > goto failed; > @@ -3896,30 +3887,25 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > > #if (NGX_PTR_SIZE == 8) > > - id = sess_id->sess_id; > - > -#else > - > - id = ngx_slab_alloc_locked(shpool, session_id_length); > - > - if (id == NULL) { > + sess_id->session = ngx_slab_alloc_locked(shpool, len); > + > + if (sess_id->session == NULL) { > > /* drop the oldest non-expired session and try once more */ > > ngx_ssl_expire_sessions(cache, shpool, 0); > > - id = ngx_slab_alloc_locked(shpool, session_id_length); > - > - if (id == NULL) { > + sess_id->session = ngx_slab_alloc_locked(shpool, len); > + > + if (sess_id->session == NULL) { > goto failed; > } > } > > #endif > > - ngx_memcpy(cached_sess, buf, len); > - > - ngx_memcpy(id, session_id, session_id_length); > + ngx_memcpy(sess_id->session, buf, len); Converting to ASN1 looks feasible without intermediate buf[] to avoid extra memory copy. > + ngx_memcpy(sess_id->id, session_id, session_id_length); > > hash = ngx_crc32_short(session_id, session_id_length); > > @@ -3929,9 +3915,7 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > > sess_id->node.key = hash; > sess_id->node.data = (u_char) session_id_length; > - sess_id->id = id; > sess_id->len = len; With comment in the previous patch#04, this makes the following: diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -3817,7 +3817,6 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ ngx_slab_pool_t *shpool; ngx_ssl_sess_id_t *sess_id; ngx_ssl_session_cache_t *cache; - u_char buf[NGX_SSL_MAX_SESSION_SIZE]; #ifdef TLS1_3_VERSION @@ -3843,9 +3842,6 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ return 0; } - p = buf; - i2d_SSL_SESSION(sess, &p); - session_id = (u_char *) SSL_SESSION_get_id(sess, &session_id_length); /* do not cache sessions with too long session id */ @@ -3907,7 +3903,10 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ #endif - ngx_memcpy(sess_id->session, buf, len); + p = sess_id->session; + i2d_SSL_SESSION(sess, &p); + sess_id->len = len; + ngx_memcpy(sess_id->id, session_id, session_id_length); hash = ngx_crc32_short(session_id, session_id_length); @@ -3918,7 +3917,6 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ sess_id->node.key = hash; sess_id->node.data = (u_char) session_id_length; - sess_id->len = len; sess_id->expire = ngx_time() + SSL_CTX_get_timeout(ssl_ctx); > - sess_id->session = cached_sess; > > sess_id->expire = ngx_time() + SSL_CTX_get_timeout(ssl_ctx); > > @@ -3945,10 +3929,6 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > > failed: > > - if (cached_sess) { > - ngx_slab_free_locked(shpool, cached_sess); > - } > - > if (sess_id) { > ngx_slab_free_locked(shpool, sess_id); > } > @@ -4045,9 +4025,8 @@ ngx_ssl_get_cached_session(ngx_ssl_conn_ > > ngx_rbtree_delete(&cache->session_rbtree, node); > > +#if (NGX_PTR_SIZE == 8) > ngx_slab_free_locked(shpool, sess_id->session); > -#if (NGX_PTR_SIZE == 4) > - ngx_slab_free_locked(shpool, sess_id->id); > #endif > ngx_slab_free_locked(shpool, sess_id); > > @@ -4135,9 +4114,8 @@ ngx_ssl_remove_session(SSL_CTX *ssl, ngx > > ngx_rbtree_delete(&cache->session_rbtree, node); > > +#if (NGX_PTR_SIZE == 8) > ngx_slab_free_locked(shpool, sess_id->session); > -#if (NGX_PTR_SIZE == 4) > - ngx_slab_free_locked(shpool, sess_id->id); > #endif > ngx_slab_free_locked(shpool, sess_id); > > @@ -4184,9 +4162,8 @@ ngx_ssl_expire_sessions(ngx_ssl_session_ > > ngx_rbtree_delete(&cache->session_rbtree, &sess_id->node); > > +#if (NGX_PTR_SIZE == 8) > ngx_slab_free_locked(shpool, sess_id->session); > -#if (NGX_PTR_SIZE == 4) > - ngx_slab_free_locked(shpool, sess_id->id); > #endif > ngx_slab_free_locked(shpool, sess_id); > } > diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h > --- a/src/event/ngx_event_openssl.h > +++ b/src/event/ngx_event_openssl.h > @@ -134,14 +134,14 @@ typedef struct ngx_ssl_sess_id_s ngx_ss > > struct ngx_ssl_sess_id_s { > ngx_rbtree_node_t node; > - u_char *id; > size_t len; > - u_char *session; > ngx_queue_t queue; > time_t expire; > + u_char id[32]; > #if (NGX_PTR_SIZE == 8) > - void *stub; > - u_char sess_id[32]; > + u_char *session; > +#else > + u_char session[1]; > #endif > }; > This reminds me that this structure coupled with ngx_ssl_ticket_key_t and ngx_ssl_session_cache_t aren't used outside and can be made private. -- Sergey Kandaurov From pluknet at nginx.com Thu Sep 15 05:42:01 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Thu, 15 Sep 2022 09:42:01 +0400 Subject: [PATCH 07 of 11] SSL: style In-Reply-To: <84919c2ee8173f704649.1661482874@vm-bsd.mdounin.ru> References: <84919c2ee8173f704649.1661482874@vm-bsd.mdounin.ru> Message-ID: > On 26 Aug 2022, at 07:01, Maxim Dounin wrote: > > # HG changeset patch > # User Maxim Dounin > # Date 1661481953 -10800 > # Fri Aug 26 05:45:53 2022 +0300 > # Node ID 84919c2ee8173f704649a8cb4901887e1bf79588 > # Parent d5c6eae914325fb6a9b19105fe09aecd04da21e2 > SSL: style. > > Runtime OCSP functions separated from configuration ones. > > diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h > --- a/src/event/ngx_event_openssl.h > +++ b/src/event/ngx_event_openssl.h > @@ -205,10 +205,12 @@ ngx_int_t ngx_ssl_ocsp(ngx_conf_t *cf, n > ngx_uint_t depth, ngx_shm_zone_t *shm_zone); > ngx_int_t ngx_ssl_ocsp_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl, > ngx_resolver_t *resolver, ngx_msec_t resolver_timeout); > + > ngx_int_t ngx_ssl_ocsp_validate(ngx_connection_t *c); > ngx_int_t ngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s); > void ngx_ssl_ocsp_cleanup(ngx_connection_t *c); > ngx_int_t ngx_ssl_ocsp_cache_init(ngx_shm_zone_t *shm_zone, void *data); > + > ngx_array_t *ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file); > ngx_array_t *ngx_ssl_preserve_passwords(ngx_conf_t *cf, > ngx_array_t *passwords); > Speaking of style, this reminds me of various more style issues. # HG changeset patch # User Sergey Kandaurov # Date 1663066823 -14400 # Tue Sep 13 15:00:23 2022 +0400 # Node ID e3da137555cfb6a3eb80aae196a49b945a4f5048 # Parent 3b0846bd090e06cf277879d4ba4a67a0a2569233 SSL: style. Using suitable naming for SSL_CTX variables. diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -64,7 +64,7 @@ static ngx_ssl_session_t *ngx_ssl_get_ca const #endif u_char *id, int len, int *copy); -static void ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess); +static void ngx_ssl_remove_session(SSL_CTX *ssl_ctx, ngx_ssl_session_t *sess); static void ngx_ssl_expire_sessions(ngx_ssl_session_cache_t *cache, ngx_slab_pool_t *shpool, ngx_uint_t n); static void ngx_ssl_session_rbtree_insert_value(ngx_rbtree_node_t *temp, @@ -4050,16 +4050,16 @@ done: void -ngx_ssl_remove_cached_session(SSL_CTX *ssl, ngx_ssl_session_t *sess) +ngx_ssl_remove_cached_session(SSL_CTX *ssl_ctx, ngx_ssl_session_t *sess) { - SSL_CTX_remove_session(ssl, sess); - - ngx_ssl_remove_session(ssl, sess); + SSL_CTX_remove_session(ssl_ctx, sess); + + ngx_ssl_remove_session(ssl_ctx, sess); } static void -ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess) +ngx_ssl_remove_session(SSL_CTX *ssl_ctx, ngx_ssl_session_t *sess) { u_char *id; uint32_t hash; @@ -4071,7 +4071,7 @@ ngx_ssl_remove_session(SSL_CTX *ssl, ngx ngx_ssl_sess_id_t *sess_id; ngx_ssl_session_cache_t *cache; - shm_zone = SSL_CTX_get_ex_data(ssl, ngx_ssl_session_cache_index); + shm_zone = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_cache_index); if (shm_zone == NULL) { return; diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -233,7 +233,7 @@ ngx_int_t ngx_ssl_session_cache_init(ngx ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags); -void ngx_ssl_remove_cached_session(SSL_CTX *ssl, ngx_ssl_session_t *sess); +void ngx_ssl_remove_cached_session(SSL_CTX *ssl_ctx, ngx_ssl_session_t *sess); ngx_int_t ngx_ssl_set_session(ngx_connection_t *c, ngx_ssl_session_t *session); ngx_ssl_session_t *ngx_ssl_get_session(ngx_connection_t *c); ngx_ssl_session_t *ngx_ssl_get0_session(ngx_connection_t *c); # HG changeset patch # User Sergey Kandaurov # Date 1663199989 -14400 # Thu Sep 15 03:59:49 2022 +0400 # Node ID b13b26ab24e9f12a808301bf4c8713d52c7944aa # Parent e3da137555cfb6a3eb80aae196a49b945a4f5048 SSL: fixed indentation. diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -998,12 +998,12 @@ static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store) { #if (NGX_DEBUG) + int err, depth; char *subject, *issuer; - int err, depth; X509 *cert; X509_NAME *sname, *iname; + ngx_ssl_conn_t *ssl_conn; ngx_connection_t *c; - ngx_ssl_conn_t *ssl_conn; ssl_conn = X509_STORE_CTX_get_ex_data(x509_store, SSL_get_ex_data_X509_STORE_CTX_idx()); @@ -2274,8 +2274,8 @@ ngx_ssl_recv(ngx_connection_t *c, u_char static ssize_t ngx_ssl_recv_early(ngx_connection_t *c, u_char *buf, size_t size) { - int n, bytes; - size_t readbytes; + int n, bytes; + size_t readbytes; if (c->ssl->last == NGX_ERROR) { c->read->error = 1; @@ -2528,9 +2528,9 @@ ngx_chain_t * ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) { int n; - ngx_uint_t flush; ssize_t send, size, file_size; ngx_buf_t *buf; + ngx_uint_t flush; ngx_chain_t *cl; if (!c->ssl->buffer) { @@ -3491,9 +3491,9 @@ ngx_ssl_error(ngx_uint_t level, ngx_log_ { int flags; u_long n; - va_list args; u_char *p, *last; u_char errstr[NGX_MAX_CONF_ERRSTR]; + va_list args; const char *data; last = errstr + NGX_MAX_CONF_ERRSTR; @@ -3809,12 +3809,12 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ int len; u_char *p, *session_id; size_t n; + SSL_CTX *ssl_ctx; uint32_t hash; - SSL_CTX *ssl_ctx; unsigned int session_id_length; ngx_shm_zone_t *shm_zone; + ngx_slab_pool_t *shpool; ngx_connection_t *c; - ngx_slab_pool_t *shpool; ngx_ssl_sess_id_t *sess_id; ngx_ssl_session_cache_t *cache; @@ -3959,12 +3959,12 @@ ngx_ssl_get_cached_session(ngx_ssl_conn_ const u_char *p; ngx_shm_zone_t *shm_zone; ngx_slab_pool_t *shpool; + ngx_connection_t *c; ngx_rbtree_node_t *node, *sentinel; ngx_ssl_session_t *sess; ngx_ssl_sess_id_t *sess_id; ngx_ssl_session_cache_t *cache; u_char buf[NGX_SSL_MAX_SESSION_SIZE]; - ngx_connection_t *c; hash = ngx_crc32_short((u_char *) (uintptr_t) id, (size_t) len); *copy = 0; @@ -4500,7 +4500,7 @@ ngx_ssl_session_ticket_key_callback(ngx_ static void ngx_ssl_session_ticket_keys_cleanup(void *data) { - ngx_array_t *keys = data; + ngx_array_t *keys = data; ngx_explicit_memzero(keys->elts, keys->nelts * sizeof(ngx_ssl_session_ticket_key_t)); @@ -4525,7 +4525,7 @@ ngx_ssl_session_ticket_keys(ngx_conf_t * void ngx_ssl_cleanup_ctx(void *data) { - ngx_ssl_t *ssl = data; + ngx_ssl_t *ssl = data; X509 *cert, *next; @@ -4544,7 +4544,7 @@ ngx_ssl_cleanup_ctx(void *data) ngx_int_t ngx_ssl_check_host(ngx_connection_t *c, ngx_str_t *name) { - X509 *cert; + X509 *cert; cert = SSL_get_peer_certificate(c->ssl->connection); if (cert == NULL) { @@ -4575,8 +4575,8 @@ ngx_ssl_check_host(ngx_connection_t *c, int n, i; X509_NAME *sname; ASN1_STRING *str; + GENERAL_NAME *altname; X509_NAME_ENTRY *entry; - GENERAL_NAME *altname; STACK_OF(GENERAL_NAME) *altnames; /* @@ -4851,9 +4851,9 @@ ngx_ssl_get_curves(ngx_connection_t *c, { #ifdef SSL_CTRL_GET_CURVES - int *curves, n, i, nid; - u_char *p; - size_t len; + int *curves, n, i, nid; + u_char *p; + size_t len; n = SSL_get1_curves(c->ssl->connection, NULL); @@ -5046,9 +5046,9 @@ ngx_ssl_get_alpn_protocol(ngx_connection ngx_int_t ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) { - size_t len; BIO *bio; X509 *cert; + size_t len; s->len = 0; @@ -5098,8 +5098,8 @@ ngx_ssl_get_certificate(ngx_connection_t { u_char *p; size_t len; + ngx_str_t cert; ngx_uint_t i; - ngx_str_t cert; if (ngx_ssl_get_raw_certificate(c, pool, &cert) != NGX_OK) { return NGX_ERROR; @@ -5280,8 +5280,8 @@ ngx_ssl_get_subject_dn_legacy(ngx_connec ngx_str_t *s) { char *p; + X509 *cert; size_t len; - X509 *cert; X509_NAME *name; s->len = 0; @@ -5328,8 +5328,8 @@ ngx_ssl_get_issuer_dn_legacy(ngx_connect ngx_str_t *s) { char *p; + X509 *cert; size_t len; - X509 *cert; X509_NAME *name; s->len = 0; @@ -5374,9 +5374,9 @@ ngx_ssl_get_issuer_dn_legacy(ngx_connect ngx_int_t ngx_ssl_get_serial_number(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) { - size_t len; + BIO *bio; X509 *cert; - BIO *bio; + size_t len; s->len = 0; -- Sergey Kandaurov From pluknet at nginx.com Thu Sep 15 05:50:24 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Thu, 15 Sep 2022 09:50:24 +0400 Subject: [PATCH 11 of 11] SSL: automatic rotation of session ticket keys In-Reply-To: <5c26fe5f6ab0bf4c0d18.1661482878@vm-bsd.mdounin.ru> References: <5c26fe5f6ab0bf4c0d18.1661482878@vm-bsd.mdounin.ru> Message-ID: > On 26 Aug 2022, at 07:01, Maxim Dounin wrote: > > # HG changeset patch > # User Maxim Dounin > # Date 1661481958 -10800 > # Fri Aug 26 05:45:58 2022 +0300 > # Node ID 5c26fe5f6ab0bf4c0d18cae8f6f6483348243d4b > # Parent 2487bf5766f79c813b3397b3bb897424c3590445 > SSL: automatic rotation of session ticket keys. > > As long as ssl_session_cache in shared memory is configured, session ticket > keys are now automatically generated in shared memory, and rotated > periodically. It looks odd how session cache is (ab)used to store ticket keys. I understand that it is comfortable to reuse existing shared zone (and not to touch configuration) but probably a more correct way is to create a separate zone to store keys? Something pretty much the same as ssl_session_cache syntax: ssl_session_ticket_key file | shared:name:size What do you think? > This can be beneficial from forward secrecy point of view, > and also avoids increased CPU usage after configuration reloads. > You can also mention that this fixes resuming sessions with BoringSSL that implements its own ticket key rotation (if ticket keys callback isn't installed) that doesn't work in multiple workers configuration. > diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c > --- a/src/event/ngx_event_openssl.c > +++ b/src/event/ngx_event_openssl.c > @@ -74,6 +74,7 @@ static void ngx_ssl_session_rbtree_inser > static int ngx_ssl_ticket_key_callback(ngx_ssl_conn_t *ssl_conn, > unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx, > HMAC_CTX *hctx, int enc); > +static ngx_int_t ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ctx, ngx_log_t *log); > static void ngx_ssl_ticket_keys_cleanup(void *data); > #endif > > @@ -3767,6 +3768,9 @@ ngx_ssl_session_cache_init(ngx_shm_zone_ > > ngx_queue_init(&cache->expire_queue); > > + cache->ticket_keys[0].expire = 0; > + cache->ticket_keys[1].expire = 0; > + > len = sizeof(" in SSL session shared cache \"\"") + shm_zone->shm.name.len; > > shpool->log_ctx = ngx_slab_alloc(shpool, len); > @@ -4234,11 +4238,13 @@ ngx_ssl_session_ticket_keys(ngx_conf_t * > ngx_pool_cleanup_t *cln; > ngx_ssl_ticket_key_t *key; > > - if (paths == NULL) { > + if (paths == NULL > + && SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_session_cache_index) == NULL) > + { > return NGX_OK; > } > > - keys = ngx_array_create(cf->pool, paths->nelts, > + keys = ngx_array_create(cf->pool, paths ? paths->nelts : 2, > sizeof(ngx_ssl_ticket_key_t)); > if (keys == NULL) { > return NGX_ERROR; > @@ -4252,6 +4258,34 @@ ngx_ssl_session_ticket_keys(ngx_conf_t * > cln->handler = ngx_ssl_ticket_keys_cleanup; > cln->data = keys; > > + if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_ticket_keys_index, keys) == 0) { > + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, > + "SSL_CTX_set_ex_data() failed"); > + return NGX_ERROR; > + } > + > + if (SSL_CTX_set_tlsext_ticket_key_cb(ssl->ctx, ngx_ssl_ticket_key_callback) > + == 0) > + { > + ngx_log_error(NGX_LOG_WARN, cf->log, 0, > + "nginx was built with Session Tickets support, however, " > + "now it is linked dynamically to an OpenSSL library " > + "which has no tlsext support, therefore Session Tickets " > + "are not available"); > + return NGX_OK; > + } > + > + if (paths == NULL) { > + > + /* placeholder for keys in shared memory */ > + > + key = ngx_array_push_n(keys, 2); > + key[0].shared = 1; > + key[1].shared = 1; > + > + return NGX_OK; > + } > + > path = paths->elts; > for (i = 0; i < paths->nelts; i++) { > > @@ -4306,6 +4340,8 @@ ngx_ssl_session_ticket_keys(ngx_conf_t * > goto failed; > } > > + key->shared = 0; > + > if (size == 48) { > key->size = 48; > ngx_memcpy(key->name, buf, 16); > @@ -4327,22 +4363,6 @@ ngx_ssl_session_ticket_keys(ngx_conf_t * > ngx_explicit_memzero(&buf, 80); > } > > - if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_ticket_keys_index, keys) == 0) { > - ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, > - "SSL_CTX_set_ex_data() failed"); > - return NGX_ERROR; > - } > - > - if (SSL_CTX_set_tlsext_ticket_key_cb(ssl->ctx, ngx_ssl_ticket_key_callback) > - == 0) > - { > - ngx_log_error(NGX_LOG_WARN, cf->log, 0, > - "nginx was built with Session Tickets support, however, " > - "now it is linked dynamically to an OpenSSL library " > - "which has no tlsext support, therefore Session Tickets " > - "are not available"); > - } > - > return NGX_OK; > > failed: > @@ -4375,6 +4395,10 @@ ngx_ssl_ticket_key_callback(ngx_ssl_conn > c = ngx_ssl_get_connection(ssl_conn); > ssl_ctx = c->ssl->session_ctx; > > + if (ngx_ssl_rotate_ticket_keys(ssl_ctx, c->log) != NGX_OK) { > + return -1; > + } > + > #ifdef OPENSSL_NO_SHA256 > digest = EVP_sha1(); > #else > @@ -4493,6 +4517,113 @@ ngx_ssl_ticket_key_callback(ngx_ssl_conn > } > > > +static ngx_int_t > +ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ctx, ngx_log_t *log) > +{ > + time_t now, expire; > + ngx_array_t *keys; > + ngx_shm_zone_t *shm_zone; > + ngx_slab_pool_t *shpool; > + ngx_ssl_ticket_key_t *key; > + ngx_ssl_session_cache_t *cache; > + u_char buf[80]; BTW, the same buf[] variable in ngx_ssl_session_ticket_keys() is placed at the beginning of declaration block. Looks like a chance to improve style consistency (but see below). > + > + keys = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_ticket_keys_index); > + if (keys == NULL) { > + return NGX_OK; > + } > + > + key = keys->elts; > + > + if (!key[0].shared) { > + return NGX_OK; > + } > + > + now = ngx_time(); > + expire = now + SSL_CTX_get_timeout(ssl_ctx); > + > + shm_zone = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_cache_index); > + > + cache = shm_zone->data; > + shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; > + > + ngx_shmtx_lock(&shpool->mutex); > + > + key = cache->ticket_keys; > + > + if (key[0].expire == 0) { > + > + /* initialize the current key */ > + > + if (RAND_bytes(buf, 80) != 1) { > + ngx_ssl_error(NGX_LOG_ALERT, log, 0, "RAND_bytes() failed"); > + ngx_shmtx_unlock(&shpool->mutex); > + return NGX_ERROR; > + } > + > + key->shared = 1; > + key->expire = expire; > + key->size = 80; > + ngx_memcpy(key->name, buf, 16); > + ngx_memcpy(key->hmac_key, buf + 16, 32); > + ngx_memcpy(key->aes_key, buf + 48, 32); > + > + ngx_explicit_memzero(&buf, 80); > + > + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, > + "ssl ticket key: \"%*xs\"", > + (size_t) 16, key->name); > + } > + > + if (key[1].expire < now) { > + > + /* > + * if the previous key is no longer needed (or not initialized), > + * replace it with the current key and generate new current key > + */ > + > + key[1] = key[0]; > + > + if (RAND_bytes(buf, 80) != 1) { > + ngx_ssl_error(NGX_LOG_ALERT, log, 0, "RAND_bytes() failed"); > + ngx_shmtx_unlock(&shpool->mutex); > + return NGX_ERROR; > + } > + > + key->shared = 1; > + key->expire = expire; > + key->size = 80; > + ngx_memcpy(key->name, buf, 16); > + ngx_memcpy(key->hmac_key, buf + 16, 32); > + ngx_memcpy(key->aes_key, buf + 48, 32); > + > + ngx_explicit_memzero(&buf, 80); > + > + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, > + "ssl ticket key: \"%*xs\"", > + (size_t) 16, key->name); > + } I wonder if cpu intensive ops can be moved from under the lock. At least, buf[] can be removed if write random data directly to ngx_ssl_ticket_key_t. This eliminates 3 memory copies (likely optimized by compiler to a single) and explicit write. diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -4527,7 +4527,6 @@ ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ ngx_slab_pool_t *shpool; ngx_ssl_ticket_key_t *key; ngx_ssl_session_cache_t *cache; - u_char buf[80]; keys = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_ticket_keys_index); if (keys == NULL) { @@ -4556,7 +4555,7 @@ ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ /* initialize the current key */ - if (RAND_bytes(buf, 80) != 1) { + if (RAND_bytes(key->name, 80) != 1) { ngx_ssl_error(NGX_LOG_ALERT, log, 0, "RAND_bytes() failed"); ngx_shmtx_unlock(&shpool->mutex); return NGX_ERROR; @@ -4565,11 +4564,6 @@ ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ key->shared = 1; key->expire = expire; key->size = 80; - ngx_memcpy(key->name, buf, 16); - ngx_memcpy(key->hmac_key, buf + 16, 32); - ngx_memcpy(key->aes_key, buf + 48, 32); - - ngx_explicit_memzero(&buf, 80); ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, "ssl ticket key: \"%*xs\"", @@ -4585,7 +4579,7 @@ ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ key[1] = key[0]; - if (RAND_bytes(buf, 80) != 1) { + if (RAND_bytes(key->name, 80) != 1) { ngx_ssl_error(NGX_LOG_ALERT, log, 0, "RAND_bytes() failed"); ngx_shmtx_unlock(&shpool->mutex); return NGX_ERROR; @@ -4594,11 +4588,6 @@ ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ key->shared = 1; key->expire = expire; key->size = 80; - ngx_memcpy(key->name, buf, 16); - ngx_memcpy(key->hmac_key, buf + 16, 32); - ngx_memcpy(key->aes_key, buf + 48, 32); - - ngx_explicit_memzero(&buf, 80); ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, "ssl ticket key: \"%*xs\"", Moving RAND_bytes() can be achieved by trying to elide shmem lock and store random data elsewhere if condition is met. But this comes in conflict with buf[] optimization :). > + > + /* > + * update expiration of the current key: it is going to be needed > + * at least till the session being created expires > + */ > + > + if (expire > key[0].expire) { > + key[0].expire = expire; > + } > + > + /* sync keys to the worker process memory */ > + > + ngx_memcpy(keys->elts, cache->ticket_keys, > + 2 * sizeof(ngx_ssl_ticket_key_t)); > + > + ngx_shmtx_unlock(&shpool->mutex); > + > + return NGX_OK; > +} > + > + > static void > ngx_ssl_ticket_keys_cleanup(void *data) > { > diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h > --- a/src/event/ngx_event_openssl.h > +++ b/src/event/ngx_event_openssl.h > @@ -147,25 +147,24 @@ struct ngx_ssl_sess_id_s { > > > typedef struct { > + u_char name[16]; > + u_char hmac_key[32]; > + u_char aes_key[32]; > + time_t expire; > + unsigned size:8; > + unsigned shared:1; > +} ngx_ssl_ticket_key_t; > + > + > +typedef struct { > ngx_rbtree_t session_rbtree; > ngx_rbtree_node_t sentinel; > ngx_queue_t expire_queue; > + ngx_ssl_ticket_key_t ticket_keys[2]; Again, with the 1st patch applied, this makes using of ticket_keys mutually exclusive to other fields. This suggests that it doesn't belong here. > time_t fail_time; > } ngx_ssl_session_cache_t; > > > -#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB > - > -typedef struct { > - size_t size; > - u_char name[16]; > - u_char hmac_key[32]; > - u_char aes_key[32]; > -} ngx_ssl_ticket_key_t; > - > -#endif > - > - > #define NGX_SSL_SSLv2 0x0002 > #define NGX_SSL_SSLv3 0x0004 > #define NGX_SSL_TLSv1 0x0008 -- Sergey Kandaurov From xeioex at nginx.com Fri Sep 16 00:41:11 2022 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 16 Sep 2022 00:41:11 +0000 Subject: [njs] Parser: properly handling unicode space characters. Message-ID: details: https://hg.nginx.org/njs/rev/46d505a902bb branches: changeset: 1953:46d505a902bb user: Dmitry Volyntsev date: Wed Sep 14 22:14:50 2022 -0700 description: Parser: properly handling unicode space characters. diffstat: src/njs_lexer.c | 35 +++++++++++++++++++++++++++++------ src/njs_str.h | 1 - src/test/njs_unit_test.c | 5 +++++ 3 files changed, 34 insertions(+), 7 deletions(-) diffs (83 lines): diff -r 05efe34376ab -r 46d505a902bb src/njs_lexer.c --- a/src/njs_lexer.c Tue Sep 13 21:13:17 2022 -0700 +++ b/src/njs_lexer.c Wed Sep 14 22:14:50 2022 -0700 @@ -45,8 +45,8 @@ static const uint8_t njs_tokens[256] n NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL, /* \t */ NJS_TOKEN_ILLEGAL, NJS_TOKEN_SPACE, - /* \n */ NJS_TOKEN_LINE_END, NJS_TOKEN_ILLEGAL, - /* \r */ NJS_TOKEN_ILLEGAL, NJS_TOKEN_SPACE, + /* \n */ NJS_TOKEN_LINE_END, NJS_TOKEN_SPACE, + /* \r */ NJS_TOKEN_SPACE, NJS_TOKEN_SPACE, NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL, /* 0x10 */ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL, @@ -437,15 +437,38 @@ njs_lexer_consume_token(njs_lexer_t *lex njs_int_t njs_lexer_make_token(njs_lexer_t *lexer, njs_lexer_token_t *token) { - u_char c, *p; + u_char c, *p; + uint32_t cp; + njs_unicode_decode_t ctx; c = ' '; + njs_utf8_decode_init(&ctx); + while (lexer->start < lexer->end) { - c = *lexer->start++; + c = *lexer->start; + + if (njs_fast_path(!(c & 0x80))) { + lexer->start++; + + if (njs_tokens[c] != NJS_TOKEN_SPACE) { + break; + } - if (njs_tokens[c] != NJS_TOKEN_SPACE) { - break; + } else { + + /* Unicode. */ + + cp = njs_utf8_decode(&ctx, (const u_char **) &lexer->start, + lexer->end); + if (njs_slow_path(cp > NJS_UNICODE_MAX_CODEPOINT)) { + c = '\0'; + break; + } + + if (!njs_utf8_is_whitespace(cp)) { + break; + } } } diff -r 05efe34376ab -r 46d505a902bb src/njs_str.h --- a/src/njs_str.h Tue Sep 13 21:13:17 2022 -0700 +++ b/src/njs_str.h Wed Sep 14 22:14:50 2022 -0700 @@ -51,7 +51,6 @@ njs_is_whitespace(u_char c) case 0x0C: /* */ case 0x0D: /* */ case 0x20: /* */ - case 0xA0: /* */ return 1; default: diff -r 05efe34376ab -r 46d505a902bb src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Tue Sep 13 21:13:17 2022 -0700 +++ b/src/test/njs_unit_test.c Wed Sep 14 22:14:50 2022 -0700 @@ -7341,6 +7341,11 @@ static njs_unit_test_t njs_test[] = "[a.length, a[33], a[34]]"), njs_str("35,a,�") }, + /* Spaces: U+0009U+000BU+000CU+0020U+00A0U+000AU+000DU+2028U+2029 */ + + { njs_str("\x09\x0a\x0b\x0c\x0d \xc2\xa0'a'\xe2\x80\xa8+\xe2\x80\xa9'b'"), + njs_str("ab") }, + /* Escape strings. */ { njs_str("'\\a \\' \\\" \\\\ \\0 \\b \\f \\n \\r \\t \\v'"), From xeioex at nginx.com Fri Sep 16 06:46:13 2022 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 16 Sep 2022 06:46:13 +0000 Subject: [njs] Introduced njs_object_to_string(). Message-ID: details: https://hg.nginx.org/njs/rev/75922905bd9c branches: changeset: 1954:75922905bd9c user: Dmitry Volyntsev date: Thu Sep 15 17:56:35 2022 -0700 description: Introduced njs_object_to_string(). diffstat: src/njs_object.c | 20 +++++++++++++------- src/njs_object.h | 20 ++++++++++++++++++-- 2 files changed, 31 insertions(+), 9 deletions(-) diffs (100 lines): diff -r 46d505a902bb -r 75922905bd9c src/njs_object.c --- a/src/njs_object.c Wed Sep 14 22:14:50 2022 -0700 +++ b/src/njs_object.c Thu Sep 15 17:56:35 2022 -0700 @@ -2343,17 +2343,23 @@ njs_int_t njs_object_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { + return njs_object_to_string(vm, &args[0], &vm->retval); +} + + +njs_int_t +njs_object_to_string(njs_vm_t *vm, njs_value_t *this, njs_value_t *retval) +{ u_char *p; njs_int_t ret; - njs_value_t tag, *this; + njs_value_t tag; njs_string_prop_t string; const njs_value_t *name; - this = njs_argument(args, 0); - if (njs_is_null_or_undefined(this)) { - vm->retval = njs_is_null(this) ? njs_object_null_string - : njs_object_undefined_string; + njs_value_assign(retval, + njs_is_null(this) ? &njs_object_null_string + : &njs_object_undefined_string); return NJS_OK; } @@ -2418,14 +2424,14 @@ njs_object_prototype_to_string(njs_vm_t return NJS_ERROR; } - vm->retval = *name; + njs_value_assign(retval, name); return NJS_OK; } (void) njs_string_prop(&string, &tag); - p = njs_string_alloc(vm, &vm->retval, string.size + njs_length("[object ]"), + p = njs_string_alloc(vm, retval, string.size + njs_length("[object ]"), string.length + njs_length("[object ]")); if (njs_slow_path(p == NULL)) { return NJS_ERROR; diff -r 46d505a902bb -r 75922905bd9c src/njs_object.h --- a/src/njs_object.h Wed Sep 14 22:14:50 2022 -0700 +++ b/src/njs_object.h Thu Sep 15 17:56:35 2022 -0700 @@ -65,6 +65,8 @@ njs_int_t njs_object_prototype_create_co njs_value_t *retval); njs_value_t *njs_property_constructor_set(njs_vm_t *vm, njs_lvlhsh_t *hash, njs_value_t *constructor); +njs_int_t njs_object_to_string(njs_vm_t *vm, njs_value_t *value, + njs_value_t *retval); njs_int_t njs_object_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused); njs_int_t njs_object_length(njs_vm_t *vm, njs_value_t *value, int64_t *dst); @@ -186,7 +188,8 @@ njs_primitive_value_to_key(njs_vm_t *vm, njs_inline njs_int_t -njs_value_to_key(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value) +njs_value_to_key2(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value, + njs_bool_t convert) { njs_int_t ret; njs_value_t primitive; @@ -197,7 +200,13 @@ njs_value_to_key(njs_vm_t *vm, njs_value value = njs_object_value(value); } else { - ret = njs_value_to_primitive(vm, &primitive, value, 1); + if (convert) { + ret = njs_value_to_primitive(vm, &primitive, value, 1); + + } else { + ret = njs_object_to_string(vm, value, &primitive); + } + if (njs_slow_path(ret != NJS_OK)) { return ret; } @@ -211,6 +220,13 @@ njs_value_to_key(njs_vm_t *vm, njs_value njs_inline njs_int_t +njs_value_to_key(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value) +{ + return njs_value_to_key2(vm, dst, value, 1); +} + + +njs_inline njs_int_t njs_key_string_get(njs_vm_t *vm, njs_value_t *key, njs_str_t *str) { njs_int_t ret; From xeioex at nginx.com Fri Sep 16 06:46:15 2022 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 16 Sep 2022 06:46:15 +0000 Subject: [njs] Fixed property set instruction when key modifies base binding. Message-ID: details: https://hg.nginx.org/njs/rev/23caaca15f08 branches: changeset: 1955:23caaca15f08 user: Dmitry Volyntsev date: Thu Sep 15 20:20:10 2022 -0700 description: Fixed property set instruction when key modifies base binding. Previously, when obj[prop] expression was evaluated, and prop was an object with custom "toString" method, which modifies obj binding as its side-effect, the binding update was visible to property set instruction which is not correct. This closes #550 issue on Github. diffstat: src/njs_object.c | 22 ++++++++++-- src/njs_value.c | 25 +++++++++------ src/njs_value.h | 4 ++ src/njs_vmcode.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++- src/test/njs_unit_test.c | 16 +++++++++- 5 files changed, 129 insertions(+), 18 deletions(-) diffs (294 lines): diff -r 75922905bd9c -r 23caaca15f08 src/njs_object.c --- a/src/njs_object.c Thu Sep 15 17:56:35 2022 -0700 +++ b/src/njs_object.c Thu Sep 15 20:20:10 2022 -0700 @@ -2450,7 +2450,7 @@ njs_object_prototype_has_own_property(nj njs_uint_t nargs, njs_index_t unused) { njs_int_t ret; - njs_value_t *value, *property; + njs_value_t *value, *property, lvalue; njs_property_query_t pq; value = njs_argument(args, 0); @@ -2461,7 +2461,14 @@ njs_object_prototype_has_own_property(nj return NJS_ERROR; } - property = njs_arg(args, nargs, 1); + property = njs_lvalue_arg(&lvalue, args, nargs, 1); + + if (njs_slow_path(!njs_is_key(property))) { + ret = njs_value_to_key(vm, property, property); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + } njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 1); @@ -2488,7 +2495,7 @@ njs_object_prototype_prop_is_enumerable( njs_uint_t nargs, njs_index_t unused) { njs_int_t ret; - njs_value_t *value, *property; + njs_value_t *value, *property, lvalue; const njs_value_t *retval; njs_object_prop_t *prop; njs_property_query_t pq; @@ -2501,7 +2508,14 @@ njs_object_prototype_prop_is_enumerable( return NJS_ERROR; } - property = njs_arg(args, nargs, 1); + property = njs_lvalue_arg(&lvalue, args, nargs, 1); + + if (njs_slow_path(!njs_is_key(property))) { + ret = njs_value_to_key(vm, property, property); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + } njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 1); diff -r 75922905bd9c -r 23caaca15f08 src/njs_value.c --- a/src/njs_value.c Thu Sep 15 17:56:35 2022 -0700 +++ b/src/njs_value.c Thu Sep 15 20:20:10 2022 -0700 @@ -535,20 +535,11 @@ njs_property_query(njs_vm_t *vm, njs_pro uint32_t index; njs_int_t ret; njs_object_t *obj; - njs_value_t prop; njs_function_t *function; - if (njs_slow_path(!njs_is_primitive(key))) { - ret = njs_value_to_string(vm, &prop, key); - if (ret != NJS_OK) { - return ret; - } - - key = ∝ - } + njs_assert(njs_is_index_or_key(key)); switch (value->type) { - case NJS_BOOLEAN: case NJS_NUMBER: case NJS_SYMBOL: @@ -1004,6 +995,8 @@ njs_value_property(njs_vm_t *vm, njs_val njs_typed_array_t *tarray; njs_property_query_t pq; + njs_assert(njs_is_index_or_key(key)); + if (njs_fast_path(njs_is_number(key))) { num = njs_number(key); @@ -1135,6 +1128,8 @@ njs_value_property_set(njs_vm_t *vm, njs static const njs_str_t length_key = njs_str("length"); + njs_assert(njs_is_index_or_key(key)); + if (njs_fast_path(njs_is_number(key))) { num = njs_number(key); @@ -1330,9 +1325,19 @@ njs_value_property_delete(njs_vm_t *vm, njs_value_t *removed, njs_bool_t thrw) { njs_int_t ret; + njs_value_t primitive; njs_object_prop_t *prop; njs_property_query_t pq; + if (njs_slow_path(!njs_is_key(key))) { + ret = njs_value_to_key(vm, &primitive, key); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + key = &primitive; + } + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_DELETE, 1); ret = njs_property_query(vm, &pq, value, key); diff -r 75922905bd9c -r 23caaca15f08 src/njs_value.h --- a/src/njs_value.h Thu Sep 15 17:56:35 2022 -0700 +++ b/src/njs_value.h Thu Sep 15 20:20:10 2022 -0700 @@ -532,6 +532,10 @@ typedef struct { (njs_is_string(value) || njs_is_symbol(value)) +#define njs_is_index_or_key(value) \ + (njs_is_number(value) || njs_is_key(value)) + + /* * The truth field coincides with short_string.size and short_string.length * so when string size and length are zero the string's value is false and diff -r 75922905bd9c -r 23caaca15f08 src/njs_vmcode.c --- a/src/njs_vmcode.c Thu Sep 15 17:56:35 2022 -0700 +++ b/src/njs_vmcode.c Thu Sep 15 20:20:10 2022 -0700 @@ -58,6 +58,8 @@ static njs_jump_off_t njs_vmcode_try_end static njs_jump_off_t njs_vmcode_finally(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval, u_char *pc); static void njs_vmcode_error(njs_vm_t *vm, u_char *pc); +static njs_int_t njs_throw_cannot_property(njs_vm_t *vm, njs_value_t *object, + njs_value_t *key, const char *what); static njs_jump_off_t njs_string_concat(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2); @@ -185,6 +187,21 @@ next: get = (njs_vmcode_prop_get_t *) pc; njs_vmcode_operand(vm, get->value, retval); + if (njs_slow_path(!njs_is_index_or_key(value2))) { + if (njs_slow_path(njs_is_null_or_undefined(value1))) { + (void) njs_throw_cannot_property(vm, value1, value2, + "get"); + goto error; + } + + ret = njs_value_to_key(vm, &primitive1, value2); + if (njs_slow_path(ret != NJS_OK)) { + goto error; + } + + value2 = &primitive1; + } + ret = njs_value_property(vm, value1, value2, retval); if (njs_slow_path(ret == NJS_ERROR)) { goto error; @@ -670,6 +687,23 @@ next: set = (njs_vmcode_prop_set_t *) pc; njs_vmcode_operand(vm, set->value, retval); + if (njs_slow_path(!njs_is_index_or_key(value2))) { + if (njs_slow_path(njs_is_null_or_undefined(value1))) { + (void) njs_throw_cannot_property(vm, value1, value2, + "set"); + goto error; + } + + njs_value_assign(&primitive1, value1); + ret = njs_value_to_key(vm, &primitive2, value2); + if (njs_slow_path(ret != NJS_OK)) { + goto error; + } + + value1 = &primitive1; + value2 = &primitive2; + } + ret = njs_value_property_set(vm, value1, value2, retval); if (njs_slow_path(ret == NJS_ERROR)) { goto error; @@ -768,6 +802,21 @@ next: case NJS_VMCODE_METHOD_FRAME: method_frame = (njs_vmcode_method_frame_t *) pc; + if (njs_slow_path(!njs_is_key(value2))) { + if (njs_slow_path(njs_is_null_or_undefined(value1))) { + (void) njs_throw_cannot_property(vm, value1, value2, + "get"); + goto error; + } + + ret = njs_value_to_key(vm, &primitive1, value2); + if (njs_slow_path(ret != NJS_OK)) { + goto error; + } + + value2 = &primitive1; + } + ret = njs_value_property(vm, value1, value2, &dst); if (njs_slow_path(ret == NJS_ERROR)) { goto error; @@ -1417,6 +1466,7 @@ static njs_jump_off_t njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *value, njs_value_t *key) { njs_int_t ret; + njs_value_t primitive; njs_property_query_t pq; if (njs_slow_path(njs_is_primitive(value))) { @@ -1425,11 +1475,13 @@ njs_vmcode_property_in(njs_vm_t *vm, njs return NJS_ERROR; } - if (njs_slow_path(!njs_is_key(key))) { - ret = njs_value_to_key(vm, key, key); + if (njs_slow_path(!njs_is_index_or_key(key))) { + ret = njs_value_to_key(vm, &primitive, key); if (njs_slow_path(ret != NJS_OK)) { - return ret; + return NJS_ERROR; } + + key = &primitive; } njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0); @@ -2186,3 +2238,25 @@ njs_vmcode_error(njs_vm_t *vm, u_char *p njs_error_fmt_new(vm, &vm->retval, err->type, "%V", &err->u.message); } } + + +static njs_int_t +njs_throw_cannot_property(njs_vm_t *vm, njs_value_t *object, njs_value_t *key, + const char *what) +{ + njs_int_t ret; + njs_str_t string; + njs_value_t dst; + + ret = njs_value_to_key2(vm, &dst, key, 0); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + njs_key_string_get(vm, &dst, &string); + + njs_type_error(vm, "cannot %s property \"%V\" of %s", what, + &string, njs_is_null(object) ? "null" : "undefined"); + + return NJS_OK; +} diff -r 75922905bd9c -r 23caaca15f08 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Thu Sep 15 17:56:35 2022 -0700 +++ b/src/test/njs_unit_test.c Thu Sep 15 20:20:10 2022 -0700 @@ -3462,7 +3462,7 @@ static njs_unit_test_t njs_test[] = { njs_str("function f() { Object.prototype.toString = 1; };" "Object.prototype.toString = f;" "(function () { try { 's'[{}](); } catch (e) { throw e; } })()"), - njs_str("TypeError: Cannot convert object to primitive value") }, + njs_str("TypeError: (intermediate value)[\"undefined\"] is not a function") }, { njs_str("var i; for (i = 0; i < 10; i++) { i += 1 } i"), njs_str("10") }, @@ -4409,6 +4409,20 @@ static njs_unit_test_t njs_test[] = "var a = [1,2]; a[1.5] = 5; '' + (n in a) + (delete a[n])"), njs_str("truetrue") }, + { njs_str("var o = {}, v = o;" + "v[{toString: () => { v = 'V'; return 'a';}}] = 1;" + "[v, o.a]"), + njs_str("V,1") }, + + { njs_str("var o = null; o[{toString:()=>{throw 'OOps'}}]"), + njs_str("TypeError: cannot get property \"[object Object]\" of null") }, + + { njs_str("var o = null; o[{toString:()=>{throw 'OOps'}}]()"), + njs_str("TypeError: cannot get property \"[object Object]\" of null") }, + + { njs_str("var o = null; o[{toString:()=>{throw 'OOps'}}] = 1"), + njs_str("TypeError: cannot set property \"[object Object]\" of null") }, + /**/ { njs_str("Array.isArray()"), From xeioex at nginx.com Fri Sep 16 06:46:17 2022 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 16 Sep 2022 06:46:17 +0000 Subject: [njs] Fixed complex assignments. Message-ID: details: https://hg.nginx.org/njs/rev/82f41f43abd4 branches: changeset: 1956:82f41f43abd4 user: Dmitry Volyntsev date: Thu Sep 15 20:20:11 2022 -0700 description: Fixed complex assignments. A new instruction is introduced NJS_VMCODE_TO_PROPERTY_KEY to ensure that property key is evaluated only once. diffstat: src/njs_disassembler.c | 2 ++ src/njs_generator.c | 45 +++++++++++++++++++++++++++++++++++++-------- src/njs_value.c | 8 +++++--- src/njs_vmcode.c | 17 +++++++++++++++++ src/njs_vmcode.h | 1 + src/test/njs_unit_test.c | 7 +++++-- 6 files changed, 67 insertions(+), 13 deletions(-) diffs (231 lines): diff -r 23caaca15f08 -r 82f41f43abd4 src/njs_disassembler.c --- a/src/njs_disassembler.c Thu Sep 15 20:20:10 2022 -0700 +++ b/src/njs_disassembler.c Thu Sep 15 20:20:11 2022 -0700 @@ -74,6 +74,8 @@ static njs_code_name_t code_names[] = { njs_str("VOID ") }, { NJS_VMCODE_TYPEOF, sizeof(njs_vmcode_2addr_t), njs_str("TYPEOF ") }, + { NJS_VMCODE_TO_PROPERTY_KEY, sizeof(njs_vmcode_3addr_t), + njs_str("TO PROPERTY KEY ") }, { NJS_VMCODE_UNARY_PLUS, sizeof(njs_vmcode_2addr_t), njs_str("PLUS ") }, diff -r 23caaca15f08 -r 82f41f43abd4 src/njs_generator.c --- a/src/njs_generator.c Thu Sep 15 20:20:10 2022 -0700 +++ b/src/njs_generator.c Thu Sep 15 20:20:11 2022 -0700 @@ -3016,9 +3016,10 @@ static njs_int_t njs_generate_operation_assignment_prop(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { - njs_index_t index, src; + njs_index_t index, src, prop_index; njs_parser_node_t *lvalue, *object, *property; njs_vmcode_move_t *move; + njs_vmcode_3addr_t *to_property_key; njs_vmcode_prop_get_t *prop_get; lvalue = node->left; @@ -3053,6 +3054,18 @@ njs_generate_operation_assignment_prop(n } } + prop_index = njs_generate_node_temp_index_get(vm, generator, node); + if (njs_slow_path(prop_index == NJS_INDEX_ERROR)) { + return NJS_ERROR; + } + + njs_generate_code(generator, njs_vmcode_3addr_t, to_property_key, + NJS_VMCODE_TO_PROPERTY_KEY, 2, property); + + to_property_key->src2 = object->index; + to_property_key->src1 = property->index; + to_property_key->dst = prop_index; + index = njs_generate_node_temp_index_get(vm, generator, node); if (njs_slow_path(index == NJS_INDEX_ERROR)) { return NJS_ERROR; @@ -3062,13 +3075,14 @@ njs_generate_operation_assignment_prop(n NJS_VMCODE_PROPERTY_GET, 3, property); prop_get->value = index; prop_get->object = object->index; - prop_get->property = property->index; + prop_get->property = prop_index; njs_generator_next(generator, njs_generate, node->right); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, - njs_generate_operation_assignment_end, NULL, 0); + njs_generate_operation_assignment_end, + &prop_index, sizeof(njs_index_t)); } @@ -3077,6 +3091,7 @@ njs_generate_operation_assignment_end(nj njs_parser_node_t *node) { njs_int_t ret; + njs_index_t prop_index; njs_parser_node_t *lvalue, *expr; njs_vmcode_3addr_t *code; njs_vmcode_prop_set_t *prop_set; @@ -3084,6 +3099,8 @@ njs_generate_operation_assignment_end(nj lvalue = node->left; expr = node->right; + prop_index = *((njs_index_t *) generator->context); + njs_generate_code(generator, njs_vmcode_3addr_t, code, node->u.operation, 3, expr); code->dst = node->index; @@ -3094,7 +3111,7 @@ njs_generate_operation_assignment_end(nj NJS_VMCODE_PROPERTY_SET, 3, expr); prop_set->value = node->index; prop_set->object = lvalue->left->index; - prop_set->property = lvalue->right->index; + prop_set->property = prop_index; ret = njs_generate_children_indexes_release(vm, generator, lvalue); if (njs_slow_path(ret != NJS_OK)) { @@ -3677,9 +3694,9 @@ njs_generate_inc_dec_operation_prop(njs_ { njs_int_t ret; njs_bool_t post; - njs_index_t index, dest_index; + njs_index_t index, dest_index, prop_index; njs_parser_node_t *lvalue; - njs_vmcode_3addr_t *code; + njs_vmcode_3addr_t *code, *to_property_key; njs_vmcode_prop_get_t *prop_get; njs_vmcode_prop_set_t *prop_set; @@ -3701,6 +3718,18 @@ njs_generate_inc_dec_operation_prop(njs_ found: + prop_index = njs_generate_temp_index_get(vm, generator, node); + if (njs_slow_path(prop_index == NJS_INDEX_ERROR)) { + return NJS_ERROR; + } + + njs_generate_code(generator, njs_vmcode_3addr_t, to_property_key, + NJS_VMCODE_TO_PROPERTY_KEY, 2, node); + + to_property_key->src2 = lvalue->left->index; + to_property_key->src1 = lvalue->right->index; + to_property_key->dst = prop_index; + post = *((njs_bool_t *) generator->context); index = post ? njs_generate_temp_index_get(vm, generator, node) @@ -3714,7 +3743,7 @@ found: NJS_VMCODE_PROPERTY_GET, 3, node); prop_get->value = index; prop_get->object = lvalue->left->index; - prop_get->property = lvalue->right->index; + prop_get->property = prop_index; njs_generate_code(generator, njs_vmcode_3addr_t, code, node->u.operation, 3, node); @@ -3726,7 +3755,7 @@ found: NJS_VMCODE_PROPERTY_SET, 3, node); prop_set->value = index; prop_set->object = lvalue->left->index; - prop_set->property = lvalue->right->index; + prop_set->property = prop_index; if (post) { ret = njs_generate_index_release(vm, generator, index); diff -r 23caaca15f08 -r 82f41f43abd4 src/njs_value.c --- a/src/njs_value.c Thu Sep 15 20:20:10 2022 -0700 +++ b/src/njs_value.c Thu Sep 15 20:20:11 2022 -0700 @@ -586,12 +586,14 @@ njs_property_query(njs_vm_t *vm, njs_pro if (njs_fast_path(ret == NJS_OK)) { njs_string_get(&pq->key, &pq->lhq.key); - njs_type_error(vm, "cannot get property \"%V\" of undefined", - &pq->lhq.key); + njs_type_error(vm, "cannot get property \"%V\" of %s", + &pq->lhq.key, njs_is_null(value) ? "null" + : "undefined"); return NJS_ERROR; } - njs_type_error(vm, "cannot get property \"unknown\" of undefined"); + njs_type_error(vm, "cannot get property \"unknown\" of %s", + njs_is_null(value) ? "null" : "undefined"); return NJS_ERROR; } diff -r 23caaca15f08 -r 82f41f43abd4 src/njs_vmcode.c --- a/src/njs_vmcode.c Thu Sep 15 20:20:10 2022 -0700 +++ b/src/njs_vmcode.c Thu Sep 15 20:20:11 2022 -0700 @@ -883,9 +883,26 @@ next: case NJS_VMCODE_ARGUMENTS: ret = njs_vmcode_arguments(vm, pc); if (njs_slow_path(ret == NJS_ERROR)) { + } + + break; + + case NJS_VMCODE_TO_PROPERTY_KEY: + njs_vmcode_operand(vm, (njs_index_t) value2, retval); + njs_vmcode_operand(vm, vmcode->operand3, value2); + + if (njs_slow_path(njs_is_null_or_undefined(value2))) { + (void) njs_throw_cannot_property(vm, value2, value1, + "get"); goto error; } + ret = njs_value_to_string(vm, retval, value1); + if (njs_fast_path(ret == NJS_ERROR)) { + goto error; + } + + ret = sizeof(njs_vmcode_3addr_t); break; case NJS_VMCODE_PROTO_INIT: diff -r 23caaca15f08 -r 82f41f43abd4 src/njs_vmcode.h --- a/src/njs_vmcode.h Thu Sep 15 20:20:10 2022 -0700 +++ b/src/njs_vmcode.h Thu Sep 15 20:20:11 2022 -0700 @@ -49,6 +49,7 @@ enum { NJS_VMCODE_THIS, NJS_VMCODE_ARGUMENTS, NJS_VMCODE_PROTO_INIT, + NJS_VMCODE_TO_PROPERTY_KEY, NJS_VMCODE_IMPORT, NJS_VMCODE_AWAIT, diff -r 23caaca15f08 -r 82f41f43abd4 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Thu Sep 15 20:20:10 2022 -0700 +++ b/src/test/njs_unit_test.c Thu Sep 15 20:20:11 2022 -0700 @@ -3651,7 +3651,7 @@ static njs_unit_test_t njs_test[] = njs_str("TypeError: cannot get property \"b\" of undefined") }, { njs_str("var a = null; a.b++; a.b"), - njs_str("TypeError: cannot get property \"b\" of undefined") }, + njs_str("TypeError: cannot get property \"b\" of null") }, { njs_str("var a = true; a.b++; a.b"), njs_str("TypeError: property set on primitive boolean type") }, @@ -4423,6 +4423,9 @@ static njs_unit_test_t njs_test[] = { njs_str("var o = null; o[{toString:()=>{throw 'OOps'}}] = 1"), njs_str("TypeError: cannot set property \"[object Object]\" of null") }, + { njs_str("var o = null; o[{toString:()=>{throw 'OOps'}}] += 1"), + njs_str("TypeError: cannot get property \"[object Object]\" of null") }, + /**/ { njs_str("Array.isArray()"), @@ -12281,7 +12284,7 @@ static njs_unit_test_t njs_test[] = njs_str("TypeError: Cyclic __proto__ value") }, { njs_str("Object.prototype.__proto__.f()"), - njs_str("TypeError: cannot get property \"f\" of undefined") }, + njs_str("TypeError: cannot get property \"f\" of null") }, { njs_str("var obj = Object.create(null); obj.one = 1;" "var res = [];" From pluknet at nginx.com Fri Sep 16 17:03:38 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Fri, 16 Sep 2022 21:03:38 +0400 Subject: [PATCH] Tests: fixed try_run() to remove temporary directory on failure In-Reply-To: References: <7eb87eaf06f4b9161cfe.1662472371@enoparse.local> Message-ID: <66AB4DFD-2923-48B0-AC85-8D8876376C12@nginx.com> > On 7 Sep 2022, at 05:46, Maxim Dounin wrote: > > Hello! > > On Tue, Sep 06, 2022 at 05:52:51PM +0400, Sergey Kandaurov wrote: > >> # HG changeset patch >> # User Sergey Kandaurov >> # Date 1662472340 -14400 >> # Tue Sep 06 17:52:20 2022 +0400 >> # Node ID 7eb87eaf06f4b9161cfe298f195947dbddedd01d >> # Parent 78fe648d54a79822a72f3a5f8cc79651b3b1f5a7 >> Tests: fixed try_run() to remove temporary directory on failure. >> >> Keeping the file handle pointing to the "stderr" file prevented removal >> of the temporary directory on win32 if execution aborted in run(). >> The fix is to move safe file operations surrounding run() out of the >> eval block such that the state of file handles is consistent. >> Fixes a1874249496d. > > I believe it might need some additional emphasis that this is > about Windows only. For example: > > Tests: fixed try_run() to remove temporary directory on win32. > Agree, thx. >> >> diff -r 78fe648d54a7 -r 7eb87eaf06f4 lib/Test/Nginx.pm >> --- a/lib/Test/Nginx.pm Tue Aug 30 17:24:16 2022 +0400 >> +++ b/lib/Test/Nginx.pm Tue Sep 06 17:52:20 2022 +0400 >> @@ -290,8 +290,9 @@ sub has_daemon($) { >> sub try_run($$) { >> my ($self, $message) = @_; >> >> + open OLDERR, ">&", \*STDERR; >> + >> eval { >> - open OLDERR, ">&", \*STDERR; >> open NEWERR, ">", $self->{_testdir} . '/stderr' >> or die "Can't open stderr: $!"; >> close STDERR; >> @@ -299,10 +300,10 @@ sub try_run($$) { >> close NEWERR; >> >> $self->run(); >> + }; >> >> - close STDERR; >> - open STDERR, ">&", \*OLDERR; >> - }; >> + close STDERR; >> + open STDERR, ">&", \*OLDERR; >> >> return $self unless $@; > > Wouldn't it be better to move all stderr handling out of the eval? > > diff --git a/lib/Test/Nginx.pm b/lib/Test/Nginx.pm > --- a/lib/Test/Nginx.pm > +++ b/lib/Test/Nginx.pm > @@ -291,14 +291,13 @@ sub try_run($$) { > my ($self, $message) = @_; > > open OLDERR, ">&", \*STDERR; > + open NEWERR, ">", $self->{_testdir} . '/stderr' > + or die "Can't open stderr: $!"; > + close STDERR; > + open STDERR, ">&", \*NEWERR; > + close NEWERR; > > eval { > - open NEWERR, ">", $self->{_testdir} . '/stderr' > - or die "Can't open stderr: $!"; > - close STDERR; > - open STDERR, ">&", \*NEWERR; > - close NEWERR; > - > $self->run(); > }; > The reason was to continue hide '/stderr' open errors in eval, but I tend to think that it's rather better to die explicitly, as that's not something to happen. > > Alternatively, it might be the right time to backout a1874249496d > completely and rely on "-e ..." instead, given that it was > introduced in 1.19.5 and already present in all supported > versions. I thought about that, looks like the time has come to cleanup things. It doesn't break out nicely into two pieces, so comes in one change. # HG changeset patch # User Sergey Kandaurov # Date 1663347154 -14400 # Fri Sep 16 20:52:34 2022 +0400 # Node ID 16c3e0b1c8878bb127425b4da873f8eba604c879 # Parent 78fe648d54a79822a72f3a5f8cc79651b3b1f5a7 Tests: updated try_run() to rely on nginx "-e". The "-e" command line option introduced in nginx 1.19.5 is now used to print error line on startup failures with TEST_NGINX_VERBOSE set. This change replaces a previous approach (a1874249496d) compatible with pre-1.19.5 nginx versions that used to redirect stderr to file. Hence, "-e" compatibility is removed. As a side effect, this fixes temporary directory removal on win32 left on startup failures because the "stderr" file was kept open. diff --git a/lib/Test/Nginx.pm b/lib/Test/Nginx.pm --- a/lib/Test/Nginx.pm +++ b/lib/Test/Nginx.pm @@ -48,8 +48,6 @@ sub new { ) or die "Can't create temp directory: $!\n"; $self->{_testdir} =~ s!\\!/!g if $^O eq 'MSWin32'; - mkdir "$self->{_testdir}/logs" - or die "Can't create logs directory: $!\n"; Test::More::BAIL_OUT("no $NGINX binary found") unless -x $NGINX; @@ -291,24 +289,16 @@ sub try_run($$) { my ($self, $message) = @_; eval { - open OLDERR, ">&", \*STDERR; - open NEWERR, ">", $self->{_testdir} . '/stderr' - or die "Can't open stderr: $!"; - close STDERR; - open STDERR, ">&", \*NEWERR; - close NEWERR; - + open OLDERR, ">&", \*STDERR; close STDERR; $self->run(); - - close STDERR; open STDERR, ">&", \*OLDERR; }; return $self unless $@; if ($ENV{TEST_NGINX_VERBOSE}) { - open F, '<', $self->{_testdir} . '/stderr' - or die "Can't open stderr: $!"; + open F, '<', $self->{_testdir} . '/error.log' + or die "Can't open error.log: $!"; log_core($_) while (); close F; } @@ -350,10 +340,8 @@ sub run(;$) { my @globals = $self->{_test_globals} ? () : ('-g', "pid $testdir/nginx.pid; " . "error_log $testdir/error.log debug;"); - my @error = $self->has_version('1.19.5') ? - ('-e', 'error.log') : (); exec($NGINX, '-p', "$testdir/", '-c', 'nginx.conf', - @error, @globals) + '-e', 'error.log', @globals) or die "Unable to exec(): $!\n"; } @@ -425,10 +413,8 @@ sub dump_config() { my @globals = $self->{_test_globals} ? () : ('-g', "pid $testdir/nginx.pid; " . "error_log $testdir/error.log debug;"); - my @error = $self->has_version('1.19.5') ? - ('-e', 'error.log') : (); my $command = "$NGINX -T -p $testdir/ -c nginx.conf " - . join(' ', @error, @globals); + . join(' ', '-e', 'error.log', @globals); return qx/$command 2>&1/; } @@ -481,10 +467,8 @@ sub reload() { my @globals = $self->{_test_globals} ? () : ('-g', "pid $testdir/nginx.pid; " . "error_log $testdir/error.log debug;"); - my @error = $self->has_version('1.19.5') ? - ('-e', 'error.log') : (); system($NGINX, '-p', $testdir, '-c', "nginx.conf", - '-s', 'reload', @error, @globals) == 0 + '-s', 'reload', '-e', 'error.log', @globals) == 0 or die "system() failed: $?\n"; } else { @@ -506,10 +490,8 @@ sub stop() { my @globals = $self->{_test_globals} ? () : ('-g', "pid $testdir/nginx.pid; " . "error_log $testdir/error.log debug;"); - my @error = $self->has_version('1.19.5') ? - ('-e', 'error.log') : (); system($NGINX, '-p', $testdir, '-c', "nginx.conf", - '-s', 'quit', @error, @globals) == 0 + '-s', 'quit', '-e', 'error.log', @globals) == 0 or die "system() failed: $?\n"; } else { @@ -530,10 +512,8 @@ sub stop() { my @globals = $self->{_test_globals} ? () : ('-g', "pid $testdir/nginx.pid; " . "error_log $testdir/error.log debug;"); - my @error = $self->has_version('1.19.5') ? - ('-e', 'error.log') : (); system($NGINX, '-p', $testdir, '-c', "nginx.conf", - '-s', 'stop', @error, @globals) == 0 + '-s', 'stop', '-e', 'error.log', @globals) == 0 or die "system() failed: $?\n"; } else { -- Sergey Kandaurov From v.zhestikov at f5.com Fri Sep 16 19:29:24 2022 From: v.zhestikov at f5.com (Vadim Zhestikov) Date: Fri, 16 Sep 2022 19:29:24 +0000 Subject: [njs] Modules: added js_preload_object directive. Message-ID: details: https://hg.nginx.org/njs/rev/52b3e1f2a3e1 branches: changeset: 1957:52b3e1f2a3e1 user: Vadim Zhestikov date: Fri Sep 16 12:27:40 2022 -0700 description: Modules: added js_preload_object directive. diffstat: nginx/ngx_http_js_module.c | 100 +++++++++++++++++++- nginx/ngx_js.c | 200 +++++++++++++++++++++++++++++++++++++++++++ nginx/ngx_js.h | 7 +- nginx/ngx_stream_js_module.c | 100 +++++++++++++++++++- 4 files changed, 392 insertions(+), 15 deletions(-) diffs (599 lines): diff -r 82f41f43abd4 -r 52b3e1f2a3e1 nginx/ngx_http_js_module.c --- a/nginx/ngx_http_js_module.c Thu Sep 15 20:20:11 2022 -0700 +++ b/nginx/ngx_http_js_module.c Fri Sep 16 12:27:40 2022 -0700 @@ -294,6 +294,13 @@ static ngx_command_t ngx_http_js_comman 0, NULL }, + { ngx_string("js_preload_object"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE13, + ngx_js_preload_object, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + { ngx_string("js_path"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_array_slot, @@ -1212,8 +1219,12 @@ ngx_http_js_init_vm(ngx_http_request_t * { njs_int_t rc; ngx_str_t exception; + njs_str_t key; + ngx_uint_t i; ngx_http_js_ctx_t *ctx; + njs_opaque_value_t retval; ngx_pool_cleanup_t *cln; + ngx_js_named_path_t *preload; ngx_http_js_loc_conf_t *jlcf; jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module); @@ -1253,6 +1264,27 @@ ngx_http_js_init_vm(ngx_http_request_t * cln->handler = ngx_http_js_cleanup_ctx; cln->data = ctx; + /* bind objects from preload vm */ + + if (jlcf->preload_objects != NGX_CONF_UNSET_PTR) { + preload = jlcf->preload_objects->elts; + + for (i = 0; i < jlcf->preload_objects->nelts; i++) { + key.start = preload[i].name.data; + key.length = preload[i].name.len; + + rc = njs_vm_value(jlcf->preload_vm, &key, njs_value_arg(&retval)); + if (rc != NJS_OK) { + return NGX_ERROR; + } + + rc = njs_vm_bind(ctx->vm, &key, njs_value_arg(&retval), 0); + if (rc != NJS_OK) { + return NGX_ERROR; + } + } + } + if (njs_vm_start(ctx->vm) == NJS_ERROR) { ngx_js_retval(ctx->vm, NULL, &exception); @@ -1288,9 +1320,13 @@ ngx_http_js_cleanup_ctx(void *data) static void ngx_http_js_cleanup_vm(void *data) { - njs_vm_t *vm = data; - - njs_vm_destroy(vm); + ngx_http_js_loc_conf_t *jlcf = data; + + njs_vm_destroy(jlcf->vm); + + if (jlcf->preload_objects != NGX_CONF_UNSET_PTR) { + njs_vm_destroy(jlcf->preload_vm); + } } @@ -4172,8 +4208,8 @@ ngx_http_js_merge_vm(ngx_conf_t *cf, ngx { ngx_str_t *path, *s; ngx_uint_t i; - ngx_array_t *imports, *paths; - ngx_js_named_path_t *import, *pi; + ngx_array_t *imports, *preload_objects, *paths; + ngx_js_named_path_t *import, *pi, *pij, *preload; if (prev->imports != NGX_CONF_UNSET_PTR && prev->vm == NULL) { if (ngx_http_js_init_conf_vm(cf, prev) != NGX_OK) { @@ -4182,16 +4218,58 @@ ngx_http_js_merge_vm(ngx_conf_t *cf, ngx } if (conf->imports == NGX_CONF_UNSET_PTR - && conf->paths == NGX_CONF_UNSET_PTR) + && conf->paths == NGX_CONF_UNSET_PTR + && conf->preload_objects == NGX_CONF_UNSET_PTR) { if (prev->vm != NULL) { + conf->preload_objects = prev->preload_objects; conf->imports = prev->imports; conf->paths = prev->paths; conf->vm = prev->vm; + + conf->preload_vm = prev->preload_vm; + return NGX_OK; } } + if (prev->preload_objects != NGX_CONF_UNSET_PTR) { + if (conf->preload_objects == NGX_CONF_UNSET_PTR) { + conf->preload_objects = prev->preload_objects; + + } else { + preload_objects = ngx_array_create(cf->pool, 4, + sizeof(ngx_js_named_path_t)); + if (preload_objects == NULL) { + return NGX_ERROR; + } + + pij = prev->preload_objects->elts; + + for (i = 0; i < prev->preload_objects->nelts; i++) { + preload = ngx_array_push(preload_objects); + if (preload == NULL) { + return NGX_ERROR; + } + + *preload = pij[i]; + } + + pij = conf->preload_objects->elts; + + for (i = 0; i < conf->preload_objects->nelts; i++) { + preload = ngx_array_push(preload_objects); + if (preload == NULL) { + return NGX_ERROR; + } + + *preload = pij[i]; + } + + conf->preload_objects = preload_objects; + } + } + if (prev->imports != NGX_CONF_UNSET_PTR) { if (conf->imports == NGX_CONF_UNSET_PTR) { conf->imports = prev->imports; @@ -4291,6 +4369,12 @@ ngx_http_js_init_conf_vm(ngx_conf_t *cf, static const njs_str_t line_number_key = njs_str("lineNumber"); static const njs_str_t file_name_key = njs_str("fileName"); + if (conf->preload_objects != NGX_CONF_UNSET_PTR) { + if (ngx_js_init_preload_vm(cf, (ngx_js_conf_t *)conf) != NGX_OK) { + return NGX_ERROR; + } + } + size = 0; import = conf->imports->elts; @@ -4352,7 +4436,7 @@ ngx_http_js_init_conf_vm(ngx_conf_t *cf, } cln->handler = ngx_http_js_cleanup_vm; - cln->data = conf->vm; + cln->data = conf; path.start = ngx_cycle->conf_prefix.data; path.length = ngx_cycle->conf_prefix.len; @@ -4619,6 +4703,7 @@ ngx_http_js_create_loc_conf(ngx_conf_t * * set by ngx_pcalloc(): * * conf->vm = NULL; + * conf->preload_vm = NULL; * conf->content = { 0, NULL }; * conf->header_filter = { 0, NULL }; * conf->body_filter = { 0, NULL }; @@ -4630,6 +4715,7 @@ ngx_http_js_create_loc_conf(ngx_conf_t * conf->paths = NGX_CONF_UNSET_PTR; conf->imports = NGX_CONF_UNSET_PTR; + conf->preload_objects = NGX_CONF_UNSET_PTR; conf->buffer_size = NGX_CONF_UNSET_SIZE; conf->max_response_body_size = NGX_CONF_UNSET_SIZE; diff -r 82f41f43abd4 -r 52b3e1f2a3e1 nginx/ngx_js.c --- a/nginx/ngx_js.c Thu Sep 15 20:20:11 2022 -0700 +++ b/nginx/ngx_js.c Fri Sep 16 12:27:40 2022 -0700 @@ -493,3 +493,203 @@ ngx_js_import(ngx_conf_t *cf, ngx_comman return NGX_CONF_OK; } + +char * +ngx_js_preload_object(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_js_conf_t *jscf = conf; + + u_char *p, *end, c; + ngx_int_t from; + ngx_str_t *value, name, path; + ngx_js_named_path_t *preload; + + value = cf->args->elts; + from = (cf->args->nelts == 4); + + if (from) { + if (ngx_strcmp(value[2].data, "from") != 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } + } + + name = value[1]; + path = (from ? value[3] : value[1]); + + if (!from) { + end = name.data + name.len; + + for (p = end - 1; p >= name.data; p--) { + if (*p == '/') { + break; + } + } + + name.data = p + 1; + name.len = end - p - 1; + + if (name.len < 5 + || ngx_memcmp(&name.data[name.len - 5], ".json", 5) != 0) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "cannot extract export name from file path " + "\"%V\", use extended \"from\" syntax", &path); + return NGX_CONF_ERROR; + } + + name.len -= 5; + } + + if (name.len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty global name"); + return NGX_CONF_ERROR; + } + + p = name.data; + end = name.data + name.len; + + while (p < end) { + c = ngx_tolower(*p); + + if (*p != '_' && (c < 'a' || c > 'z')) { + if (p == name.data) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "cannot start " + "with \"%c\" in global name \"%V\"", *p, + &name); + return NGX_CONF_ERROR; + } + + if (*p < '0' || *p > '9' || *p == '.') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid character " + "\"%c\" in global name \"%V\"", *p, + &name); + return NGX_CONF_ERROR; + } + } + + p++; + } + + if (ngx_strchr(path.data, '\'') != NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid character \"'\" " + "in file path \"%V\"", &path); + return NGX_CONF_ERROR; + } + + if (jscf->preload_objects == NGX_CONF_UNSET_PTR) { + jscf->preload_objects = ngx_array_create(cf->pool, 4, + sizeof(ngx_js_named_path_t)); + if (jscf->preload_objects == NULL) { + return NGX_CONF_ERROR; + } + } + + preload = ngx_array_push(jscf->preload_objects); + if (preload == NULL) { + return NGX_CONF_ERROR; + } + + preload->name = name; + preload->path = path; + preload->file = cf->conf_file->file.name.data; + preload->line = cf->conf_file->line; + + return NGX_CONF_OK; +} + + +ngx_int_t +ngx_js_init_preload_vm(ngx_conf_t *cf, ngx_js_conf_t *conf) +{ + u_char *p, *start; + size_t size; + njs_vm_t *vm; + njs_int_t ret; + ngx_uint_t i; + njs_vm_opt_t options; + ngx_js_named_path_t *preload; + + njs_vm_opt_init(&options); + + options.init = 1; + + vm = njs_vm_create(&options); + if (vm == NULL) { + goto error; + } + + ret = ngx_js_core_init(vm, cf->log); + if (njs_slow_path(ret != NJS_OK)) { + goto error; + } + + njs_str_t str = njs_str( + "import fs from 'fs';" + + "let g = (function (np, no, nf, nsp, r) {" + "return function (n, p) {" + "p = (p[0] == '/') ? p : ngx.conf_prefix + p;" + "let o = r(p);" + "globalThis[n] = np(" + "o," + "function (k, v) {" + "if (v instanceof no) {" + "nf(nsp(v, null));" + "}" + "return v;" + "}" + ");" + "return;" + "}" + "})(JSON.parse,Object,Object.freeze," + "Object.setPrototypeOf,fs.readFileSync);\n" + ); + + size = str.length; + + preload = conf->preload_objects->elts; + for (i = 0; i < conf->preload_objects->nelts; i++) { + size += sizeof("g('','');\n") - 1 + preload[i].name.len + + preload[i].path.len; + } + + start = ngx_pnalloc(cf->pool, size); + if (start == NULL) { + return NGX_ERROR; + } + + p = ngx_cpymem(start, str.start, str.length); + + preload = conf->preload_objects->elts; + for (i = 0; i < conf->preload_objects->nelts; i++) { + p = ngx_cpymem(p, "g('", sizeof("g('") - 1); + p = ngx_cpymem(p, preload[i].name.data, preload[i].name.len); + p = ngx_cpymem(p, "','", sizeof("','") - 1); + p = ngx_cpymem(p, preload[i].path.data, preload[i].path.len); + p = ngx_cpymem(p, "');\n", sizeof("');\n") - 1); + } + + ret = njs_vm_compile(vm, &start, start + size); + if (ret != NJS_OK) { + goto error; + } + + ret = njs_vm_start(vm); + if (ret != NJS_OK) { + goto error; + } + + conf->preload_vm = vm; + + return NGX_OK; + +error: + + if (vm != NULL) { + njs_vm_destroy(vm); + } + + return NGX_ERROR; +} diff -r 82f41f43abd4 -r 52b3e1f2a3e1 nginx/ngx_js.h --- a/nginx/ngx_js.h Thu Sep 15 20:20:11 2022 -0700 +++ b/nginx/ngx_js.h Fri Sep 16 12:27:40 2022 -0700 @@ -49,7 +49,10 @@ typedef struct { #define NGX_JS_COMMON_CONF \ njs_vm_t *vm; \ ngx_array_t *imports; \ - ngx_array_t *paths \ + ngx_array_t *paths; \ + \ + njs_vm_t *preload_vm; \ + ngx_array_t *preload_objects \ typedef struct { NGX_JS_COMMON_CONF; @@ -93,6 +96,8 @@ njs_int_t ngx_js_ext_log(njs_vm_t *vm, n void ngx_js_logger(njs_vm_t *vm, njs_external_ptr_t external, njs_log_level_t level, const u_char *start, size_t length); char * ngx_js_import(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char * ngx_js_preload_object(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +ngx_int_t ngx_js_init_preload_vm(ngx_conf_t *cf, ngx_js_conf_t *conf); njs_int_t ngx_js_ext_string(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); diff -r 82f41f43abd4 -r 52b3e1f2a3e1 nginx/ngx_stream_js_module.c --- a/nginx/ngx_stream_js_module.c Thu Sep 15 20:20:11 2022 -0700 +++ b/nginx/ngx_stream_js_module.c Fri Sep 16 12:27:40 2022 -0700 @@ -169,6 +169,13 @@ static ngx_command_t ngx_stream_js_comm 0, NULL }, + { ngx_string("js_preload_object"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE13, + ngx_js_preload_object, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + { ngx_string("js_path"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_array_slot, @@ -858,8 +865,12 @@ static ngx_int_t ngx_stream_js_init_vm(ngx_stream_session_t *s) { njs_int_t rc; + njs_str_t key; ngx_str_t exception; + ngx_uint_t i; + njs_opaque_value_t retval; ngx_pool_cleanup_t *cln; + ngx_js_named_path_t *preload; ngx_stream_js_ctx_t *ctx; ngx_stream_js_srv_conf_t *jscf; @@ -898,6 +909,27 @@ ngx_stream_js_init_vm(ngx_stream_session cln->handler = ngx_stream_js_cleanup; cln->data = s; + /* bind objects from preload vm */ + + if (jscf->preload_objects != NGX_CONF_UNSET_PTR) { + preload = jscf->preload_objects->elts; + + for (i = 0; i < jscf->preload_objects->nelts; i++) { + key.start = preload[i].name.data; + key.length = preload[i].name.len; + + rc = njs_vm_value(jscf->preload_vm, &key, njs_value_arg(&retval)); + if (rc != NJS_OK) { + return NGX_ERROR; + } + + rc = njs_vm_bind(ctx->vm, &key, njs_value_arg(&retval), 0); + if (rc != NJS_OK) { + return NGX_ERROR; + } + } + } + if (njs_vm_start(ctx->vm) == NJS_ERROR) { ngx_js_retval(ctx->vm, NULL, &exception); @@ -953,9 +985,13 @@ ngx_stream_js_cleanup(void *data) static void ngx_stream_js_cleanup_vm(void *data) { - njs_vm_t *vm = data; - - njs_vm_destroy(vm); + ngx_stream_js_srv_conf_t *jscf = data; + + njs_vm_destroy(jscf->vm); + + if (jscf->preload_objects != NGX_CONF_UNSET_PTR) { + njs_vm_destroy(jscf->preload_vm); + } } @@ -1644,8 +1680,8 @@ ngx_stream_js_merge_vm(ngx_conf_t *cf, n { ngx_str_t *path, *s; ngx_uint_t i; - ngx_array_t *imports, *paths; - ngx_js_named_path_t *import, *pi; + ngx_array_t *imports, *preload_objects, *paths; + ngx_js_named_path_t *import, *pi, *pij, *preload; if (prev->imports != NGX_CONF_UNSET_PTR && prev->vm == NULL) { if (ngx_stream_js_init_conf_vm(cf, prev) != NGX_OK) { @@ -1654,16 +1690,58 @@ ngx_stream_js_merge_vm(ngx_conf_t *cf, n } if (conf->imports == NGX_CONF_UNSET_PTR - && conf->paths == NGX_CONF_UNSET_PTR) + && conf->paths == NGX_CONF_UNSET_PTR + && conf->preload_objects == NGX_CONF_UNSET_PTR) { if (prev->vm != NULL) { + conf->preload_objects = prev->preload_objects; conf->imports = prev->imports; conf->paths = prev->paths; conf->vm = prev->vm; + + conf->preload_vm = prev->preload_vm; + return NGX_OK; } } + if (prev->preload_objects != NGX_CONF_UNSET_PTR) { + if (conf->preload_objects == NGX_CONF_UNSET_PTR) { + conf->preload_objects = prev->preload_objects; + + } else { + preload_objects = ngx_array_create(cf->pool, 4, + sizeof(ngx_js_named_path_t)); + if (preload_objects == NULL) { + return NGX_ERROR; + } + + pij = prev->preload_objects->elts; + + for (i = 0; i < prev->preload_objects->nelts; i++) { + preload = ngx_array_push(preload_objects); + if (preload == NULL) { + return NGX_ERROR; + } + + *preload = pij[i]; + } + + pij = conf->preload_objects->elts; + + for (i = 0; i < conf->preload_objects->nelts; i++) { + preload = ngx_array_push(preload_objects); + if (preload == NULL) { + return NGX_ERROR; + } + + *preload = pij[i]; + } + + conf->preload_objects = preload_objects; + } + } + if (prev->imports != NGX_CONF_UNSET_PTR) { if (conf->imports == NGX_CONF_UNSET_PTR) { conf->imports = prev->imports; @@ -1763,6 +1841,12 @@ ngx_stream_js_init_conf_vm(ngx_conf_t *c static const njs_str_t line_number_key = njs_str("lineNumber"); static const njs_str_t file_name_key = njs_str("fileName"); + if (conf->preload_objects != NGX_CONF_UNSET_PTR) { + if (ngx_js_init_preload_vm(cf, (ngx_js_conf_t *)conf) != NGX_OK) { + return NGX_ERROR; + } + } + size = 0; import = conf->imports->elts; @@ -1823,7 +1907,7 @@ ngx_stream_js_init_conf_vm(ngx_conf_t *c } cln->handler = ngx_stream_js_cleanup_vm; - cln->data = conf->vm; + cln->data = conf; path.start = ngx_cycle->conf_prefix.data; path.length = ngx_cycle->conf_prefix.len; @@ -2026,6 +2110,7 @@ ngx_stream_js_create_srv_conf(ngx_conf_t * set by ngx_pcalloc(): * * conf->vm = NULL; + * conf->preload_vm = NULL; * conf->access = { 0, NULL }; * conf->preread = { 0, NULL }; * conf->filter = { 0, NULL }; @@ -2040,6 +2125,7 @@ ngx_stream_js_create_srv_conf(ngx_conf_t conf->buffer_size = NGX_CONF_UNSET_SIZE; conf->max_response_body_size = NGX_CONF_UNSET_SIZE; conf->timeout = NGX_CONF_UNSET_MSEC; + conf->preload_objects = NGX_CONF_UNSET_PTR; #if (NGX_STREAM_SSL) conf->ssl_verify = NGX_CONF_UNSET; From mdounin at mdounin.ru Fri Sep 16 20:58:31 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Fri, 16 Sep 2022 23:58:31 +0300 Subject: [PATCH 01 of 11] SSL: disabled saving tickets to session cache In-Reply-To: References: <2cd8fbeb4edc5a99b725.1661482868@vm-bsd.mdounin.ru> Message-ID: Hello! On Thu, Sep 15, 2022 at 09:36:31AM +0400, Sergey Kandaurov wrote: > > On 26 Aug 2022, at 07:01, Maxim Dounin wrote: > > > > # HG changeset patch > > # User Maxim Dounin > > # Date 1661481945 -10800 > > # Fri Aug 26 05:45:45 2022 +0300 > > # Node ID 2cd8fbeb4edc5a99b725585edc02a16a8a0c503e > > # Parent 069a4813e8d6d7ec662d282a10f5f7062ebd817f > > SSL: disabled saving tickets to session cache. > > > > OpenSSL for TLSv1.3 tries to save tickets into session cache "because some > > applications just want to know about the creation of a session". To avoid > > trashing session cache with useless data, we do not save such sessions now. > > > > For the record, BoringSSL doesn't seem to call new_session_cb for TLSv1.3 > at all, so there is no way to resume sessions with SSL_OP_NO_TICKET set. > In contrary, OpenSSL emits stateful tickets in this case, which contain > dummy session id used then as a session cache lookup key on server > (much like session ids in TLSv1.2) > > OTOH, without SSL_OP_NO_TICKET set, OpenSSL emits self-containing tickets > with enough info to resume session, so nothing to lookup in session cache. > The latter makes impractical storing something in session cache, except > to use the callback for things like tracking "the creation of a session". > Namely, OpenSSL puts session (i.e. something that SSL_get_session returns) > and supplementary info to session ticket message as the ticket value. It looks like you are trying to introduce "stateful tickets" and "self-containing tickets" terms, which is somewhat confusing unless carefully explained. OpenSSL itself tries to use terms "stateful tickets" and "stateless tickets", with the similar drawbacks. A better explanation would be to follow generic term "session ticket", as originally introduced in RFC 4507 for TLS session resumption without server-side state. In these terms (as always used before introduction of TLSv1.3 and currently used in many places, including nginx own documentation and the source code) there are two basic mechanisms to resume sessions: server-side session cache and session tickets (used to resume sessions without server-side state). Without SSL_OP_NO_TICKET set, OpenSSL uses tickets as long as supported by the client. With SSL_OP_NO_TICKET set, OpenSSL does not use tickets, and uses server-side session cache instead (if configured). The only difference between TLSv1.3 and previous protocols is how session ids are sent to the client if server-side session cache is used. In case of SSL and TLS up to and including TLSv1.2, session ids are sent in the dedicated fields of the ServerHello and ClientHello handshake messages. In case of TLSv1.3, dedicated fields were removed, so session ids are sent in the NewSessionTicket messages ("a database lookup key" in terms of RFC 8446). > With these thoughts in mind, I think log could be clarified to emphasize: > - it's not tickets that are stored in cache > - with SSL_OP_NO_TICKET set TLSv1.3 session are still saved to lookup by id. Hope it is clear enough now. > > > diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c > > --- a/src/event/ngx_event_openssl.c > > +++ b/src/event/ngx_event_openssl.c > > @@ -3815,6 +3815,22 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > > ngx_ssl_session_cache_t *cache; > > u_char buf[NGX_SSL_MAX_SESSION_SIZE]; > > > > +#ifdef TLS1_3_VERSION > > + > > + /* > > + * OpenSSL for TLSv1.3 tries to save tickets into session cache > > + * "because some applications just want to know about the creation > > + * of a session"; do not cache such sessions > > + */ > > + > > + if (SSL_version(ssl_conn) == TLS1_3_VERSION > > + && (SSL_get_options(ssl_conn) & SSL_OP_NO_TICKET) == 0) > > + { > > + return 0; > > + } > > + > > +#endif > > + > > len = i2d_SSL_SESSION(sess, NULL); > > > > /* do not cache too big session */ > > > > Looks good. > > BTW, looking through this code I see that casting NGX_SSL_MAX_SESSION_SIZE, > as seen below the above comment, should be safe to remove, as it stopped > using offsetof since 5ffd76a9ccf3. > > # HG changeset patch > # User Sergey Kandaurov > # Date 1663213568 -14400 > # Thu Sep 15 07:46:08 2022 +0400 > # Node ID 45f239ff9ad31d33b58822eff8686657e0862e44 > # Parent bcdd372dd558c6bcef981704b788b3b224173347 > SSL: removed cast not needed after 5ffd76a9ccf3. > > diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c > --- a/src/event/ngx_event_openssl.c > +++ b/src/event/ngx_event_openssl.c > @@ -3842,7 +3842,7 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > > /* do not cache too big session */ > > - if (len > (int) NGX_SSL_MAX_SESSION_SIZE) { > + if (len > NGX_SSL_MAX_SESSION_SIZE) { > return 0; > } > Looks good. (Especially given that the cast is not used in similar tests in the upstream zone code.) -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Fri Sep 16 21:01:53 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sat, 17 Sep 2022 00:01:53 +0300 Subject: [PATCH 02 of 11] SSL: reduced logging of session cache failures (ticket #621) In-Reply-To: <42A8BF68-4BA9-41F4-A3BE-8E0B40CD2234@nginx.com> References: <5b137f110e84af974ef2.1661482869@vm-bsd.mdounin.ru> <42A8BF68-4BA9-41F4-A3BE-8E0B40CD2234@nginx.com> Message-ID: Hello! On Thu, Sep 15, 2022 at 09:37:17AM +0400, Sergey Kandaurov wrote: > > On 26 Aug 2022, at 07:01, Maxim Dounin wrote: > > > > # HG changeset patch > > # User Maxim Dounin > > # Date 1661481947 -10800 > > # Fri Aug 26 05:45:47 2022 +0300 > > # Node ID 5b137f110e84af974ef2b9efcf35bec2d883c187 > > # Parent 2cd8fbeb4edc5a99b725585edc02a16a8a0c503e > > SSL: reduced logging of session cache failures (ticket #621). > > > > Session cache allocations might fail as long as the new session is different > > in size from the one least recently used (and freed when the first allocation > > fails). In particular, it might not be possible to allocate space for > > sessions with client certificates, since they are noticeably bigger than > > normal sessions. > > > > To ensure such allocation failures won't clutter logs, logging level changed > > to "warn", and logging is now limited to at most one warning per second. > > > > diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c > > --- a/src/event/ngx_event_openssl.c > > +++ b/src/event/ngx_event_openssl.c > > @@ -3949,8 +3949,11 @@ failed: > > > > ngx_shmtx_unlock(&shpool->mutex); > > > > - ngx_log_error(NGX_LOG_ALERT, c->log, 0, > > - "could not allocate new session%s", shpool->log_ctx); > > + if (cache->fail_time != ngx_time()) { > > + cache->fail_time = ngx_time(); > > + ngx_log_error(NGX_LOG_WARN, c->log, 0, > > + "could not allocate new session%s", shpool->log_ctx); > > + } > > > > This makes three ngx_time() calls in this function in total. > A good reason to cache value in a local variable. Well, not really. This is an error code path, which is not normally used. In the hot path, there is still just one ngx_time() call. Trying to provide appropriate cached value in all cases where this error path is followed means that it have to be done at the very start of the function, and it is going to make the function less readable, and might actually make it less optimal. Further, the if (fail_time != ngx_time()) { fail_time = ngx_time(); ... } pattern is used elsewhere, and I would rather keep the code similar in all cases. Not to mention that ngx_time() is not really a call, but rather a macro, which expands to ngx_cached_time->sec. While it might be beneficial to cache the value if it is used multiple times, since ngx_cached_time is volatile, the performance impact is minimal. > > return 0; > > } > > diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h > > --- a/src/event/ngx_event_openssl.h > > +++ b/src/event/ngx_event_openssl.h > > @@ -150,6 +150,7 @@ typedef struct { > > ngx_rbtree_t session_rbtree; > > ngx_rbtree_node_t sentinel; > > ngx_queue_t expire_queue; > > + time_t fail_time; > > } ngx_ssl_session_cache_t; > > > > Missed initialization to something sensible (zero?). It doesn't really matter here, but won't hurt anyway. Added the following chunk: diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -3770,6 +3770,8 @@ ngx_ssl_session_cache_init(ngx_shm_zone_ ngx_queue_init(&cache->expire_queue); + cache->fail_time = 0; + len = sizeof(" in SSL session shared cache \"\"") + shm_zone->shm.name.len; shpool->log_ctx = ngx_slab_alloc(shpool, len); -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Fri Sep 16 21:03:41 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sat, 17 Sep 2022 00:03:41 +0300 Subject: [PATCH 04 of 11] SSL: explicit session id length checking In-Reply-To: References: Message-ID: Hello! On Thu, Sep 15, 2022 at 09:40:32AM +0400, Sergey Kandaurov wrote: > > On 26 Aug 2022, at 07:01, Maxim Dounin wrote: > > > > # HG changeset patch > > # User Maxim Dounin > > # Date 1661481949 -10800 > > # Fri Aug 26 05:45:49 2022 +0300 > > # Node ID f4ae0f4ee928cf20346530e96f1431314ecd0171 > > # Parent 86d827338fdd13ea899d618b0bcb2be23469cbac > > SSL: explicit session id length checking. > > > > Session ids are not expected to be longer than 32 bytes, but this is > > theoretically possible with TLSv1.3, where session ids are essentially > > arbitrary and sent as session tickets. Since on 64-bit platforms we > > use fixed 32-byte buffer for session ids, added an explicit length check > > to make sure the buffer is large enough. > > > > I don't follow how session ids could be "essentially arbitrary" > (except a library bug that justifies such safety belt). > For TLSv1.3, this callback is used to update session cache as part > of constructing NewSessionTicket. It's called after generating > dummy session ids, which, and regardless of protocol version, are > capped to SSL3_SSL_SESSION_ID_LENGTH (32). In TLSv1.2 and below, session id lengths are limited by the protocol. For example, in TLSv1.2: opaque SessionID<0..32>; In TLSv1.3 there is no such limitation, and any length can be used by the library (well, almost any, up to 2^16-1 bytes). While OpenSSL currently uses 32 bytes, there is no guarantee that at some point it won't switch to using, for example, 64 bytes. > > diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c > > --- a/src/event/ngx_event_openssl.c > > +++ b/src/event/ngx_event_openssl.c > > @@ -3842,6 +3842,14 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > > p = buf; > > i2d_SSL_SESSION(sess, &p); > > > > + session_id = (u_char *) SSL_SESSION_get_id(sess, &session_id_length); > > + > > + /* do not cache sessions with too long session id */ > > + > > + if (session_id_length > 32) { > > + return 0; > > + } > > + > > The check can be moved above the cpu expensive i2d_SSL_SESSION call. > Or rather move i2d_SSL_SESSION closer to corresponding ngx_memcpy(). > (but see my reply on the next patch) There is no practical difference, as this check is not expected to catch anything. > > c = ngx_ssl_get_connection(ssl_conn); > > > > ssl_ctx = c->ssl->session_ctx; > > @@ -3886,8 +3894,6 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > > } > > } > > > > - session_id = (u_char *) SSL_SESSION_get_id(sess, &session_id_length); > > - > > #if (NGX_PTR_SIZE == 8) > > > > id = sess_id->sess_id; > > -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Fri Sep 16 21:04:30 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sat, 17 Sep 2022 00:04:30 +0300 Subject: [PATCH 05 of 11] SSL: single allocation in session cache on 32-bit platforms In-Reply-To: <336FD698-C706-4B4C-B24F-25E7BD9113E2@nginx.com> References: <336FD698-C706-4B4C-B24F-25E7BD9113E2@nginx.com> Message-ID: Hello! On Thu, Sep 15, 2022 at 09:41:36AM +0400, Sergey Kandaurov wrote: > > On 26 Aug 2022, at 07:01, Maxim Dounin wrote: > > > > # HG changeset patch > > # User Maxim Dounin > > # Date 1661481950 -10800 > > # Fri Aug 26 05:45:50 2022 +0300 > > # Node ID e88baee178eed529c6170678e373f5e2e0883c37 > > # Parent f4ae0f4ee928cf20346530e96f1431314ecd0171 > > SSL: single allocation in session cache on 32-bit platforms. > > > > Given the present typical SSL session sizes, on 32-bit platforms it is > > now beneficial to store all data in a single allocation, since rbtree > > node + session id + ASN1 representation of a session takes 256 bytes of > > shared memory (36 + 32 + 150 = about 218 bytes plus SNI server name). > > > > Storing all data in a single allocation is beneficial for SNI names up to > > about 40 characters long and makes it possible to store about 4000 sessions > > in one megabyte (instead of about 3000 sessions now). This also slightly > > simplifies the code. > > > > diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c > > --- a/src/event/ngx_event_openssl.c > > +++ b/src/event/ngx_event_openssl.c > > @@ -3789,9 +3789,9 @@ ngx_ssl_session_cache_init(ngx_shm_zone_ > > * Typical length of the external ASN1 representation of a session > > * is about 150 bytes plus SNI server name. > > * > > - * On 32-bit platforms we allocate separately an rbtree node, > > - * a session id, and an ASN1 representation, they take accordingly > > - * 64, 32, and 256 bytes. > > + * On 32-bit platforms we allocate an rbtree node, a session id, and > > + * an ASN1 representation in a single allocation, it typically takes > > + * 256 bytes. > > * > > * On 64-bit platforms we allocate separately an rbtree node + session_id, > > * and an ASN1 representation, they take accordingly 128 and 256 bytes. > > @@ -3804,7 +3804,8 @@ static int > > ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess) > > { > > int len; > > - u_char *p, *id, *cached_sess, *session_id; > > + u_char *p, *session_id; > > + size_t n; > > uint32_t hash; > > SSL_CTX *ssl_ctx; > > unsigned int session_id_length; > > @@ -3863,23 +3864,13 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > > /* drop one or two expired sessions */ > > ngx_ssl_expire_sessions(cache, shpool, 1); > > > > - cached_sess = ngx_slab_alloc_locked(shpool, len); > > - > > - if (cached_sess == NULL) { > > - > > - /* drop the oldest non-expired session and try once more */ > > - > > - ngx_ssl_expire_sessions(cache, shpool, 0); > > - > > - cached_sess = ngx_slab_alloc_locked(shpool, len); > > - > > - if (cached_sess == NULL) { > > - sess_id = NULL; > > - goto failed; > > - } > > - } > > - > > - sess_id = ngx_slab_alloc_locked(shpool, sizeof(ngx_ssl_sess_id_t)); > > +#if (NGX_PTR_SIZE == 8) > > + n = sizeof(ngx_ssl_sess_id_t); > > +#else > > + n = offsetof(ngx_ssl_sess_id_t, session) + len; > > +#endif > > + > > + sess_id = ngx_slab_alloc_locked(shpool, n); > > > > if (sess_id == NULL) { > > > > @@ -3887,7 +3878,7 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > > > > ngx_ssl_expire_sessions(cache, shpool, 0); > > > > - sess_id = ngx_slab_alloc_locked(shpool, sizeof(ngx_ssl_sess_id_t)); > > + sess_id = ngx_slab_alloc_locked(shpool, n); > > > > if (sess_id == NULL) { > > goto failed; > > @@ -3896,30 +3887,25 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > > > > #if (NGX_PTR_SIZE == 8) > > > > - id = sess_id->sess_id; > > - > > -#else > > - > > - id = ngx_slab_alloc_locked(shpool, session_id_length); > > - > > - if (id == NULL) { > > + sess_id->session = ngx_slab_alloc_locked(shpool, len); > > + > > + if (sess_id->session == NULL) { > > > > /* drop the oldest non-expired session and try once more */ > > > > ngx_ssl_expire_sessions(cache, shpool, 0); > > > > - id = ngx_slab_alloc_locked(shpool, session_id_length); > > - > > - if (id == NULL) { > > + sess_id->session = ngx_slab_alloc_locked(shpool, len); > > + > > + if (sess_id->session == NULL) { > > goto failed; > > } > > } > > > > #endif > > > > - ngx_memcpy(cached_sess, buf, len); > > - > > - ngx_memcpy(id, session_id, session_id_length); > > + ngx_memcpy(sess_id->session, buf, len); > > Converting to ASN1 looks feasible without intermediate buf[] > to avoid extra memory copy. Quoting the comment before the function: * OpenSSL's i2d_SSL_SESSION() and d2i_SSL_SESSION are slow, * so they are outside the code locked by shared pool mutex Hope this helps. > > + ngx_memcpy(sess_id->id, session_id, session_id_length); > > > > hash = ngx_crc32_short(session_id, session_id_length); > > > > @@ -3929,9 +3915,7 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > > > > sess_id->node.key = hash; > > sess_id->node.data = (u_char) session_id_length; > > - sess_id->id = id; > > sess_id->len = len; > > With comment in the previous patch#04, this makes the following: > > diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c > --- a/src/event/ngx_event_openssl.c > +++ b/src/event/ngx_event_openssl.c > @@ -3817,7 +3817,6 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > ngx_slab_pool_t *shpool; > ngx_ssl_sess_id_t *sess_id; > ngx_ssl_session_cache_t *cache; > - u_char buf[NGX_SSL_MAX_SESSION_SIZE]; > > #ifdef TLS1_3_VERSION > > @@ -3843,9 +3842,6 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > return 0; > } > > - p = buf; > - i2d_SSL_SESSION(sess, &p); > - > session_id = (u_char *) SSL_SESSION_get_id(sess, &session_id_length); > > /* do not cache sessions with too long session id */ > @@ -3907,7 +3903,10 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > > #endif > > - ngx_memcpy(sess_id->session, buf, len); > + p = sess_id->session; > + i2d_SSL_SESSION(sess, &p); > + sess_id->len = len; > + > ngx_memcpy(sess_id->id, session_id, session_id_length); > > hash = ngx_crc32_short(session_id, session_id_length); > @@ -3918,7 +3917,6 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > > sess_id->node.key = hash; > sess_id->node.data = (u_char) session_id_length; > - sess_id->len = len; > > sess_id->expire = ngx_time() + SSL_CTX_get_timeout(ssl_ctx); > > > > - sess_id->session = cached_sess; > > > > sess_id->expire = ngx_time() + SSL_CTX_get_timeout(ssl_ctx); > > > > @@ -3945,10 +3929,6 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > > > > failed: > > > > - if (cached_sess) { > > - ngx_slab_free_locked(shpool, cached_sess); > > - } > > - > > if (sess_id) { > > ngx_slab_free_locked(shpool, sess_id); > > } > > @@ -4045,9 +4025,8 @@ ngx_ssl_get_cached_session(ngx_ssl_conn_ > > > > ngx_rbtree_delete(&cache->session_rbtree, node); > > > > +#if (NGX_PTR_SIZE == 8) > > ngx_slab_free_locked(shpool, sess_id->session); > > -#if (NGX_PTR_SIZE == 4) > > - ngx_slab_free_locked(shpool, sess_id->id); > > #endif > > ngx_slab_free_locked(shpool, sess_id); > > > > @@ -4135,9 +4114,8 @@ ngx_ssl_remove_session(SSL_CTX *ssl, ngx > > > > ngx_rbtree_delete(&cache->session_rbtree, node); > > > > +#if (NGX_PTR_SIZE == 8) > > ngx_slab_free_locked(shpool, sess_id->session); > > -#if (NGX_PTR_SIZE == 4) > > - ngx_slab_free_locked(shpool, sess_id->id); > > #endif > > ngx_slab_free_locked(shpool, sess_id); > > > > @@ -4184,9 +4162,8 @@ ngx_ssl_expire_sessions(ngx_ssl_session_ > > > > ngx_rbtree_delete(&cache->session_rbtree, &sess_id->node); > > > > +#if (NGX_PTR_SIZE == 8) > > ngx_slab_free_locked(shpool, sess_id->session); > > -#if (NGX_PTR_SIZE == 4) > > - ngx_slab_free_locked(shpool, sess_id->id); > > #endif > > ngx_slab_free_locked(shpool, sess_id); > > } > > diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h > > --- a/src/event/ngx_event_openssl.h > > +++ b/src/event/ngx_event_openssl.h > > @@ -134,14 +134,14 @@ typedef struct ngx_ssl_sess_id_s ngx_ss > > > > struct ngx_ssl_sess_id_s { > > ngx_rbtree_node_t node; > > - u_char *id; > > size_t len; > > - u_char *session; > > ngx_queue_t queue; > > time_t expire; > > + u_char id[32]; > > #if (NGX_PTR_SIZE == 8) > > - void *stub; > > - u_char sess_id[32]; > > + u_char *session; > > +#else > > + u_char session[1]; > > #endif > > }; > > > > This reminds me that this structure coupled with ngx_ssl_ticket_key_t > and ngx_ssl_session_cache_t aren't used outside and can be made private. I don't think it worth the effort. Either way, it is completely unrelated to this patch series. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Fri Sep 16 21:05:18 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sat, 17 Sep 2022 00:05:18 +0300 Subject: [PATCH 07 of 11] SSL: style In-Reply-To: References: <84919c2ee8173f704649.1661482874@vm-bsd.mdounin.ru> Message-ID: Hello! On Thu, Sep 15, 2022 at 09:42:01AM +0400, Sergey Kandaurov wrote: > > On 26 Aug 2022, at 07:01, Maxim Dounin wrote: > > > > # HG changeset patch > > # User Maxim Dounin > > # Date 1661481953 -10800 > > # Fri Aug 26 05:45:53 2022 +0300 > > # Node ID 84919c2ee8173f704649a8cb4901887e1bf79588 > > # Parent d5c6eae914325fb6a9b19105fe09aecd04da21e2 > > SSL: style. > > > > Runtime OCSP functions separated from configuration ones. > > > > diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h > > --- a/src/event/ngx_event_openssl.h > > +++ b/src/event/ngx_event_openssl.h > > @@ -205,10 +205,12 @@ ngx_int_t ngx_ssl_ocsp(ngx_conf_t *cf, n > > ngx_uint_t depth, ngx_shm_zone_t *shm_zone); > > ngx_int_t ngx_ssl_ocsp_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl, > > ngx_resolver_t *resolver, ngx_msec_t resolver_timeout); > > + > > ngx_int_t ngx_ssl_ocsp_validate(ngx_connection_t *c); > > ngx_int_t ngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s); > > void ngx_ssl_ocsp_cleanup(ngx_connection_t *c); > > ngx_int_t ngx_ssl_ocsp_cache_init(ngx_shm_zone_t *shm_zone, void *data); > > + > > ngx_array_t *ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file); > > ngx_array_t *ngx_ssl_preserve_passwords(ngx_conf_t *cf, > > ngx_array_t *passwords); > > > > Speaking of style, this reminds me of various more style issues. There was no goal to fix all the style issues. This particular one interfered with ngx_event_openssl.h changes, hence it was fixed. Note well that a generic rule is to avoid style changes without a good reason. > # HG changeset patch > # User Sergey Kandaurov > # Date 1663066823 -14400 > # Tue Sep 13 15:00:23 2022 +0400 > # Node ID e3da137555cfb6a3eb80aae196a49b945a4f5048 > # Parent 3b0846bd090e06cf277879d4ba4a67a0a2569233 > SSL: style. > > Using suitable naming for SSL_CTX variables. I would rather say that ssl_ctx isn't, but in most cases we have little to no choice. [...] > # HG changeset patch > # User Sergey Kandaurov > # Date 1663199989 -14400 > # Thu Sep 15 03:59:49 2022 +0400 > # Node ID b13b26ab24e9f12a808301bf4c8713d52c7944aa > # Parent e3da137555cfb6a3eb80aae196a49b945a4f5048 > SSL: fixed indentation. > > diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c > --- a/src/event/ngx_event_openssl.c > +++ b/src/event/ngx_event_openssl.c > @@ -998,12 +998,12 @@ static int > ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store) > { > #if (NGX_DEBUG) > + int err, depth; > char *subject, *issuer; > - int err, depth; > X509 *cert; > X509_NAME *sname, *iname; > + ngx_ssl_conn_t *ssl_conn; > ngx_connection_t *c; > - ngx_ssl_conn_t *ssl_conn; > > ssl_conn = X509_STORE_CTX_get_ex_data(x509_store, > SSL_get_ex_data_X509_STORE_CTX_idx()); > @@ -2274,8 +2274,8 @@ ngx_ssl_recv(ngx_connection_t *c, u_char > static ssize_t > ngx_ssl_recv_early(ngx_connection_t *c, u_char *buf, size_t size) > { > - int n, bytes; > - size_t readbytes; > + int n, bytes; > + size_t readbytes; > > if (c->ssl->last == NGX_ERROR) { > c->read->error = 1; > @@ -2528,9 +2528,9 @@ ngx_chain_t * > ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) > { > int n; > - ngx_uint_t flush; > ssize_t send, size, file_size; > ngx_buf_t *buf; > + ngx_uint_t flush; > ngx_chain_t *cl; > > if (!c->ssl->buffer) { > @@ -3491,9 +3491,9 @@ ngx_ssl_error(ngx_uint_t level, ngx_log_ > { > int flags; > u_long n; > - va_list args; > u_char *p, *last; > u_char errstr[NGX_MAX_CONF_ERRSTR]; > + va_list args; > const char *data; > > last = errstr + NGX_MAX_CONF_ERRSTR; > @@ -3809,12 +3809,12 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > int len; > u_char *p, *session_id; > size_t n; > + SSL_CTX *ssl_ctx; > uint32_t hash; > - SSL_CTX *ssl_ctx; > unsigned int session_id_length; > ngx_shm_zone_t *shm_zone; > + ngx_slab_pool_t *shpool; > ngx_connection_t *c; > - ngx_slab_pool_t *shpool; > ngx_ssl_sess_id_t *sess_id; > ngx_ssl_session_cache_t *cache; > > @@ -3959,12 +3959,12 @@ ngx_ssl_get_cached_session(ngx_ssl_conn_ > const u_char *p; > ngx_shm_zone_t *shm_zone; > ngx_slab_pool_t *shpool; > + ngx_connection_t *c; > ngx_rbtree_node_t *node, *sentinel; > ngx_ssl_session_t *sess; > ngx_ssl_sess_id_t *sess_id; > ngx_ssl_session_cache_t *cache; > u_char buf[NGX_SSL_MAX_SESSION_SIZE]; > - ngx_connection_t *c; > > hash = ngx_crc32_short((u_char *) (uintptr_t) id, (size_t) len); > *copy = 0; > @@ -4500,7 +4500,7 @@ ngx_ssl_session_ticket_key_callback(ngx_ > static void > ngx_ssl_session_ticket_keys_cleanup(void *data) > { > - ngx_array_t *keys = data; > + ngx_array_t *keys = data; Last time I've checked, there were no clear rule to use just one space in such cases. Rather, one space is allowed and may be preferred, but certainly not required. $ grep -re '^ [0-9a-z_]\+ [0-9a-z_*]\+ = ' src/ | wc -l 169 $ grep -re '^ [0-9a-z_]\+ [0-9a-z_*]\+ = ' src/ | wc -l 257 > > ngx_explicit_memzero(keys->elts, > keys->nelts * sizeof(ngx_ssl_session_ticket_key_t)); > @@ -4525,7 +4525,7 @@ ngx_ssl_session_ticket_keys(ngx_conf_t * > void > ngx_ssl_cleanup_ctx(void *data) > { > - ngx_ssl_t *ssl = data; > + ngx_ssl_t *ssl = data; > > X509 *cert, *next; > > @@ -4544,7 +4544,7 @@ ngx_ssl_cleanup_ctx(void *data) > ngx_int_t > ngx_ssl_check_host(ngx_connection_t *c, ngx_str_t *name) > { > - X509 *cert; > + X509 *cert; > > cert = SSL_get_peer_certificate(c->ssl->connection); > if (cert == NULL) { > @@ -4575,8 +4575,8 @@ ngx_ssl_check_host(ngx_connection_t *c, > int n, i; > X509_NAME *sname; > ASN1_STRING *str; > + GENERAL_NAME *altname; > X509_NAME_ENTRY *entry; > - GENERAL_NAME *altname; > STACK_OF(GENERAL_NAME) *altnames; > > /* Here GENERAL_NAME and STACK_OF(GENERAL_NAME) are intentionally defined close to each other. > @@ -4851,9 +4851,9 @@ ngx_ssl_get_curves(ngx_connection_t *c, > { > #ifdef SSL_CTRL_GET_CURVES > > - int *curves, n, i, nid; > - u_char *p; > - size_t len; > + int *curves, n, i, nid; > + u_char *p; > + size_t len; > > n = SSL_get1_curves(c->ssl->connection, NULL); > > @@ -5046,9 +5046,9 @@ ngx_ssl_get_alpn_protocol(ngx_connection > ngx_int_t > ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) > { > - size_t len; > BIO *bio; > X509 *cert; > + size_t len; > > s->len = 0; > > @@ -5098,8 +5098,8 @@ ngx_ssl_get_certificate(ngx_connection_t > { > u_char *p; > size_t len; > + ngx_str_t cert; > ngx_uint_t i; > - ngx_str_t cert; > > if (ngx_ssl_get_raw_certificate(c, pool, &cert) != NGX_OK) { > return NGX_ERROR; > @@ -5280,8 +5280,8 @@ ngx_ssl_get_subject_dn_legacy(ngx_connec > ngx_str_t *s) > { > char *p; > + X509 *cert; > size_t len; > - X509 *cert; > X509_NAME *name; > > s->len = 0; > @@ -5328,8 +5328,8 @@ ngx_ssl_get_issuer_dn_legacy(ngx_connect > ngx_str_t *s) > { > char *p; > + X509 *cert; > size_t len; > - X509 *cert; > X509_NAME *name; > > s->len = 0; > @@ -5374,9 +5374,9 @@ ngx_ssl_get_issuer_dn_legacy(ngx_connect > ngx_int_t > ngx_ssl_get_serial_number(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) > { > - size_t len; > + BIO *bio; > X509 *cert; > - BIO *bio; > + size_t len; > > s->len = 0; > In many SSL functions, including these, generic types and SSL types are defined separately. This is a clear style pattern, not a style issue. Overall, I would rather not. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Fri Sep 16 21:08:19 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sat, 17 Sep 2022 00:08:19 +0300 Subject: [PATCH 11 of 11] SSL: automatic rotation of session ticket keys In-Reply-To: References: <5c26fe5f6ab0bf4c0d18.1661482878@vm-bsd.mdounin.ru> Message-ID: Hello! On Thu, Sep 15, 2022 at 09:50:24AM +0400, Sergey Kandaurov wrote: > > On 26 Aug 2022, at 07:01, Maxim Dounin wrote: > > > > # HG changeset patch > > # User Maxim Dounin > > # Date 1661481958 -10800 > > # Fri Aug 26 05:45:58 2022 +0300 > > # Node ID 5c26fe5f6ab0bf4c0d18cae8f6f6483348243d4b > > # Parent 2487bf5766f79c813b3397b3bb897424c3590445 > > SSL: automatic rotation of session ticket keys. > > > > As long as ssl_session_cache in shared memory is configured, session ticket > > keys are now automatically generated in shared memory, and rotated > > periodically. > > It looks odd how session cache is (ab)used to store ticket keys. > I understand that it is comfortable to reuse existing shared zone > (and not to touch configuration) but probably a more correct way > is to create a separate zone to store keys? > Something pretty much the same as ssl_session_cache syntax: > ssl_session_ticket_key file | shared:name:size > > What do you think? I don't think it is a good idea to introduce additional shared zones here, especially given that it only needs about 160 bytes of shared memory. It really complicates things for users for no real reason. Rather, I was thinking about using nginx_shared_zone, which is always available, but it is rather special and will require additional integration code. Further, as a single contention point it might be a problem, at least unless lock avoidance is also implemented (see below). And a single key for all servers might not be the best solution either. On the other hand, ssl_session_cache in shared memory is readily available and logically related, and I see no reasons not to use it to rotate ticket keys if it's configured. If there will be understanding that we need to rotate ticket keys in all cases, even without ssl_session_cache in shared memory being configured, the idea of using nginx_shared_zone might be revisited. > > This can be beneficial from forward secrecy point of view, > > and also avoids increased CPU usage after configuration reloads. > > > > You can also mention that this fixes resuming sessions with BoringSSL > that implements its own ticket key rotation (if ticket keys callback > isn't installed) that doesn't work in multiple workers configuration. Thanks, forgot about this BoringSSL misfeature. Added the following paragraph: This also helps BoringSSL to properly resume sessions in configurations with multiple worker processes and no ssl_session_ticket_key directives, as BoringSSL tries to automatically rotate session ticket keys and does this independently in different worker processes, thus breaking session resumption between worker processes. > > > diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c > > --- a/src/event/ngx_event_openssl.c > > +++ b/src/event/ngx_event_openssl.c > > @@ -74,6 +74,7 @@ static void ngx_ssl_session_rbtree_inser > > static int ngx_ssl_ticket_key_callback(ngx_ssl_conn_t *ssl_conn, > > unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx, > > HMAC_CTX *hctx, int enc); > > +static ngx_int_t ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ctx, ngx_log_t *log); > > static void ngx_ssl_ticket_keys_cleanup(void *data); > > #endif > > > > @@ -3767,6 +3768,9 @@ ngx_ssl_session_cache_init(ngx_shm_zone_ > > > > ngx_queue_init(&cache->expire_queue); > > > > + cache->ticket_keys[0].expire = 0; > > + cache->ticket_keys[1].expire = 0; > > + > > len = sizeof(" in SSL session shared cache \"\"") + shm_zone->shm.name.len; > > > > shpool->log_ctx = ngx_slab_alloc(shpool, len); > > @@ -4234,11 +4238,13 @@ ngx_ssl_session_ticket_keys(ngx_conf_t * > > ngx_pool_cleanup_t *cln; > > ngx_ssl_ticket_key_t *key; > > > > - if (paths == NULL) { > > + if (paths == NULL > > + && SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_session_cache_index) == NULL) > > + { > > return NGX_OK; > > } > > > > - keys = ngx_array_create(cf->pool, paths->nelts, > > + keys = ngx_array_create(cf->pool, paths ? paths->nelts : 2, > > sizeof(ngx_ssl_ticket_key_t)); > > if (keys == NULL) { > > return NGX_ERROR; > > @@ -4252,6 +4258,34 @@ ngx_ssl_session_ticket_keys(ngx_conf_t * > > cln->handler = ngx_ssl_ticket_keys_cleanup; > > cln->data = keys; > > > > + if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_ticket_keys_index, keys) == 0) { > > + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, > > + "SSL_CTX_set_ex_data() failed"); > > + return NGX_ERROR; > > + } > > + > > + if (SSL_CTX_set_tlsext_ticket_key_cb(ssl->ctx, ngx_ssl_ticket_key_callback) > > + == 0) > > + { > > + ngx_log_error(NGX_LOG_WARN, cf->log, 0, > > + "nginx was built with Session Tickets support, however, " > > + "now it is linked dynamically to an OpenSSL library " > > + "which has no tlsext support, therefore Session Tickets " > > + "are not available"); > > + return NGX_OK; > > + } > > + > > + if (paths == NULL) { > > + > > + /* placeholder for keys in shared memory */ > > + > > + key = ngx_array_push_n(keys, 2); > > + key[0].shared = 1; > > + key[1].shared = 1; > > + > > + return NGX_OK; > > + } > > + > > path = paths->elts; > > for (i = 0; i < paths->nelts; i++) { > > > > @@ -4306,6 +4340,8 @@ ngx_ssl_session_ticket_keys(ngx_conf_t * > > goto failed; > > } > > > > + key->shared = 0; > > + > > if (size == 48) { > > key->size = 48; > > ngx_memcpy(key->name, buf, 16); > > @@ -4327,22 +4363,6 @@ ngx_ssl_session_ticket_keys(ngx_conf_t * > > ngx_explicit_memzero(&buf, 80); > > } > > > > - if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_ticket_keys_index, keys) == 0) { > > - ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, > > - "SSL_CTX_set_ex_data() failed"); > > - return NGX_ERROR; > > - } > > - > > - if (SSL_CTX_set_tlsext_ticket_key_cb(ssl->ctx, ngx_ssl_ticket_key_callback) > > - == 0) > > - { > > - ngx_log_error(NGX_LOG_WARN, cf->log, 0, > > - "nginx was built with Session Tickets support, however, " > > - "now it is linked dynamically to an OpenSSL library " > > - "which has no tlsext support, therefore Session Tickets " > > - "are not available"); > > - } > > - > > return NGX_OK; > > > > failed: > > @@ -4375,6 +4395,10 @@ ngx_ssl_ticket_key_callback(ngx_ssl_conn > > c = ngx_ssl_get_connection(ssl_conn); > > ssl_ctx = c->ssl->session_ctx; > > > > + if (ngx_ssl_rotate_ticket_keys(ssl_ctx, c->log) != NGX_OK) { > > + return -1; > > + } > > + > > #ifdef OPENSSL_NO_SHA256 > > digest = EVP_sha1(); > > #else > > @@ -4493,6 +4517,113 @@ ngx_ssl_ticket_key_callback(ngx_ssl_conn > > } > > > > > > +static ngx_int_t > > +ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ctx, ngx_log_t *log) > > +{ > > + time_t now, expire; > > + ngx_array_t *keys; > > + ngx_shm_zone_t *shm_zone; > > + ngx_slab_pool_t *shpool; > > + ngx_ssl_ticket_key_t *key; > > + ngx_ssl_session_cache_t *cache; > > + u_char buf[80]; > > BTW, the same buf[] variable in ngx_ssl_session_ticket_keys() > is placed at the beginning of declaration block. > Looks like a chance to improve style consistency (but see below). There are enough style changes in this series already. > > + > > + keys = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_ticket_keys_index); > > + if (keys == NULL) { > > + return NGX_OK; > > + } > > + > > + key = keys->elts; > > + > > + if (!key[0].shared) { > > + return NGX_OK; > > + } > > + > > + now = ngx_time(); > > + expire = now + SSL_CTX_get_timeout(ssl_ctx); > > + > > + shm_zone = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_cache_index); > > + > > + cache = shm_zone->data; > > + shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; > > + > > + ngx_shmtx_lock(&shpool->mutex); > > + > > + key = cache->ticket_keys; > > + > > + if (key[0].expire == 0) { > > + > > + /* initialize the current key */ > > + > > + if (RAND_bytes(buf, 80) != 1) { > > + ngx_ssl_error(NGX_LOG_ALERT, log, 0, "RAND_bytes() failed"); > > + ngx_shmtx_unlock(&shpool->mutex); > > + return NGX_ERROR; > > + } > > + > > + key->shared = 1; > > + key->expire = expire; > > + key->size = 80; > > + ngx_memcpy(key->name, buf, 16); > > + ngx_memcpy(key->hmac_key, buf + 16, 32); > > + ngx_memcpy(key->aes_key, buf + 48, 32); > > + > > + ngx_explicit_memzero(&buf, 80); > > + > > + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, > > + "ssl ticket key: \"%*xs\"", > > + (size_t) 16, key->name); > > + } > > + > > + if (key[1].expire < now) { > > + > > + /* > > + * if the previous key is no longer needed (or not initialized), > > + * replace it with the current key and generate new current key > > + */ > > + > > + key[1] = key[0]; > > + > > + if (RAND_bytes(buf, 80) != 1) { > > + ngx_ssl_error(NGX_LOG_ALERT, log, 0, "RAND_bytes() failed"); > > + ngx_shmtx_unlock(&shpool->mutex); > > + return NGX_ERROR; > > + } > > + > > + key->shared = 1; > > + key->expire = expire; > > + key->size = 80; > > + ngx_memcpy(key->name, buf, 16); > > + ngx_memcpy(key->hmac_key, buf + 16, 32); > > + ngx_memcpy(key->aes_key, buf + 48, 32); > > + > > + ngx_explicit_memzero(&buf, 80); > > + > > + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, > > + "ssl ticket key: \"%*xs\"", > > + (size_t) 16, key->name); > > + } > > I wonder if cpu intensive ops can be moved from under the lock. > At least, buf[] can be removed if write random data directly > to ngx_ssl_ticket_key_t. This eliminates 3 memory copies > (likely optimized by compiler to a single) and explicit write. Micro-optimizations are not something important here: new keys are generated once per SSL session timeout, that is, once per 300 seconds by default. As such, the code is kept as close as possible to the ticket keys reading in ngx_ssl_session_ticket_keys(), and not dependant on the ngx_ssl_ticket_key_t structure layout. What can be interesting here is to avoid locking at all unless we are going to update keys (change expiration of the current key, or switch to the next key). To do so without races, the code has to maintains 3 keys: current, previous, and next. If a worker will switch to the next key earlier, other workers will still be able to decrypt new tickets, since they will be encrypted with the next key. But with the SSL session cache in shared memory we have to lock shared zone anyway to create sessions, and this was never seen to be a performance bottleneck. Given that, and the added code complexity, I've dropped the patch which avoids locking, at least for now. This might worth revisiting though. Patch provided below for reference. > > diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c > --- a/src/event/ngx_event_openssl.c > +++ b/src/event/ngx_event_openssl.c > @@ -4527,7 +4527,6 @@ ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ > ngx_slab_pool_t *shpool; > ngx_ssl_ticket_key_t *key; > ngx_ssl_session_cache_t *cache; > - u_char buf[80]; > > keys = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_ticket_keys_index); > if (keys == NULL) { > @@ -4556,7 +4555,7 @@ ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ > > /* initialize the current key */ > > - if (RAND_bytes(buf, 80) != 1) { > + if (RAND_bytes(key->name, 80) != 1) { > ngx_ssl_error(NGX_LOG_ALERT, log, 0, "RAND_bytes() failed"); > ngx_shmtx_unlock(&shpool->mutex); > return NGX_ERROR; > @@ -4565,11 +4564,6 @@ ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ > key->shared = 1; > key->expire = expire; > key->size = 80; > - ngx_memcpy(key->name, buf, 16); > - ngx_memcpy(key->hmac_key, buf + 16, 32); > - ngx_memcpy(key->aes_key, buf + 48, 32); > - > - ngx_explicit_memzero(&buf, 80); > > ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, > "ssl ticket key: \"%*xs\"", > @@ -4585,7 +4579,7 @@ ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ > > key[1] = key[0]; > > - if (RAND_bytes(buf, 80) != 1) { > + if (RAND_bytes(key->name, 80) != 1) { > ngx_ssl_error(NGX_LOG_ALERT, log, 0, "RAND_bytes() failed"); > ngx_shmtx_unlock(&shpool->mutex); > return NGX_ERROR; > @@ -4594,11 +4588,6 @@ ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ > key->shared = 1; > key->expire = expire; > key->size = 80; > - ngx_memcpy(key->name, buf, 16); > - ngx_memcpy(key->hmac_key, buf + 16, 32); > - ngx_memcpy(key->aes_key, buf + 48, 32); > - > - ngx_explicit_memzero(&buf, 80); > > ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, > "ssl ticket key: \"%*xs\"", > > Moving RAND_bytes() can be achieved by trying to elide shmem lock > and store random data elsewhere if condition is met. > But this comes in conflict with buf[] optimization :). You can't really move RAND_bytes() out of the lock unless you are ok with doing unneeded RAND_bytes() calls in workers which weren't first to update keys. And see above, you are trying to micro-optimize something which happens once per 300 seconds or even less frequently. > > + > > + /* > > + * update expiration of the current key: it is going to be needed > > + * at least till the session being created expires > > + */ > > + > > + if (expire > key[0].expire) { > > + key[0].expire = expire; > > + } > > + > > + /* sync keys to the worker process memory */ > > + > > + ngx_memcpy(keys->elts, cache->ticket_keys, > > + 2 * sizeof(ngx_ssl_ticket_key_t)); > > + > > + ngx_shmtx_unlock(&shpool->mutex); > > + > > + return NGX_OK; > > +} > > + > > + > > static void > > ngx_ssl_ticket_keys_cleanup(void *data) > > { > > diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h > > --- a/src/event/ngx_event_openssl.h > > +++ b/src/event/ngx_event_openssl.h > > @@ -147,25 +147,24 @@ struct ngx_ssl_sess_id_s { > > > > > > typedef struct { > > + u_char name[16]; > > + u_char hmac_key[32]; > > + u_char aes_key[32]; > > + time_t expire; > > + unsigned size:8; > > + unsigned shared:1; > > +} ngx_ssl_ticket_key_t; > > + > > + > > +typedef struct { > > ngx_rbtree_t session_rbtree; > > ngx_rbtree_node_t sentinel; > > ngx_queue_t expire_queue; > > + ngx_ssl_ticket_key_t ticket_keys[2]; > > Again, with the 1st patch applied, this makes using > of ticket_keys mutually exclusive to other fields. > This suggests that it doesn't belong here. It look like you are incorrectly assuming that all clients support session tickets. While this is true in TLSv1.3, it's not for TLSv1.2 and below. Further, the same ssl_session_cache can be used in multiple server blocks with different ssl_session_ticket settings. > > time_t fail_time; > > } ngx_ssl_session_cache_t; > > > > > > -#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB > > - > > -typedef struct { > > - size_t size; > > - u_char name[16]; > > - u_char hmac_key[32]; > > - u_char aes_key[32]; > > -} ngx_ssl_ticket_key_t; > > - > > -#endif > > - > > - > > #define NGX_SSL_SSLv2 0x0002 > > #define NGX_SSL_SSLv3 0x0004 > > #define NGX_SSL_TLSv1 0x0008 Lock avoidance patch below. # HG changeset patch # User Maxim Dounin # Date 1663356500 -10800 # Fri Sep 16 22:28:20 2022 +0300 # Node ID 93cd7641604d85488ee4c6144e51cdeb60caa123 # Parent 6d94781680d6750268cd697fb72f7b72b73e0df1 SSL: optimized rotation of session ticket keys. Instead of syncing keys with shared memory on each ticket operation, the code now does this only when the worker is going to change expiration of the current key, or going to switch to a new key: that is, usually at most once per second. To do so without races, the code maintains 3 keys: current, previous, and next. If a worker will switch to the next key earlier, other workers will still be able to decrypt new tickets, since they will be encrypted with the next key. diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -3773,6 +3773,7 @@ ngx_ssl_session_cache_init(ngx_shm_zone_ cache->ticket_keys[0].expire = 0; cache->ticket_keys[1].expire = 0; + cache->ticket_keys[2].expire = 0; cache->fail_time = 0; @@ -4249,7 +4250,7 @@ ngx_ssl_session_ticket_keys(ngx_conf_t * return NGX_OK; } - keys = ngx_array_create(cf->pool, paths ? paths->nelts : 2, + keys = ngx_array_create(cf->pool, paths ? paths->nelts : 3, sizeof(ngx_ssl_ticket_key_t)); if (keys == NULL) { return NGX_ERROR; @@ -4284,9 +4285,13 @@ ngx_ssl_session_ticket_keys(ngx_conf_t * /* placeholder for keys in shared memory */ - key = ngx_array_push_n(keys, 2); + key = ngx_array_push_n(keys, 3); key[0].shared = 1; + key[0].expire = 0; key[1].shared = 1; + key[1].expire = 0; + key[2].shared = 1; + key[2].expire = 0; return NGX_OK; } @@ -4346,6 +4351,7 @@ ngx_ssl_session_ticket_keys(ngx_conf_t * } key->shared = 0; + key->expire = 1; if (size == 48) { key->size = 48; @@ -4513,7 +4519,7 @@ ngx_ssl_ticket_key_callback(ngx_ssl_conn /* renew if non-default key */ - if (i != 0) { + if (i != 0 && key[i].expire) { return 2; } @@ -4544,9 +4550,21 @@ ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ return NGX_OK; } + /* + * if we don't need to update expiration of the current key + * and the previous key is still needed, don't sync with shared + * memory to save some work; in the worst case other worker process + * will switch to the next key, but this process will still be able + * to decrypt tickets encrypted with it + */ + now = ngx_time(); expire = now + SSL_CTX_get_timeout(ssl_ctx); + if (key[0].expire >= expire && key[1].expire >= now) { + return NGX_OK; + } + shm_zone = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_cache_index); cache = shm_zone->data; @@ -4566,28 +4584,38 @@ ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ return NGX_ERROR; } - key->shared = 1; - key->expire = expire; - key->size = 80; - ngx_memcpy(key->name, buf, 16); - ngx_memcpy(key->hmac_key, buf + 16, 32); - ngx_memcpy(key->aes_key, buf + 48, 32); + key[0].shared = 1; + key[0].expire = expire; + key[0].size = 80; + ngx_memcpy(key[0].name, buf, 16); + ngx_memcpy(key[0].hmac_key, buf + 16, 32); + ngx_memcpy(key[0].aes_key, buf + 48, 32); ngx_explicit_memzero(&buf, 80); ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, "ssl ticket key: \"%*xs\"", - (size_t) 16, key->name); + (size_t) 16, key[0].name); + + /* + * copy the current key to the next key, as initialization of + * the previous key will replace the current key with the next + * key + */ + + key[2] = key[0]; } if (key[1].expire < now) { /* * if the previous key is no longer needed (or not initialized), - * replace it with the current key and generate new current key + * replace it with the current key, replace the current key with + * the next key, and generate new next key */ key[1] = key[0]; + key[0] = key[2]; if (RAND_bytes(buf, 80) != 1) { ngx_ssl_error(NGX_LOG_ALERT, log, 0, "RAND_bytes() failed"); @@ -4595,18 +4623,18 @@ ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ return NGX_ERROR; } - key->shared = 1; - key->expire = expire; - key->size = 80; - ngx_memcpy(key->name, buf, 16); - ngx_memcpy(key->hmac_key, buf + 16, 32); - ngx_memcpy(key->aes_key, buf + 48, 32); + key[2].shared = 1; + key[2].expire = 0; + key[2].size = 80; + ngx_memcpy(key[2].name, buf, 16); + ngx_memcpy(key[2].hmac_key, buf + 16, 32); + ngx_memcpy(key[2].aes_key, buf + 48, 32); ngx_explicit_memzero(&buf, 80); ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, "ssl ticket key: \"%*xs\"", - (size_t) 16, key->name); + (size_t) 16, key[2].name); } /* diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -160,7 +160,7 @@ typedef struct { ngx_rbtree_t session_rbtree; ngx_rbtree_node_t sentinel; ngx_queue_t expire_queue; - ngx_ssl_ticket_key_t ticket_keys[2]; + ngx_ssl_ticket_key_t ticket_keys[3]; time_t fail_time; } ngx_ssl_session_cache_t; -- Maxim Dounin http://mdounin.ru/ From xeioex at nginx.com Sat Sep 17 00:19:38 2022 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Sat, 17 Sep 2022 00:19:38 +0000 Subject: [njs] Fixed typo introduced in 82f41f43abd4. Message-ID: details: https://hg.nginx.org/njs/rev/75e7a7bb6b06 branches: changeset: 1958:75e7a7bb6b06 user: Dmitry Volyntsev date: Fri Sep 16 17:07:04 2022 -0700 description: Fixed typo introduced in 82f41f43abd4. diffstat: src/njs_vmcode.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diffs (11 lines): diff -r 52b3e1f2a3e1 -r 75e7a7bb6b06 src/njs_vmcode.c --- a/src/njs_vmcode.c Fri Sep 16 12:27:40 2022 -0700 +++ b/src/njs_vmcode.c Fri Sep 16 17:07:04 2022 -0700 @@ -883,6 +883,7 @@ next: case NJS_VMCODE_ARGUMENTS: ret = njs_vmcode_arguments(vm, pc); if (njs_slow_path(ret == NJS_ERROR)) { + goto error; } break; From mdounin at mdounin.ru Sat Sep 17 17:51:24 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sat, 17 Sep 2022 20:51:24 +0300 Subject: [PATCH] Tests: fixed try_run() to remove temporary directory on failure In-Reply-To: <66AB4DFD-2923-48B0-AC85-8D8876376C12@nginx.com> References: <7eb87eaf06f4b9161cfe.1662472371@enoparse.local> <66AB4DFD-2923-48B0-AC85-8D8876376C12@nginx.com> Message-ID: Hello! On Fri, Sep 16, 2022 at 09:03:38PM +0400, Sergey Kandaurov wrote: > > On 7 Sep 2022, at 05:46, Maxim Dounin wrote: > > > > Hello! > > > > On Tue, Sep 06, 2022 at 05:52:51PM +0400, Sergey Kandaurov wrote: > > > >> # HG changeset patch > >> # User Sergey Kandaurov > >> # Date 1662472340 -14400 > >> # Tue Sep 06 17:52:20 2022 +0400 > >> # Node ID 7eb87eaf06f4b9161cfe298f195947dbddedd01d > >> # Parent 78fe648d54a79822a72f3a5f8cc79651b3b1f5a7 > >> Tests: fixed try_run() to remove temporary directory on failure. > >> > >> Keeping the file handle pointing to the "stderr" file prevented removal > >> of the temporary directory on win32 if execution aborted in run(). > >> The fix is to move safe file operations surrounding run() out of the > >> eval block such that the state of file handles is consistent. > >> Fixes a1874249496d. > > > > I believe it might need some additional emphasis that this is > > about Windows only. For example: > > > > Tests: fixed try_run() to remove temporary directory on win32. > > > > Agree, thx. > > >> > >> diff -r 78fe648d54a7 -r 7eb87eaf06f4 lib/Test/Nginx.pm > >> --- a/lib/Test/Nginx.pm Tue Aug 30 17:24:16 2022 +0400 > >> +++ b/lib/Test/Nginx.pm Tue Sep 06 17:52:20 2022 +0400 > >> @@ -290,8 +290,9 @@ sub has_daemon($) { > >> sub try_run($$) { > >> my ($self, $message) = @_; > >> > >> + open OLDERR, ">&", \*STDERR; > >> + > >> eval { > >> - open OLDERR, ">&", \*STDERR; > >> open NEWERR, ">", $self->{_testdir} . '/stderr' > >> or die "Can't open stderr: $!"; > >> close STDERR; > >> @@ -299,10 +300,10 @@ sub try_run($$) { > >> close NEWERR; > >> > >> $self->run(); > >> + }; > >> > >> - close STDERR; > >> - open STDERR, ">&", \*OLDERR; > >> - }; > >> + close STDERR; > >> + open STDERR, ">&", \*OLDERR; > >> > >> return $self unless $@; > > > > Wouldn't it be better to move all stderr handling out of the eval? > > > > diff --git a/lib/Test/Nginx.pm b/lib/Test/Nginx.pm > > --- a/lib/Test/Nginx.pm > > +++ b/lib/Test/Nginx.pm > > @@ -291,14 +291,13 @@ sub try_run($$) { > > my ($self, $message) = @_; > > > > open OLDERR, ">&", \*STDERR; > > + open NEWERR, ">", $self->{_testdir} . '/stderr' > > + or die "Can't open stderr: $!"; > > + close STDERR; > > + open STDERR, ">&", \*NEWERR; > > + close NEWERR; > > > > eval { > > - open NEWERR, ">", $self->{_testdir} . '/stderr' > > - or die "Can't open stderr: $!"; > > - close STDERR; > > - open STDERR, ">&", \*NEWERR; > > - close NEWERR; > > - > > $self->run(); > > }; > > > > The reason was to continue hide '/stderr' open errors in eval, > but I tend to think that it's rather better to die explicitly, > as that's not something to happen. > > > > > Alternatively, it might be the right time to backout a1874249496d > > completely and rely on "-e ..." instead, given that it was > > introduced in 1.19.5 and already present in all supported > > versions. > > I thought about that, looks like the time has come to cleanup things. > It doesn't break out nicely into two pieces, so comes in one change. > > # HG changeset patch > # User Sergey Kandaurov > # Date 1663347154 -14400 > # Fri Sep 16 20:52:34 2022 +0400 > # Node ID 16c3e0b1c8878bb127425b4da873f8eba604c879 > # Parent 78fe648d54a79822a72f3a5f8cc79651b3b1f5a7 > Tests: updated try_run() to rely on nginx "-e". > > The "-e" command line option introduced in nginx 1.19.5 is now used > to print error line on startup failures with TEST_NGINX_VERBOSE set. > This change replaces a previous approach (a1874249496d) compatible > with pre-1.19.5 nginx versions that used to redirect stderr to file. > Hence, "-e" compatibility is removed. > > As a side effect, this fixes temporary directory removal on win32 > left on startup failures because the "stderr" file was kept open. > > diff --git a/lib/Test/Nginx.pm b/lib/Test/Nginx.pm > --- a/lib/Test/Nginx.pm > +++ b/lib/Test/Nginx.pm > @@ -48,8 +48,6 @@ sub new { > ) > or die "Can't create temp directory: $!\n"; > $self->{_testdir} =~ s!\\!/!g if $^O eq 'MSWin32'; > - mkdir "$self->{_testdir}/logs" > - or die "Can't create logs directory: $!\n"; > > Test::More::BAIL_OUT("no $NGINX binary found") > unless -x $NGINX; > @@ -291,24 +289,16 @@ sub try_run($$) { > my ($self, $message) = @_; > > eval { > - open OLDERR, ">&", \*STDERR; > - open NEWERR, ">", $self->{_testdir} . '/stderr' > - or die "Can't open stderr: $!"; > - close STDERR; > - open STDERR, ">&", \*NEWERR; > - close NEWERR; > - > + open OLDERR, ">&", \*STDERR; close STDERR; > $self->run(); > - > - close STDERR; > open STDERR, ">&", \*OLDERR; > }; > > return $self unless $@; > > if ($ENV{TEST_NGINX_VERBOSE}) { > - open F, '<', $self->{_testdir} . '/stderr' > - or die "Can't open stderr: $!"; > + open F, '<', $self->{_testdir} . '/error.log' > + or die "Can't open error.log: $!"; > log_core($_) while (); > close F; > } > @@ -350,10 +340,8 @@ sub run(;$) { > my @globals = $self->{_test_globals} ? > () : ('-g', "pid $testdir/nginx.pid; " > . "error_log $testdir/error.log debug;"); > - my @error = $self->has_version('1.19.5') ? > - ('-e', 'error.log') : (); > exec($NGINX, '-p', "$testdir/", '-c', 'nginx.conf', > - @error, @globals) > + '-e', 'error.log', @globals) > or die "Unable to exec(): $!\n"; > } > > @@ -425,10 +413,8 @@ sub dump_config() { > my @globals = $self->{_test_globals} ? > () : ('-g', "pid $testdir/nginx.pid; " > . "error_log $testdir/error.log debug;"); > - my @error = $self->has_version('1.19.5') ? > - ('-e', 'error.log') : (); > my $command = "$NGINX -T -p $testdir/ -c nginx.conf " > - . join(' ', @error, @globals); > + . join(' ', '-e', 'error.log', @globals); This should be changed to explicitly use "-e error.log", much like "-c nginx.conf" above. > > return qx/$command 2>&1/; > } > @@ -481,10 +467,8 @@ sub reload() { > my @globals = $self->{_test_globals} ? > () : ('-g', "pid $testdir/nginx.pid; " > . "error_log $testdir/error.log debug;"); > - my @error = $self->has_version('1.19.5') ? > - ('-e', 'error.log') : (); > system($NGINX, '-p', $testdir, '-c', "nginx.conf", > - '-s', 'reload', @error, @globals) == 0 > + '-s', 'reload', '-e', 'error.log', @globals) == 0 > or die "system() failed: $?\n"; > > } else { > @@ -506,10 +490,8 @@ sub stop() { > my @globals = $self->{_test_globals} ? > () : ('-g', "pid $testdir/nginx.pid; " > . "error_log $testdir/error.log debug;"); > - my @error = $self->has_version('1.19.5') ? > - ('-e', 'error.log') : (); > system($NGINX, '-p', $testdir, '-c', "nginx.conf", > - '-s', 'quit', @error, @globals) == 0 > + '-s', 'quit', '-e', 'error.log', @globals) == 0 > or die "system() failed: $?\n"; > > } else { > @@ -530,10 +512,8 @@ sub stop() { > my @globals = $self->{_test_globals} ? > () : ('-g', "pid $testdir/nginx.pid; " > . "error_log $testdir/error.log debug;"); > - my @error = $self->has_version('1.19.5') ? > - ('-e', 'error.log') : (); > system($NGINX, '-p', $testdir, '-c', "nginx.conf", > - '-s', 'stop', @error, @globals) == 0 > + '-s', 'stop', '-e', 'error.log', @globals) == 0 > or die "system() failed: $?\n"; > > } else { Otherwise looks good. -- Maxim Dounin http://mdounin.ru/ From xeioex at nginx.com Tue Sep 20 00:39:12 2022 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 20 Sep 2022 00:39:12 +0000 Subject: [njs] Improved complex assignments when key is a primitive object. Message-ID: details: https://hg.nginx.org/njs/rev/33fcc9fc6704 branches: changeset: 1959:33fcc9fc6704 user: Dmitry Volyntsev date: Mon Sep 19 17:36:02 2022 -0700 description: Improved complex assignments when key is a primitive object. When key is a primitive object NJS_VMCODE_TO_PROPERTY_KEY is not necessary. diffstat: src/njs_generator.c | 52 +++++++++++++++++++++++++++-------------------- src/njs_parser.h | 5 ++++ src/test/njs_unit_test.c | 30 +++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 22 deletions(-) diffs (124 lines): diff -r 75e7a7bb6b06 -r 33fcc9fc6704 src/njs_generator.c --- a/src/njs_generator.c Fri Sep 16 17:07:04 2022 -0700 +++ b/src/njs_generator.c Mon Sep 19 17:36:02 2022 -0700 @@ -3054,17 +3054,21 @@ njs_generate_operation_assignment_prop(n } } - prop_index = njs_generate_node_temp_index_get(vm, generator, node); - if (njs_slow_path(prop_index == NJS_INDEX_ERROR)) { - return NJS_ERROR; - } - - njs_generate_code(generator, njs_vmcode_3addr_t, to_property_key, - NJS_VMCODE_TO_PROPERTY_KEY, 2, property); - - to_property_key->src2 = object->index; - to_property_key->src1 = property->index; - to_property_key->dst = prop_index; + prop_index = property->index; + + if (!njs_parser_is_primitive(property)) { + prop_index = njs_generate_node_temp_index_get(vm, generator, node); + if (njs_slow_path(prop_index == NJS_INDEX_ERROR)) { + return NJS_ERROR; + } + + njs_generate_code(generator, njs_vmcode_3addr_t, to_property_key, + NJS_VMCODE_TO_PROPERTY_KEY, 2, property); + + to_property_key->src2 = object->index; + to_property_key->src1 = property->index; + to_property_key->dst = prop_index; + } index = njs_generate_node_temp_index_get(vm, generator, node); if (njs_slow_path(index == NJS_INDEX_ERROR)) { @@ -3718,17 +3722,21 @@ njs_generate_inc_dec_operation_prop(njs_ found: - prop_index = njs_generate_temp_index_get(vm, generator, node); - if (njs_slow_path(prop_index == NJS_INDEX_ERROR)) { - return NJS_ERROR; - } - - njs_generate_code(generator, njs_vmcode_3addr_t, to_property_key, - NJS_VMCODE_TO_PROPERTY_KEY, 2, node); - - to_property_key->src2 = lvalue->left->index; - to_property_key->src1 = lvalue->right->index; - to_property_key->dst = prop_index; + prop_index = lvalue->right->index; + + if (!njs_parser_is_primitive(lvalue->right)) { + prop_index = njs_generate_temp_index_get(vm, generator, node); + if (njs_slow_path(prop_index == NJS_INDEX_ERROR)) { + return NJS_ERROR; + } + + njs_generate_code(generator, njs_vmcode_3addr_t, to_property_key, + NJS_VMCODE_TO_PROPERTY_KEY, 2, node); + + to_property_key->src2 = lvalue->left->index; + to_property_key->src1 = lvalue->right->index; + to_property_key->dst = prop_index; + } post = *((njs_bool_t *) generator->context); diff -r 75e7a7bb6b06 -r 33fcc9fc6704 src/njs_parser.h --- a/src/njs_parser.h Fri Sep 16 17:07:04 2022 -0700 +++ b/src/njs_parser.h Mon Sep 19 17:36:02 2022 -0700 @@ -157,6 +157,11 @@ njs_int_t njs_parser_serialize_ast(njs_p || (node)->token_type == NJS_TOKEN_PROPERTY) +#define njs_parser_is_primitive(node) \ + ((node)->token_type >= NJS_TOKEN_NULL \ + && (node)->token_type <= NJS_TOKEN_STRING) + + #define njs_parser_syntax_error(parser, fmt, ...) \ njs_parser_lexer_error(parser, NJS_OBJ_TYPE_SYNTAX_ERROR, fmt, \ ##__VA_ARGS__) diff -r 75e7a7bb6b06 -r 33fcc9fc6704 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Fri Sep 16 17:07:04 2022 -0700 +++ b/src/test/njs_unit_test.c Mon Sep 19 17:36:02 2022 -0700 @@ -3647,6 +3647,36 @@ static njs_unit_test_t njs_test[] = { njs_str("var a = 1; var b = { x:2 }; a = b.x += (a = 1)"), njs_str("3") }, + { njs_str("var o = {true:1}; o[true] += 1; o.true"), + njs_str("2") }, + + { njs_str("var o = {false:1}; o[false] += 1; o.false"), + njs_str("2") }, + + { njs_str("var o = {undefined:1}; o[undefined] += 1; o.undefined"), + njs_str("2") }, + + { njs_str("var o = {'5':1}; o[5] += 1; o[5]"), + njs_str("2") }, + + { njs_str("var o = {a:1}; o[{toString:()=>'a'}] += 1; o.a"), + njs_str("2") }, + + { njs_str("var o = {true:1}; o[true]++; o.true"), + njs_str("2") }, + + { njs_str("var o = {false:1}; o[false]++; o.false"), + njs_str("2") }, + + { njs_str("var o = {undefined:1}; o[undefined]++; o.undefined"), + njs_str("2") }, + + { njs_str("var o = {'5':1}; o[5]++; o[5]"), + njs_str("2") }, + + { njs_str("var o = {a:1}; o[{toString:()=>'a'}]++; o.a"), + njs_str("2") }, + { njs_str("var a = undefined; a.b++; a.b"), njs_str("TypeError: cannot get property \"b\" of undefined") }, From mdounin at mdounin.ru Tue Sep 20 02:46:50 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 20 Sep 2022 05:46:50 +0300 Subject: [PATCH] Core: support for reading PROXY protocol v2 TLVs In-Reply-To: <20220913150304.k2fjjdxgesgzbilu@N00W24XTQX> References: <4b856f1dff939e4eb9c1.1661961135@arut-laptop> <20220905132318.s27wgtof6wuqde7x@N00W24XTQX> <20220909154658.fpnpndo2opnnzywx@N00W24XTQX> <20220913150304.k2fjjdxgesgzbilu@N00W24XTQX> Message-ID: Hello! On Tue, Sep 13, 2022 at 07:03:04PM +0400, Roman Arutyunyan wrote: > On Tue, Sep 13, 2022 at 12:30:17AM +0300, Maxim Dounin wrote: > > Hello! > > > > On Fri, Sep 09, 2022 at 07:46:58PM +0400, Roman Arutyunyan wrote: > > > > > On Mon, Sep 05, 2022 at 06:58:38PM +0300, Maxim Dounin wrote: > > > > Hello! > > > > > > > > On Mon, Sep 05, 2022 at 05:23:18PM +0400, Roman Arutyunyan wrote: > > > > > > > > > Hi, > > > > > > > > > > On Mon, Sep 05, 2022 at 03:52:49AM +0300, Maxim Dounin wrote: > > > > > > Hello! > > > > > > > > > > > > On Wed, Aug 31, 2022 at 07:52:15PM +0400, Roman Arutyunyan wrote: > > > > > > > > > > > > > # HG changeset patch > > > > > > > # User Roman Arutyunyan > > > > > > > # Date 1661436099 -14400 > > > > > > > # Thu Aug 25 18:01:39 2022 +0400 > > > > > > > # Node ID 4b856f1dff939e4eb9c131e17af061cf2c38cfac > > > > > > > # Parent 069a4813e8d6d7ec662d282a10f5f7062ebd817f > > > > > > > Core: support for reading PROXY protocol v2 TLVs. > > > > > > > > > > > > First of all, could you please provide details on the use case? > > > > > > I've seen requests for writing proxy protocol TLVs to upstream > > > > > > servers (see ticket #1639), but not yet seen any meaningful > > > > > > reading requests. > > > > > > > > > > The known cases are these: > > > > > > > > > > - https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-target-groups.html#proxy-protocol > > > > > - https://docs.microsoft.com/en-us/azure/private-link/private-link-service-overview#getting-connection-information-using-tcp-proxy-v2 > > > > > - https://cloud.google.com/vpc/docs/configure-private-service-connect-producer#proxy-protocol > > > > > > > > > > The data may need further parsing, but it can be done in njs or perl. > > > > > > > > Thanks for the details. So, basically, it's about vendor-specific > > > > endpoint IDs. > > > > > > > > > > > The TLV values are available in HTTP and Stream variables > > > > > > > $proxy_protocol_tlv_0xN, where N is a hexadecimal TLV type number with no > > > > > > > leading zeroes. > > > > > > > > > > > > I can't say I like the "hexadecimal TLV type number with no > > > > > > leading zeroes" approach, especially given that the specification > > > > > > uses leading zeroes in TLV types. With leading zeros might be > > > > > > better, to match specification. > > > > > > With on-demand approach this is no longer an issue. > > > > > > > > > Also, it might worth the effort to actually add names for known > > > > > > types instead or in addition to numbers. > > > > > > > > > > This is indeed a good idea and we have such plans as a further extenion of this > > > > > work. One of the problems is however that the abovementioned TLV variables > > > > > are specified in internal documents of AWS/Azure/GCP which are not standards. > > > > > They can be changed anytime, while we have to maintain those variables in > > > > > nginx. Also, raw variables give more flexibility in supporting less known TLVs. > > > > > > > > Of course I'm not suggesting to ditch raw variables, at least not > > > > for unknown/non-standard values. But for known/standard values it > > > > should be easy enough to provide alternative names for easier use, > > > > probably with type-specific parsing. > > > > > > > > With on-demand parsing it would be trivial to support both > > > > $proxy_protocol_tlv_alpn and $proxy_protocol_tlv_0x01. Further, > > > > it will be trivial to support $proxy_protocol_tlv_aws_vpc_id while > > > > still providing $proxy_protocol_tlv_0xea for raw data. > > > > [...] > > > > > # HG changeset patch > > > # User Roman Arutyunyan > > > # Date 1662718130 -14400 > > > # Fri Sep 09 14:08:50 2022 +0400 > > > # Node ID 832f6c96b26c3009640072e3f4b1f0bf43e644d0 > > > # Parent 80714f1b0f597ce5e530e7274457450db4587fc9 > > > Standard PROXY protocol v2 TLV variables. > > > > > > diff --git a/src/core/ngx_proxy_protocol.h b/src/core/ngx_proxy_protocol.h > > > --- a/src/core/ngx_proxy_protocol.h > > > +++ b/src/core/ngx_proxy_protocol.h > > > @@ -13,7 +13,18 @@ > > > #include > > > > > > > > > -#define NGX_PROXY_PROTOCOL_MAX_HEADER 107 > > > +#define NGX_PROXY_PROTOCOL_MAX_HEADER 107 > > > + > > > +#define NGX_PROXY_PROTOCOL_TLV_ALPN 0x01 > > > +#define NGX_PROXY_PROTOCOL_TLV_AUTHORITY 0x02 > > > +#define NGX_PROXY_PROTOCOL_TLV_UNIQUE_ID 0x05 > > > +#define NGX_PROXY_PROTOCOL_TLV_SSL 0x20 > > > +#define NGX_PROXY_PROTOCOL_TLV_SSL_VERSION 0x21 > > > +#define NGX_PROXY_PROTOCOL_TLV_SSL_CN 0x22 > > > +#define NGX_PROXY_PROTOCOL_TLV_SSL_CIPHER 0x23 > > > +#define NGX_PROXY_PROTOCOL_TLV_SSL_SIG_ALG 0x24 > > > +#define NGX_PROXY_PROTOCOL_TLV_SSL_KEY_ALG 0x25 > > > +#define NGX_PROXY_PROTOCOL_TLV_NETNS 0x30 > > > > > > > > > struct ngx_proxy_protocol_s { > > > @@ -25,6 +36,12 @@ struct ngx_proxy_protocol_s { > > > }; > > > > > > > > > +typedef struct { > > > + u_char client; > > > + u_char verify[4]; > > > > Note that these two are completely ignored. > > Yes, currently they are. I thought about adding variables for them too, > but then decided to postpone this. Do you think these have enough value > to be added to this patch? The "client" field seems to be mostly useless, since all the flags can be derived from the TLVs provided. In particular, PP2_CLIENT_SSL from PP2_SUBTYPE_SSL_VERSION, and PP2_CLIENT_CERT_SESS from PP2_SUBTYPE_SSL_CN (if provided). The PP2_CLIENT_CERT_CONN seems to be completely useless, since from SSL point of view there is no difference if the certificate was provided in this connection or previously within the same SSL session. I'm not sure if "verify" can be derived from the other data provided though. Looks like it's not, at least that's my impression from the specification and the code available[1]. As far as I understand, all certificate-related variables are basically useless without checking the verify field it is explicitly known that all clients with certificate verification errors were rejected by the proxy. [1] https://github.com/haproxy/haproxy/blob/master/src/connection.c#L1951 > > > > +} ngx_proxy_protocol_tlv_ssl_t; > > > + > > > + > > > u_char *ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, > > > u_char *last); > > > u_char *ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, > > > diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c > > > --- a/src/http/ngx_http_variables.c > > > +++ b/src/http/ngx_http_variables.c > > > @@ -63,6 +63,10 @@ static ngx_int_t ngx_http_variable_proxy > > > ngx_http_variable_value_t *v, uintptr_t data); > > > static ngx_int_t ngx_http_variable_proxy_protocol_tlv_0x(ngx_http_request_t *r, > > > ngx_http_variable_value_t *v, uintptr_t data); > > > +static ngx_int_t ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r, > > > + ngx_http_variable_value_t *v, uintptr_t data); > > > +static ngx_int_t ngx_http_variable_proxy_protocol_tlv_ssl( > > > + ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); > > > static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r, > > > ngx_http_variable_value_t *v, uintptr_t data); > > > static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r, > > > @@ -220,6 +224,42 @@ static ngx_http_variable_t ngx_http_cor > > > ngx_http_variable_proxy_protocol_tlv_0x, > > > 0, NGX_HTTP_VAR_PREFIX, 0 }, > > > > > > + { ngx_string("proxy_protocol_tlv_alpn"), NULL, > > > + ngx_http_variable_proxy_protocol_tlv, > > > + NGX_PROXY_PROTOCOL_TLV_ALPN, 0, 0 }, > > > + > > > + { ngx_string("proxy_protocol_tlv_authority"), NULL, > > > + ngx_http_variable_proxy_protocol_tlv, > > > + NGX_PROXY_PROTOCOL_TLV_AUTHORITY, 0, 0 }, > > > + > > > + { ngx_string("proxy_protocol_tlv_unique_id"), NULL, > > > + ngx_http_variable_proxy_protocol_tlv, > > > + NGX_PROXY_PROTOCOL_TLV_UNIQUE_ID, 0, 0 }, > > > + > > > + { ngx_string("proxy_protocol_tlv_ssl_version"), NULL, > > > + ngx_http_variable_proxy_protocol_tlv_ssl, > > > + NGX_PROXY_PROTOCOL_TLV_SSL_VERSION, 0, 0 }, > > > + > > > + { ngx_string("proxy_protocol_tlv_ssl_cn"), NULL, > > > + ngx_http_variable_proxy_protocol_tlv_ssl, > > > + NGX_PROXY_PROTOCOL_TLV_SSL_CN, 0, 0 }, > > > + > > > + { ngx_string("proxy_protocol_tlv_ssl_cipher"), NULL, > > > + ngx_http_variable_proxy_protocol_tlv_ssl, > > > + NGX_PROXY_PROTOCOL_TLV_SSL_CIPHER, 0, 0 }, > > > + > > > + { ngx_string("proxy_protocol_tlv_ssl_sig_alg"), NULL, > > > + ngx_http_variable_proxy_protocol_tlv_ssl, > > > + NGX_PROXY_PROTOCOL_TLV_SSL_SIG_ALG, 0, 0 }, > > > + > > > + { ngx_string("proxy_protocol_tlv_ssl_key_alg"), NULL, > > > + ngx_http_variable_proxy_protocol_tlv_ssl, > > > + NGX_PROXY_PROTOCOL_TLV_SSL_KEY_ALG, 0, 0 }, > > > + > > > + { ngx_string("proxy_protocol_tlv_netns"), NULL, > > > + ngx_http_variable_proxy_protocol_tlv, > > > + NGX_PROXY_PROTOCOL_TLV_NETNS, 0, 0 }, > > > + > > > > Further, these provide no interface to support arbitrary SSL TLVs > > (that is, something like "$proxy_protocol_tlv_ssl_0x10"). > > > > Overall, I tend to think it would be much easier / less intrusive > > to keep all the TLV handling logic in the > > src/core/ngx_proxy_protocol.c, including both arbitrary TLVs with > > hexadecimal type and known named TLVs, and only provide a single > > prefix variable in stream / http modules. > > > > That is, use "$proxy_protocol_tlv_" prefix variable and call a > > function to obtain TLV with the rest of the variable name. > > > > This might imply slightly more complex/less efficient name parsing > > logic for known TLVs, but I don't think it will be noticeable. On > > the other hand, it will reduce clutter in the variables hash, and > > therefore will save some resources in configurations where these > > variables are not used. > > OK. Stream/HTTP part now looks simpler. > > -- > Roman Arutyunyan > # HG changeset patch > # User Roman Arutyunyan > # Date 1663080928 -14400 > # Tue Sep 13 18:55:28 2022 +0400 > # Node ID 0736b29bfafac445fb8cf57312c3b7a2c3247a60 > # Parent ba5cf8f73a2d0a3615565bf9545f3d65216a0530 > PROXY protocol v2 TLV variables. > > The variables have prefix $proxy_protocol_tlv_ and are accessible by name > and by type. Examples are: $proxy_protocol_tlv_0x01, $proxy_protocol_tlv_alpn. > > diff --git a/src/core/ngx_proxy_protocol.c b/src/core/ngx_proxy_protocol.c > --- a/src/core/ngx_proxy_protocol.c > +++ b/src/core/ngx_proxy_protocol.c > @@ -40,6 +40,24 @@ typedef struct { > } ngx_proxy_protocol_inet6_addrs_t; > > > +typedef struct { > + u_char type; > + u_char len[2]; > +} ngx_proxy_protocol_tlv_t; > + > + > +typedef struct { > + u_char client; > + u_char verify[4]; > +} ngx_proxy_protocol_tlv_ssl_t; > + > + > +typedef struct { > + ngx_str_t name; > + ngx_uint_t type; > +} ngx_proxy_protocol_tlv_entry_t; > + > + > static u_char *ngx_proxy_protocol_read_addr(ngx_connection_t *c, u_char *p, > u_char *last, ngx_str_t *addr); > static u_char *ngx_proxy_protocol_read_port(u_char *p, u_char *last, > @@ -48,6 +66,26 @@ static u_char *ngx_proxy_protocol_v2_rea > u_char *last); > > > +static ngx_proxy_protocol_tlv_entry_t ngx_proxy_protocol_tlv_entries[] = { > + { ngx_string("alpn"), 0x01 }, > + { ngx_string("authority"), 0x02 }, > + { ngx_string("unique_id"), 0x05 }, > + { ngx_string("ssl"), 0x20 }, > + { ngx_string("netns"), 0x30 }, > + { ngx_null_string, 0x00 } > +}; > + > + > +static ngx_proxy_protocol_tlv_entry_t ngx_proxy_protocol_tlv_ssl_entries[] = { > + { ngx_string("version"), 0x21 }, > + { ngx_string("cn"), 0x22 }, > + { ngx_string("cipher"), 0x23 }, > + { ngx_string("sig_alg"), 0x24 }, > + { ngx_string("key_alg"), 0x25 }, > + { ngx_null_string, 0x00 } > +}; > + > + > u_char * > ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, u_char *last) > { > @@ -412,11 +450,128 @@ ngx_proxy_protocol_v2_read(ngx_connectio > &pp->src_addr, pp->src_port, &pp->dst_addr, pp->dst_port); > > if (buf < end) { > - ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, > - "PROXY protocol v2 %z bytes of tlv ignored", end - buf); > + pp->tlvs.data = ngx_pnalloc(c->pool, end - buf); > + if (pp->tlvs.data == NULL) { > + return NULL; > + } > + > + ngx_memcpy(pp->tlvs.data, buf, end - buf); > + pp->tlvs.len = end - buf; > } > > c->proxy_protocol = pp; > > return end; > } > + > + > +ngx_int_t > +ngx_proxy_protocol_lookup_tlv(ngx_connection_t *c, ngx_str_t *tlvs, > + ngx_uint_t type, ngx_str_t *value) > +{ > + u_char *p; > + size_t n, len; > + ngx_proxy_protocol_tlv_t *tlv; > + > + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, > + "PROXY protocol v2 lookup tlv:%02xi", type); > + > + p = tlvs->data; > + n = tlvs->len; > + > + while (n) { > + if (n < sizeof(ngx_proxy_protocol_tlv_t)) { > + ngx_log_error(NGX_LOG_ERR, c->log, 0, "broken PROXY protocol TLV"); > + return NGX_ERROR; > + } > + > + tlv = (ngx_proxy_protocol_tlv_t *) p; > + len = ngx_proxy_protocol_parse_uint16(tlv->len); > + > + p += sizeof(ngx_proxy_protocol_tlv_t); > + n -= sizeof(ngx_proxy_protocol_tlv_t); > + > + if (n < len) { > + ngx_log_error(NGX_LOG_ERR, c->log, 0, "broken PROXY protocol TLV"); > + return NGX_ERROR; > + } > + > + ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, > + "PROXY protocol v2 tlv:0x%02xd len:%uz", tlv->type, len); > + > + if (tlv->type == type) { > + value->data = p; > + value->len = len; > + return NGX_OK; > + } > + > + p += len; > + n -= len; > + } > + > + return NGX_DECLINED; > +} > + > + > +ngx_int_t > +ngx_proxy_protocol_get_tlv(ngx_connection_t *c, ngx_str_t *name, > + ngx_str_t *value) > +{ > + u_char *p; > + size_t n; > + ngx_str_t ssl, *tlvs; > + ngx_int_t rc, type; > + ngx_proxy_protocol_tlv_entry_t *te; > + > + if (c->proxy_protocol == NULL) { > + return NGX_DECLINED; > + } > + > + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, > + "PROXY protocol v2 get tlv \"%V\"", name); > + > + te = ngx_proxy_protocol_tlv_entries; > + tlvs = &c->proxy_protocol->tlvs; > + > + p = name->data; > + n = name->len; > + > + if (n >= 4 && p[0] == 's' && p[1] == 's' && p[2] == 'l' && p[3] == '_') { > + > + rc = ngx_proxy_protocol_lookup_tlv(c, tlvs, 0x20, &ssl); > + if (rc != NGX_OK) { > + return rc; > + } > + > + if (ssl.len < sizeof(ngx_proxy_protocol_tlv_ssl_t)) { > + return NGX_ERROR; > + } > + > + ssl.data += sizeof(ngx_proxy_protocol_tlv_ssl_t); > + ssl.len -= sizeof(ngx_proxy_protocol_tlv_ssl_t); > + > + te = ngx_proxy_protocol_tlv_ssl_entries; > + tlvs = &ssl; > + > + p += 4; > + n -= 4; > + } > + > + if (n >= 2 && p[0] == '0' && p[1] == 'x') { > + > + type = ngx_hextoi(p + 2, n - 2); > + if (type == NGX_ERROR) { > + return NGX_ERROR; > + } > + > + return ngx_proxy_protocol_lookup_tlv(c, tlvs, type, value); > + } > + > + for ( /* void */ ; te->type; te++) { > + if (te->name.len == n && ngx_strncmp(te->name.data, p, n) == 0) { > + return ngx_proxy_protocol_lookup_tlv(c, tlvs, te->type, value); > + } > + } > + > + return NGX_DECLINED; > +} > diff --git a/src/core/ngx_proxy_protocol.h b/src/core/ngx_proxy_protocol.h > --- a/src/core/ngx_proxy_protocol.h > +++ b/src/core/ngx_proxy_protocol.h > @@ -21,6 +21,7 @@ struct ngx_proxy_protocol_s { > ngx_str_t dst_addr; > in_port_t src_port; > in_port_t dst_port; > + ngx_str_t tlvs; > }; > > > @@ -28,6 +29,10 @@ u_char *ngx_proxy_protocol_read(ngx_conn > u_char *last); > u_char *ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, > u_char *last); > +ngx_int_t ngx_proxy_protocol_lookup_tlv(ngx_connection_t *c, ngx_str_t *tlvs, > + ngx_uint_t type, ngx_str_t *value); > +ngx_int_t ngx_proxy_protocol_get_tlv(ngx_connection_t *c, ngx_str_t *name, > + ngx_str_t *value); > > > #endif /* _NGX_PROXY_PROTOCOL_H_INCLUDED_ */ > diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c > --- a/src/http/ngx_http_variables.c > +++ b/src/http/ngx_http_variables.c > @@ -61,6 +61,8 @@ static ngx_int_t ngx_http_variable_proxy > ngx_http_variable_value_t *v, uintptr_t data); > static ngx_int_t ngx_http_variable_proxy_protocol_port(ngx_http_request_t *r, > ngx_http_variable_value_t *v, uintptr_t data); > +static ngx_int_t ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r, > + ngx_http_variable_value_t *v, uintptr_t data); > static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r, > ngx_http_variable_value_t *v, uintptr_t data); > static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r, > @@ -214,6 +216,10 @@ static ngx_http_variable_t ngx_http_cor > ngx_http_variable_proxy_protocol_port, > offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 }, > > + { ngx_string("proxy_protocol_tlv_"), NULL, > + ngx_http_variable_proxy_protocol_tlv, > + 0, NGX_HTTP_VAR_PREFIX, 0 }, > + > { ngx_string("server_addr"), NULL, ngx_http_variable_server_addr, 0, 0, 0 }, > > { ngx_string("server_port"), NULL, ngx_http_variable_server_port, 0, 0, 0 }, > @@ -1387,6 +1393,39 @@ ngx_http_variable_proxy_protocol_port(ng > > > static ngx_int_t > +ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r, > + ngx_http_variable_value_t *v, uintptr_t data) > +{ > + ngx_str_t *name = (ngx_str_t *) data; > + > + ngx_int_t rc; > + ngx_str_t tlv, value; > + > + tlv.len = name->len - (sizeof("proxy_protocol_tlv_") - 1); > + tlv.data = name->data + sizeof("proxy_protocol_tlv_") - 1; > + > + rc = ngx_proxy_protocol_get_tlv(r->connection, &tlv, &value); > + > + if (rc == NGX_ERROR) { > + return NGX_ERROR; > + } > + > + if (rc == NGX_DECLINED) { > + v->not_found = 1; > + return NGX_OK; > + } > + > + v->len = value.len; > + v->valid = 1; > + v->no_cacheable = 0; > + v->not_found = 0; > + v->data = value.data; > + > + return NGX_OK; > +} > + > + > +static ngx_int_t > ngx_http_variable_server_addr(ngx_http_request_t *r, > ngx_http_variable_value_t *v, uintptr_t data) > { > diff --git a/src/stream/ngx_stream_variables.c b/src/stream/ngx_stream_variables.c > --- a/src/stream/ngx_stream_variables.c > +++ b/src/stream/ngx_stream_variables.c > @@ -23,6 +23,8 @@ static ngx_int_t ngx_stream_variable_pro > ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); > static ngx_int_t ngx_stream_variable_proxy_protocol_port( > ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); > +static ngx_int_t ngx_stream_variable_proxy_protocol_tlv( > + ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); > static ngx_int_t ngx_stream_variable_server_addr(ngx_stream_session_t *s, > ngx_stream_variable_value_t *v, uintptr_t data); > static ngx_int_t ngx_stream_variable_server_port(ngx_stream_session_t *s, > @@ -79,6 +81,10 @@ static ngx_stream_variable_t ngx_stream > ngx_stream_variable_proxy_protocol_port, > offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 }, > > + { ngx_string("proxy_protocol_tlv_"), NULL, > + ngx_stream_variable_proxy_protocol_tlv, > + 0, NGX_STREAM_VAR_PREFIX, 0 }, > + > { ngx_string("server_addr"), NULL, > ngx_stream_variable_server_addr, 0, 0, 0 }, > > @@ -622,6 +628,39 @@ ngx_stream_variable_proxy_protocol_port( > > > static ngx_int_t > +ngx_stream_variable_proxy_protocol_tlv(ngx_stream_session_t *s, > + ngx_stream_variable_value_t *v, uintptr_t data) > +{ > + ngx_str_t *name = (ngx_str_t *) data; > + > + ngx_int_t rc; > + ngx_str_t tlv, value; > + > + tlv.len = name->len - (sizeof("proxy_protocol_tlv_") - 1); > + tlv.data = name->data + sizeof("proxy_protocol_tlv_") - 1; > + > + rc = ngx_proxy_protocol_get_tlv(s->connection, &tlv, &value); > + > + if (rc == NGX_ERROR) { > + return NGX_ERROR; > + } > + > + if (rc == NGX_DECLINED) { > + v->not_found = 1; > + return NGX_OK; > + } > + > + v->len = value.len; > + v->valid = 1; > + v->no_cacheable = 0; > + v->not_found = 0; > + v->data = value.data; > + > + return NGX_OK; > +} > + > + > +static ngx_int_t > ngx_stream_variable_server_addr(ngx_stream_session_t *s, > ngx_stream_variable_value_t *v, uintptr_t data) > { Looks good, but see above: probably we should add at least the $proxy_protocol_tlv_ssl_verify variable (and probably with special handling to convert binary data into a string). -- Maxim Dounin http://mdounin.ru/ From xeioex at nginx.com Wed Sep 21 00:46:00 2022 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 21 Sep 2022 00:46:00 +0000 Subject: [njs] Introduced "name" instance property for a function object. Message-ID: details: https://hg.nginx.org/njs/rev/74d30c2d70f3 branches: changeset: 1960:74d30c2d70f3 user: Dmitry Volyntsev date: Tue Sep 20 17:44:25 2022 -0700 description: Introduced "name" instance property for a function object. This closes #360 issue on Github. diffstat: src/njs_async.c | 7 +++ src/njs_disassembler.c | 8 ++- src/njs_error.c | 5 ++ src/njs_function.c | 99 +++++++++++++++++++++++++++++++++++------------ src/njs_function.h | 4 + src/njs_generator.c | 92 ++++++++++++++++++++++++++++++++++++++++--- src/njs_object_prop.c | 5 ++ src/njs_parser.c | 10 ++-- src/njs_value.c | 1 + src/njs_value.h | 1 + src/njs_vm.c | 1 + src/njs_vm.h | 1 + src/njs_vmcode.c | 36 +++++++++++++--- src/njs_vmcode.h | 2 + src/test/njs_unit_test.c | 71 +++++++++++++++++++++++++++++++++- 15 files changed, 293 insertions(+), 50 deletions(-) diffs (670 lines): diff -r 33fcc9fc6704 -r 74d30c2d70f3 src/njs_async.c --- a/src/njs_async.c Mon Sep 19 17:36:02 2022 -0700 +++ b/src/njs_async.c Tue Sep 20 17:44:25 2022 -0700 @@ -228,6 +228,13 @@ const njs_object_prop_t njs_async_funct .value = njs_prop_handler(njs_function_instance_length), .configurable = 1, }, + + { + .type = NJS_PROPERTY_HANDLER, + .name = njs_string("name"), + .value = njs_prop_handler(njs_function_instance_name), + .configurable = 1, + }, }; diff -r 33fcc9fc6704 -r 74d30c2d70f3 src/njs_disassembler.c --- a/src/njs_disassembler.c Mon Sep 19 17:36:02 2022 -0700 +++ b/src/njs_disassembler.c Tue Sep 20 17:44:25 2022 -0700 @@ -74,8 +74,12 @@ static njs_code_name_t code_names[] = { njs_str("VOID ") }, { NJS_VMCODE_TYPEOF, sizeof(njs_vmcode_2addr_t), njs_str("TYPEOF ") }, - { NJS_VMCODE_TO_PROPERTY_KEY, sizeof(njs_vmcode_3addr_t), - njs_str("TO PROPERTY KEY ") }, + { NJS_VMCODE_TO_PROPERTY_KEY, sizeof(njs_vmcode_2addr_t), + njs_str("TO PROP KEY ") }, + { NJS_VMCODE_TO_PROPERTY_KEY_CHK, sizeof(njs_vmcode_3addr_t), + njs_str("TO PROP KEY CHK ") }, + { NJS_VMCODE_SET_FUNCTION_NAME, sizeof(njs_vmcode_2addr_t), + njs_str("SET FUNC NAME ") }, { NJS_VMCODE_UNARY_PLUS, sizeof(njs_vmcode_2addr_t), njs_str("PLUS ") }, diff -r 33fcc9fc6704 -r 74d30c2d70f3 src/njs_error.c --- a/src/njs_error.c Mon Sep 19 17:36:02 2022 -0700 +++ b/src/njs_error.c Tue Sep 20 17:44:25 2022 -0700 @@ -1319,6 +1319,11 @@ njs_add_backtrace_entry(njs_vm_t *vm, nj if (code != NULL) { be->name = code->name; + + if (be->name.length == 0) { + be->name = njs_entry_anonymous; + } + be->line = njs_lookup_line(code->lines, native_frame->pc - code->start); if (!vm->options.quiet) { be->file = code->file; diff -r 33fcc9fc6704 -r 74d30c2d70f3 src/njs_function.c --- a/src/njs_function.c Mon Sep 19 17:36:02 2022 -0700 +++ b/src/njs_function.c Tue Sep 20 17:44:25 2022 -0700 @@ -145,29 +145,34 @@ njs_function_name_set(njs_vm_t *vm, njs_ } if (prefix != NULL || symbol != 0) { - value = prop->value; - (void) njs_string_prop(&string, &value); + if (njs_is_defined(&prop->value)) { + value = prop->value; + (void) njs_string_prop(&string, &value); - len = (prefix != NULL) ? njs_strlen(prefix) + 1: 0; - p = njs_string_alloc(vm, &prop->value, string.size + len + symbol, - string.length + len + symbol); - if (njs_slow_path(p == NULL)) { - return NJS_ERROR; - } + len = (prefix != NULL) ? njs_strlen(prefix) + 1: 0; + p = njs_string_alloc(vm, &prop->value, string.size + len + symbol, + string.length + len + symbol); + if (njs_slow_path(p == NULL)) { + return NJS_ERROR; + } - if (len != 0) { - p = njs_cpymem(p, prefix, len - 1); - *p++ = ' '; - } + if (len != 0) { + p = njs_cpymem(p, prefix, len - 1); + *p++ = ' '; + } + + if (symbol != 0) { + *p++ = '['; + } - if (symbol != 0) { - *p++ = '['; - } + p = njs_cpymem(p, string.start, string.size); - p = njs_cpymem(p, string.start, string.size); + if (symbol != 0) { + *p++ = ']'; + } - if (symbol != 0) { - *p++ = ']'; + } else { + njs_value_assign(&prop->value, &njs_string_empty); } } @@ -1253,6 +1258,12 @@ njs_function_constructor(njs_vm_t *vm, n function->global_this = 1; function->args_count = lambda->nargs - lambda->rest_parameters; + ret = njs_function_name_set(vm, function, + njs_value_arg(&njs_string_anonymous), NULL); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + njs_set_function(&vm->retval, function); return NJS_OK; @@ -1324,6 +1335,30 @@ njs_function_instance_length(njs_vm_t *v } +njs_int_t +njs_function_instance_name(njs_vm_t *vm, njs_object_prop_t *prop, + njs_value_t *value, njs_value_t *setval, njs_value_t *retval) +{ + njs_function_t *function; + + function = njs_object_proto_lookup(njs_object(value), NJS_FUNCTION, + njs_function_t); + if (njs_slow_path(function == NULL)) { + njs_set_undefined(retval); + return NJS_DECLINED; + } + + if (!function->native) { + njs_value_assign(retval, &function->u.lambda->name); + return NJS_OK; + } + + njs_value_assign(retval, &njs_string_empty); + + return NJS_OK; +} + + static njs_int_t njs_function_prototype_call(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) @@ -1447,11 +1482,10 @@ static njs_int_t njs_function_prototype_bind(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - size_t size; - njs_int_t ret; - njs_value_t *values, name; - njs_function_t *function; - njs_lvlhsh_query_t lhq; + size_t size; + njs_int_t ret; + njs_value_t *values, name; + njs_function_t *function; if (!njs_is_function(&args[0])) { njs_type_error(vm, "\"this\" argument is not a function"); @@ -1476,9 +1510,8 @@ njs_function_prototype_bind(njs_vm_t *vm function->u.bound_target = njs_function(&args[0]); - njs_object_property_init(&lhq, &njs_string_name, NJS_NAME_HASH); - - ret = njs_object_property(vm, &args[0], &lhq, &name); + ret = njs_value_property(vm, &args[0], njs_value_arg(&njs_string_name), + &name); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } @@ -1616,6 +1649,13 @@ const njs_object_prop_t njs_function_in { .type = NJS_PROPERTY_HANDLER, + .name = njs_string("name"), + .value = njs_prop_handler(njs_function_instance_name), + .configurable = 1, + }, + + { + .type = NJS_PROPERTY_HANDLER, .name = njs_string("prototype"), .value = njs_prop_handler(njs_function_prototype_create), .writable = 1 @@ -1637,6 +1677,13 @@ const njs_object_prop_t njs_arrow_insta .value = njs_prop_handler(njs_function_instance_length), .configurable = 1, }, + + { + .type = NJS_PROPERTY_HANDLER, + .name = njs_string("name"), + .value = njs_prop_handler(njs_function_instance_name), + .configurable = 1, + }, }; diff -r 33fcc9fc6704 -r 74d30c2d70f3 src/njs_function.h --- a/src/njs_function.h Mon Sep 19 17:36:02 2022 -0700 +++ b/src/njs_function.h Tue Sep 20 17:44:25 2022 -0700 @@ -23,6 +23,8 @@ struct njs_function_lambda_s { uint8_t ctor; /* 1 bit */ uint8_t rest_parameters; /* 1 bit */ + njs_value_t name; + u_char *start; }; @@ -99,6 +101,8 @@ njs_int_t njs_function_constructor(njs_v njs_uint_t nargs, njs_index_t unused); njs_int_t njs_function_instance_length(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); +njs_int_t njs_function_instance_name(njs_vm_t *vm, njs_object_prop_t *prop, + njs_value_t *value, njs_value_t *setval, njs_value_t *retval); njs_int_t njs_eval_function(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused); njs_int_t njs_function_native_frame(njs_vm_t *vm, njs_function_t *function, diff -r 33fcc9fc6704 -r 74d30c2d70f3 src/njs_generator.c --- a/src/njs_generator.c Mon Sep 19 17:36:02 2022 -0700 +++ b/src/njs_generator.c Tue Sep 20 17:44:25 2022 -0700 @@ -1050,10 +1050,12 @@ static njs_int_t njs_generate_var_statement_after(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { - njs_int_t ret; - njs_variable_t *var; - njs_parser_node_t *lvalue, *expr; - njs_vmcode_move_t *move; + ssize_t length; + njs_int_t ret; + njs_variable_t *var; + njs_parser_node_t *lvalue, *expr; + njs_vmcode_move_t *move; + const njs_lexer_entry_t *lex_entry; lvalue = node->left; expr = node->right; @@ -1080,6 +1082,29 @@ njs_generate_var_statement_after(njs_vm_ node->index = expr->index; node->temporary = expr->temporary; + if ((expr->token_type == NJS_TOKEN_FUNCTION_EXPRESSION + || expr->token_type == NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION) + && njs_values_same(&expr->u.value.data.u.lambda->name, + &njs_string_empty)) + { + lex_entry = njs_lexer_entry(node->left->u.reference.unique_id); + if (njs_slow_path(lex_entry == NULL)) { + return NJS_ERROR; + } + + length = njs_utf8_length(lex_entry->name.start, lex_entry->name.length); + if (njs_slow_path(length < 0)) { + return NJS_ERROR; + } + + ret = njs_string_new(vm, &expr->u.value.data.u.lambda->name, + lex_entry->name.start, lex_entry->name.length, + length); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + } + return njs_generator_stack_pop(vm, generator, NULL); } @@ -2854,7 +2879,9 @@ njs_generate_assignment_end(njs_vm_t *vm njs_parser_node_t *node) { njs_int_t ret; + njs_index_t prop_index; njs_parser_node_t *lvalue, *expr, *object, *property; + njs_vmcode_2addr_t *set_function, *to_prop_key; njs_vmcode_prop_set_t *prop_set; lvalue = node->left; @@ -2862,9 +2889,34 @@ njs_generate_assignment_end(njs_vm_t *vm object = lvalue->left; property = lvalue->right; + prop_index = property->index; switch (lvalue->token_type) { case NJS_TOKEN_PROPERTY_INIT: + + if ((expr->token_type == NJS_TOKEN_FUNCTION + || expr->token_type == NJS_TOKEN_FUNCTION_EXPRESSION + || expr->token_type == NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION)) + { + if (property->token_type == NJS_TOKEN_STRING) { + njs_value_assign(&expr->u.value.data.u.lambda->name, + &property->u.value); + + } else { + njs_generate_code(generator, njs_vmcode_2addr_t, to_prop_key, + NJS_VMCODE_TO_PROPERTY_KEY, 2, property); + + to_prop_key->src = property->index; + to_prop_key->dst = prop_index; + + njs_generate_code(generator, njs_vmcode_2addr_t, set_function, + NJS_VMCODE_SET_FUNCTION_NAME, 2, expr); + + set_function->dst = expr->index; + set_function->src = prop_index; + } + } + njs_generate_code(generator, njs_vmcode_prop_set_t, prop_set, NJS_VMCODE_PROPERTY_INIT, 3, expr); break; @@ -2882,7 +2934,7 @@ njs_generate_assignment_end(njs_vm_t *vm prop_set->value = expr->index; prop_set->object = object->index; - prop_set->property = property->index; + prop_set->property = prop_index; node->index = expr->index; node->temporary = expr->temporary; @@ -3063,7 +3115,7 @@ njs_generate_operation_assignment_prop(n } njs_generate_code(generator, njs_vmcode_3addr_t, to_property_key, - NJS_VMCODE_TO_PROPERTY_KEY, 2, property); + NJS_VMCODE_TO_PROPERTY_KEY_CHK, 2, property); to_property_key->src2 = object->index; to_property_key->src1 = property->index; @@ -3233,6 +3285,7 @@ static njs_int_t njs_generate_function_expression(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { + ssize_t length; njs_int_t ret; njs_variable_t *var; njs_function_lambda_t *lambda; @@ -3262,6 +3315,17 @@ njs_generate_function_expression(njs_vm_ return ret; } + length = njs_utf8_length(lex_entry->name.start, lex_entry->name.length); + if (njs_slow_path(length < 0)) { + return NJS_ERROR; + } + + ret = njs_string_new(vm, &lambda->name, lex_entry->name.start, + lex_entry->name.length, length); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + njs_generate_code(generator, njs_vmcode_function_t, function, NJS_VMCODE_FUNCTION, 1, node); function->lambda = lambda; @@ -3289,7 +3353,7 @@ njs_generate_function(njs_vm_t *vm, njs_ lambda = node->u.value.data.u.lambda; ret = njs_generate_function_scope(vm, generator, lambda, node, - &njs_entry_anonymous); + &njs_entry_empty); if (njs_slow_path(ret != NJS_OK)) { return ret; } @@ -3731,7 +3795,7 @@ found: } njs_generate_code(generator, njs_vmcode_3addr_t, to_property_key, - NJS_VMCODE_TO_PROPERTY_KEY, 2, node); + NJS_VMCODE_TO_PROPERTY_KEY_CHK, 2, node); to_property_key->src2 = lvalue->left->index; to_property_key->src1 = lvalue->right->index; @@ -3787,6 +3851,7 @@ static njs_int_t njs_generate_function_declaration(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { + ssize_t length; njs_int_t ret; njs_bool_t async; njs_variable_t *var; @@ -3811,6 +3876,17 @@ njs_generate_function_declaration(njs_vm return NJS_ERROR; } + length = njs_utf8_length(lex_entry->name.start, lex_entry->name.length); + if (njs_slow_path(length < 0)) { + return NJS_ERROR; + } + + ret = njs_string_new(vm, &lambda->name, lex_entry->name.start, + lex_entry->name.length, length); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + ret = njs_generate_function_scope(vm, generator, lambda, node, &lex_entry->name); if (njs_slow_path(ret != NJS_OK)) { diff -r 33fcc9fc6704 -r 74d30c2d70f3 src/njs_object_prop.c --- a/src/njs_object_prop.c Mon Sep 19 17:36:02 2022 -0700 +++ b/src/njs_object_prop.c Tue Sep 20 17:44:25 2022 -0700 @@ -452,6 +452,11 @@ done: pq.lhq.value = NULL; goto set_prop; } + + } else { + + prev->type = prop->type; + njs_value_assign(&prev->value, &prop->value); } } else { diff -r 33fcc9fc6704 -r 74d30c2d70f3 src/njs_parser.c --- a/src/njs_parser.c Mon Sep 19 17:36:02 2022 -0700 +++ b/src/njs_parser.c Tue Sep 20 17:44:25 2022 -0700 @@ -6872,9 +6872,9 @@ njs_parser_function_parse(njs_parser_t * } -static const njs_lexer_entry_t njs_parser_anonymous_entry = -{ - .name = njs_str("anonymous") +static const njs_lexer_entry_t njs_parser_empty_entry = +{ + .name = njs_str("") }; @@ -6909,7 +6909,7 @@ njs_parser_function_expression(njs_parse } } else { - unique_id = (uintptr_t) &njs_parser_anonymous_entry; + unique_id = (uintptr_t) &njs_parser_empty_entry; } if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { @@ -7155,7 +7155,7 @@ njs_parser_arrow_function(njs_parser_t * node->left = name; - unique_id = (uintptr_t) &njs_parser_anonymous_entry; + unique_id = (uintptr_t) &njs_parser_empty_entry; var = njs_variable_scope_add(parser, parser->scope, parser->scope, unique_id, NJS_VARIABLE_FUNCTION, 1); diff -r 33fcc9fc6704 -r 74d30c2d70f3 src/njs_value.c --- a/src/njs_value.c Mon Sep 19 17:36:02 2022 -0700 +++ b/src/njs_value.c Tue Sep 20 17:44:25 2022 -0700 @@ -54,6 +54,7 @@ const njs_value_t njs_string_external = const njs_value_t njs_string_invalid = njs_string("invalid"); const njs_value_t njs_string_object = njs_string("object"); const njs_value_t njs_string_function = njs_string("function"); +const njs_value_t njs_string_anonymous = njs_string("anonymous"); const njs_value_t njs_string_memory_error = njs_string("MemoryError"); diff -r 33fcc9fc6704 -r 74d30c2d70f3 src/njs_value.h --- a/src/njs_value.h Mon Sep 19 17:36:02 2022 -0700 +++ b/src/njs_value.h Tue Sep 20 17:44:25 2022 -0700 @@ -836,6 +836,7 @@ extern const njs_value_t njs_string_ext extern const njs_value_t njs_string_invalid; extern const njs_value_t njs_string_object; extern const njs_value_t njs_string_function; +extern const njs_value_t njs_string_anonymous; extern const njs_value_t njs_string_memory_error; diff -r 33fcc9fc6704 -r 74d30c2d70f3 src/njs_vm.c --- a/src/njs_vm.c Mon Sep 19 17:36:02 2022 -0700 +++ b/src/njs_vm.c Tue Sep 20 17:44:25 2022 -0700 @@ -12,6 +12,7 @@ static njs_int_t njs_vm_init(njs_vm_t *v static njs_int_t njs_vm_handle_events(njs_vm_t *vm); +const njs_str_t njs_entry_empty = njs_str(""); const njs_str_t njs_entry_main = njs_str("main"); const njs_str_t njs_entry_module = njs_str("module"); const njs_str_t njs_entry_native = njs_str("native"); diff -r 33fcc9fc6704 -r 74d30c2d70f3 src/njs_vm.h --- a/src/njs_vm.h Mon Sep 19 17:36:02 2022 -0700 +++ b/src/njs_vm.h Tue Sep 20 17:44:25 2022 -0700 @@ -267,6 +267,7 @@ void *njs_lvlhsh_alloc(void *data, size_ void njs_lvlhsh_free(void *data, void *p, size_t size); +extern const njs_str_t njs_entry_empty; extern const njs_str_t njs_entry_main; extern const njs_str_t njs_entry_module; extern const njs_str_t njs_entry_native; diff -r 33fcc9fc6704 -r 74d30c2d70f3 src/njs_vmcode.c --- a/src/njs_vmcode.c Mon Sep 19 17:36:02 2022 -0700 +++ b/src/njs_vmcode.c Tue Sep 20 17:44:25 2022 -0700 @@ -889,21 +889,41 @@ next: break; case NJS_VMCODE_TO_PROPERTY_KEY: + case NJS_VMCODE_TO_PROPERTY_KEY_CHK: njs_vmcode_operand(vm, (njs_index_t) value2, retval); - njs_vmcode_operand(vm, vmcode->operand3, value2); - - if (njs_slow_path(njs_is_null_or_undefined(value2))) { - (void) njs_throw_cannot_property(vm, value2, value1, - "get"); - goto error; + + if (op == NJS_VMCODE_TO_PROPERTY_KEY_CHK) { + njs_vmcode_operand(vm, vmcode->operand3, value2); + + if (njs_slow_path(njs_is_null_or_undefined(value2))) { + (void) njs_throw_cannot_property(vm, value2, value1, + "get"); + goto error; + } } - ret = njs_value_to_string(vm, retval, value1); + ret = njs_value_to_key(vm, retval, value1); if (njs_fast_path(ret == NJS_ERROR)) { goto error; } - ret = sizeof(njs_vmcode_3addr_t); + ret = (op == NJS_VMCODE_TO_PROPERTY_KEY) + ? sizeof(njs_vmcode_2addr_t) + : sizeof(njs_vmcode_3addr_t); + break; + + case NJS_VMCODE_SET_FUNCTION_NAME: + njs_vmcode_operand(vm, (njs_index_t) value2, value2); + + njs_assert(njs_is_function(value2)); + + ret = njs_function_name_set(vm, njs_function(value2), value1, + NULL); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + ret = sizeof(njs_vmcode_2addr_t); break; case NJS_VMCODE_PROTO_INIT: diff -r 33fcc9fc6704 -r 74d30c2d70f3 src/njs_vmcode.h --- a/src/njs_vmcode.h Mon Sep 19 17:36:02 2022 -0700 +++ b/src/njs_vmcode.h Tue Sep 20 17:44:25 2022 -0700 @@ -50,6 +50,8 @@ enum { NJS_VMCODE_ARGUMENTS, NJS_VMCODE_PROTO_INIT, NJS_VMCODE_TO_PROPERTY_KEY, + NJS_VMCODE_TO_PROPERTY_KEY_CHK, + NJS_VMCODE_SET_FUNCTION_NAME, NJS_VMCODE_IMPORT, NJS_VMCODE_AWAIT, diff -r 33fcc9fc6704 -r 74d30c2d70f3 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Mon Sep 19 17:36:02 2022 -0700 +++ b/src/test/njs_unit_test.c Tue Sep 20 17:44:25 2022 -0700 @@ -3846,6 +3846,24 @@ static njs_unit_test_t njs_test[] = { njs_str("({[{toString(){return {}}}]:1})"), njs_str("TypeError: Cannot convert object to primitive value") }, + { njs_str("({['a' + 'v'](){}}).av.name"), + njs_str("av") }, + + { njs_str("({[Symbol.toStringTag](){}})[Symbol.toStringTag].name"), + njs_str("[Symbol.toStringTag]") }, + + { njs_str("var anonSym = Symbol(); ({[anonSym]: () => {}})[anonSym].name"), + njs_str("") }, + + { njs_str("var named = Symbol('xxx'); ({[named]: () => {}})[named].name"), + njs_str("[xxx]") }, + + { njs_str("var called = false;" + "({" + " [{toString(){ if (called) throw 'OOps'; called = true; return 'a'}}](){}" + "}).a.name"), + njs_str("a") }, + { njs_str("var o = { [new Number(12345)]: 1000 }; o[12345]"), njs_str("1000") }, @@ -7740,6 +7758,10 @@ static njs_unit_test_t njs_test[] = "var o = { toString: f }; o"), njs_str("0,1,2") }, + { njs_str("var f = function F() {};" + "[f.name, f.bind().name, f.bind().bind().name]"), + njs_str("F,bound F,bound bound F") }, + { njs_str("var f = Object.defineProperty(function() {}, 'name', {value: 'F'});" "[f.name, f.bind().name, f.bind().bind().name]"), njs_str("F,bound F,bound bound F") }, @@ -9948,6 +9970,53 @@ static njs_unit_test_t njs_test[] = "f.length"), njs_str("42") }, + { njs_str("function f(){}; f.name"), + njs_str("f") }, + + { njs_str("function f(){}; njs.dump(Object.getOwnPropertyDescriptor(f, 'name'))"), + njs_str("{value:'f',writable:false,enumerable:false,configurable:true}") }, + + { njs_str("function f(){}; Object.defineProperty(f, 'name', {value: 'F'}); f.name"), + njs_str("F") }, + + { njs_str("function f(){}; Object.defineProperty(f, 'name', {value: 'F'});" + "njs.dump(Object.getOwnPropertyDescriptor(f, 'name'))"), + njs_str("{value:'F',writable:false,enumerable:false,configurable:true}") }, + + { njs_str("function f() {}; f.name = 'a'"), + njs_str("TypeError: Cannot assign to read-only property \"name\" of function") }, + + { njs_str("(function f () { return f.name})()"), + njs_str("f") }, + + { njs_str("var a = function () {}; a.name"), + njs_str("a") }, + + { njs_str("(function () {}).name"), + njs_str("") }, + + { njs_str("var a = (null, function () {}); a.name"), + njs_str("") }, + + { njs_str("var a = async function () {}; a.name"), + njs_str("a") }, + + { njs_str("let a = () => {}; a.name"), + njs_str("a") }, + + { njs_str("let a = async () => {}; a.name"), + njs_str("a") }, + + { njs_str("Function().name"), + njs_str("anonymous") }, + + { njs_str("var o = {f: function (){}, g: () => {}, h: async function(){}};" + "[o.f.name, o.g.name, o.h.name]"), + njs_str("f,g,h") }, + + { njs_str("({t(){}}).t.name"), + njs_str("t") }, + /* Function nesting depth. */ { njs_str("() => () => () => () => () => () => () => () => () => () => () =>" @@ -14789,7 +14858,7 @@ static njs_unit_test_t njs_test[] = njs_str("0,1,2,length") }, { njs_str("Object.getOwnPropertyNames(function() {})"), - njs_str("length,prototype") }, + njs_str("length,name,prototype") }, { njs_str("Object.getOwnPropertyNames(Array)"), njs_str("name,length,prototype,isArray,of") }, From xeioex at nginx.com Fri Sep 23 02:18:08 2022 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 23 Sep 2022 02:18:08 +0000 Subject: [njs] HTTP: adding a warning for ignored outgoing header assignments. Message-ID: details: https://hg.nginx.org/njs/rev/ef0e05668f39 branches: changeset: 1961:ef0e05668f39 user: Dmitry Volyntsev date: Thu Sep 22 19:05:36 2022 -0700 description: HTTP: adding a warning for ignored outgoing header assignments. diffstat: nginx/ngx_http_js_module.c | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) diffs (15 lines): diff -r 74d30c2d70f3 -r ef0e05668f39 nginx/ngx_http_js_module.c --- a/nginx/ngx_http_js_module.c Tue Sep 20 17:44:25 2022 -0700 +++ b/nginx/ngx_http_js_module.c Thu Sep 22 19:05:36 2022 -0700 @@ -1565,6 +1565,11 @@ ngx_http_js_ext_header_out(njs_vm_t *vm, return NJS_DECLINED; } + if (r->header_sent && setval != NULL) { + njs_vm_warn(vm, "ignored setting of response header \"%V\" because" + " headers were already sent", &name); + } + for (h = headers_out; h->name.length > 0; h++) { if (h->name.length == name.length && ngx_strncasecmp(h->name.start, name.start, name.length) == 0) From pluknet at nginx.com Mon Sep 26 10:11:17 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Mon, 26 Sep 2022 14:11:17 +0400 Subject: [PATCH 01 of 11] SSL: disabled saving tickets to session cache In-Reply-To: References: <2cd8fbeb4edc5a99b725.1661482868@vm-bsd.mdounin.ru> Message-ID: <11914CEC-850E-4A8F-8B2D-BC8C15AC7CA3@nginx.com> > On 17 Sep 2022, at 00:58, Maxim Dounin wrote: > > Hello! > > On Thu, Sep 15, 2022 at 09:36:31AM +0400, Sergey Kandaurov wrote: > >>> On 26 Aug 2022, at 07:01, Maxim Dounin wrote: >>> >>> # HG changeset patch >>> # User Maxim Dounin >>> # Date 1661481945 -10800 >>> # Fri Aug 26 05:45:45 2022 +0300 >>> # Node ID 2cd8fbeb4edc5a99b725585edc02a16a8a0c503e >>> # Parent 069a4813e8d6d7ec662d282a10f5f7062ebd817f >>> SSL: disabled saving tickets to session cache. >>> >>> OpenSSL for TLSv1.3 tries to save tickets into session cache "because some >>> applications just want to know about the creation of a session". To avoid >>> trashing session cache with useless data, we do not save such sessions now. >>> >> >> For the record, BoringSSL doesn't seem to call new_session_cb for TLSv1.3 >> at all, so there is no way to resume sessions with SSL_OP_NO_TICKET set. >> In contrary, OpenSSL emits stateful tickets in this case, which contain >> dummy session id used then as a session cache lookup key on server >> (much like session ids in TLSv1.2) >> >> OTOH, without SSL_OP_NO_TICKET set, OpenSSL emits self-containing tickets >> with enough info to resume session, so nothing to lookup in session cache. >> The latter makes impractical storing something in session cache, except >> to use the callback for things like tracking "the creation of a session". >> Namely, OpenSSL puts session (i.e. something that SSL_get_session returns) >> and supplementary info to session ticket message as the ticket value. > > It looks like you are trying to introduce "stateful tickets" and > "self-containing tickets" terms, which is somewhat confusing > unless carefully explained. OpenSSL itself tries to use terms > "stateful tickets" and "stateless tickets", with the similar > drawbacks. Indeed, OpenSSL SSL_OP_NO_TICKET documentation is what I refer to. While OpenSSL terms may look odd, they are useful to describe the difference (and tricks) in TLSv1.3 session resumption with and without SSL_OP_NO_TICKET, as implemented to OpenSSL specifically. > > A better explanation would be to follow generic term "session > ticket", as originally introduced in RFC 4507 for TLS session > resumption without server-side state. > > In these terms (as always used before introduction of TLSv1.3 and > currently used in many places, including nginx own documentation > and the source code) there are two basic mechanisms to resume > sessions: server-side session cache and session tickets (used to > resume sessions without server-side state). > > Without SSL_OP_NO_TICKET set, OpenSSL uses tickets as long as > supported by the client. > > With SSL_OP_NO_TICKET set, OpenSSL does not use tickets, and uses > server-side session cache instead (if configured). > > The only difference between TLSv1.3 and previous protocols is how > session ids are sent to the client if server-side session cache is > used. In case of SSL and TLS up to and including TLSv1.2, session > ids are sent in the dedicated fields of the ServerHello and > ClientHello handshake messages. In case of TLSv1.3, dedicated > fields were removed, so session ids are sent in the > NewSessionTicket messages ("a database lookup key" in terms of RFC > 8446). > >> With these thoughts in mind, I think log could be clarified to emphasize: >> - it's not tickets that are stored in cache >> - with SSL_OP_NO_TICKET set TLSv1.3 session are still saved to lookup by id. > > Hope it is clear enough now. What I'd like to clarify is the difference between session and (session) ticket applied to session cache. Ticket is a container used to envelope session state (essentially, a session) and send in the NewSessionTicket message "to resume sessions and avoid keeping per-client session state", as seen in RFC 8446, 4.6.1 (and somewhat similar in 4507/5077). struct { uint32 ticket_lifetime; uint32 ticket_age_add; opaque ticket_nonce<0..255>; opaque ticket<1..2^16-1>; Extension extensions<0..2^16-2>; } NewSessionTicket; So it looks inappropriate to say that tickets are saved in cache. OTOH, "ticket" can be seen a correct wording if speaking in terms of NewSessionTicket ticket field, or in contrast to "session id" synthesized by OpenSSL for the SSL_OP_NO_TICKET case in TLSv1.3. In that sense, I'm fine with using "ticket" (not to say changing "ticket" to "session" or "session state" brings a tautology). However this looks not so important to spend more time on this, I'm fine with either case. > >> >>> diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c >>> --- a/src/event/ngx_event_openssl.c >>> +++ b/src/event/ngx_event_openssl.c >>> @@ -3815,6 +3815,22 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ >>> ngx_ssl_session_cache_t *cache; >>> u_char buf[NGX_SSL_MAX_SESSION_SIZE]; >>> >>> +#ifdef TLS1_3_VERSION >>> + >>> + /* >>> + * OpenSSL for TLSv1.3 tries to save tickets into session cache >>> + * "because some applications just want to know about the creation >>> + * of a session"; do not cache such sessions >>> + */ >>> + >>> + if (SSL_version(ssl_conn) == TLS1_3_VERSION >>> + && (SSL_get_options(ssl_conn) & SSL_OP_NO_TICKET) == 0) >>> + { >>> + return 0; >>> + } >>> + >>> +#endif >>> + >>> len = i2d_SSL_SESSION(sess, NULL); >>> >>> /* do not cache too big session */ >>> >> >> Looks good. >> [..] -- Sergey Kandaurov From pluknet at nginx.com Mon Sep 26 10:12:44 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Mon, 26 Sep 2022 14:12:44 +0400 Subject: [PATCH 02 of 11] SSL: reduced logging of session cache failures (ticket #621) In-Reply-To: References: <5b137f110e84af974ef2.1661482869@vm-bsd.mdounin.ru> <42A8BF68-4BA9-41F4-A3BE-8E0B40CD2234@nginx.com> Message-ID: <54CFCAC0-3B19-484C-B04E-1BD07C68E033@nginx.com> > On 17 Sep 2022, at 01:01, Maxim Dounin wrote: > > Hello! > > On Thu, Sep 15, 2022 at 09:37:17AM +0400, Sergey Kandaurov wrote: > >>> On 26 Aug 2022, at 07:01, Maxim Dounin wrote: >>> >>> # HG changeset patch >>> # User Maxim Dounin >>> # Date 1661481947 -10800 >>> # Fri Aug 26 05:45:47 2022 +0300 >>> # Node ID 5b137f110e84af974ef2b9efcf35bec2d883c187 >>> # Parent 2cd8fbeb4edc5a99b725585edc02a16a8a0c503e >>> SSL: reduced logging of session cache failures (ticket #621). >>> >>> Session cache allocations might fail as long as the new session is different >>> in size from the one least recently used (and freed when the first allocation >>> fails). In particular, it might not be possible to allocate space for >>> sessions with client certificates, since they are noticeably bigger than >>> normal sessions. >>> >>> To ensure such allocation failures won't clutter logs, logging level changed >>> to "warn", and logging is now limited to at most one warning per second. >>> >>> diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c >>> --- a/src/event/ngx_event_openssl.c >>> +++ b/src/event/ngx_event_openssl.c >>> @@ -3949,8 +3949,11 @@ failed: >>> >>> ngx_shmtx_unlock(&shpool->mutex); >>> >>> - ngx_log_error(NGX_LOG_ALERT, c->log, 0, >>> - "could not allocate new session%s", shpool->log_ctx); >>> + if (cache->fail_time != ngx_time()) { >>> + cache->fail_time = ngx_time(); >>> + ngx_log_error(NGX_LOG_WARN, c->log, 0, >>> + "could not allocate new session%s", shpool->log_ctx); >>> + } >>> >> >> This makes three ngx_time() calls in this function in total. >> A good reason to cache value in a local variable. > > Well, not really. > > This is an error code path, which is not normally used. In the > hot path, there is still just one ngx_time() call. Trying to > provide appropriate cached value in all cases where this error > path is followed means that it have to be done at the very start > of the function, and it is going to make the function less > readable, and might actually make it less optimal. > > Further, the > > if (fail_time != ngx_time()) { > fail_time = ngx_time(); > ... > } > > pattern is used elsewhere, and I would rather keep the code > similar in all cases. > > Not to mention that ngx_time() is not really a call, but rather a > macro, which expands to ngx_cached_time->sec. While it might be > beneficial to cache the value if it is used multiple times, since > ngx_cached_time is volatile, the performance impact is minimal. Ok, fair enough. > >>> return 0; >>> } >>> diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h >>> --- a/src/event/ngx_event_openssl.h >>> +++ b/src/event/ngx_event_openssl.h >>> @@ -150,6 +150,7 @@ typedef struct { >>> ngx_rbtree_t session_rbtree; >>> ngx_rbtree_node_t sentinel; >>> ngx_queue_t expire_queue; >>> + time_t fail_time; >>> } ngx_ssl_session_cache_t; >>> >> >> Missed initialization to something sensible (zero?). > > It doesn't really matter here, but won't hurt anyway. Added the following > chunk: > > diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c > --- a/src/event/ngx_event_openssl.c > +++ b/src/event/ngx_event_openssl.c > @@ -3770,6 +3770,8 @@ ngx_ssl_session_cache_init(ngx_shm_zone_ > > ngx_queue_init(&cache->expire_queue); > > + cache->fail_time = 0; > + > len = sizeof(" in SSL session shared cache \"\"") + shm_zone->shm.name.len; > > shpool->log_ctx = ngx_slab_alloc(shpool, len); > Yeah, this is mostly cosmetics and doesn't buy anything, but still prefer to make it explicit for completeness and to be safe in future hypothetical changes. -- Sergey Kandaurov From pluknet at nginx.com Mon Sep 26 10:13:10 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Mon, 26 Sep 2022 14:13:10 +0400 Subject: [PATCH 04 of 11] SSL: explicit session id length checking In-Reply-To: References: Message-ID: <6E112679-F456-4AF6-AE46-FAD7204F3A6D@nginx.com> > On 17 Sep 2022, at 01:03, Maxim Dounin wrote: > > Hello! > > On Thu, Sep 15, 2022 at 09:40:32AM +0400, Sergey Kandaurov wrote: > >>> On 26 Aug 2022, at 07:01, Maxim Dounin wrote: >>> >>> # HG changeset patch >>> # User Maxim Dounin >>> # Date 1661481949 -10800 >>> # Fri Aug 26 05:45:49 2022 +0300 >>> # Node ID f4ae0f4ee928cf20346530e96f1431314ecd0171 >>> # Parent 86d827338fdd13ea899d618b0bcb2be23469cbac >>> SSL: explicit session id length checking. >>> >>> Session ids are not expected to be longer than 32 bytes, but this is >>> theoretically possible with TLSv1.3, where session ids are essentially >>> arbitrary and sent as session tickets. Since on 64-bit platforms we >>> use fixed 32-byte buffer for session ids, added an explicit length check >>> to make sure the buffer is large enough. >>> >> >> I don't follow how session ids could be "essentially arbitrary" >> (except a library bug that justifies such safety belt). >> For TLSv1.3, this callback is used to update session cache as part >> of constructing NewSessionTicket. It's called after generating >> dummy session ids, which, and regardless of protocol version, are >> capped to SSL3_SSL_SESSION_ID_LENGTH (32). > > In TLSv1.2 and below, session id lengths are limited by the > protocol. For example, in TLSv1.2: > > opaque SessionID<0..32>; > > In TLSv1.3 there is no such limitation, and any length can be used > by the library (well, almost any, up to 2^16-1 bytes). While > OpenSSL currently uses 32 bytes, there is no guarantee that at > some point it won't switch to using, for example, 64 bytes. So that's for a rather theoretical and unlikely reason. I agree though, such explicit and cheap guarantee won't hurt. > >>> diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c >>> --- a/src/event/ngx_event_openssl.c >>> +++ b/src/event/ngx_event_openssl.c >>> @@ -3842,6 +3842,14 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ >>> p = buf; >>> i2d_SSL_SESSION(sess, &p); >>> >>> + session_id = (u_char *) SSL_SESSION_get_id(sess, &session_id_length); >>> + >>> + /* do not cache sessions with too long session id */ >>> + >>> + if (session_id_length > 32) { >>> + return 0; >>> + } >>> + >> >> The check can be moved above the cpu expensive i2d_SSL_SESSION call. >> Or rather move i2d_SSL_SESSION closer to corresponding ngx_memcpy(). >> (but see my reply on the next patch) > > There is no practical difference, as this check is not expected > to catch anything. > Agree, with that in mind it won't make a difference. >>> c = ngx_ssl_get_connection(ssl_conn); >>> >>> ssl_ctx = c->ssl->session_ctx; >>> @@ -3886,8 +3894,6 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ >>> } >>> } >>> >>> - session_id = (u_char *) SSL_SESSION_get_id(sess, &session_id_length); >>> - >>> #if (NGX_PTR_SIZE == 8) >>> >>> id = sess_id->sess_id; >>> > -- Sergey Kandaurov From pluknet at nginx.com Mon Sep 26 10:13:30 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Mon, 26 Sep 2022 14:13:30 +0400 Subject: [PATCH 05 of 11] SSL: single allocation in session cache on 32-bit platforms In-Reply-To: References: <336FD698-C706-4B4C-B24F-25E7BD9113E2@nginx.com> Message-ID: <62DA77CA-129E-42EE-9697-4A8921BF754C@nginx.com> > On 17 Sep 2022, at 01:04, Maxim Dounin wrote: > > Hello! > > On Thu, Sep 15, 2022 at 09:41:36AM +0400, Sergey Kandaurov wrote: > >>> On 26 Aug 2022, at 07:01, Maxim Dounin wrote: >>> >>> # HG changeset patch >>> # User Maxim Dounin >>> # Date 1661481950 -10800 >>> # Fri Aug 26 05:45:50 2022 +0300 >>> # Node ID e88baee178eed529c6170678e373f5e2e0883c37 >>> # Parent f4ae0f4ee928cf20346530e96f1431314ecd0171 >>> SSL: single allocation in session cache on 32-bit platforms. >>> >>> Given the present typical SSL session sizes, on 32-bit platforms it is >>> now beneficial to store all data in a single allocation, since rbtree >>> node + session id + ASN1 representation of a session takes 256 bytes of >>> shared memory (36 + 32 + 150 = about 218 bytes plus SNI server name). >>> >>> Storing all data in a single allocation is beneficial for SNI names up to >>> about 40 characters long and makes it possible to store about 4000 sessions >>> in one megabyte (instead of about 3000 sessions now). This also slightly >>> simplifies the code. >>> >>> diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c >>> --- a/src/event/ngx_event_openssl.c >>> +++ b/src/event/ngx_event_openssl.c >>> @@ -3789,9 +3789,9 @@ ngx_ssl_session_cache_init(ngx_shm_zone_ >>> * Typical length of the external ASN1 representation of a session >>> * is about 150 bytes plus SNI server name. >>> * >>> - * On 32-bit platforms we allocate separately an rbtree node, >>> - * a session id, and an ASN1 representation, they take accordingly >>> - * 64, 32, and 256 bytes. >>> + * On 32-bit platforms we allocate an rbtree node, a session id, and >>> + * an ASN1 representation in a single allocation, it typically takes >>> + * 256 bytes. >>> * >>> * On 64-bit platforms we allocate separately an rbtree node + session_id, >>> * and an ASN1 representation, they take accordingly 128 and 256 bytes. >>> @@ -3804,7 +3804,8 @@ static int >>> ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess) >>> { >>> int len; >>> - u_char *p, *id, *cached_sess, *session_id; >>> + u_char *p, *session_id; >>> + size_t n; >>> uint32_t hash; >>> SSL_CTX *ssl_ctx; >>> unsigned int session_id_length; >>> @@ -3863,23 +3864,13 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ >>> /* drop one or two expired sessions */ >>> ngx_ssl_expire_sessions(cache, shpool, 1); >>> >>> - cached_sess = ngx_slab_alloc_locked(shpool, len); >>> - >>> - if (cached_sess == NULL) { >>> - >>> - /* drop the oldest non-expired session and try once more */ >>> - >>> - ngx_ssl_expire_sessions(cache, shpool, 0); >>> - >>> - cached_sess = ngx_slab_alloc_locked(shpool, len); >>> - >>> - if (cached_sess == NULL) { >>> - sess_id = NULL; >>> - goto failed; >>> - } >>> - } >>> - >>> - sess_id = ngx_slab_alloc_locked(shpool, sizeof(ngx_ssl_sess_id_t)); >>> +#if (NGX_PTR_SIZE == 8) >>> + n = sizeof(ngx_ssl_sess_id_t); >>> +#else >>> + n = offsetof(ngx_ssl_sess_id_t, session) + len; >>> +#endif >>> + >>> + sess_id = ngx_slab_alloc_locked(shpool, n); >>> >>> if (sess_id == NULL) { >>> >>> @@ -3887,7 +3878,7 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ >>> >>> ngx_ssl_expire_sessions(cache, shpool, 0); >>> >>> - sess_id = ngx_slab_alloc_locked(shpool, sizeof(ngx_ssl_sess_id_t)); >>> + sess_id = ngx_slab_alloc_locked(shpool, n); >>> >>> if (sess_id == NULL) { >>> goto failed; >>> @@ -3896,30 +3887,25 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ >>> >>> #if (NGX_PTR_SIZE == 8) >>> >>> - id = sess_id->sess_id; >>> - >>> -#else >>> - >>> - id = ngx_slab_alloc_locked(shpool, session_id_length); >>> - >>> - if (id == NULL) { >>> + sess_id->session = ngx_slab_alloc_locked(shpool, len); >>> + >>> + if (sess_id->session == NULL) { >>> >>> /* drop the oldest non-expired session and try once more */ >>> >>> ngx_ssl_expire_sessions(cache, shpool, 0); >>> >>> - id = ngx_slab_alloc_locked(shpool, session_id_length); >>> - >>> - if (id == NULL) { >>> + sess_id->session = ngx_slab_alloc_locked(shpool, len); >>> + >>> + if (sess_id->session == NULL) { >>> goto failed; >>> } >>> } >>> >>> #endif >>> >>> - ngx_memcpy(cached_sess, buf, len); >>> - >>> - ngx_memcpy(id, session_id, session_id_length); >>> + ngx_memcpy(sess_id->session, buf, len); >> >> Converting to ASN1 looks feasible without intermediate buf[] >> to avoid extra memory copy. > > Quoting the comment before the function: > > * OpenSSL's i2d_SSL_SESSION() and d2i_SSL_SESSION are slow, > * so they are outside the code locked by shared pool mutex > > Hope this helps. Thanks, somehow missed that. [..] >>> diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h >>> --- a/src/event/ngx_event_openssl.h >>> +++ b/src/event/ngx_event_openssl.h >>> @@ -134,14 +134,14 @@ typedef struct ngx_ssl_sess_id_s ngx_ss >>> >>> struct ngx_ssl_sess_id_s { >>> ngx_rbtree_node_t node; >>> - u_char *id; >>> size_t len; >>> - u_char *session; >>> ngx_queue_t queue; >>> time_t expire; >>> + u_char id[32]; >>> #if (NGX_PTR_SIZE == 8) >>> - void *stub; >>> - u_char sess_id[32]; >>> + u_char *session; >>> +#else >>> + u_char session[1]; >>> #endif >>> }; >>> >> >> This reminds me that this structure coupled with ngx_ssl_ticket_key_t >> and ngx_ssl_session_cache_t aren't used outside and can be made private. > > I don't think it worth the effort. Either way, it is completely > unrelated to this patch series. > Sure. -- Sergey Kandaurov From pluknet at nginx.com Mon Sep 26 10:13:44 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Mon, 26 Sep 2022 14:13:44 +0400 Subject: [PATCH 07 of 11] SSL: style In-Reply-To: References: <84919c2ee8173f704649.1661482874@vm-bsd.mdounin.ru> Message-ID: > On 17 Sep 2022, at 01:05, Maxim Dounin wrote: > > Hello! > > On Thu, Sep 15, 2022 at 09:42:01AM +0400, Sergey Kandaurov wrote: > >>> On 26 Aug 2022, at 07:01, Maxim Dounin wrote: >>> >>> # HG changeset patch >>> # User Maxim Dounin >>> # Date 1661481953 -10800 >>> # Fri Aug 26 05:45:53 2022 +0300 >>> # Node ID 84919c2ee8173f704649a8cb4901887e1bf79588 >>> # Parent d5c6eae914325fb6a9b19105fe09aecd04da21e2 >>> SSL: style. >>> >>> Runtime OCSP functions separated from configuration ones. >>> >>> diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h >>> --- a/src/event/ngx_event_openssl.h >>> +++ b/src/event/ngx_event_openssl.h >>> @@ -205,10 +205,12 @@ ngx_int_t ngx_ssl_ocsp(ngx_conf_t *cf, n >>> ngx_uint_t depth, ngx_shm_zone_t *shm_zone); >>> ngx_int_t ngx_ssl_ocsp_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl, >>> ngx_resolver_t *resolver, ngx_msec_t resolver_timeout); >>> + >>> ngx_int_t ngx_ssl_ocsp_validate(ngx_connection_t *c); >>> ngx_int_t ngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s); >>> void ngx_ssl_ocsp_cleanup(ngx_connection_t *c); >>> ngx_int_t ngx_ssl_ocsp_cache_init(ngx_shm_zone_t *shm_zone, void *data); >>> + >>> ngx_array_t *ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file); >>> ngx_array_t *ngx_ssl_preserve_passwords(ngx_conf_t *cf, >>> ngx_array_t *passwords); >>> >> >> Speaking of style, this reminds me of various more style issues. > > There was no goal to fix all the style issues. This particular > one interfered with ngx_event_openssl.h changes, hence it was > fixed. > > Note well that a generic rule is to avoid style changes without a > good reason. Sure, that's rather an observation of what could be done. [..] -- Sergey Kandaurov From pluknet at nginx.com Mon Sep 26 10:17:18 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Mon, 26 Sep 2022 14:17:18 +0400 Subject: [PATCH 11 of 11] SSL: automatic rotation of session ticket keys In-Reply-To: References: <5c26fe5f6ab0bf4c0d18.1661482878@vm-bsd.mdounin.ru> Message-ID: <384C1C36-43CA-490B-9559-DD77DE6346E6@nginx.com> > On 17 Sep 2022, at 01:08, Maxim Dounin wrote: > > Hello! > > On Thu, Sep 15, 2022 at 09:50:24AM +0400, Sergey Kandaurov wrote: > >>> On 26 Aug 2022, at 07:01, Maxim Dounin wrote: >>> >>> # HG changeset patch >>> # User Maxim Dounin >>> # Date 1661481958 -10800 >>> # Fri Aug 26 05:45:58 2022 +0300 >>> # Node ID 5c26fe5f6ab0bf4c0d18cae8f6f6483348243d4b >>> # Parent 2487bf5766f79c813b3397b3bb897424c3590445 >>> SSL: automatic rotation of session ticket keys. >>> >>> As long as ssl_session_cache in shared memory is configured, session ticket >>> keys are now automatically generated in shared memory, and rotated >>> periodically. >> >> It looks odd how session cache is (ab)used to store ticket keys. >> I understand that it is comfortable to reuse existing shared zone >> (and not to touch configuration) but probably a more correct way >> is to create a separate zone to store keys? >> Something pretty much the same as ssl_session_cache syntax: >> ssl_session_ticket_key file | shared:name:size >> >> What do you think? > > I don't think it is a good idea to introduce additional shared > zones here, especially given that it only needs about 160 bytes of > shared memory. It really complicates things for users for no real > reason. > > Rather, I was thinking about using nginx_shared_zone, which is > always available, but it is rather special and will require > additional integration code. Further, as a single contention > point it might be a problem, at least unless lock avoidance is > also implemented (see below). And a single key for all servers > might not be the best solution either. On the other hand, > ssl_session_cache in shared memory is readily available and > logically related, and I see no reasons not to use it to rotate > ticket keys if it's configured. > I've been pondering for a while and tend to agree to go with the ssl_session_cache route for its simplicity. I share the dislike to introduce new syntax, while nginx_shared_zone looks unrelated enough to put SSL bits in. > If there will be understanding that we need to rotate ticket keys > in all cases, even without ssl_session_cache in shared memory > being configured, the idea of using nginx_shared_zone might be > revisited. Well, an obvious downside is the need to enable a session cache which might be undesirable in certain memory constrained systems if you intend to resume only with session tickets. Even though the impact is reduced by keeping shared memory zone to the minimum, (re)using session cache to store only ticket keys nonetheless requires to allocate memory shared zone of at least 8 page sizes. OTOH, it quite uncommon to have clients that use tickets only. > >>> This can be beneficial from forward secrecy point of view, >>> and also avoids increased CPU usage after configuration reloads. >>> >> >> You can also mention that this fixes resuming sessions with BoringSSL >> that implements its own ticket key rotation (if ticket keys callback >> isn't installed) that doesn't work in multiple workers configuration. > > Thanks, forgot about this BoringSSL misfeature. Added the > following paragraph: > > This also helps BoringSSL to properly resume sessions in configurations > with multiple worker processes and no ssl_session_ticket_key directives, > as BoringSSL tries to automatically rotate session ticket keys and does > this independently in different worker processes, thus breaking session > resumption between worker processes. Looks good. > >> >>> diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c >>> --- a/src/event/ngx_event_openssl.c >>> +++ b/src/event/ngx_event_openssl.c >>> @@ -74,6 +74,7 @@ static void ngx_ssl_session_rbtree_inser >>> static int ngx_ssl_ticket_key_callback(ngx_ssl_conn_t *ssl_conn, >>> unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx, >>> HMAC_CTX *hctx, int enc); >>> +static ngx_int_t ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ctx, ngx_log_t *log); >>> static void ngx_ssl_ticket_keys_cleanup(void *data); >>> #endif >>> >>> @@ -3767,6 +3768,9 @@ ngx_ssl_session_cache_init(ngx_shm_zone_ >>> >>> ngx_queue_init(&cache->expire_queue); >>> >>> + cache->ticket_keys[0].expire = 0; >>> + cache->ticket_keys[1].expire = 0; >>> + >>> len = sizeof(" in SSL session shared cache \"\"") + shm_zone->shm.name.len; >>> >>> shpool->log_ctx = ngx_slab_alloc(shpool, len); >>> @@ -4234,11 +4238,13 @@ ngx_ssl_session_ticket_keys(ngx_conf_t * >>> ngx_pool_cleanup_t *cln; >>> ngx_ssl_ticket_key_t *key; >>> >>> - if (paths == NULL) { >>> + if (paths == NULL >>> + && SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_session_cache_index) == NULL) >>> + { >>> return NGX_OK; >>> } >>> >>> - keys = ngx_array_create(cf->pool, paths->nelts, >>> + keys = ngx_array_create(cf->pool, paths ? paths->nelts : 2, >>> sizeof(ngx_ssl_ticket_key_t)); >>> if (keys == NULL) { >>> return NGX_ERROR; >>> @@ -4252,6 +4258,34 @@ ngx_ssl_session_ticket_keys(ngx_conf_t * >>> cln->handler = ngx_ssl_ticket_keys_cleanup; >>> cln->data = keys; >>> >>> + if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_ticket_keys_index, keys) == 0) { >>> + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, >>> + "SSL_CTX_set_ex_data() failed"); >>> + return NGX_ERROR; >>> + } >>> + >>> + if (SSL_CTX_set_tlsext_ticket_key_cb(ssl->ctx, ngx_ssl_ticket_key_callback) >>> + == 0) >>> + { >>> + ngx_log_error(NGX_LOG_WARN, cf->log, 0, >>> + "nginx was built with Session Tickets support, however, " >>> + "now it is linked dynamically to an OpenSSL library " >>> + "which has no tlsext support, therefore Session Tickets " >>> + "are not available"); >>> + return NGX_OK; >>> + } >>> + >>> + if (paths == NULL) { >>> + >>> + /* placeholder for keys in shared memory */ >>> + >>> + key = ngx_array_push_n(keys, 2); >>> + key[0].shared = 1; >>> + key[1].shared = 1; >>> + >>> + return NGX_OK; >>> + } >>> + >>> path = paths->elts; >>> for (i = 0; i < paths->nelts; i++) { >>> >>> @@ -4306,6 +4340,8 @@ ngx_ssl_session_ticket_keys(ngx_conf_t * >>> goto failed; >>> } >>> >>> + key->shared = 0; >>> + >>> if (size == 48) { >>> key->size = 48; >>> ngx_memcpy(key->name, buf, 16); >>> @@ -4327,22 +4363,6 @@ ngx_ssl_session_ticket_keys(ngx_conf_t * >>> ngx_explicit_memzero(&buf, 80); >>> } >>> >>> - if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_ticket_keys_index, keys) == 0) { >>> - ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, >>> - "SSL_CTX_set_ex_data() failed"); >>> - return NGX_ERROR; >>> - } >>> - >>> - if (SSL_CTX_set_tlsext_ticket_key_cb(ssl->ctx, ngx_ssl_ticket_key_callback) >>> - == 0) >>> - { >>> - ngx_log_error(NGX_LOG_WARN, cf->log, 0, >>> - "nginx was built with Session Tickets support, however, " >>> - "now it is linked dynamically to an OpenSSL library " >>> - "which has no tlsext support, therefore Session Tickets " >>> - "are not available"); >>> - } >>> - >>> return NGX_OK; >>> >>> failed: >>> @@ -4375,6 +4395,10 @@ ngx_ssl_ticket_key_callback(ngx_ssl_conn >>> c = ngx_ssl_get_connection(ssl_conn); >>> ssl_ctx = c->ssl->session_ctx; >>> >>> + if (ngx_ssl_rotate_ticket_keys(ssl_ctx, c->log) != NGX_OK) { >>> + return -1; >>> + } >>> + >>> #ifdef OPENSSL_NO_SHA256 >>> digest = EVP_sha1(); >>> #else >>> @@ -4493,6 +4517,113 @@ ngx_ssl_ticket_key_callback(ngx_ssl_conn >>> } >>> >>> >>> +static ngx_int_t >>> +ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ctx, ngx_log_t *log) >>> +{ >>> + time_t now, expire; >>> + ngx_array_t *keys; >>> + ngx_shm_zone_t *shm_zone; >>> + ngx_slab_pool_t *shpool; >>> + ngx_ssl_ticket_key_t *key; >>> + ngx_ssl_session_cache_t *cache; >>> + u_char buf[80]; >> >> BTW, the same buf[] variable in ngx_ssl_session_ticket_keys() >> is placed at the beginning of declaration block. >> Looks like a chance to improve style consistency (but see below). > > There are enough style changes in this series already. > >>> + >>> + keys = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_ticket_keys_index); >>> + if (keys == NULL) { >>> + return NGX_OK; >>> + } >>> + >>> + key = keys->elts; >>> + >>> + if (!key[0].shared) { >>> + return NGX_OK; >>> + } >>> + >>> + now = ngx_time(); >>> + expire = now + SSL_CTX_get_timeout(ssl_ctx); >>> + >>> + shm_zone = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_cache_index); >>> + >>> + cache = shm_zone->data; >>> + shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; >>> + >>> + ngx_shmtx_lock(&shpool->mutex); >>> + >>> + key = cache->ticket_keys; >>> + >>> + if (key[0].expire == 0) { >>> + >>> + /* initialize the current key */ >>> + >>> + if (RAND_bytes(buf, 80) != 1) { >>> + ngx_ssl_error(NGX_LOG_ALERT, log, 0, "RAND_bytes() failed"); >>> + ngx_shmtx_unlock(&shpool->mutex); >>> + return NGX_ERROR; >>> + } >>> + >>> + key->shared = 1; >>> + key->expire = expire; >>> + key->size = 80; >>> + ngx_memcpy(key->name, buf, 16); >>> + ngx_memcpy(key->hmac_key, buf + 16, 32); >>> + ngx_memcpy(key->aes_key, buf + 48, 32); >>> + >>> + ngx_explicit_memzero(&buf, 80); >>> + >>> + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, >>> + "ssl ticket key: \"%*xs\"", >>> + (size_t) 16, key->name); >>> + } >>> + >>> + if (key[1].expire < now) { >>> + >>> + /* >>> + * if the previous key is no longer needed (or not initialized), >>> + * replace it with the current key and generate new current key >>> + */ >>> + >>> + key[1] = key[0]; >>> + >>> + if (RAND_bytes(buf, 80) != 1) { >>> + ngx_ssl_error(NGX_LOG_ALERT, log, 0, "RAND_bytes() failed"); >>> + ngx_shmtx_unlock(&shpool->mutex); >>> + return NGX_ERROR; >>> + } >>> + >>> + key->shared = 1; >>> + key->expire = expire; >>> + key->size = 80; >>> + ngx_memcpy(key->name, buf, 16); >>> + ngx_memcpy(key->hmac_key, buf + 16, 32); >>> + ngx_memcpy(key->aes_key, buf + 48, 32); >>> + >>> + ngx_explicit_memzero(&buf, 80); >>> + >>> + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, >>> + "ssl ticket key: \"%*xs\"", >>> + (size_t) 16, key->name); >>> + } >> >> I wonder if cpu intensive ops can be moved from under the lock. >> At least, buf[] can be removed if write random data directly >> to ngx_ssl_ticket_key_t. This eliminates 3 memory copies >> (likely optimized by compiler to a single) and explicit write. > > Micro-optimizations are not something important here: new keys are > generated once per SSL session timeout, that is, once per 300 > seconds by default. As such, the code is kept as close as > possible to the ticket keys reading in > ngx_ssl_session_ticket_keys(), and not dependant on the > ngx_ssl_ticket_key_t structure layout. Ok, that makes sense. > > What can be interesting here is to avoid locking at all unless we > are going to update keys (change expiration of the current key, or > switch to the next key). > > To do so without races, the code has to maintains 3 keys: current, > previous, and next. If a worker will switch to the next key > earlier, other workers will still be able to decrypt new tickets, > since they will be encrypted with the next key. > > But with the SSL session cache in shared memory we have to lock > shared zone anyway to create sessions, and this was never seen to > be a performance bottleneck. Good to know this is not a hot path. Nonetheless, it is good to preserve a status-quo regarding the locking and/or keep to the minimum its coverage/impact. For the record, with this patch and ticket keys rotation enabled, each ticket operation resembles existing locking in session cache: - 1 lock per new ticket encryption - 1 lock per each ticket decryption + 1 lock for possible renew In TLSv1.3 this is a bit worse: - 2 locks, each for issuing 2 new tickets - 2 locks, each for ticket decryption + obligatory ticket renew With the locking patch, locking acquirement reduces at least to: - 1 lock total for decryption and renew, as that happens in 1 sec. - 1 lock for issuing 2 new tickets in TLSv1.3, the same reasons In loaded scenarios, working with tickets requires obtaining at most 1 lock per 1 sec. per each worker process. And by the way, while reviewing this patch, I noticed that OpenSSL doesn't allow a client to gracefully renew TLSv1.2 session when the client receives a new session ticket in resumed sessions. In practice, it is visible when client resumes a not yet expired session encrypted with not a fresh ticket key (after rotation), which results in sending a new session ticket. See ssl_update_cache() for the !s->hit condition. In the opposite, BoringSSL always allows to renew TLSv1.2 sessions. Additionally, BoringSSL view a bit differs on a session freshness. Unlike in OpenSSL, BoringSSL treats sessions as fresh only if the session's time of issuance + timeout points to the future. In the opposite, OpenSSL resumes sessions expiring in the current second. See math in ssl_get_prev_session() vs ssl_session_is_time_valid(). This is an insignificant detail, though. > Given that, and the added code > complexity, I've dropped the patch which avoids locking, at least > for now. > > This might worth revisiting though. Patch provided below for > reference. > I see no reasons not to commit it. Though, it might make sense to let the dust settle down a bit and commit the locking patch later. >> >> diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c >> --- a/src/event/ngx_event_openssl.c >> +++ b/src/event/ngx_event_openssl.c >> @@ -4527,7 +4527,6 @@ ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ >> ngx_slab_pool_t *shpool; >> ngx_ssl_ticket_key_t *key; >> ngx_ssl_session_cache_t *cache; >> - u_char buf[80]; >> >> keys = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_ticket_keys_index); >> if (keys == NULL) { >> @@ -4556,7 +4555,7 @@ ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ >> >> /* initialize the current key */ >> >> - if (RAND_bytes(buf, 80) != 1) { >> + if (RAND_bytes(key->name, 80) != 1) { >> ngx_ssl_error(NGX_LOG_ALERT, log, 0, "RAND_bytes() failed"); >> ngx_shmtx_unlock(&shpool->mutex); >> return NGX_ERROR; >> @@ -4565,11 +4564,6 @@ ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ >> key->shared = 1; >> key->expire = expire; >> key->size = 80; >> - ngx_memcpy(key->name, buf, 16); >> - ngx_memcpy(key->hmac_key, buf + 16, 32); >> - ngx_memcpy(key->aes_key, buf + 48, 32); >> - >> - ngx_explicit_memzero(&buf, 80); >> >> ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, >> "ssl ticket key: \"%*xs\"", >> @@ -4585,7 +4579,7 @@ ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ >> >> key[1] = key[0]; >> >> - if (RAND_bytes(buf, 80) != 1) { >> + if (RAND_bytes(key->name, 80) != 1) { >> ngx_ssl_error(NGX_LOG_ALERT, log, 0, "RAND_bytes() failed"); >> ngx_shmtx_unlock(&shpool->mutex); >> return NGX_ERROR; >> @@ -4594,11 +4588,6 @@ ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ >> key->shared = 1; >> key->expire = expire; >> key->size = 80; >> - ngx_memcpy(key->name, buf, 16); >> - ngx_memcpy(key->hmac_key, buf + 16, 32); >> - ngx_memcpy(key->aes_key, buf + 48, 32); >> - >> - ngx_explicit_memzero(&buf, 80); >> >> ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, >> "ssl ticket key: \"%*xs\"", >> >> Moving RAND_bytes() can be achieved by trying to elide shmem lock >> and store random data elsewhere if condition is met. >> But this comes in conflict with buf[] optimization :). > > You can't really move RAND_bytes() out of the lock unless you are > ok with doing unneeded RAND_bytes() calls in workers which weren't > first to update keys. And see above, you are trying to > micro-optimize something which happens once per 300 seconds or > even less frequently. Agree. Even in advanced cases that don't throw away an unneeded result, moving RAND_bytes() looks like out of practical interest, since this requires a storage to save the result between callbacks for future use, and this apparently affects simplicity and security. > >>> + >>> + /* >>> + * update expiration of the current key: it is going to be needed >>> + * at least till the session being created expires >>> + */ >>> + >>> + if (expire > key[0].expire) { >>> + key[0].expire = expire; >>> + } >>> + >>> + /* sync keys to the worker process memory */ >>> + >>> + ngx_memcpy(keys->elts, cache->ticket_keys, >>> + 2 * sizeof(ngx_ssl_ticket_key_t)); >>> + >>> + ngx_shmtx_unlock(&shpool->mutex); >>> + >>> + return NGX_OK; >>> +} >>> + >>> + >>> static void >>> ngx_ssl_ticket_keys_cleanup(void *data) >>> { >>> diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h >>> --- a/src/event/ngx_event_openssl.h >>> +++ b/src/event/ngx_event_openssl.h >>> @@ -147,25 +147,24 @@ struct ngx_ssl_sess_id_s { >>> >>> >>> typedef struct { >>> + u_char name[16]; >>> + u_char hmac_key[32]; >>> + u_char aes_key[32]; >>> + time_t expire; >>> + unsigned size:8; >>> + unsigned shared:1; >>> +} ngx_ssl_ticket_key_t; >>> + >>> + >>> +typedef struct { >>> ngx_rbtree_t session_rbtree; >>> ngx_rbtree_node_t sentinel; >>> ngx_queue_t expire_queue; >>> + ngx_ssl_ticket_key_t ticket_keys[2]; >> >> Again, with the 1st patch applied, this makes using >> of ticket_keys mutually exclusive to other fields. >> This suggests that it doesn't belong here. > > It look like you are incorrectly assuming that all clients support > session tickets. While this is true in TLSv1.3, it's not for > TLSv1.2 and below. > > Further, the same ssl_session_cache can be used in multiple server > blocks with different ssl_session_ticket settings. See above about memory constrains, but in general I agree. > >>> time_t fail_time; >>> } ngx_ssl_session_cache_t; >>> >>> >>> -#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB >>> - >>> -typedef struct { >>> - size_t size; >>> - u_char name[16]; >>> - u_char hmac_key[32]; >>> - u_char aes_key[32]; >>> -} ngx_ssl_ticket_key_t; >>> - >>> -#endif >>> - >>> - >>> #define NGX_SSL_SSLv2 0x0002 >>> #define NGX_SSL_SSLv3 0x0004 >>> #define NGX_SSL_TLSv1 0x0008 > > Lock avoidance patch below. > > # HG changeset patch > # User Maxim Dounin > # Date 1663356500 -10800 > # Fri Sep 16 22:28:20 2022 +0300 > # Node ID 93cd7641604d85488ee4c6144e51cdeb60caa123 > # Parent 6d94781680d6750268cd697fb72f7b72b73e0df1 > SSL: optimized rotation of session ticket keys. > > Instead of syncing keys with shared memory on each ticket operation, > the code now does this only when the worker is going to change expiration > of the current key, or going to switch to a new key: that is, usually > at most once per second. > > To do so without races, the code maintains 3 keys: current, previous, > and next. If a worker will switch to the next key earlier, other workers > will still be able to decrypt new tickets, since they will be encrypted > with the next key. > > diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c > --- a/src/event/ngx_event_openssl.c > +++ b/src/event/ngx_event_openssl.c > @@ -3773,6 +3773,7 @@ ngx_ssl_session_cache_init(ngx_shm_zone_ > > cache->ticket_keys[0].expire = 0; > cache->ticket_keys[1].expire = 0; > + cache->ticket_keys[2].expire = 0; > > cache->fail_time = 0; > > @@ -4249,7 +4250,7 @@ ngx_ssl_session_ticket_keys(ngx_conf_t * > return NGX_OK; > } > > - keys = ngx_array_create(cf->pool, paths ? paths->nelts : 2, > + keys = ngx_array_create(cf->pool, paths ? paths->nelts : 3, > sizeof(ngx_ssl_ticket_key_t)); > if (keys == NULL) { > return NGX_ERROR; > @@ -4284,9 +4285,13 @@ ngx_ssl_session_ticket_keys(ngx_conf_t * > > /* placeholder for keys in shared memory */ > > - key = ngx_array_push_n(keys, 2); > + key = ngx_array_push_n(keys, 3); > key[0].shared = 1; > + key[0].expire = 0; > key[1].shared = 1; > + key[1].expire = 0; > + key[2].shared = 1; > + key[2].expire = 0; > > return NGX_OK; > } > @@ -4346,6 +4351,7 @@ ngx_ssl_session_ticket_keys(ngx_conf_t * > } > > key->shared = 0; > + key->expire = 1; > > if (size == 48) { > key->size = 48; > @@ -4513,7 +4519,7 @@ ngx_ssl_ticket_key_callback(ngx_ssl_conn > > /* renew if non-default key */ > > - if (i != 0) { > + if (i != 0 && key[i].expire) { > return 2; > } > > @@ -4544,9 +4550,21 @@ ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ > return NGX_OK; > } > > + /* > + * if we don't need to update expiration of the current key > + * and the previous key is still needed, don't sync with shared > + * memory to save some work; in the worst case other worker process > + * will switch to the next key, but this process will still be able > + * to decrypt tickets encrypted with it > + */ > + > now = ngx_time(); > expire = now + SSL_CTX_get_timeout(ssl_ctx); > > + if (key[0].expire >= expire && key[1].expire >= now) { > + return NGX_OK; > + } > + > shm_zone = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_cache_index); > > cache = shm_zone->data; > @@ -4566,28 +4584,38 @@ ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ > return NGX_ERROR; > } > > - key->shared = 1; > - key->expire = expire; > - key->size = 80; > - ngx_memcpy(key->name, buf, 16); > - ngx_memcpy(key->hmac_key, buf + 16, 32); > - ngx_memcpy(key->aes_key, buf + 48, 32); > + key[0].shared = 1; > + key[0].expire = expire; > + key[0].size = 80; > + ngx_memcpy(key[0].name, buf, 16); > + ngx_memcpy(key[0].hmac_key, buf + 16, 32); > + ngx_memcpy(key[0].aes_key, buf + 48, 32); > > ngx_explicit_memzero(&buf, 80); > > ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, > "ssl ticket key: \"%*xs\"", > - (size_t) 16, key->name); > + (size_t) 16, key[0].name); > + > + /* > + * copy the current key to the next key, as initialization of > + * the previous key will replace the current key with the next > + * key > + */ > + > + key[2] = key[0]; > } > > if (key[1].expire < now) { > > /* > * if the previous key is no longer needed (or not initialized), > - * replace it with the current key and generate new current key > + * replace it with the current key, replace the current key with > + * the next key, and generate new next key > */ > > key[1] = key[0]; > + key[0] = key[2]; > > if (RAND_bytes(buf, 80) != 1) { > ngx_ssl_error(NGX_LOG_ALERT, log, 0, "RAND_bytes() failed"); > @@ -4595,18 +4623,18 @@ ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ > return NGX_ERROR; > } > > - key->shared = 1; > - key->expire = expire; > - key->size = 80; > - ngx_memcpy(key->name, buf, 16); > - ngx_memcpy(key->hmac_key, buf + 16, 32); > - ngx_memcpy(key->aes_key, buf + 48, 32); > + key[2].shared = 1; > + key[2].expire = 0; > + key[2].size = 80; > + ngx_memcpy(key[2].name, buf, 16); > + ngx_memcpy(key[2].hmac_key, buf + 16, 32); > + ngx_memcpy(key[2].aes_key, buf + 48, 32); > > ngx_explicit_memzero(&buf, 80); > > ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, > "ssl ticket key: \"%*xs\"", > - (size_t) 16, key->name); > + (size_t) 16, key[2].name); > } > > /* > diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h > --- a/src/event/ngx_event_openssl.h > +++ b/src/event/ngx_event_openssl.h > @@ -160,7 +160,7 @@ typedef struct { > ngx_rbtree_t session_rbtree; > ngx_rbtree_node_t sentinel; > ngx_queue_t expire_queue; > - ngx_ssl_ticket_key_t ticket_keys[2]; > + ngx_ssl_ticket_key_t ticket_keys[3]; > time_t fail_time; > } ngx_ssl_session_cache_t; > Looks good. -- Sergey Kandaurov From serg.brester at sebres.de Mon Sep 26 21:16:05 2022 From: serg.brester at sebres.de (Dipl. Ing. Sergey Brester) Date: Mon, 26 Sep 2022 23:16:05 +0200 Subject: [PATCH] fix weakness by logging of broken header by incorect proxy protocol (IDS/IPS/LOG-analysis) Message-ID: Hi, below is a patch to fix a weakness by logging of broken header by incorrect proxy protocol. If some service (IDS/IPS) analyzing or monitoring log-file, regularly formatted lines may be simply confused with lines written not escaped directly from buffer supplied from foreign source. Not to mention it may open a certain vector allowing "injection" of user input in order to avoid detection of failures or even to simulate malicious traffic from legitimate service. How to reproduce: - enable proxy_protocol for listener and start nginx (here localhost on port 80); - echo 'set s [socket localhost 80]; puts $s "testntestntest"; close $s' | tclsh Error-log before fix: 2022/09/26 19:29:58 [error] 10104#17144: *3 broken header: "test test test " while reading PROXY protocol, client: 127.0.0.1, server: 0.0.0.0:80 Error-log after fix: 2022/09/26 22:48:50 [error] 13868#6132: *1 broken header: "test→→test→→test→→" while reading PROXY protocol, client: 127.0.0.1, server: 0.0.0.0:80 It is not advisable to log such foreign user input unescaped to the formatted log-file: instead of "...ntestn..." the attacker can write correctly formatted line simulating a 401-, 403-failure or rate-limit overran, so IDS could block a innocent service or mistakenly ban legitimate user. The patch proposes simplest escape (LF/CR-char with →, double quote with single quote and additionally every char larger or equal than 0x80 to avoid possible logging of "broken" utf-8 sequences or unsupported surrogates, just as a safest variant for not-valid foreign buffer) in-place in the malicious buffer directly (without mem-alloc, etc). Real life example - https://github.com/fail2ban/fail2ban/issues/3303#issuecomment-1148691902 Regards, Sergey. diff --git "a/src/core/ngx_proxy_protocol.c" "b/src/core/ngx_proxy_protocol.c" --- "a/src/core/ngx_proxy_protocol.c" +++ "b/src/core/ngx_proxy_protocol.c" @@ -139,6 +139,20 @@ skip: invalid: + p = buf; + while (p < last) { + const u_char c = *p; + switch (c) { + case LF: + case CR: + *p = (u_char)'x1a'; + break; + case '"': + *p = (u_char)'''; + break; + default: + if (c >= 0x80) {*p = '?';} + break; + } + p++; + } ngx_log_error(NGX_LOG_ERR, c->log, 0, "broken header: "%*s"", (size_t) (last - buf), buf); -------------- next part -------------- An HTML attachment was scrubbed... URL: From xeioex at nginx.com Tue Sep 27 01:23:09 2022 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 27 Sep 2022 01:23:09 +0000 Subject: [njs] Stream: improved async callback support for s.send(). Message-ID: details: https://hg.nginx.org/njs/rev/7a4f1f8a2cae branches: changeset: 1962:7a4f1f8a2cae user: Dmitry Volyntsev date: Mon Sep 26 17:49:39 2022 -0700 description: Stream: improved async callback support for s.send(). Previously, the "from_upstream" flag (introduced in b33aae5e8dc6) was ignored for s.send() calls invoked directly from a body filter. This makes the s.send() behaviour context dependent. The fix is to always take "from_upstream" flag into account when it is provided. This fixed #552 issue on Github. diffstat: nginx/ngx_js.h | 4 ++++ nginx/ngx_stream_js_module.c | 32 +++++++++++++++++--------------- 2 files changed, 21 insertions(+), 15 deletions(-) diffs (88 lines): diff -r ef0e05668f39 -r 7a4f1f8a2cae nginx/ngx_js.h --- a/nginx/ngx_js.h Thu Sep 22 19:05:36 2022 -0700 +++ b/nginx/ngx_js.h Mon Sep 26 17:49:39 2022 -0700 @@ -22,6 +22,10 @@ #define NGX_JS_BOOLEAN 8 #define NGX_JS_NUMBER 16 +#define NGX_JS_BOOL_FALSE 0 +#define NGX_JS_BOOL_TRUE 1 +#define NGX_JS_BOOL_UNSET 2 + #define ngx_js_buffer_type(btype) ((btype) & ~NGX_JS_DEPRECATED) diff -r ef0e05668f39 -r 7a4f1f8a2cae nginx/ngx_stream_js_module.c --- a/nginx/ngx_stream_js_module.c Thu Sep 22 19:05:36 2022 -0700 +++ b/nginx/ngx_stream_js_module.c Mon Sep 26 17:49:39 2022 -0700 @@ -101,7 +101,7 @@ static njs_int_t ngx_stream_js_ext_on(nj static njs_int_t ngx_stream_js_ext_off(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused); static njs_int_t ngx_stream_js_ext_send(njs_vm_t *vm, njs_value_t *args, - njs_uint_t nargs, njs_index_t unused); + njs_uint_t nargs, njs_index_t from_upstream); static njs_int_t ngx_stream_js_ext_set_return_value(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused); @@ -442,6 +442,7 @@ static njs_external_t ngx_stream_js_ext .enumerable = 1, .u.method = { .native = ngx_stream_js_ext_send, + .magic8 = NGX_JS_BOOL_UNSET, } }, @@ -1286,7 +1287,7 @@ ngx_stream_js_ext_off(njs_vm_t *vm, njs_ static njs_int_t ngx_stream_js_ext_send(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) + njs_index_t from_upstream) { unsigned last_buf, flush; njs_str_t buffer; @@ -1349,6 +1350,17 @@ ngx_stream_js_ext_send(njs_vm_t *vm, njs if (value != NULL) { last_buf = njs_value_bool(value); } + + if (from_upstream == NGX_JS_BOOL_UNSET) { + value = njs_vm_object_prop(vm, flags, &from_key, &lvalue); + if (value != NULL) { + from_upstream = njs_value_bool(value); + } + + if (value == NULL && ctx->buf == NULL) { + goto exception; + } + } } cl = ngx_chain_get_free_buf(c->pool, &ctx->free); @@ -1371,23 +1383,13 @@ ngx_stream_js_ext_send(njs_vm_t *vm, njs b->pos = b->start; b->last = b->end; - if (ctx->buf != NULL) { + if (from_upstream == NGX_JS_BOOL_UNSET) { *ctx->last_out = cl; ctx->last_out = &cl->next; } else { - if (!njs_value_is_object(flags)) { - goto exception; - } - - value = njs_vm_object_prop(vm, flags, &from_key, &lvalue); - if (value == NULL) { - goto exception; - } - - if (ngx_stream_js_next_filter(s, ctx, cl, njs_value_bool(value)) - == NGX_ERROR) - { + + if (ngx_stream_js_next_filter(s, ctx, cl, from_upstream) == NGX_ERROR) { njs_vm_error(vm, "ngx_stream_js_next_filter() failed"); return NJS_ERROR; } From xeioex at nginx.com Tue Sep 27 01:23:11 2022 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 27 Sep 2022 01:23:11 +0000 Subject: [njs] Stream: introduced s.sendUpstream() and s.sendDownstream(). Message-ID: details: https://hg.nginx.org/njs/rev/301cc9eaa3bf branches: changeset: 1963:301cc9eaa3bf user: Dmitry Volyntsev date: Mon Sep 26 17:50:24 2022 -0700 description: Stream: introduced s.sendUpstream() and s.sendDownstream(). diffstat: nginx/ngx_stream_js_module.c | 24 ++++++++++++++++++++++++ 1 files changed, 24 insertions(+), 0 deletions(-) diffs (34 lines): diff -r 7a4f1f8a2cae -r 301cc9eaa3bf nginx/ngx_stream_js_module.c --- a/nginx/ngx_stream_js_module.c Mon Sep 26 17:49:39 2022 -0700 +++ b/nginx/ngx_stream_js_module.c Mon Sep 26 17:50:24 2022 -0700 @@ -448,6 +448,30 @@ static njs_external_t ngx_stream_js_ext { .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("sendDownstream"), + .writable = 1, + .configurable = 1, + .enumerable = 1, + .u.method = { + .native = ngx_stream_js_ext_send, + .magic8 = NGX_JS_BOOL_TRUE, + } + }, + + { + .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("sendUpstream"), + .writable = 1, + .configurable = 1, + .enumerable = 1, + .u.method = { + .native = ngx_stream_js_ext_send, + .magic8 = NGX_JS_BOOL_FALSE, + } + }, + + { + .flags = NJS_EXTERN_METHOD, .name.string = njs_str("setReturnValue"), .writable = 1, .configurable = 1, From arut at nginx.com Tue Sep 27 09:41:25 2022 From: arut at nginx.com (Roman Arutyunyan) Date: Tue, 27 Sep 2022 13:41:25 +0400 Subject: [PATCH] Core: support for reading PROXY protocol v2 TLVs In-Reply-To: References: <4b856f1dff939e4eb9c1.1661961135@arut-laptop> <20220905132318.s27wgtof6wuqde7x@N00W24XTQX> <20220909154658.fpnpndo2opnnzywx@N00W24XTQX> <20220913150304.k2fjjdxgesgzbilu@N00W24XTQX> Message-ID: <20220927094125.w7oo4g2quw3yyqfh@N00W24XTQX> Hi, On Tue, Sep 20, 2022 at 05:46:50AM +0300, Maxim Dounin wrote: > Hello! > > On Tue, Sep 13, 2022 at 07:03:04PM +0400, Roman Arutyunyan wrote: > > > On Tue, Sep 13, 2022 at 12:30:17AM +0300, Maxim Dounin wrote: > > > Hello! > > > > > > On Fri, Sep 09, 2022 at 07:46:58PM +0400, Roman Arutyunyan wrote: > > > > > > > On Mon, Sep 05, 2022 at 06:58:38PM +0300, Maxim Dounin wrote: > > > > > Hello! > > > > > > > > > > On Mon, Sep 05, 2022 at 05:23:18PM +0400, Roman Arutyunyan wrote: > > > > > > > > > > > Hi, > > > > > > > > > > > > On Mon, Sep 05, 2022 at 03:52:49AM +0300, Maxim Dounin wrote: > > > > > > > Hello! > > > > > > > > > > > > > > On Wed, Aug 31, 2022 at 07:52:15PM +0400, Roman Arutyunyan wrote: > > > > > > > > > > > > > > > # HG changeset patch > > > > > > > > # User Roman Arutyunyan > > > > > > > > # Date 1661436099 -14400 > > > > > > > > # Thu Aug 25 18:01:39 2022 +0400 > > > > > > > > # Node ID 4b856f1dff939e4eb9c131e17af061cf2c38cfac > > > > > > > > # Parent 069a4813e8d6d7ec662d282a10f5f7062ebd817f > > > > > > > > Core: support for reading PROXY protocol v2 TLVs. > > > > > > > > > > > > > > First of all, could you please provide details on the use case? > > > > > > > I've seen requests for writing proxy protocol TLVs to upstream > > > > > > > servers (see ticket #1639), but not yet seen any meaningful > > > > > > > reading requests. > > > > > > > > > > > > The known cases are these: > > > > > > > > > > > > - https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-target-groups.html#proxy-protocol > > > > > > - https://docs.microsoft.com/en-us/azure/private-link/private-link-service-overview#getting-connection-information-using-tcp-proxy-v2 > > > > > > - https://cloud.google.com/vpc/docs/configure-private-service-connect-producer#proxy-protocol > > > > > > > > > > > > The data may need further parsing, but it can be done in njs or perl. > > > > > > > > > > Thanks for the details. So, basically, it's about vendor-specific > > > > > endpoint IDs. > > > > > > > > > > > > > The TLV values are available in HTTP and Stream variables > > > > > > > > $proxy_protocol_tlv_0xN, where N is a hexadecimal TLV type number with no > > > > > > > > leading zeroes. > > > > > > > > > > > > > > I can't say I like the "hexadecimal TLV type number with no > > > > > > > leading zeroes" approach, especially given that the specification > > > > > > > uses leading zeroes in TLV types. With leading zeros might be > > > > > > > better, to match specification. > > > > > > > > With on-demand approach this is no longer an issue. > > > > > > > > > > > Also, it might worth the effort to actually add names for known > > > > > > > types instead or in addition to numbers. > > > > > > > > > > > > This is indeed a good idea and we have such plans as a further extenion of this > > > > > > work. One of the problems is however that the abovementioned TLV variables > > > > > > are specified in internal documents of AWS/Azure/GCP which are not standards. > > > > > > They can be changed anytime, while we have to maintain those variables in > > > > > > nginx. Also, raw variables give more flexibility in supporting less known TLVs. > > > > > > > > > > Of course I'm not suggesting to ditch raw variables, at least not > > > > > for unknown/non-standard values. But for known/standard values it > > > > > should be easy enough to provide alternative names for easier use, > > > > > probably with type-specific parsing. > > > > > > > > > > With on-demand parsing it would be trivial to support both > > > > > $proxy_protocol_tlv_alpn and $proxy_protocol_tlv_0x01. Further, > > > > > it will be trivial to support $proxy_protocol_tlv_aws_vpc_id while > > > > > still providing $proxy_protocol_tlv_0xea for raw data. > > > > > > [...] > > > > > > > # HG changeset patch > > > > # User Roman Arutyunyan > > > > # Date 1662718130 -14400 > > > > # Fri Sep 09 14:08:50 2022 +0400 > > > > # Node ID 832f6c96b26c3009640072e3f4b1f0bf43e644d0 > > > > # Parent 80714f1b0f597ce5e530e7274457450db4587fc9 > > > > Standard PROXY protocol v2 TLV variables. > > > > > > > > diff --git a/src/core/ngx_proxy_protocol.h b/src/core/ngx_proxy_protocol.h > > > > --- a/src/core/ngx_proxy_protocol.h > > > > +++ b/src/core/ngx_proxy_protocol.h > > > > @@ -13,7 +13,18 @@ > > > > #include > > > > > > > > > > > > -#define NGX_PROXY_PROTOCOL_MAX_HEADER 107 > > > > +#define NGX_PROXY_PROTOCOL_MAX_HEADER 107 > > > > + > > > > +#define NGX_PROXY_PROTOCOL_TLV_ALPN 0x01 > > > > +#define NGX_PROXY_PROTOCOL_TLV_AUTHORITY 0x02 > > > > +#define NGX_PROXY_PROTOCOL_TLV_UNIQUE_ID 0x05 > > > > +#define NGX_PROXY_PROTOCOL_TLV_SSL 0x20 > > > > +#define NGX_PROXY_PROTOCOL_TLV_SSL_VERSION 0x21 > > > > +#define NGX_PROXY_PROTOCOL_TLV_SSL_CN 0x22 > > > > +#define NGX_PROXY_PROTOCOL_TLV_SSL_CIPHER 0x23 > > > > +#define NGX_PROXY_PROTOCOL_TLV_SSL_SIG_ALG 0x24 > > > > +#define NGX_PROXY_PROTOCOL_TLV_SSL_KEY_ALG 0x25 > > > > +#define NGX_PROXY_PROTOCOL_TLV_NETNS 0x30 > > > > > > > > > > > > struct ngx_proxy_protocol_s { > > > > @@ -25,6 +36,12 @@ struct ngx_proxy_protocol_s { > > > > }; > > > > > > > > > > > > +typedef struct { > > > > + u_char client; > > > > + u_char verify[4]; > > > > > > Note that these two are completely ignored. > > > > Yes, currently they are. I thought about adding variables for them too, > > but then decided to postpone this. Do you think these have enough value > > to be added to this patch? > > The "client" field seems to be mostly useless, since all the flags > can be derived from the TLVs provided. In particular, > PP2_CLIENT_SSL from PP2_SUBTYPE_SSL_VERSION, and > PP2_CLIENT_CERT_SESS from PP2_SUBTYPE_SSL_CN (if provided). The > PP2_CLIENT_CERT_CONN seems to be completely useless, since from > SSL point of view there is no difference if the certificate was > provided in this connection or previously within the same SSL > session. > > I'm not sure if "verify" can be derived from the other data > provided though. Looks like it's not, at least that's my > impression from the specification and the code available[1]. As > far as I understand, all certificate-related variables are > basically useless without checking the verify field it is > explicitly known that all clients with certificate verification > errors were rejected by the proxy. > > [1] https://github.com/haproxy/haproxy/blob/master/src/connection.c#L1951 > > > > > > > +} ngx_proxy_protocol_tlv_ssl_t; > > > > + > > > > + > > > > u_char *ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, > > > > u_char *last); > > > > u_char *ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, > > > > diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c > > > > --- a/src/http/ngx_http_variables.c > > > > +++ b/src/http/ngx_http_variables.c > > > > @@ -63,6 +63,10 @@ static ngx_int_t ngx_http_variable_proxy > > > > ngx_http_variable_value_t *v, uintptr_t data); > > > > static ngx_int_t ngx_http_variable_proxy_protocol_tlv_0x(ngx_http_request_t *r, > > > > ngx_http_variable_value_t *v, uintptr_t data); > > > > +static ngx_int_t ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r, > > > > + ngx_http_variable_value_t *v, uintptr_t data); > > > > +static ngx_int_t ngx_http_variable_proxy_protocol_tlv_ssl( > > > > + ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); > > > > static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r, > > > > ngx_http_variable_value_t *v, uintptr_t data); > > > > static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r, > > > > @@ -220,6 +224,42 @@ static ngx_http_variable_t ngx_http_cor > > > > ngx_http_variable_proxy_protocol_tlv_0x, > > > > 0, NGX_HTTP_VAR_PREFIX, 0 }, > > > > > > > > + { ngx_string("proxy_protocol_tlv_alpn"), NULL, > > > > + ngx_http_variable_proxy_protocol_tlv, > > > > + NGX_PROXY_PROTOCOL_TLV_ALPN, 0, 0 }, > > > > + > > > > + { ngx_string("proxy_protocol_tlv_authority"), NULL, > > > > + ngx_http_variable_proxy_protocol_tlv, > > > > + NGX_PROXY_PROTOCOL_TLV_AUTHORITY, 0, 0 }, > > > > + > > > > + { ngx_string("proxy_protocol_tlv_unique_id"), NULL, > > > > + ngx_http_variable_proxy_protocol_tlv, > > > > + NGX_PROXY_PROTOCOL_TLV_UNIQUE_ID, 0, 0 }, > > > > + > > > > + { ngx_string("proxy_protocol_tlv_ssl_version"), NULL, > > > > + ngx_http_variable_proxy_protocol_tlv_ssl, > > > > + NGX_PROXY_PROTOCOL_TLV_SSL_VERSION, 0, 0 }, > > > > + > > > > + { ngx_string("proxy_protocol_tlv_ssl_cn"), NULL, > > > > + ngx_http_variable_proxy_protocol_tlv_ssl, > > > > + NGX_PROXY_PROTOCOL_TLV_SSL_CN, 0, 0 }, > > > > + > > > > + { ngx_string("proxy_protocol_tlv_ssl_cipher"), NULL, > > > > + ngx_http_variable_proxy_protocol_tlv_ssl, > > > > + NGX_PROXY_PROTOCOL_TLV_SSL_CIPHER, 0, 0 }, > > > > + > > > > + { ngx_string("proxy_protocol_tlv_ssl_sig_alg"), NULL, > > > > + ngx_http_variable_proxy_protocol_tlv_ssl, > > > > + NGX_PROXY_PROTOCOL_TLV_SSL_SIG_ALG, 0, 0 }, > > > > + > > > > + { ngx_string("proxy_protocol_tlv_ssl_key_alg"), NULL, > > > > + ngx_http_variable_proxy_protocol_tlv_ssl, > > > > + NGX_PROXY_PROTOCOL_TLV_SSL_KEY_ALG, 0, 0 }, > > > > + > > > > + { ngx_string("proxy_protocol_tlv_netns"), NULL, > > > > + ngx_http_variable_proxy_protocol_tlv, > > > > + NGX_PROXY_PROTOCOL_TLV_NETNS, 0, 0 }, > > > > + > > > > > > Further, these provide no interface to support arbitrary SSL TLVs > > > (that is, something like "$proxy_protocol_tlv_ssl_0x10"). > > > > > > Overall, I tend to think it would be much easier / less intrusive > > > to keep all the TLV handling logic in the > > > src/core/ngx_proxy_protocol.c, including both arbitrary TLVs with > > > hexadecimal type and known named TLVs, and only provide a single > > > prefix variable in stream / http modules. > > > > > > That is, use "$proxy_protocol_tlv_" prefix variable and call a > > > function to obtain TLV with the rest of the variable name. > > > > > > This might imply slightly more complex/less efficient name parsing > > > logic for known TLVs, but I don't think it will be noticeable. On > > > the other hand, it will reduce clutter in the variables hash, and > > > therefore will save some resources in configurations where these > > > variables are not used. > > > > OK. Stream/HTTP part now looks simpler. > > > > -- > > Roman Arutyunyan > > > # HG changeset patch > > # User Roman Arutyunyan > > # Date 1663080928 -14400 > > # Tue Sep 13 18:55:28 2022 +0400 > > # Node ID 0736b29bfafac445fb8cf57312c3b7a2c3247a60 > > # Parent ba5cf8f73a2d0a3615565bf9545f3d65216a0530 > > PROXY protocol v2 TLV variables. > > > > The variables have prefix $proxy_protocol_tlv_ and are accessible by name > > and by type. Examples are: $proxy_protocol_tlv_0x01, $proxy_protocol_tlv_alpn. > > > > diff --git a/src/core/ngx_proxy_protocol.c b/src/core/ngx_proxy_protocol.c > > --- a/src/core/ngx_proxy_protocol.c > > +++ b/src/core/ngx_proxy_protocol.c > > @@ -40,6 +40,24 @@ typedef struct { > > } ngx_proxy_protocol_inet6_addrs_t; > > > > > > +typedef struct { > > + u_char type; > > + u_char len[2]; > > +} ngx_proxy_protocol_tlv_t; > > + > > + > > +typedef struct { > > + u_char client; > > + u_char verify[4]; > > +} ngx_proxy_protocol_tlv_ssl_t; > > + > > + > > +typedef struct { > > + ngx_str_t name; > > + ngx_uint_t type; > > +} ngx_proxy_protocol_tlv_entry_t; > > + > > + > > static u_char *ngx_proxy_protocol_read_addr(ngx_connection_t *c, u_char *p, > > u_char *last, ngx_str_t *addr); > > static u_char *ngx_proxy_protocol_read_port(u_char *p, u_char *last, > > @@ -48,6 +66,26 @@ static u_char *ngx_proxy_protocol_v2_rea > > u_char *last); > > > > > > +static ngx_proxy_protocol_tlv_entry_t ngx_proxy_protocol_tlv_entries[] = { > > + { ngx_string("alpn"), 0x01 }, > > + { ngx_string("authority"), 0x02 }, > > + { ngx_string("unique_id"), 0x05 }, > > + { ngx_string("ssl"), 0x20 }, > > + { ngx_string("netns"), 0x30 }, > > + { ngx_null_string, 0x00 } > > +}; > > + > > + > > +static ngx_proxy_protocol_tlv_entry_t ngx_proxy_protocol_tlv_ssl_entries[] = { > > + { ngx_string("version"), 0x21 }, > > + { ngx_string("cn"), 0x22 }, > > + { ngx_string("cipher"), 0x23 }, > > + { ngx_string("sig_alg"), 0x24 }, > > + { ngx_string("key_alg"), 0x25 }, > > + { ngx_null_string, 0x00 } > > +}; > > + > > + > > u_char * > > ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, u_char *last) > > { > > @@ -412,11 +450,128 @@ ngx_proxy_protocol_v2_read(ngx_connectio > > &pp->src_addr, pp->src_port, &pp->dst_addr, pp->dst_port); > > > > if (buf < end) { > > - ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, > > - "PROXY protocol v2 %z bytes of tlv ignored", end - buf); > > + pp->tlvs.data = ngx_pnalloc(c->pool, end - buf); > > + if (pp->tlvs.data == NULL) { > > + return NULL; > > + } > > + > > + ngx_memcpy(pp->tlvs.data, buf, end - buf); > > + pp->tlvs.len = end - buf; > > } > > > > c->proxy_protocol = pp; > > > > return end; > > } > > + > > + > > +ngx_int_t > > +ngx_proxy_protocol_lookup_tlv(ngx_connection_t *c, ngx_str_t *tlvs, > > + ngx_uint_t type, ngx_str_t *value) > > +{ > > + u_char *p; > > + size_t n, len; > > + ngx_proxy_protocol_tlv_t *tlv; > > + > > + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, > > + "PROXY protocol v2 lookup tlv:%02xi", type); > > + > > + p = tlvs->data; > > + n = tlvs->len; > > + > > + while (n) { > > + if (n < sizeof(ngx_proxy_protocol_tlv_t)) { > > + ngx_log_error(NGX_LOG_ERR, c->log, 0, "broken PROXY protocol TLV"); > > + return NGX_ERROR; > > + } > > + > > + tlv = (ngx_proxy_protocol_tlv_t *) p; > > + len = ngx_proxy_protocol_parse_uint16(tlv->len); > > + > > + p += sizeof(ngx_proxy_protocol_tlv_t); > > + n -= sizeof(ngx_proxy_protocol_tlv_t); > > + > > + if (n < len) { > > + ngx_log_error(NGX_LOG_ERR, c->log, 0, "broken PROXY protocol TLV"); > > + return NGX_ERROR; > > + } > > + > > + ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, > > + "PROXY protocol v2 tlv:0x%02xd len:%uz", tlv->type, len); > > + > > + if (tlv->type == type) { > > + value->data = p; > > + value->len = len; > > + return NGX_OK; > > + } > > + > > + p += len; > > + n -= len; > > + } > > + > > + return NGX_DECLINED; > > +} > > + > > + > > +ngx_int_t > > +ngx_proxy_protocol_get_tlv(ngx_connection_t *c, ngx_str_t *name, > > + ngx_str_t *value) > > +{ > > + u_char *p; > > + size_t n; > > + ngx_str_t ssl, *tlvs; > > + ngx_int_t rc, type; > > + ngx_proxy_protocol_tlv_entry_t *te; > > + > > + if (c->proxy_protocol == NULL) { > > + return NGX_DECLINED; > > + } > > + > > + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, > > + "PROXY protocol v2 get tlv \"%V\"", name); > > + > > + te = ngx_proxy_protocol_tlv_entries; > > + tlvs = &c->proxy_protocol->tlvs; > > + > > + p = name->data; > > + n = name->len; > > + > > + if (n >= 4 && p[0] == 's' && p[1] == 's' && p[2] == 'l' && p[3] == '_') { > > + > > + rc = ngx_proxy_protocol_lookup_tlv(c, tlvs, 0x20, &ssl); > > + if (rc != NGX_OK) { > > + return rc; > > + } > > + > > + if (ssl.len < sizeof(ngx_proxy_protocol_tlv_ssl_t)) { > > + return NGX_ERROR; > > + } > > + > > + ssl.data += sizeof(ngx_proxy_protocol_tlv_ssl_t); > > + ssl.len -= sizeof(ngx_proxy_protocol_tlv_ssl_t); > > + > > + te = ngx_proxy_protocol_tlv_ssl_entries; > > + tlvs = &ssl; > > + > > + p += 4; > > + n -= 4; > > + } > > + > > + if (n >= 2 && p[0] == '0' && p[1] == 'x') { > > + > > + type = ngx_hextoi(p + 2, n - 2); > > + if (type == NGX_ERROR) { > > + return NGX_ERROR; > > + } > > + > > + return ngx_proxy_protocol_lookup_tlv(c, tlvs, type, value); > > + } > > + > > + for ( /* void */ ; te->type; te++) { > > + if (te->name.len == n && ngx_strncmp(te->name.data, p, n) == 0) { > > + return ngx_proxy_protocol_lookup_tlv(c, tlvs, te->type, value); > > + } > > + } > > + > > + return NGX_DECLINED; > > +} > > diff --git a/src/core/ngx_proxy_protocol.h b/src/core/ngx_proxy_protocol.h > > --- a/src/core/ngx_proxy_protocol.h > > +++ b/src/core/ngx_proxy_protocol.h > > @@ -21,6 +21,7 @@ struct ngx_proxy_protocol_s { > > ngx_str_t dst_addr; > > in_port_t src_port; > > in_port_t dst_port; > > + ngx_str_t tlvs; > > }; > > > > > > @@ -28,6 +29,10 @@ u_char *ngx_proxy_protocol_read(ngx_conn > > u_char *last); > > u_char *ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, > > u_char *last); > > +ngx_int_t ngx_proxy_protocol_lookup_tlv(ngx_connection_t *c, ngx_str_t *tlvs, > > + ngx_uint_t type, ngx_str_t *value); > > +ngx_int_t ngx_proxy_protocol_get_tlv(ngx_connection_t *c, ngx_str_t *name, > > + ngx_str_t *value); > > > > > > #endif /* _NGX_PROXY_PROTOCOL_H_INCLUDED_ */ > > diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c > > --- a/src/http/ngx_http_variables.c > > +++ b/src/http/ngx_http_variables.c > > @@ -61,6 +61,8 @@ static ngx_int_t ngx_http_variable_proxy > > ngx_http_variable_value_t *v, uintptr_t data); > > static ngx_int_t ngx_http_variable_proxy_protocol_port(ngx_http_request_t *r, > > ngx_http_variable_value_t *v, uintptr_t data); > > +static ngx_int_t ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r, > > + ngx_http_variable_value_t *v, uintptr_t data); > > static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r, > > ngx_http_variable_value_t *v, uintptr_t data); > > static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r, > > @@ -214,6 +216,10 @@ static ngx_http_variable_t ngx_http_cor > > ngx_http_variable_proxy_protocol_port, > > offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 }, > > > > + { ngx_string("proxy_protocol_tlv_"), NULL, > > + ngx_http_variable_proxy_protocol_tlv, > > + 0, NGX_HTTP_VAR_PREFIX, 0 }, > > + > > { ngx_string("server_addr"), NULL, ngx_http_variable_server_addr, 0, 0, 0 }, > > > > { ngx_string("server_port"), NULL, ngx_http_variable_server_port, 0, 0, 0 }, > > @@ -1387,6 +1393,39 @@ ngx_http_variable_proxy_protocol_port(ng > > > > > > static ngx_int_t > > +ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r, > > + ngx_http_variable_value_t *v, uintptr_t data) > > +{ > > + ngx_str_t *name = (ngx_str_t *) data; > > + > > + ngx_int_t rc; > > + ngx_str_t tlv, value; > > + > > + tlv.len = name->len - (sizeof("proxy_protocol_tlv_") - 1); > > + tlv.data = name->data + sizeof("proxy_protocol_tlv_") - 1; > > + > > + rc = ngx_proxy_protocol_get_tlv(r->connection, &tlv, &value); > > + > > + if (rc == NGX_ERROR) { > > + return NGX_ERROR; > > + } > > + > > + if (rc == NGX_DECLINED) { > > + v->not_found = 1; > > + return NGX_OK; > > + } > > + > > + v->len = value.len; > > + v->valid = 1; > > + v->no_cacheable = 0; > > + v->not_found = 0; > > + v->data = value.data; > > + > > + return NGX_OK; > > +} > > + > > + > > +static ngx_int_t > > ngx_http_variable_server_addr(ngx_http_request_t *r, > > ngx_http_variable_value_t *v, uintptr_t data) > > { > > diff --git a/src/stream/ngx_stream_variables.c b/src/stream/ngx_stream_variables.c > > --- a/src/stream/ngx_stream_variables.c > > +++ b/src/stream/ngx_stream_variables.c > > @@ -23,6 +23,8 @@ static ngx_int_t ngx_stream_variable_pro > > ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); > > static ngx_int_t ngx_stream_variable_proxy_protocol_port( > > ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); > > +static ngx_int_t ngx_stream_variable_proxy_protocol_tlv( > > + ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); > > static ngx_int_t ngx_stream_variable_server_addr(ngx_stream_session_t *s, > > ngx_stream_variable_value_t *v, uintptr_t data); > > static ngx_int_t ngx_stream_variable_server_port(ngx_stream_session_t *s, > > @@ -79,6 +81,10 @@ static ngx_stream_variable_t ngx_stream > > ngx_stream_variable_proxy_protocol_port, > > offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 }, > > > > + { ngx_string("proxy_protocol_tlv_"), NULL, > > + ngx_stream_variable_proxy_protocol_tlv, > > + 0, NGX_STREAM_VAR_PREFIX, 0 }, > > + > > { ngx_string("server_addr"), NULL, > > ngx_stream_variable_server_addr, 0, 0, 0 }, > > > > @@ -622,6 +628,39 @@ ngx_stream_variable_proxy_protocol_port( > > > > > > static ngx_int_t > > +ngx_stream_variable_proxy_protocol_tlv(ngx_stream_session_t *s, > > + ngx_stream_variable_value_t *v, uintptr_t data) > > +{ > > + ngx_str_t *name = (ngx_str_t *) data; > > + > > + ngx_int_t rc; > > + ngx_str_t tlv, value; > > + > > + tlv.len = name->len - (sizeof("proxy_protocol_tlv_") - 1); > > + tlv.data = name->data + sizeof("proxy_protocol_tlv_") - 1; > > + > > + rc = ngx_proxy_protocol_get_tlv(s->connection, &tlv, &value); > > + > > + if (rc == NGX_ERROR) { > > + return NGX_ERROR; > > + } > > + > > + if (rc == NGX_DECLINED) { > > + v->not_found = 1; > > + return NGX_OK; > > + } > > + > > + v->len = value.len; > > + v->valid = 1; > > + v->no_cacheable = 0; > > + v->not_found = 0; > > + v->data = value.data; > > + > > + return NGX_OK; > > +} > > + > > + > > +static ngx_int_t > > ngx_stream_variable_server_addr(ngx_stream_session_t *s, > > ngx_stream_variable_value_t *v, uintptr_t data) > > { > > Looks good, but see above: probably we should add at least the > $proxy_protocol_tlv_ssl_verify variable (and probably with special > handling to convert binary data into a string). Added $proxy_protocol_tlv_ssl_verify. Also, added patches for macro refactoring and tests. -- Roman Arutyunyan -------------- next part -------------- # HG changeset patch # User Roman Arutyunyan # Date 1664263604 -14400 # Tue Sep 27 11:26:44 2022 +0400 # Node ID 38940ff7246574aa19a19c76b072073c34f191be # Parent ba5cf8f73a2d0a3615565bf9545f3d65216a0530 PROXY protocol v2 TLV variables. The variables have prefix $proxy_protocol_tlv_ and are accessible by name and by type. Examples are: $proxy_protocol_tlv_0x01, $proxy_protocol_tlv_alpn. diff --git a/src/core/ngx_proxy_protocol.c b/src/core/ngx_proxy_protocol.c --- a/src/core/ngx_proxy_protocol.c +++ b/src/core/ngx_proxy_protocol.c @@ -15,6 +15,12 @@ #define ngx_proxy_protocol_parse_uint16(p) ((p)[0] << 8 | (p)[1]) +#define ngx_proxy_protocol_parse_uint32(p) \ + ( ((uint32_t) (p)[0] << 24) \ + + ( (p)[1] << 16) \ + + ( (p)[2] << 8) \ + + ( (p)[3]) ) + typedef struct { u_char signature[12]; @@ -40,6 +46,24 @@ typedef struct { } ngx_proxy_protocol_inet6_addrs_t; +typedef struct { + u_char type; + u_char len[2]; +} ngx_proxy_protocol_tlv_t; + + +typedef struct { + u_char client; + u_char verify[4]; +} ngx_proxy_protocol_tlv_ssl_t; + + +typedef struct { + ngx_str_t name; + ngx_uint_t type; +} ngx_proxy_protocol_tlv_entry_t; + + static u_char *ngx_proxy_protocol_read_addr(ngx_connection_t *c, u_char *p, u_char *last, ngx_str_t *addr); static u_char *ngx_proxy_protocol_read_port(u_char *p, u_char *last, @@ -48,6 +72,26 @@ static u_char *ngx_proxy_protocol_v2_rea u_char *last); +static ngx_proxy_protocol_tlv_entry_t ngx_proxy_protocol_tlv_entries[] = { + { ngx_string("alpn"), 0x01 }, + { ngx_string("authority"), 0x02 }, + { ngx_string("unique_id"), 0x05 }, + { ngx_string("ssl"), 0x20 }, + { ngx_string("netns"), 0x30 }, + { ngx_null_string, 0x00 } +}; + + +static ngx_proxy_protocol_tlv_entry_t ngx_proxy_protocol_tlv_ssl_entries[] = { + { ngx_string("version"), 0x21 }, + { ngx_string("cn"), 0x22 }, + { ngx_string("cipher"), 0x23 }, + { ngx_string("sig_alg"), 0x24 }, + { ngx_string("key_alg"), 0x25 }, + { ngx_null_string, 0x00 } +}; + + u_char * ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, u_char *last) { @@ -412,11 +456,145 @@ ngx_proxy_protocol_v2_read(ngx_connectio &pp->src_addr, pp->src_port, &pp->dst_addr, pp->dst_port); if (buf < end) { - ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, - "PROXY protocol v2 %z bytes of tlv ignored", end - buf); + pp->tlvs.data = ngx_pnalloc(c->pool, end - buf); + if (pp->tlvs.data == NULL) { + return NULL; + } + + ngx_memcpy(pp->tlvs.data, buf, end - buf); + pp->tlvs.len = end - buf; } c->proxy_protocol = pp; return end; } + + +ngx_int_t +ngx_proxy_protocol_lookup_tlv(ngx_connection_t *c, ngx_str_t *tlvs, + ngx_uint_t type, ngx_str_t *value) +{ + u_char *p; + size_t n, len; + ngx_proxy_protocol_tlv_t *tlv; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 lookup tlv:%02xi", type); + + p = tlvs->data; + n = tlvs->len; + + while (n) { + if (n < sizeof(ngx_proxy_protocol_tlv_t)) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, "broken PROXY protocol TLV"); + return NGX_ERROR; + } + + tlv = (ngx_proxy_protocol_tlv_t *) p; + len = ngx_proxy_protocol_parse_uint16(tlv->len); + + p += sizeof(ngx_proxy_protocol_tlv_t); + n -= sizeof(ngx_proxy_protocol_tlv_t); + + if (n < len) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, "broken PROXY protocol TLV"); + return NGX_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 tlv:0x%02xd len:%uz", tlv->type, len); + + if (tlv->type == type) { + value->data = p; + value->len = len; + return NGX_OK; + } + + p += len; + n -= len; + } + + return NGX_DECLINED; +} + + +ngx_int_t +ngx_proxy_protocol_get_tlv(ngx_connection_t *c, ngx_str_t *name, + ngx_str_t *value) +{ + u_char *p; + size_t n; + uint32_t verify; + ngx_str_t ssl, *tlvs; + ngx_int_t rc, type; + ngx_proxy_protocol_tlv_ssl_t *tlv_ssl; + ngx_proxy_protocol_tlv_entry_t *te; + + if (c->proxy_protocol == NULL) { + return NGX_DECLINED; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 get tlv \"%V\"", name); + + te = ngx_proxy_protocol_tlv_entries; + tlvs = &c->proxy_protocol->tlvs; + + p = name->data; + n = name->len; + + if (n >= 4 && p[0] == 's' && p[1] == 's' && p[2] == 'l' && p[3] == '_') { + + rc = ngx_proxy_protocol_lookup_tlv(c, tlvs, 0x20, &ssl); + if (rc != NGX_OK) { + return rc; + } + + if (ssl.len < sizeof(ngx_proxy_protocol_tlv_ssl_t)) { + return NGX_ERROR; + } + + p += 4; + n -= 4; + + if (n == 6 && ngx_strncmp(p, "verify", 6) == 0) { + + tlv_ssl = (ngx_proxy_protocol_tlv_ssl_t *) ssl.data; + verify = ngx_proxy_protocol_parse_uint32(tlv_ssl->verify); + + value->data = ngx_pnalloc(c->pool, NGX_INT32_LEN); + if (value->data == NULL) { + return NGX_ERROR; + } + + value->len = ngx_sprintf(value->data, "%uD", verify) + - value->data; + return NGX_OK; + } + + ssl.data += sizeof(ngx_proxy_protocol_tlv_ssl_t); + ssl.len -= sizeof(ngx_proxy_protocol_tlv_ssl_t); + + te = ngx_proxy_protocol_tlv_ssl_entries; + tlvs = &ssl; + } + + if (n >= 2 && p[0] == '0' && p[1] == 'x') { + + type = ngx_hextoi(p + 2, n - 2); + if (type == NGX_ERROR) { + return NGX_ERROR; + } + + return ngx_proxy_protocol_lookup_tlv(c, tlvs, type, value); + } + + for ( /* void */ ; te->type; te++) { + if (te->name.len == n && ngx_strncmp(te->name.data, p, n) == 0) { + return ngx_proxy_protocol_lookup_tlv(c, tlvs, te->type, value); + } + } + + return NGX_DECLINED; +} diff --git a/src/core/ngx_proxy_protocol.h b/src/core/ngx_proxy_protocol.h --- a/src/core/ngx_proxy_protocol.h +++ b/src/core/ngx_proxy_protocol.h @@ -21,6 +21,7 @@ struct ngx_proxy_protocol_s { ngx_str_t dst_addr; in_port_t src_port; in_port_t dst_port; + ngx_str_t tlvs; }; @@ -28,6 +29,10 @@ u_char *ngx_proxy_protocol_read(ngx_conn u_char *last); u_char *ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, u_char *last); +ngx_int_t ngx_proxy_protocol_lookup_tlv(ngx_connection_t *c, ngx_str_t *tlvs, + ngx_uint_t type, ngx_str_t *value); +ngx_int_t ngx_proxy_protocol_get_tlv(ngx_connection_t *c, ngx_str_t *name, + ngx_str_t *value); #endif /* _NGX_PROXY_PROTOCOL_H_INCLUDED_ */ diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -61,6 +61,8 @@ static ngx_int_t ngx_http_variable_proxy ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_proxy_protocol_port(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r, @@ -214,6 +216,10 @@ static ngx_http_variable_t ngx_http_cor ngx_http_variable_proxy_protocol_port, offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 }, + { ngx_string("proxy_protocol_tlv_"), NULL, + ngx_http_variable_proxy_protocol_tlv, + 0, NGX_HTTP_VAR_PREFIX, 0 }, + { ngx_string("server_addr"), NULL, ngx_http_variable_server_addr, 0, 0, 0 }, { ngx_string("server_port"), NULL, ngx_http_variable_server_port, 0, 0, 0 }, @@ -1387,6 +1393,39 @@ ngx_http_variable_proxy_protocol_port(ng static ngx_int_t +ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_str_t *name = (ngx_str_t *) data; + + ngx_int_t rc; + ngx_str_t tlv, value; + + tlv.len = name->len - (sizeof("proxy_protocol_tlv_") - 1); + tlv.data = name->data + sizeof("proxy_protocol_tlv_") - 1; + + rc = ngx_proxy_protocol_get_tlv(r->connection, &tlv, &value); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_DECLINED) { + v->not_found = 1; + return NGX_OK; + } + + v->len = value.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = value.data; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { diff --git a/src/stream/ngx_stream_variables.c b/src/stream/ngx_stream_variables.c --- a/src/stream/ngx_stream_variables.c +++ b/src/stream/ngx_stream_variables.c @@ -23,6 +23,8 @@ static ngx_int_t ngx_stream_variable_pro ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_variable_proxy_protocol_port( ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_proxy_protocol_tlv( + ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_variable_server_addr(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_variable_server_port(ngx_stream_session_t *s, @@ -79,6 +81,10 @@ static ngx_stream_variable_t ngx_stream ngx_stream_variable_proxy_protocol_port, offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 }, + { ngx_string("proxy_protocol_tlv_"), NULL, + ngx_stream_variable_proxy_protocol_tlv, + 0, NGX_STREAM_VAR_PREFIX, 0 }, + { ngx_string("server_addr"), NULL, ngx_stream_variable_server_addr, 0, 0, 0 }, @@ -622,6 +628,39 @@ ngx_stream_variable_proxy_protocol_port( static ngx_int_t +ngx_stream_variable_proxy_protocol_tlv(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_str_t *name = (ngx_str_t *) data; + + ngx_int_t rc; + ngx_str_t tlv, value; + + tlv.len = name->len - (sizeof("proxy_protocol_tlv_") - 1); + tlv.data = name->data + sizeof("proxy_protocol_tlv_") - 1; + + rc = ngx_proxy_protocol_get_tlv(s->connection, &tlv, &value); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_DECLINED) { + v->not_found = 1; + return NGX_OK; + } + + v->len = value.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = value.data; + + return NGX_OK; +} + + +static ngx_int_t ngx_stream_variable_server_addr(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data) { -------------- next part -------------- # HG changeset patch # User Roman Arutyunyan # Date 1664263876 -14400 # Tue Sep 27 11:31:16 2022 +0400 # Node ID 615268a957ab930dc4be49fe5f6f88cd7e377f12 # Parent 38940ff7246574aa19a19c76b072073c34f191be Added type cast to ngx_proxy_protocol_parse_uint16(). The cast is added to make ngx_proxy_protocol_parse_uint16() similar to ngx_proxy_protocol_parse_uint32(). diff --git a/src/core/ngx_proxy_protocol.c b/src/core/ngx_proxy_protocol.c --- a/src/core/ngx_proxy_protocol.c +++ b/src/core/ngx_proxy_protocol.c @@ -13,7 +13,9 @@ #define NGX_PROXY_PROTOCOL_AF_INET6 2 -#define ngx_proxy_protocol_parse_uint16(p) ((p)[0] << 8 | (p)[1]) +#define ngx_proxy_protocol_parse_uint16(p) \ + ( ((uint16_t) (p)[0] << 8) \ + + ( (p)[1]) ) #define ngx_proxy_protocol_parse_uint32(p) \ ( ((uint32_t) (p)[0] << 24) \ -------------- next part -------------- # HG changeset patch # User Roman Arutyunyan # Date 1664200613 -14400 # Mon Sep 26 17:56:53 2022 +0400 # Node ID ff87ed4999b49433a9abecaaf2e574cbfa502961 # Parent 95ba1e704b7b29c39447135e18ed003ecd305924 Tests: client PROXY protocol v2 TLV variables. diff --git a/proxy_protocol2.t b/proxy_protocol2.t --- a/proxy_protocol2.t +++ b/proxy_protocol2.t @@ -24,7 +24,7 @@ select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http access realip/); -$t->write_file_expand('nginx.conf', <<'EOF')->plan(23); +$t->write_file_expand('nginx.conf', <<'EOF')->plan(26); %%TEST_GLOBALS%% @@ -45,6 +45,8 @@ http { set_real_ip_from 127.0.0.1/32; add_header X-IP $remote_addr!$remote_port; add_header X-PP $proxy_protocol_addr!$proxy_protocol_port; + add_header X-TL $proxy_protocol_tlv_0x3-$proxy_protocol_tlv_0x0000ae-$proxy_protocol_tlv_0x0f; + add_header X-NT $proxy_protocol_tlv_unique_id-$proxy_protocol_tlv_ssl_cn-$proxy_protocol_tlv_ssl_0x22-$proxy_protocol_tlv_ssl_verify; location /pp { real_ip_header proxy_protocol; @@ -76,7 +78,11 @@ my $p = pack("N3C", 0x0D0A0D0A, 0x000D0A my $tcp4 = $p . pack("CnN2n2", 0x11, 12, 0xc0000201, 0xc0000202, 123, 5678); my $tcp6 = $p . pack("CnNx8NNx8Nn2", 0x21, 36, 0x20010db8, 0x00000001, 0x20010db8, 0x00000002, 123, 5678); -my $tlv = $p . pack("CnN2n2x9", 0x11, 21, 0xc0000201, 0xc0000202, 123, 5678); +my $tlv = $p . pack("CnN2n2N3", 0x11, 24, 0xc0000201, 0xc0000202, 123, 5678, + 0x03000141, 0xAE000531, 0x32333435); +my $tlv2 = $p . pack("CnN2n2N7", 0x11, 40, 0xc0000201, 0xc0000202, 123, 5678, + 0x05000555, 0x4E495151, + 0x20001100, 0xdeadbeef, 0x22000966, 0x6f6f2e62, 0x61727272); my $unk1 = $p . pack("Cxx", 0x01); my $unk2 = $p . pack("CnC4", 0x41, 4, 1, 2, 3, 4); my $r; @@ -97,6 +103,11 @@ unlike($r, qr/X-IP: (2001:DB8::1|[^!]+!1 like($r, qr/SEE-THIS/, 'tlv request'); like($r, qr/X-PP: 192.0.2.1!123\x0d/, 'tlv proxy'); unlike($r, qr/X-IP: (192.0.2.1|[^!]+!123\x0d)/, 'tlv client'); +like($r, qr/X-TL: A-12345-\x0d/, 'tlv raw variables'); +like($r, qr/X-NT: ---\x0d/, 'tlv missing variables'); + +$r = pp_get('/t1', $tlv2); +like($r, qr/X-NT: UNIQQ-foo.barrr-foo.barrr-3735928559\x0d/, 'tlv named variables'); $r = pp_get('/t1', $unk1); like($r, qr/SEE-THIS/, 'unknown request 1'); From thresh at nginx.com Tue Sep 27 12:14:58 2022 From: thresh at nginx.com (=?iso-8859-1?q?Konstantin_Pavlov?=) Date: Tue, 27 Sep 2022 16:14:58 +0400 Subject: [PATCH] Linux packages: reword to mention supported RHEL derivatives Message-ID: <9d1eec5b03a4ff9d863a.1664280898@QGCD7XG9R9> # HG changeset patch # User Konstantin Pavlov # Date 1664280815 -14400 # Tue Sep 27 16:13:35 2022 +0400 # Node ID 9d1eec5b03a4ff9d863ad49b47721d713dcec76f # Parent 8878680962d05f778f187efcfb163a76c1dfacb7 Linux packages: reword to mention supported RHEL derivatives. diff -r 8878680962d0 -r 9d1eec5b03a4 xml/en/linux_packages.xml --- a/xml/en/linux_packages.xml Fri Sep 23 18:30:33 2022 -0700 +++ b/xml/en/linux_packages.xml Tue Sep 27 16:13:35 2022 +0400 @@ -7,7 +7,7 @@
+ rev="79">
@@ -17,7 +17,7 @@ versions: -RHEL/CentOS +RHEL and derivatives @@ -175,7 +175,12 @@ set up the nginx packages repository. Afterward, you can install and update nginx from the repository. -
+
+ + +This section applies to Red Hat Enterprise Linux and its derivatives such as +CentOS, Oracle Linux, Rocky Linux, AlmaLinux. + Install the prerequisites: @@ -578,7 +583,8 @@ mainline version, while stable- sources for stable releases. To build binary packages, run make in debian/ directory on Debian/Ubuntu, or in -rpm/SPECS/ on RHEL/CentOS/SLES/Amazon Linux, or in +rpm/SPECS/ on +RHEL/CentOS/Oracle Linux/Rocky Linux/AlmaLinux/SLES/Amazon Linux, or in alpine/ on Alpine. diff -r 8878680962d0 -r 9d1eec5b03a4 xml/ru/linux_packages.xml --- a/xml/ru/linux_packages.xml Fri Sep 23 18:30:33 2022 -0700 +++ b/xml/ru/linux_packages.xml Tue Sep 27 16:13:35 2022 +0400 @@ -7,7 +7,7 @@
+ rev="79">
@@ -17,7 +17,7 @@ -RHEL/CentOS +RHEL и производные
@@ -175,7 +175,12 @@ После этого можно будет установить и обновлять nginx из этого репозитория. -
+
+ + +Эта секция применима к Red Hat Enterprise Linux и его производным, таким как +CentOS, Oracle Linux, Rocky Linux, AlmaLinux. + Установите пакеты, необходимые для подключения yum-репозитория: @@ -575,7 +580,8 @@ mainline-версии, в то время как ветки stable-* содержат исходные коды пакетов для стабильных релизов. Для сборки бинарных пакетов запустите make в каталоге debian/ для Debian/Ubuntu, или в каталоге -rpm/SPECS/ для RHEL/CentOS/SLES/Amazon Linux, или в каталоге +rpm/SPECS/ для +RHEL/CentOS/Oracle Linux/Rocky Linux/AlmaLinux/SLES/Amazon Linux, или в каталоге alpine/ для Alpine. From pluknet at nginx.com Tue Sep 27 13:48:26 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Tue, 27 Sep 2022 17:48:26 +0400 Subject: [PATCH] Linux packages: reword to mention supported RHEL derivatives In-Reply-To: <9d1eec5b03a4ff9d863a.1664280898@QGCD7XG9R9> References: <9d1eec5b03a4ff9d863a.1664280898@QGCD7XG9R9> Message-ID: <35CE2BCF-C9EA-4824-8BB4-5D5802432322@nginx.com> > On 27 Sep 2022, at 16:14, Konstantin Pavlov wrote: > > # HG changeset patch > # User Konstantin Pavlov > # Date 1664280815 -14400 > # Tue Sep 27 16:13:35 2022 +0400 > # Node ID 9d1eec5b03a4ff9d863ad49b47721d713dcec76f > # Parent 8878680962d05f778f187efcfb163a76c1dfacb7 > Linux packages: reword to mention supported RHEL derivatives. > > diff -r 8878680962d0 -r 9d1eec5b03a4 xml/en/linux_packages.xml > --- a/xml/en/linux_packages.xml Fri Sep 23 18:30:33 2022 -0700 > +++ b/xml/en/linux_packages.xml Tue Sep 27 16:13:35 2022 +0400 > @@ -7,7 +7,7 @@ >
link="/en/linux_packages.html" > lang="en" > - rev="78"> > + rev="79"> > >
> > @@ -17,7 +17,7 @@ versions: > > > > -RHEL/CentOS > +RHEL and derivatives > >
> > @@ -175,7 +175,12 @@ set up the nginx packages repository. > Afterward, you can install and update nginx from the repository. > > > -
> +
> + > + > +This section applies to Red Hat Enterprise Linux and its derivatives such as > +CentOS, Oracle Linux, Rocky Linux, AlmaLinux. > + > > > Install the prerequisites: > @@ -578,7 +583,8 @@ mainline version, while stable- > sources for stable releases. > To build binary packages, run make in > debian/ directory on Debian/Ubuntu, or in > -rpm/SPECS/ on RHEL/CentOS/SLES/Amazon Linux, or in > +rpm/SPECS/ on > +RHEL/CentOS/Oracle Linux/Rocky Linux/AlmaLinux/SLES/Amazon Linux, or in > alpine/ on Alpine. > > Such enumeration looks cluttered, what about breaking into a list? @@ -577,9 +582,20 @@ The default branch ho mainline version, while stable-* branches contain latest sources for stable releases. To build binary packages, run make in -debian/ directory on Debian/Ubuntu, or in -rpm/SPECS/ on RHEL/CentOS/SLES/Amazon Linux, or in -alpine/ on Alpine. +the following directory: + + +debian/ +on Debian, Ubuntu; + +rpm/SPECS/ +on RHEL, CentOS, Oracle Linux, Rocky Linux, AlmaLinux, SLES, +Amazon Linux; + +alpine/ +on Alpine. + + -- Sergey Kandaurov From v.zhestikov at f5.com Tue Sep 27 18:30:30 2022 From: v.zhestikov at f5.com (Vadim Zhestikov) Date: Tue, 27 Sep 2022 18:30:30 +0000 Subject: [njs] Modules: common code is moved to shared library. Message-ID: details: https://hg.nginx.org/njs/rev/b92fa931a0f2 branches: changeset: 1964:b92fa931a0f2 user: Vadim Zhestikov date: Tue Sep 27 09:32:56 2022 -0700 description: Modules: common code is moved to shared library. diffstat: nginx/ngx_http_js_module.c | 373 ++---------------------------------------- nginx/ngx_js.c | 348 ++++++++++++++++++++++++++++++++++++++++- nginx/ngx_js.h | 13 +- nginx/ngx_stream_js_module.c | 373 ++---------------------------------------- 4 files changed, 409 insertions(+), 698 deletions(-) diffs (truncated from 1283 to 1000 lines): diff -r 301cc9eaa3bf -r b92fa931a0f2 nginx/ngx_http_js_module.c --- a/nginx/ngx_http_js_module.c Mon Sep 26 17:50:24 2022 -0700 +++ b/nginx/ngx_http_js_module.c Tue Sep 27 09:32:56 2022 -0700 @@ -20,10 +20,6 @@ typedef struct { ngx_str_t body_filter; ngx_uint_t buffer_type; - size_t buffer_size; - size_t max_response_body_size; - ngx_msec_t timeout; - #if (NGX_HTTP_SSL) ngx_ssl_t *ssl; ngx_str_t ssl_ciphers; @@ -103,7 +99,6 @@ static ngx_int_t ngx_http_js_variable_va ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_js_init_vm(ngx_http_request_t *r); static void ngx_http_js_cleanup_ctx(void *data); -static void ngx_http_js_cleanup_vm(void *data); static njs_int_t ngx_http_js_ext_keys_header(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys, ngx_list_t *headers); @@ -259,10 +254,8 @@ static char *ngx_http_js_content(ngx_con void *conf); static char *ngx_http_js_body_filter_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static ngx_int_t ngx_http_js_merge_vm(ngx_conf_t *cf, - ngx_http_js_loc_conf_t *conf, ngx_http_js_loc_conf_t *prev); static ngx_int_t ngx_http_js_init_conf_vm(ngx_conf_t *cf, - ngx_http_js_loc_conf_t *conf); + ngx_js_conf_t *conf); static void *ngx_http_js_create_loc_conf(ngx_conf_t *cf); static char *ngx_http_js_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); @@ -1317,19 +1310,6 @@ ngx_http_js_cleanup_ctx(void *data) } -static void -ngx_http_js_cleanup_vm(void *data) -{ - ngx_http_js_loc_conf_t *jlcf = data; - - njs_vm_destroy(jlcf->vm); - - if (jlcf->preload_objects != NGX_CONF_UNSET_PTR) { - njs_vm_destroy(jlcf->preload_vm); - } -} - - static njs_int_t ngx_http_js_ext_keys_header(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys, ngx_list_t *headers) @@ -4208,211 +4188,27 @@ ngx_http_js_handle_event(ngx_http_reques static ngx_int_t -ngx_http_js_merge_vm(ngx_conf_t *cf, ngx_http_js_loc_conf_t *conf, - ngx_http_js_loc_conf_t *prev) +ngx_http_js_externals_init(ngx_conf_t *cf, ngx_js_conf_t *conf_in) { - ngx_str_t *path, *s; - ngx_uint_t i; - ngx_array_t *imports, *preload_objects, *paths; - ngx_js_named_path_t *import, *pi, *pij, *preload; - - if (prev->imports != NGX_CONF_UNSET_PTR && prev->vm == NULL) { - if (ngx_http_js_init_conf_vm(cf, prev) != NGX_OK) { - return NGX_ERROR; - } - } - - if (conf->imports == NGX_CONF_UNSET_PTR - && conf->paths == NGX_CONF_UNSET_PTR - && conf->preload_objects == NGX_CONF_UNSET_PTR) - { - if (prev->vm != NULL) { - conf->preload_objects = prev->preload_objects; - conf->imports = prev->imports; - conf->paths = prev->paths; - conf->vm = prev->vm; - - conf->preload_vm = prev->preload_vm; - - return NGX_OK; - } - } - - if (prev->preload_objects != NGX_CONF_UNSET_PTR) { - if (conf->preload_objects == NGX_CONF_UNSET_PTR) { - conf->preload_objects = prev->preload_objects; - - } else { - preload_objects = ngx_array_create(cf->pool, 4, - sizeof(ngx_js_named_path_t)); - if (preload_objects == NULL) { - return NGX_ERROR; - } - - pij = prev->preload_objects->elts; - - for (i = 0; i < prev->preload_objects->nelts; i++) { - preload = ngx_array_push(preload_objects); - if (preload == NULL) { - return NGX_ERROR; - } - - *preload = pij[i]; - } - - pij = conf->preload_objects->elts; - - for (i = 0; i < conf->preload_objects->nelts; i++) { - preload = ngx_array_push(preload_objects); - if (preload == NULL) { - return NGX_ERROR; - } - - *preload = pij[i]; - } - - conf->preload_objects = preload_objects; - } - } - - if (prev->imports != NGX_CONF_UNSET_PTR) { - if (conf->imports == NGX_CONF_UNSET_PTR) { - conf->imports = prev->imports; - - } else { - imports = ngx_array_create(cf->pool, 4, - sizeof(ngx_js_named_path_t)); - if (imports == NULL) { - return NGX_ERROR; - } - - pi = prev->imports->elts; - - for (i = 0; i < prev->imports->nelts; i++) { - import = ngx_array_push(imports); - if (import == NULL) { - return NGX_ERROR; - } - - *import = pi[i]; - } - - pi = conf->imports->elts; - - for (i = 0; i < conf->imports->nelts; i++) { - import = ngx_array_push(imports); - if (import == NULL) { - return NGX_ERROR; - } - - *import = pi[i]; - } - - conf->imports = imports; - } - } - - if (prev->paths != NGX_CONF_UNSET_PTR) { - if (conf->paths == NGX_CONF_UNSET_PTR) { - conf->paths = prev->paths; - - } else { - paths = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t)); - if (paths == NULL) { - return NGX_ERROR; - } - - s = prev->imports->elts; - - for (i = 0; i < prev->paths->nelts; i++) { - path = ngx_array_push(paths); - if (path == NULL) { - return NGX_ERROR; - } - - *path = s[i]; - } - - s = conf->imports->elts; - - for (i = 0; i < conf->paths->nelts; i++) { - path = ngx_array_push(paths); - if (path == NULL) { - return NGX_ERROR; - } - - *path = s[i]; - } - - conf->paths = paths; - } - } - - if (conf->imports == NGX_CONF_UNSET_PTR) { - return NGX_OK; - } - - return ngx_http_js_init_conf_vm(cf, conf); + ngx_http_js_loc_conf_t *conf = (ngx_http_js_loc_conf_t *) conf_in; + + ngx_http_js_request_proto_id = njs_vm_external_prototype(conf->vm, + ngx_http_js_ext_request, + njs_nitems(ngx_http_js_ext_request)); + if (ngx_http_js_request_proto_id < 0) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "failed to add js request proto"); + return NGX_ERROR; + } + + return NGX_OK; } static ngx_int_t -ngx_http_js_init_conf_vm(ngx_conf_t *cf, ngx_http_js_loc_conf_t *conf) +ngx_http_js_init_conf_vm(ngx_conf_t *cf, ngx_js_conf_t *conf) { - size_t size; - u_char *start, *end, *p; - ngx_str_t *m, file; - njs_int_t rc; - njs_str_t text, path; - ngx_uint_t i; - njs_value_t *value; - njs_vm_opt_t options; - ngx_pool_cleanup_t *cln; - njs_opaque_value_t lvalue, exception; - ngx_js_named_path_t *import; - - static const njs_str_t line_number_key = njs_str("lineNumber"); - static const njs_str_t file_name_key = njs_str("fileName"); - - if (conf->preload_objects != NGX_CONF_UNSET_PTR) { - if (ngx_js_init_preload_vm(cf, (ngx_js_conf_t *)conf) != NGX_OK) { - return NGX_ERROR; - } - } - - size = 0; - - import = conf->imports->elts; - for (i = 0; i < conf->imports->nelts; i++) { - - /* import from ''; globalThis. = ; */ - - size += sizeof("import from '';") - 1 + import[i].name.len * 3 - + import[i].path.len - + sizeof(" globalThis. = ;\n") - 1; - } - - start = ngx_pnalloc(cf->pool, size); - if (start == NULL) { - return NGX_ERROR; - } - - p = start; - import = conf->imports->elts; - for (i = 0; i < conf->imports->nelts; i++) { - - /* import from ''; globalThis. = ; */ - - p = ngx_cpymem(p, "import ", sizeof("import ") - 1); - p = ngx_cpymem(p, import[i].name.data, import[i].name.len); - p = ngx_cpymem(p, " from '", sizeof(" from '") - 1); - p = ngx_cpymem(p, import[i].path.data, import[i].path.len); - p = ngx_cpymem(p, "'; globalThis.", sizeof("'; globalThis.") - 1); - p = ngx_cpymem(p, import[i].name.data, import[i].name.len); - p = ngx_cpymem(p, " = ", sizeof(" = ") - 1); - p = ngx_cpymem(p, import[i].name.data, import[i].name.len); - p = ngx_cpymem(p, ";\n", sizeof(";\n") - 1); - } + njs_vm_opt_t options; njs_vm_opt_init(&options); @@ -4424,108 +4220,7 @@ ngx_http_js_init_conf_vm(ngx_conf_t *cf, options.argv = ngx_argv; options.argc = ngx_argc; - file = ngx_cycle->conf_prefix; - - options.file.start = file.data; - options.file.length = file.len; - - conf->vm = njs_vm_create(&options); - if (conf->vm == NULL) { - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "failed to create js VM"); - return NGX_ERROR; - } - - cln = ngx_pool_cleanup_add(cf->pool, 0); - if (cln == NULL) { - return NGX_ERROR; - } - - cln->handler = ngx_http_js_cleanup_vm; - cln->data = conf; - - path.start = ngx_cycle->conf_prefix.data; - path.length = ngx_cycle->conf_prefix.len; - - rc = njs_vm_add_path(conf->vm, &path); - if (rc != NJS_OK) { - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "failed to add \"js_path\""); - return NGX_ERROR; - } - - if (conf->paths != NGX_CONF_UNSET_PTR) { - m = conf->paths->elts; - - for (i = 0; i < conf->paths->nelts; i++) { - if (ngx_conf_full_name(cf->cycle, &m[i], 1) != NGX_OK) { - return NGX_ERROR; - } - - path.start = m[i].data; - path.length = m[i].len; - - rc = njs_vm_add_path(conf->vm, &path); - if (rc != NJS_OK) { - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "failed to add \"js_path\""); - return NGX_ERROR; - } - } - } - - ngx_http_js_request_proto_id = njs_vm_external_prototype(conf->vm, - ngx_http_js_ext_request, - njs_nitems(ngx_http_js_ext_request)); - if (ngx_http_js_request_proto_id < 0) { - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "failed to add js request proto"); - return NGX_ERROR; - } - - rc = ngx_js_core_init(conf->vm, cf->log); - if (njs_slow_path(rc != NJS_OK)) { - return NGX_ERROR; - } - - end = start + size; - - rc = njs_vm_compile(conf->vm, &start, end); - - if (rc != NJS_OK) { - njs_value_assign(&exception, njs_vm_retval(conf->vm)); - njs_vm_retval_string(conf->vm, &text); - - value = njs_vm_object_prop(conf->vm, njs_value_arg(&exception), - &file_name_key, &lvalue); - if (value == NULL) { - value = njs_vm_object_prop(conf->vm, njs_value_arg(&exception), - &line_number_key, &lvalue); - - if (value != NULL) { - i = njs_value_number(value) - 1; - - if (i < conf->imports->nelts) { - import = conf->imports->elts; - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "%*s, included in %s:%ui", text.length, - text.start, import[i].file, import[i].line); - return NGX_ERROR; - } - } - } - - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "%*s", text.length, - text.start); - return NGX_ERROR; - } - - if (start != end) { - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "extra characters in js script: \"%*s\"", - end - start, start); - return NGX_ERROR; - } - - return NGX_OK; + return ngx_js_init_conf_vm(cf, conf, &options, ngx_http_js_externals_init); } @@ -4697,40 +4392,17 @@ ngx_http_js_body_filter_set(ngx_conf_t * static void * ngx_http_js_create_loc_conf(ngx_conf_t *cf) { - ngx_http_js_loc_conf_t *conf; - - conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_js_loc_conf_t)); + ngx_http_js_loc_conf_t *conf = + (ngx_http_js_loc_conf_t *) ngx_js_create_conf( + cf, sizeof(ngx_http_js_loc_conf_t)); if (conf == NULL) { return NULL; } - /* - * set by ngx_pcalloc(): - * - * conf->vm = NULL; - * conf->preload_vm = NULL; - * conf->content = { 0, NULL }; - * conf->header_filter = { 0, NULL }; - * conf->body_filter = { 0, NULL }; - * conf->buffer_type = NGX_JS_UNSET; - * conf->ssl_ciphers = { 0, NULL }; - * conf->ssl_protocols = 0; - * conf->ssl_trusted_certificate = { 0, NULL }; - */ - - conf->paths = NGX_CONF_UNSET_PTR; - conf->imports = NGX_CONF_UNSET_PTR; - conf->preload_objects = NGX_CONF_UNSET_PTR; - - conf->buffer_size = NGX_CONF_UNSET_SIZE; - conf->max_response_body_size = NGX_CONF_UNSET_SIZE; - conf->timeout = NGX_CONF_UNSET_MSEC; - #if (NGX_HTTP_SSL) conf->ssl_verify = NGX_CONF_UNSET; conf->ssl_verify_depth = NGX_CONF_UNSET; #endif - return conf; } @@ -4752,7 +4424,10 @@ ngx_http_js_merge_loc_conf(ngx_conf_t *c ngx_conf_merge_size_value(conf->max_response_body_size, prev->max_response_body_size, 1048576); - if (ngx_http_js_merge_vm(cf, conf, prev) != NGX_OK) { + if (ngx_js_merge_vm(cf, (ngx_js_conf_t *) conf, (ngx_js_conf_t *) prev, + ngx_http_js_init_conf_vm) + != NGX_OK) + { return NGX_CONF_ERROR; } diff -r 301cc9eaa3bf -r b92fa931a0f2 nginx/ngx_js.c --- a/nginx/ngx_js.c Mon Sep 26 17:50:24 2022 -0700 +++ b/nginx/ngx_js.c Tue Sep 27 09:32:56 2022 -0700 @@ -12,8 +12,9 @@ #include "ngx_js_fetch.h" -njs_int_t ngx_js_ext_conf_prefix(njs_vm_t *vm, njs_object_prop_t *prop, +static njs_int_t ngx_js_ext_conf_prefix(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); +static void ngx_js_cleanup_vm(void *data); extern njs_module_t njs_webcrypto_module; @@ -693,3 +694,348 @@ error: return NGX_ERROR; } + + +ngx_int_t +ngx_js_merge_vm(ngx_conf_t *cf, ngx_js_conf_t *conf, + ngx_js_conf_t *prev, + ngx_int_t (*init_vm) (ngx_conf_t *cf, ngx_js_conf_t *conf)) +{ + ngx_str_t *path, *s; + ngx_uint_t i; + ngx_array_t *imports, *preload_objects, *paths; + ngx_js_named_path_t *import, *pi, *pij, *preload; + + if (prev->imports != NGX_CONF_UNSET_PTR && prev->vm == NULL) { + if (init_vm(cf, (ngx_js_conf_t *) prev) != NGX_OK) { + return NGX_ERROR; + } + } + + if (conf->imports == NGX_CONF_UNSET_PTR + && conf->paths == NGX_CONF_UNSET_PTR + && conf->preload_objects == NGX_CONF_UNSET_PTR) + { + if (prev->vm != NULL) { + conf->preload_objects = prev->preload_objects; + conf->imports = prev->imports; + conf->paths = prev->paths; + conf->vm = prev->vm; + + conf->preload_vm = prev->preload_vm; + + return NGX_OK; + } + } + + if (prev->preload_objects != NGX_CONF_UNSET_PTR) { + if (conf->preload_objects == NGX_CONF_UNSET_PTR) { + conf->preload_objects = prev->preload_objects; + + } else { + preload_objects = ngx_array_create(cf->pool, 4, + sizeof(ngx_js_named_path_t)); + if (preload_objects == NULL) { + return NGX_ERROR; + } + + pij = prev->preload_objects->elts; + + for (i = 0; i < prev->preload_objects->nelts; i++) { + preload = ngx_array_push(preload_objects); + if (preload == NULL) { + return NGX_ERROR; + } + + *preload = pij[i]; + } + + pij = conf->preload_objects->elts; + + for (i = 0; i < conf->preload_objects->nelts; i++) { + preload = ngx_array_push(preload_objects); + if (preload == NULL) { + return NGX_ERROR; + } + + *preload = pij[i]; + } + + conf->preload_objects = preload_objects; + } + } + + if (prev->imports != NGX_CONF_UNSET_PTR) { + if (conf->imports == NGX_CONF_UNSET_PTR) { + conf->imports = prev->imports; + + } else { + imports = ngx_array_create(cf->pool, 4, + sizeof(ngx_js_named_path_t)); + if (imports == NULL) { + return NGX_ERROR; + } + + pi = prev->imports->elts; + + for (i = 0; i < prev->imports->nelts; i++) { + import = ngx_array_push(imports); + if (import == NULL) { + return NGX_ERROR; + } + + *import = pi[i]; + } + + pi = conf->imports->elts; + + for (i = 0; i < conf->imports->nelts; i++) { + import = ngx_array_push(imports); + if (import == NULL) { + return NGX_ERROR; + } + + *import = pi[i]; + } + + conf->imports = imports; + } + } + + if (prev->paths != NGX_CONF_UNSET_PTR) { + if (conf->paths == NGX_CONF_UNSET_PTR) { + conf->paths = prev->paths; + + } else { + paths = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t)); + if (paths == NULL) { + return NGX_ERROR; + } + + s = prev->imports->elts; + + for (i = 0; i < prev->paths->nelts; i++) { + path = ngx_array_push(paths); + if (path == NULL) { + return NGX_ERROR; + } + + *path = s[i]; + } + + s = conf->imports->elts; + + for (i = 0; i < conf->paths->nelts; i++) { + path = ngx_array_push(paths); + if (path == NULL) { + return NGX_ERROR; + } + + *path = s[i]; + } + + conf->paths = paths; + } + } + + if (conf->imports == NGX_CONF_UNSET_PTR) { + return NGX_OK; + } + + return init_vm(cf, (ngx_js_conf_t *) conf); +} + + +ngx_int_t +ngx_js_init_conf_vm(ngx_conf_t *cf, ngx_js_conf_t *conf, + njs_vm_opt_t *options, + ngx_int_t (*externals_init)(ngx_conf_t *cf, ngx_js_conf_t *conf)) +{ + size_t size; + u_char *start, *end, *p; + ngx_str_t *m, file; + njs_int_t rc; + njs_str_t text, path; + ngx_uint_t i; + njs_value_t *value; + ngx_pool_cleanup_t *cln; + njs_opaque_value_t lvalue, exception; + ngx_js_named_path_t *import; + + static const njs_str_t line_number_key = njs_str("lineNumber"); + static const njs_str_t file_name_key = njs_str("fileName"); + + if (conf->preload_objects != NGX_CONF_UNSET_PTR) { + if (ngx_js_init_preload_vm(cf, (ngx_js_conf_t *)conf) != NGX_OK) { + return NGX_ERROR; + } + } + + size = 0; + + import = conf->imports->elts; + for (i = 0; i < conf->imports->nelts; i++) { + + /* import from ''; globalThis. = ; */ + + size += sizeof("import from '';") - 1 + import[i].name.len * 3 + + import[i].path.len + + sizeof(" globalThis. = ;\n") - 1; + } + + start = ngx_pnalloc(cf->pool, size); + if (start == NULL) { + return NGX_ERROR; + } + + p = start; + import = conf->imports->elts; + for (i = 0; i < conf->imports->nelts; i++) { + + /* import from ''; globalThis. = ; */ + + p = ngx_cpymem(p, "import ", sizeof("import ") - 1); + p = ngx_cpymem(p, import[i].name.data, import[i].name.len); + p = ngx_cpymem(p, " from '", sizeof(" from '") - 1); + p = ngx_cpymem(p, import[i].path.data, import[i].path.len); + p = ngx_cpymem(p, "'; globalThis.", sizeof("'; globalThis.") - 1); + p = ngx_cpymem(p, import[i].name.data, import[i].name.len); + p = ngx_cpymem(p, " = ", sizeof(" = ") - 1); + p = ngx_cpymem(p, import[i].name.data, import[i].name.len); + p = ngx_cpymem(p, ";\n", sizeof(";\n") - 1); + } + + file = ngx_cycle->conf_prefix; + + options->file.start = file.data; + options->file.length = file.len; + + conf->vm = njs_vm_create(options); + if (conf->vm == NULL) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "failed to create js VM"); + return NGX_ERROR; + } + + cln = ngx_pool_cleanup_add(cf->pool, 0); + if (cln == NULL) { + return NGX_ERROR; + } + + cln->handler = ngx_js_cleanup_vm; + cln->data = conf; + + path.start = ngx_cycle->conf_prefix.data; + path.length = ngx_cycle->conf_prefix.len; + + rc = njs_vm_add_path(conf->vm, &path); + if (rc != NJS_OK) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "failed to add \"js_path\""); + return NGX_ERROR; + } + + if (conf->paths != NGX_CONF_UNSET_PTR) { + m = conf->paths->elts; + + for (i = 0; i < conf->paths->nelts; i++) { + if (ngx_conf_full_name(cf->cycle, &m[i], 1) != NGX_OK) { + return NGX_ERROR; + } + + path.start = m[i].data; + path.length = m[i].len; + + rc = njs_vm_add_path(conf->vm, &path); + if (rc != NJS_OK) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "failed to add \"js_path\""); + return NGX_ERROR; + } + } + } + + rc = externals_init(cf, conf); + if (rc != NGX_OK) { + return NGX_ERROR; + } + + rc = ngx_js_core_init(conf->vm, cf->log); + if (njs_slow_path(rc != NJS_OK)) { + return NGX_ERROR; + } + + end = start + size; + + rc = njs_vm_compile(conf->vm, &start, end); + + if (rc != NJS_OK) { + njs_value_assign(&exception, njs_vm_retval(conf->vm)); + njs_vm_retval_string(conf->vm, &text); + + value = njs_vm_object_prop(conf->vm, njs_value_arg(&exception), + &file_name_key, &lvalue); + if (value == NULL) { + value = njs_vm_object_prop(conf->vm, njs_value_arg(&exception), + &line_number_key, &lvalue); + + if (value != NULL) { + i = njs_value_number(value) - 1; + + if (i < conf->imports->nelts) { + import = conf->imports->elts; + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "%*s, included in %s:%ui", text.length, + text.start, import[i].file, import[i].line); + return NGX_ERROR; + } + } + } + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "%*s", text.length, + text.start); + return NGX_ERROR; + } + + if (start != end) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "extra characters in js script: \"%*s\"", + end - start, start); + return NGX_ERROR; + } + + return NGX_OK; +} + + +static void +ngx_js_cleanup_vm(void *data) +{ + ngx_js_conf_t *jscf = data; + + njs_vm_destroy(jscf->vm); + + if (jscf->preload_objects != NGX_CONF_UNSET_PTR) { + njs_vm_destroy(jscf->preload_vm); + } +} + + +ngx_js_conf_t * +ngx_js_create_conf(ngx_conf_t *cf, size_t size) +{ + ngx_js_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, size); + if (conf == NULL) { + return NULL; + } + + conf->paths = NGX_CONF_UNSET_PTR; + conf->imports = NGX_CONF_UNSET_PTR; + conf->preload_objects = NGX_CONF_UNSET_PTR; + + conf->buffer_size = NGX_CONF_UNSET_SIZE; + conf->max_response_body_size = NGX_CONF_UNSET_SIZE; + conf->timeout = NGX_CONF_UNSET_MSEC; + + return conf; +} diff -r 301cc9eaa3bf -r b92fa931a0f2 nginx/ngx_js.h --- a/nginx/ngx_js.h Mon Sep 26 17:50:24 2022 -0700 +++ b/nginx/ngx_js.h Tue Sep 27 09:32:56 2022 -0700 @@ -56,7 +56,11 @@ typedef struct { ngx_array_t *paths; \ \ njs_vm_t *preload_vm; \ - ngx_array_t *preload_objects \ + ngx_array_t *preload_objects; \ + \ + size_t buffer_size; \ + size_t max_response_body_size; \ + ngx_msec_t timeout typedef struct { NGX_JS_COMMON_CONF; @@ -102,6 +106,13 @@ void ngx_js_logger(njs_vm_t *vm, njs_ext char * ngx_js_import(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char * ngx_js_preload_object(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); ngx_int_t ngx_js_init_preload_vm(ngx_conf_t *cf, ngx_js_conf_t *conf); +ngx_int_t ngx_js_merge_vm(ngx_conf_t *cf, ngx_js_conf_t *conf, + ngx_js_conf_t *prev, + ngx_int_t (*init_vm)(ngx_conf_t *cf, ngx_js_conf_t *conf)); +ngx_int_t ngx_js_init_conf_vm(ngx_conf_t *cf, ngx_js_conf_t *conf, + njs_vm_opt_t *options, + ngx_int_t (*externals_init)(ngx_conf_t *cf, ngx_js_conf_t *conf)); +ngx_js_conf_t *ngx_js_create_conf(ngx_conf_t *cf, size_t size); njs_int_t ngx_js_ext_string(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); diff -r 301cc9eaa3bf -r b92fa931a0f2 nginx/ngx_stream_js_module.c --- a/nginx/ngx_stream_js_module.c Mon Sep 26 17:50:24 2022 -0700 +++ b/nginx/ngx_stream_js_module.c Tue Sep 27 09:32:56 2022 -0700 @@ -19,10 +19,6 @@ typedef struct { ngx_str_t preread; ngx_str_t filter; - size_t buffer_size; - size_t max_response_body_size; - ngx_msec_t timeout; - #if (NGX_STREAM_SSL) ngx_ssl_t *ssl; ngx_str_t ssl_ciphers; @@ -82,7 +78,6 @@ static ngx_int_t ngx_stream_js_variable_ static ngx_int_t ngx_stream_js_init_vm(ngx_stream_session_t *s); static void ngx_stream_js_drop_events(ngx_stream_js_ctx_t *ctx); static void ngx_stream_js_cleanup(void *data); -static void ngx_stream_js_cleanup_vm(void *data); static njs_int_t ngx_stream_js_run_event(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx, ngx_stream_js_ev_t *event, ngx_uint_t from_upstream); @@ -131,10 +126,8 @@ static char *ngx_stream_js_set(ngx_conf_ void *conf); static char *ngx_stream_js_var(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static ngx_int_t ngx_stream_js_merge_vm(ngx_conf_t *cf, - ngx_stream_js_srv_conf_t *conf, ngx_stream_js_srv_conf_t *prev); static ngx_int_t ngx_stream_js_init_conf_vm(ngx_conf_t *cf, - ngx_stream_js_srv_conf_t *conf); + ngx_js_conf_t *conf); static void *ngx_stream_js_create_srv_conf(ngx_conf_t *cf); static char *ngx_stream_js_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child); @@ -1007,19 +1000,6 @@ ngx_stream_js_cleanup(void *data) } -static void -ngx_stream_js_cleanup_vm(void *data) -{ - ngx_stream_js_srv_conf_t *jscf = data; - - njs_vm_destroy(jscf->vm); - - if (jscf->preload_objects != NGX_CONF_UNSET_PTR) { - njs_vm_destroy(jscf->preload_vm); - } -} - - static njs_int_t ngx_stream_js_run_event(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx, ngx_stream_js_ev_t *event, ngx_uint_t from_upstream) @@ -1701,268 +1681,9 @@ ngx_stream_js_handle_event(ngx_stream_se static ngx_int_t -ngx_stream_js_merge_vm(ngx_conf_t *cf, ngx_stream_js_srv_conf_t *conf, - ngx_stream_js_srv_conf_t *prev) +ngx_stream_js_externals_init(ngx_conf_t *cf, ngx_js_conf_t *conf_in) { - ngx_str_t *path, *s; - ngx_uint_t i; - ngx_array_t *imports, *preload_objects, *paths; - ngx_js_named_path_t *import, *pi, *pij, *preload; - - if (prev->imports != NGX_CONF_UNSET_PTR && prev->vm == NULL) { - if (ngx_stream_js_init_conf_vm(cf, prev) != NGX_OK) { - return NGX_ERROR; - } - } - - if (conf->imports == NGX_CONF_UNSET_PTR - && conf->paths == NGX_CONF_UNSET_PTR - && conf->preload_objects == NGX_CONF_UNSET_PTR) - { - if (prev->vm != NULL) { - conf->preload_objects = prev->preload_objects; - conf->imports = prev->imports; - conf->paths = prev->paths; - conf->vm = prev->vm; - - conf->preload_vm = prev->preload_vm; - - return NGX_OK; - } - } - - if (prev->preload_objects != NGX_CONF_UNSET_PTR) { - if (conf->preload_objects == NGX_CONF_UNSET_PTR) { - conf->preload_objects = prev->preload_objects; - - } else { - preload_objects = ngx_array_create(cf->pool, 4, - sizeof(ngx_js_named_path_t)); - if (preload_objects == NULL) { - return NGX_ERROR; - } - - pij = prev->preload_objects->elts; - - for (i = 0; i < prev->preload_objects->nelts; i++) { - preload = ngx_array_push(preload_objects); - if (preload == NULL) { - return NGX_ERROR; - } - - *preload = pij[i]; - } - - pij = conf->preload_objects->elts; - - for (i = 0; i < conf->preload_objects->nelts; i++) { - preload = ngx_array_push(preload_objects); - if (preload == NULL) { - return NGX_ERROR; - } - - *preload = pij[i]; - } - - conf->preload_objects = preload_objects; - } - } - - if (prev->imports != NGX_CONF_UNSET_PTR) { - if (conf->imports == NGX_CONF_UNSET_PTR) { - conf->imports = prev->imports; - - } else { - imports = ngx_array_create(cf->pool, 4, - sizeof(ngx_js_named_path_t)); - if (imports == NULL) { - return NGX_ERROR; - } - - pi = prev->imports->elts; - - for (i = 0; i < prev->imports->nelts; i++) { - import = ngx_array_push(imports); - if (import == NULL) { - return NGX_ERROR; - } - - *import = pi[i]; - } - - pi = conf->imports->elts; - - for (i = 0; i < conf->imports->nelts; i++) { - import = ngx_array_push(imports); - if (import == NULL) { - return NGX_ERROR; - } - - *import = pi[i]; - } - - conf->imports = imports; From v.zhestikov at f5.com Tue Sep 27 18:30:32 2022 From: v.zhestikov at f5.com (Vadim Zhestikov) Date: Tue, 27 Sep 2022 18:30:32 +0000 Subject: [njs] Modules: js_merge_conf is moved to shared library. Message-ID: details: https://hg.nginx.org/njs/rev/43e35b05fd1b branches: changeset: 1965:43e35b05fd1b user: Vadim Zhestikov date: Tue Sep 27 10:56:54 2022 -0700 description: Modules: js_merge_conf is moved to shared library. diffstat: nginx/ngx_http_js_module.c | 88 +------------------------------------------- nginx/ngx_js.c | 87 +++++++++++++++++++++++++++++++++++++++++++ nginx/ngx_js.h | 21 +++++++++- nginx/ngx_stream_js_module.c | 89 +------------------------------------------- 4 files changed, 109 insertions(+), 176 deletions(-) diffs (374 lines): diff -r b92fa931a0f2 -r 43e35b05fd1b nginx/ngx_http_js_module.c --- a/nginx/ngx_http_js_module.c Tue Sep 27 09:32:56 2022 -0700 +++ b/nginx/ngx_http_js_module.c Tue Sep 27 10:56:54 2022 -0700 @@ -19,15 +19,6 @@ typedef struct { ngx_str_t header_filter; ngx_str_t body_filter; ngx_uint_t buffer_type; - -#if (NGX_HTTP_SSL) - ngx_ssl_t *ssl; - ngx_str_t ssl_ciphers; - ngx_uint_t ssl_protocols; - ngx_flag_t ssl_verify; - ngx_int_t ssl_verify_depth; - ngx_str_t ssl_trusted_certificate; -#endif } ngx_http_js_loc_conf_t; @@ -260,9 +251,6 @@ static void *ngx_http_js_create_loc_conf static char *ngx_http_js_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); -#if (NGX_HTTP_SSL) -static char * ngx_http_js_set_ssl(ngx_conf_t *cf, ngx_http_js_loc_conf_t *jlcf); -#endif static ngx_ssl_t *ngx_http_js_ssl(njs_vm_t *vm, ngx_http_request_t *r); static ngx_flag_t ngx_http_js_ssl_verify(njs_vm_t *vm, ngx_http_request_t *r); @@ -4419,84 +4407,10 @@ ngx_http_js_merge_loc_conf(ngx_conf_t *c ngx_conf_merge_uint_value(conf->buffer_type, prev->buffer_type, NGX_JS_STRING); - ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000); - ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, 16384); - ngx_conf_merge_size_value(conf->max_response_body_size, - prev->max_response_body_size, 1048576); - - if (ngx_js_merge_vm(cf, (ngx_js_conf_t *) conf, (ngx_js_conf_t *) prev, - ngx_http_js_init_conf_vm) - != NGX_OK) - { - return NGX_CONF_ERROR; - } - -#if (NGX_HTTP_SSL) - ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); - - ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); - - ngx_conf_merge_value(conf->ssl_verify, prev->ssl_verify, 1); - ngx_conf_merge_value(conf->ssl_verify_depth, prev->ssl_verify_depth, 100); - - ngx_conf_merge_str_value(conf->ssl_trusted_certificate, - prev->ssl_trusted_certificate, ""); - - return ngx_http_js_set_ssl(cf, conf); -#else - return NGX_CONF_OK; -#endif + return ngx_js_merge_conf(cf, parent, child, ngx_http_js_init_conf_vm); } -#if (NGX_HTTP_SSL) - -static char * -ngx_http_js_set_ssl(ngx_conf_t *cf, ngx_http_js_loc_conf_t *jlcf) -{ - ngx_ssl_t *ssl; - ngx_pool_cleanup_t *cln; - - ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t)); - if (ssl == NULL) { - return NGX_CONF_ERROR; - } - - jlcf->ssl = ssl; - ssl->log = cf->log; - - if (ngx_ssl_create(ssl, jlcf->ssl_protocols, NULL) != NGX_OK) { - return NGX_CONF_ERROR; - } - - cln = ngx_pool_cleanup_add(cf->pool, 0); - if (cln == NULL) { - ngx_ssl_cleanup_ctx(ssl); - return NGX_CONF_ERROR; - } - - cln->handler = ngx_ssl_cleanup_ctx; - cln->data = ssl; - - if (ngx_ssl_ciphers(NULL, ssl, &jlcf->ssl_ciphers, 0) != NGX_OK) { - return NGX_CONF_ERROR; - } - - if (ngx_ssl_trusted_certificate(cf, ssl, &jlcf->ssl_trusted_certificate, - jlcf->ssl_verify_depth) - != NGX_OK) - { - return NGX_CONF_ERROR; - } - - return NGX_CONF_OK; -} - -#endif - - static ngx_ssl_t * ngx_http_js_ssl(njs_vm_t *vm, ngx_http_request_t *r) { diff -r b92fa931a0f2 -r 43e35b05fd1b nginx/ngx_js.c --- a/nginx/ngx_js.c Tue Sep 27 09:32:56 2022 -0700 +++ b/nginx/ngx_js.c Tue Sep 27 10:56:54 2022 -0700 @@ -1039,3 +1039,90 @@ ngx_js_create_conf(ngx_conf_t *cf, size_ return conf; } + + +#if defined(NGX_HTTP_SSL) || defined(NGX_STREAM_SSL) + +static char * +ngx_js_set_ssl(ngx_conf_t *cf, ngx_js_conf_t *conf) +{ + ngx_ssl_t *ssl; + ngx_pool_cleanup_t *cln; + + ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t)); + if (ssl == NULL) { + return NGX_CONF_ERROR; + } + + conf->ssl = ssl; + ssl->log = cf->log; + + if (ngx_ssl_create(ssl, conf->ssl_protocols, NULL) != NGX_OK) { + return NGX_CONF_ERROR; + } + + cln = ngx_pool_cleanup_add(cf->pool, 0); + if (cln == NULL) { + ngx_ssl_cleanup_ctx(ssl); + return NGX_CONF_ERROR; + } + + cln->handler = ngx_ssl_cleanup_ctx; + cln->data = ssl; + + if (ngx_ssl_ciphers(NULL, ssl, &conf->ssl_ciphers, 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (ngx_ssl_trusted_certificate(cf, ssl, &conf->ssl_trusted_certificate, + conf->ssl_verify_depth) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + +#endif + + +char * +ngx_js_merge_conf(ngx_conf_t *cf, void *parent, void *child, + ngx_int_t (*init_vm)(ngx_conf_t *cf, ngx_js_conf_t *conf)) +{ + ngx_js_conf_t *prev = parent; + ngx_js_conf_t *conf = child; + + ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000); + ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, 16384); + ngx_conf_merge_size_value(conf->max_response_body_size, + prev->max_response_body_size, 1048576); + + if (ngx_js_merge_vm(cf, (ngx_js_conf_t *) conf, (ngx_js_conf_t *) prev, + init_vm) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + +#if defined(NGX_HTTP_SSL) || defined(NGX_STREAM_SSL) + ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, + "DEFAULT"); + + ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, + (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 + |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); + + ngx_conf_merge_value(conf->ssl_verify, prev->ssl_verify, 1); + ngx_conf_merge_value(conf->ssl_verify_depth, prev->ssl_verify_depth, + 100); + + ngx_conf_merge_str_value(conf->ssl_trusted_certificate, + prev->ssl_trusted_certificate, ""); + + return ngx_js_set_ssl(cf, conf); +#else + return NGX_CONF_OK; +#endif +} diff -r b92fa931a0f2 -r 43e35b05fd1b nginx/ngx_js.h --- a/nginx/ngx_js.h Tue Sep 27 09:32:56 2022 -0700 +++ b/nginx/ngx_js.h Tue Sep 27 10:56:54 2022 -0700 @@ -50,7 +50,7 @@ typedef struct { } ngx_js_named_path_t; -#define NGX_JS_COMMON_CONF \ +#define _NGX_JS_COMMON_CONF \ njs_vm_t *vm; \ ngx_array_t *imports; \ ngx_array_t *paths; \ @@ -62,6 +62,23 @@ typedef struct { size_t max_response_body_size; \ ngx_msec_t timeout + +#if defined(NGX_HTTP_SSL) || defined(NGX_STREAM_SSL) +#define NGX_JS_COMMON_CONF \ + _NGX_JS_COMMON_CONF; \ + \ + ngx_ssl_t *ssl; \ + ngx_str_t ssl_ciphers; \ + ngx_uint_t ssl_protocols; \ + ngx_flag_t ssl_verify; \ + ngx_int_t ssl_verify_depth; \ + ngx_str_t ssl_trusted_certificate + +#else +#define NGX_JS_COMMON_CONF _NGX_JS_COMMON_CONF +#endif + + typedef struct { NGX_JS_COMMON_CONF; } ngx_js_conf_t; @@ -113,6 +130,8 @@ ngx_int_t ngx_js_init_conf_vm(ngx_conf_t njs_vm_opt_t *options, ngx_int_t (*externals_init)(ngx_conf_t *cf, ngx_js_conf_t *conf)); ngx_js_conf_t *ngx_js_create_conf(ngx_conf_t *cf, size_t size); +char * ngx_js_merge_conf(ngx_conf_t *cf, void *parent, void *child, + ngx_int_t (*init_vm)(ngx_conf_t *cf, ngx_js_conf_t *conf)); njs_int_t ngx_js_ext_string(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); diff -r b92fa931a0f2 -r 43e35b05fd1b nginx/ngx_stream_js_module.c --- a/nginx/ngx_stream_js_module.c Tue Sep 27 09:32:56 2022 -0700 +++ b/nginx/ngx_stream_js_module.c Tue Sep 27 10:56:54 2022 -0700 @@ -18,15 +18,6 @@ typedef struct { ngx_str_t access; ngx_str_t preread; ngx_str_t filter; - -#if (NGX_STREAM_SSL) - ngx_ssl_t *ssl; - ngx_str_t ssl_ciphers; - ngx_flag_t ssl_verify; - ngx_uint_t ssl_protocols; - ngx_int_t ssl_verify_depth; - ngx_str_t ssl_trusted_certificate; -#endif } ngx_stream_js_srv_conf_t; @@ -133,10 +124,6 @@ static char *ngx_stream_js_merge_srv_con void *child); static ngx_int_t ngx_stream_js_init(ngx_conf_t *cf); -#if (NGX_STREAM_SSL) -static char * ngx_stream_js_set_ssl(ngx_conf_t *cf, - ngx_stream_js_srv_conf_t *jscf); -#endif static ngx_ssl_t *ngx_stream_js_ssl(njs_vm_t *vm, ngx_stream_session_t *s); static ngx_flag_t ngx_stream_js_ssl_verify(njs_vm_t *vm, ngx_stream_session_t *s); @@ -1847,35 +1834,7 @@ ngx_stream_js_merge_srv_conf(ngx_conf_t ngx_conf_merge_str_value(conf->preread, prev->preread, ""); ngx_conf_merge_str_value(conf->filter, prev->filter, ""); - ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000); - ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, 16384); - ngx_conf_merge_size_value(conf->max_response_body_size, - prev->max_response_body_size, 1048576); - - if (ngx_js_merge_vm(cf, (ngx_js_conf_t *) conf, (ngx_js_conf_t *) prev, - ngx_stream_js_init_conf_vm) - != NGX_OK) - { - return NGX_CONF_ERROR; - } - -#if (NGX_STREAM_SSL) - ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); - - ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); - - ngx_conf_merge_value(conf->ssl_verify, prev->ssl_verify, 1); - ngx_conf_merge_value(conf->ssl_verify_depth, prev->ssl_verify_depth, 100); - - ngx_conf_merge_str_value(conf->ssl_trusted_certificate, - prev->ssl_trusted_certificate, ""); - - return ngx_stream_js_set_ssl(cf, conf); -#else - return NGX_CONF_OK; -#endif + return ngx_js_merge_conf(cf, parent, child, ngx_stream_js_init_conf_vm); } @@ -1908,52 +1867,6 @@ ngx_stream_js_init(ngx_conf_t *cf) } -#if (NGX_STREAM_SSL) - -static char * -ngx_stream_js_set_ssl(ngx_conf_t *cf, ngx_stream_js_srv_conf_t *jscf) -{ - ngx_ssl_t *ssl; - ngx_pool_cleanup_t *cln; - - ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t)); - if (ssl == NULL) { - return NGX_CONF_ERROR; - } - - jscf->ssl = ssl; - ssl->log = cf->log; - - if (ngx_ssl_create(ssl, jscf->ssl_protocols, NULL) != NGX_OK) { - return NGX_CONF_ERROR; - } - - cln = ngx_pool_cleanup_add(cf->pool, 0); - if (cln == NULL) { - ngx_ssl_cleanup_ctx(ssl); - return NGX_CONF_ERROR; - } - - cln->handler = ngx_ssl_cleanup_ctx; - cln->data = ssl; - - if (ngx_ssl_ciphers(NULL, ssl, &jscf->ssl_ciphers, 0) != NGX_OK) { - return NGX_CONF_ERROR; - } - - if (ngx_ssl_trusted_certificate(cf, ssl, &jscf->ssl_trusted_certificate, - jscf->ssl_verify_depth) - != NGX_OK) - { - return NGX_CONF_ERROR; - } - - return NGX_CONF_OK; -} - -#endif - - static ngx_ssl_t * ngx_stream_js_ssl(njs_vm_t *vm, ngx_stream_session_t *s) { From mdounin at mdounin.ru Tue Sep 27 20:14:23 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 27 Sep 2022 23:14:23 +0300 Subject: [PATCH 01 of 11] SSL: disabled saving tickets to session cache In-Reply-To: <11914CEC-850E-4A8F-8B2D-BC8C15AC7CA3@nginx.com> References: <2cd8fbeb4edc5a99b725.1661482868@vm-bsd.mdounin.ru> <11914CEC-850E-4A8F-8B2D-BC8C15AC7CA3@nginx.com> Message-ID: Hello! On Mon, Sep 26, 2022 at 02:11:17PM +0400, Sergey Kandaurov wrote: > > On 17 Sep 2022, at 00:58, Maxim Dounin wrote: > > > > On Thu, Sep 15, 2022 at 09:36:31AM +0400, Sergey Kandaurov wrote: > > > >>> On 26 Aug 2022, at 07:01, Maxim Dounin wrote: > >>> > >>> # HG changeset patch > >>> # User Maxim Dounin > >>> # Date 1661481945 -10800 > >>> # Fri Aug 26 05:45:45 2022 +0300 > >>> # Node ID 2cd8fbeb4edc5a99b725585edc02a16a8a0c503e > >>> # Parent 069a4813e8d6d7ec662d282a10f5f7062ebd817f > >>> SSL: disabled saving tickets to session cache. > >>> > >>> OpenSSL for TLSv1.3 tries to save tickets into session cache "because some > >>> applications just want to know about the creation of a session". To avoid > >>> trashing session cache with useless data, we do not save such sessions now. > >>> > >> > >> For the record, BoringSSL doesn't seem to call new_session_cb for TLSv1.3 > >> at all, so there is no way to resume sessions with SSL_OP_NO_TICKET set. > >> In contrary, OpenSSL emits stateful tickets in this case, which contain > >> dummy session id used then as a session cache lookup key on server > >> (much like session ids in TLSv1.2) > >> > >> OTOH, without SSL_OP_NO_TICKET set, OpenSSL emits self-containing tickets > >> with enough info to resume session, so nothing to lookup in session cache. > >> The latter makes impractical storing something in session cache, except > >> to use the callback for things like tracking "the creation of a session". > >> Namely, OpenSSL puts session (i.e. something that SSL_get_session returns) > >> and supplementary info to session ticket message as the ticket value. > > > > It looks like you are trying to introduce "stateful tickets" and > > "self-containing tickets" terms, which is somewhat confusing > > unless carefully explained. OpenSSL itself tries to use terms > > "stateful tickets" and "stateless tickets", with the similar > > drawbacks. > > Indeed, OpenSSL SSL_OP_NO_TICKET documentation is what I refer to. > While OpenSSL terms may look odd, they are useful to describe the > difference (and tricks) in TLSv1.3 session resumption with and > without SSL_OP_NO_TICKET, as implemented to OpenSSL specifically. Sure. The problem is that OpenSSL's SSL_OP_NO_TICKET documentation is far from perfect, and I would rather avoid using it for anything but explanation of how SSL_OP_NO_TICKET works with TLSv1.3. > > A better explanation would be to follow generic term "session > > ticket", as originally introduced in RFC 4507 for TLS session > > resumption without server-side state. > > > > In these terms (as always used before introduction of TLSv1.3 and > > currently used in many places, including nginx own documentation > > and the source code) there are two basic mechanisms to resume > > sessions: server-side session cache and session tickets (used to > > resume sessions without server-side state). > > > > Without SSL_OP_NO_TICKET set, OpenSSL uses tickets as long as > > supported by the client. > > > > With SSL_OP_NO_TICKET set, OpenSSL does not use tickets, and uses > > server-side session cache instead (if configured). > > > > The only difference between TLSv1.3 and previous protocols is how > > session ids are sent to the client if server-side session cache is > > used. In case of SSL and TLS up to and including TLSv1.2, session > > ids are sent in the dedicated fields of the ServerHello and > > ClientHello handshake messages. In case of TLSv1.3, dedicated > > fields were removed, so session ids are sent in the > > NewSessionTicket messages ("a database lookup key" in terms of RFC > > 8446). > > > >> With these thoughts in mind, I think log could be clarified to emphasize: > >> - it's not tickets that are stored in cache > >> - with SSL_OP_NO_TICKET set TLSv1.3 session are still saved to lookup by id. > > > > Hope it is clear enough now. > > What I'd like to clarify is the difference between session and (session) > ticket applied to session cache. Ticket is a container used to envelope > session state (essentially, a session) and send in the NewSessionTicket > message "to resume sessions and avoid keeping per-client session state", > as seen in RFC 8446, 4.6.1 (and somewhat similar in 4507/5077). > > struct { > uint32 ticket_lifetime; > uint32 ticket_age_add; > opaque ticket_nonce<0..255>; > opaque ticket<1..2^16-1>; > Extension extensions<0..2^16-2>; > } NewSessionTicket; > > So it looks inappropriate to say that tickets are saved in cache. > OTOH, "ticket" can be seen a correct wording if speaking in terms > of NewSessionTicket ticket field, or in contrast to "session id" > synthesized by OpenSSL for the SSL_OP_NO_TICKET case in TLSv1.3. > In that sense, I'm fine with using "ticket" (not to say changing > "ticket" to "session" or "session state" brings a tautology). > However this looks not so important to spend more time on this, > I'm fine with either case. Sure, it would be more correct to say something like "OpenSSL tries to save sessions into session cache while using tickets for stateless session resumption" (mostly borrowed from SSL_OP_NO_TICKET documentation in OpenSSL 1.0.2[1]). I've used "tickets" instead of "sessions using tickets for stateless session resumption", as it's clearly shorter and only slightly less correct. [1] https://www.openssl.org/docs/man1.0.2/man3/SSL_set_options.html Updated with the above wording for clarity (still with the same summary line though, for simplicity): # HG changeset patch # User Maxim Dounin # Date 1664290542 -10800 # Tue Sep 27 17:55:42 2022 +0300 # Node ID e6b5a2aa0dd91a7f497014359e19458c78f480b3 # Parent a423e314c22fe99fe9faf28f033c266426993105 SSL: disabled saving tickets to session cache. OpenSSL tries to save TLSv1.3 sessions into session cache even when using tickets for stateless session resumption, "because some applications just want to know about the creation of a session". To avoid trashing session cache with useless data, we do not save such sessions now. diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -3818,6 +3818,23 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ ngx_ssl_session_cache_t *cache; u_char buf[NGX_SSL_MAX_SESSION_SIZE]; +#ifdef TLS1_3_VERSION + + /* + * OpenSSL tries to save TLSv1.3 sessions into session cache + * even when using tickets for stateless session resumption, + * "because some applications just want to know about the creation + * of a session"; do not cache such sessions + */ + + if (SSL_version(ssl_conn) == TLS1_3_VERSION + && (SSL_get_options(ssl_conn) & SSL_OP_NO_TICKET) == 0) + { + return 0; + } + +#endif + len = i2d_SSL_SESSION(sess, NULL); /* do not cache too big session */ -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Tue Sep 27 20:57:43 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 27 Sep 2022 23:57:43 +0300 Subject: [PATCH] Linux packages: reword to mention supported RHEL derivatives In-Reply-To: <9d1eec5b03a4ff9d863a.1664280898@QGCD7XG9R9> References: <9d1eec5b03a4ff9d863a.1664280898@QGCD7XG9R9> Message-ID: Hello! On Tue, Sep 27, 2022 at 04:14:58PM +0400, Konstantin Pavlov wrote: > # HG changeset patch > # User Konstantin Pavlov > # Date 1664280815 -14400 > # Tue Sep 27 16:13:35 2022 +0400 > # Node ID 9d1eec5b03a4ff9d863ad49b47721d713dcec76f > # Parent 8878680962d05f778f187efcfb163a76c1dfacb7 > Linux packages: reword to mention supported RHEL derivatives. > > diff -r 8878680962d0 -r 9d1eec5b03a4 xml/en/linux_packages.xml > --- a/xml/en/linux_packages.xml Fri Sep 23 18:30:33 2022 -0700 > +++ b/xml/en/linux_packages.xml Tue Sep 27 16:13:35 2022 +0400 > @@ -7,7 +7,7 @@ >
link="/en/linux_packages.html" > lang="en" > - rev="78"> > + rev="79"> > >
> > @@ -17,7 +17,7 @@ versions: > > > > -RHEL/CentOS > +RHEL and derivatives > >
> > @@ -175,7 +175,12 @@ set up the nginx packages repository. > Afterward, you can install and update nginx from the repository. > > > -
> +
> + > + > +This section applies to Red Hat Enterprise Linux and its derivatives such as > +CentOS, Oracle Linux, Rocky Linux, AlmaLinux. > + > > > Install the prerequisites: > @@ -578,7 +583,8 @@ mainline version, while stable- > sources for stable releases. > To build binary packages, run make in > debian/ directory on Debian/Ubuntu, or in > -rpm/SPECS/ on RHEL/CentOS/SLES/Amazon Linux, or in > +rpm/SPECS/ on > +RHEL/CentOS/Oracle Linux/Rocky Linux/AlmaLinux/SLES/Amazon Linux, or in Shouldn't it be "on RHEL and derivatives, SLES, and Amazon Linux"? Otherwise looks good. > alpine/ on Alpine. > > [...] -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Tue Sep 27 21:06:45 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 28 Sep 2022 00:06:45 +0300 Subject: [PATCH] fix weakness by logging of broken header by incorect proxy protocol (IDS/IPS/LOG-analysis) In-Reply-To: References: Message-ID: Hello! On Mon, Sep 26, 2022 at 11:16:05PM +0200, Dipl. Ing. Sergey Brester via nginx-devel wrote: > below is a patch to fix a weakness by logging of broken header by > incorrect proxy protocol. > > If some service (IDS/IPS) analyzing or monitoring log-file, regularly > formatted lines may be simply confused with lines written not escaped > directly from buffer supplied from foreign source. > Not to mention it may open a certain vector allowing "injection" of user > input in order to avoid detection of failures or even to simulate > malicious traffic from legitimate service. https://trac.nginx.org/nginx/ticket/191 -- Maxim Dounin http://mdounin.ru/ From xeioex at nginx.com Tue Sep 27 23:41:06 2022 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 27 Sep 2022 23:41:06 +0000 Subject: [njs] Fetch: added support for HEAD method. Message-ID: details: https://hg.nginx.org/njs/rev/770f64020ada branches: changeset: 1966:770f64020ada user: Dmitry Volyntsev date: Tue Sep 27 16:40:06 2022 -0700 description: Fetch: added support for HEAD method. This closes #577 issue on Github. diffstat: nginx/ngx_js_fetch.c | 15 ++++++++++++--- 1 files changed, 12 insertions(+), 3 deletions(-) diffs (52 lines): diff -r 43e35b05fd1b -r 770f64020ada nginx/ngx_js_fetch.c --- a/nginx/ngx_js_fetch.c Tue Sep 27 10:56:54 2022 -0700 +++ b/nginx/ngx_js_fetch.c Tue Sep 27 16:40:06 2022 -0700 @@ -66,6 +66,8 @@ struct ngx_js_http_s { njs_str_t url; ngx_array_t headers; + unsigned header_only; + #if (NGX_SSL) ngx_str_t tls_name; ngx_ssl_t *ssl; @@ -473,6 +475,8 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value njs_chb_init(&http->chain, njs_vm_memory_pool(vm)); + http->header_only = njs_strstr_case_eq(&method, &njs_str_value("HEAD")); + njs_chb_append(&http->chain, method.start, method.length); njs_chb_append_literal(&http->chain, " "); @@ -1404,14 +1408,16 @@ ngx_js_http_process_body(ngx_js_http_t * return NGX_ERROR; } - if (http->http_parse.chunked + if (!http->header_only + && http->http_parse.chunked && http->http_parse.content_length_n == -1) { ngx_js_http_error(http, 0, "invalid fetch chunked response"); return NGX_ERROR; } - if (http->http_parse.content_length_n == -1 + if (http->header_only + || http->http_parse.content_length_n == -1 || size == http->http_parse.content_length_n) { ret = njs_vm_external_create(http->vm, njs_value_arg(&http->reply), @@ -1459,7 +1465,10 @@ ngx_js_http_process_body(ngx_js_http_t * } else { size = njs_chb_size(&http->chain); - if (http->http_parse.content_length_n == -1) { + if (http->header_only) { + need = 0; + + } else if (http->http_parse.content_length_n == -1) { need = http->max_response_body_size - size; } else { From xeioex at nginx.com Wed Sep 28 02:30:48 2022 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 28 Sep 2022 02:30:48 +0000 Subject: [njs] Fixed handling of unhandled promise rejection. Message-ID: details: https://hg.nginx.org/njs/rev/1137ad409fee branches: changeset: 1967:1137ad409fee user: Dmitry Volyntsev date: Tue Sep 27 16:52:31 2022 -0700 description: Fixed handling of unhandled promise rejection. Previously, a direct pointer to the first element of an array of rejected promise values was used to convert that element to a string. This is not correct because that pointer may become invalid if rejected promise values array is resized between invocation of "toString" and "valueOf" methods which are called while converting the element to a string. The fix is to ensure that the rejected promise value is never changed. This closes #580 issue on Github. diffstat: src/njs_vm.c | 4 ++-- test/js/promise_rejection_tracker_recursive.t.js | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diffs (32 lines): diff -r 770f64020ada -r 1137ad409fee src/njs_vm.c --- a/src/njs_vm.c Tue Sep 27 16:40:06 2022 -0700 +++ b/src/njs_vm.c Tue Sep 27 16:52:31 2022 -0700 @@ -579,8 +579,8 @@ njs_vm_handle_events(njs_vm_t *vm) } if (njs_vm_unhandled_rejection(vm)) { - ret = njs_value_to_string(vm, &string, - &vm->promise_reason->start[0]); + njs_value_assign(&string, &vm->promise_reason->start[0]); + ret = njs_value_to_string(vm, &string, &string); if (njs_slow_path(ret != NJS_OK)) { return ret; } diff -r 770f64020ada -r 1137ad409fee test/js/promise_rejection_tracker_recursive.t.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/js/promise_rejection_tracker_recursive.t.js Tue Sep 27 16:52:31 2022 -0700 @@ -0,0 +1,14 @@ +/*--- +includes: [] +flags: [] +negative: + phase: runtime +---*/ + +String.toString = async () => { + String.prototype.concat([String, {toString(){ throw String; }}]); + throw 1; +}; +String.valueOf = String; + +(async function() { throw String; })() From thresh at nginx.com Wed Sep 28 08:16:58 2022 From: thresh at nginx.com (Konstantin Pavlov) Date: Wed, 28 Sep 2022 12:16:58 +0400 Subject: [PATCH] Linux packages: reword to mention supported RHEL derivatives In-Reply-To: References: <9d1eec5b03a4ff9d863a.1664280898@QGCD7XG9R9> Message-ID: Hi, On 28/09/2022 12:57 AM, Maxim Dounin wrote: > Hello! > > On Tue, Sep 27, 2022 at 04:14:58PM +0400, Konstantin Pavlov wrote: > >> # HG changeset patch >> # User Konstantin Pavlov >> # Date 1664280815 -14400 >> # Tue Sep 27 16:13:35 2022 +0400 >> # Node ID 9d1eec5b03a4ff9d863ad49b47721d713dcec76f >> # Parent 8878680962d05f778f187efcfb163a76c1dfacb7 >> Linux packages: reword to mention supported RHEL derivatives. >> >> diff -r 8878680962d0 -r 9d1eec5b03a4 xml/en/linux_packages.xml >> --- a/xml/en/linux_packages.xml Fri Sep 23 18:30:33 2022 -0700 >> +++ b/xml/en/linux_packages.xml Tue Sep 27 16:13:35 2022 +0400 >> @@ -7,7 +7,7 @@ >>
> link="/en/linux_packages.html" >> lang="en" >> - rev="78"> >> + rev="79"> >> >>
>> >> @@ -17,7 +17,7 @@ versions: >> >> >> >> -RHEL/CentOS >> +RHEL and derivatives >> >>
>> >> @@ -175,7 +175,12 @@ set up the nginx packages repository. >> Afterward, you can install and update nginx from the repository. >> >> >> -
>> +
>> + >> + >> +This section applies to Red Hat Enterprise Linux and its derivatives such as >> +CentOS, Oracle Linux, Rocky Linux, AlmaLinux. >> + >> >> >> Install the prerequisites: >> @@ -578,7 +583,8 @@ mainline version, while stable- >> sources for stable releases. >> To build binary packages, run make in >> debian/ directory on Debian/Ubuntu, or in >> -rpm/SPECS/ on RHEL/CentOS/SLES/Amazon Linux, or in >> +rpm/SPECS/ on >> +RHEL/CentOS/Oracle Linux/Rocky Linux/AlmaLinux/SLES/Amazon Linux, or in > Shouldn't it be "on RHEL and derivatives, SLES, and Amazon Linux"? > > Otherwise looks good. Indeed, I think that's the best option. Pushed. From arut at nginx.com Wed Sep 28 10:02:30 2022 From: arut at nginx.com (Roman Arutyunyan) Date: Wed, 28 Sep 2022 14:02:30 +0400 Subject: [PATCH] fix weakness by logging of broken header by incorect proxy protocol (IDS/IPS/LOG-analysis) In-Reply-To: References: Message-ID: <20220928100230.a77g7sxygrbwei4x@N00W24XTQX> Hi Sergey, On Mon, Sep 26, 2022 at 11:16:05PM +0200, Dipl. Ing. Sergey Brester via nginx-devel wrote: > > > Hi, > > below is a patch to fix a weakness by logging of broken header by > incorrect proxy protocol. > > If some service (IDS/IPS) analyzing or monitoring log-file, regularly > formatted lines may be simply confused with lines written not escaped > directly from buffer supplied from foreign source. > Not to mention it may open a certain vector allowing "injection" of user > input in order to avoid detection of failures or even to simulate > malicious traffic from legitimate service. > > How to reproduce: > > - enable proxy_protocol for listener and start nginx (here localhost on > port 80); > - echo 'set s [socket localhost 80]; puts $s "testntestntest"; close $s' > | tclsh > > Error-log before fix: > > 2022/09/26 19:29:58 [error] 10104#17144: *3 broken header: "test > test > test > " while reading PROXY protocol, client: 127.0.0.1, server: 0.0.0.0:80 > > Error-log after fix: > > 2022/09/26 22:48:50 [error] 13868#6132: *1 broken header: > "test→→test→→test→→" while reading PROXY protocol, client: 127.0.0.1, > server: 0.0.0.0:80 > > It is not advisable to log such foreign user input unescaped to the > formatted log-file: instead of "...ntestn..." the attacker can write > correctly formatted line simulating a 401-, 403-failure or rate-limit > overran, so IDS could block a innocent service or mistakenly ban > legitimate user. > > The patch proposes simplest escape (LF/CR-char with →, double quote with > single quote and additionally every char larger or equal than 0x80 to > avoid possible logging of "broken" utf-8 sequences or unsupported > surrogates, just as a safest variant for not-valid foreign buffer) > in-place in the malicious buffer directly (without mem-alloc, etc). > > Real life example - > https://github.com/fail2ban/fail2ban/issues/3303#issuecomment-1148691902 Thanks for reporting this. The issue indeed needs to be fixed. Attached is a patch similar to yours that does this. I don't think we need to do anything beyond just cutting the first line since there's another similar place in nginx - ngx_http_log_error_handler(), where exactly that is implemented. Whether we need to skip special characters when logging to nginx log is a topic for a bigger discussion and this will require a much bigger patch. I suggest that we only limit user data to the first line now. [..] -- Roman Arutyunyan -------------- next part -------------- # HG changeset patch # User Roman Arutyunyan # Date 1664359213 -14400 # Wed Sep 28 14:00:13 2022 +0400 # Node ID 001b2449cfd730fd688a7298458e25113c15a947 # Parent 615268a957ab930dc4be49fe5f6f88cd7e377f12 Log only the first line of user input on PROXY protocol v1 error. Previously, all received user input was logged. If a multi-line text was received from client and logged, it could reduce log readability and also make it harder to parse nginx log by scripts. The change brings to PROXY protocol the same behavior that exists for HTTP request line in ngx_http_log_error_handler(). diff --git a/src/core/ngx_proxy_protocol.c b/src/core/ngx_proxy_protocol.c --- a/src/core/ngx_proxy_protocol.c +++ b/src/core/ngx_proxy_protocol.c @@ -185,8 +185,14 @@ skip: invalid: + for (p = buf; p != last; p++) { + if (*p == CR || *p == LF) { + break; + } + } + ngx_log_error(NGX_LOG_ERR, c->log, 0, - "broken header: \"%*s\"", (size_t) (last - buf), buf); + "broken header: \"%*s\"", (size_t) (p - buf), buf); return NULL; } From serg.brester at sebres.de Wed Sep 28 11:07:18 2022 From: serg.brester at sebres.de (Dipl. Ing. Sergey Brester) Date: Wed, 28 Sep 2022 13:07:18 +0200 Subject: [PATCH] fix weakness by logging of broken header by incorect proxy protocol (IDS/IPS/LOG-analysis) In-Reply-To: <20220928100230.a77g7sxygrbwei4x@N00W24XTQX> References: <20220928100230.a77g7sxygrbwei4x@N00W24XTQX> Message-ID: <4fdb470d5cf2a6c87ad6aad3d47c9ad9@sebres.de> Sure, this was also my first intention. Just after all I thought the whole buffer could be better in order to provide a possibility to debug for someone searching for a bug. But there are another aids that would help, so indeed let it be so. As for the rest, well it is surely a subject of different discussion. However I think someone who monitoring logs with foreign data is able to ignore wrong chars or unsupported surrogates using appropriate encoding facilities, but... But I would suggest at least to replace a quote-char to provide opportunity to bypass the buffer in quotes with something similar this `... header: "[^"]*" ...` if parsing of such lines is needed. So may be this one either: + for (p = buf; p != last; p++) { + if (*p == '"') { + *p = '''; continue; + } + if (*p == CR || *p == LF) { + break; + } + } Although I don't believe the safe data (like IP, etc) shall take place after "unsafe" (foreign) input (especially of variable length), but it is rather a matter of common logging format for the error-log. I mean normally one would rather expect something like that: - [error] 13868#6132: *1 broken header: "...unsafe..." while reading PROXY protocol, client: 127.0.0.1, server: 0.0.0.0:80 + [error] 13868#6132: while reading PROXY protocol, client: 127.0.0.1, server: 0.0.0.0:80 - *1 broken header: "...unsafe..." Unfortunatelly errorlog is not configurable at the moment at all. 28.09.2022 12:02, Roman Arutyunyan wrote: > Hi Sergey, > > Thanks for reporting this. The issue indeed needs to be fixed. Attached is > a patch similar to yours that does this. I don't think we need to do anything > beyond just cutting the first line since there's another similar place in > nginx - ngx_http_log_error_handler(), where exactly that is implemented. > > Whether we need to skip special characters when logging to nginx log is > a topic for a bigger discussion and this will require a much bigger patch. > I suggest that we only limit user data to the first line now. > > [..] > > -- > Roman Arutyunyan -------------- next part -------------- An HTML attachment was scrubbed... URL: From mat999 at gmail.com Wed Sep 28 13:32:01 2022 From: mat999 at gmail.com (Mathew Heard) Date: Wed, 28 Sep 2022 23:32:01 +1000 Subject: [PATCH] fix weakness by logging of broken header by incorect proxy protocol (IDS/IPS/LOG-analysis) In-Reply-To: <4fdb470d5cf2a6c87ad6aad3d47c9ad9@sebres.de> References: <20220928100230.a77g7sxygrbwei4x@N00W24XTQX> <4fdb470d5cf2a6c87ad6aad3d47c9ad9@sebres.de> Message-ID: I really like the making safe of the error log as opposed to truncation. The more information logged in cases like this the better. Alternatively what about something that indicates further data was truncated? On Wed, 28 Sep 2022, 21:07 Dipl. Ing. Sergey Brester via nginx-devel, < nginx-devel at nginx.org> wrote: > Sure, this was also my first intention. Just after all I thought the whole > buffer > could be better in order to provide a possibility to debug for someone > searching > for a bug. But there are another aids that would help, so indeed let it be > so. > > As for the rest, well it is surely a subject of different discussion. > However I think > someone who monitoring logs with foreign data is able to ignore wrong chars > or unsupported surrogates using appropriate encoding facilities, but... > But I would suggest at least to replace a quote-char to provide opportunity > to bypass the buffer in quotes with something similar this `... header: > "[^"]*" ...` > if parsing of such lines is needed. > > So may be this one either: > > + for (p = buf; p != last; p++) {*+ if (*p == '"') { > + *p = '\''; continue; > + } > *+ if (*p == CR || *p == LF) { > + break; > + } > + } > > Although I don't believe the safe data (like IP, etc) shall take place > after "unsafe" > (foreign) input (especially of variable length), but it is rather a matter > of common > logging format for the error-log. > I mean normally one would rather expect something like that: > > - [error] 13868#6132: *1 broken header: "...unsafe..." while reading PROXY protocol, client: 127.0.0.1, server: 0.0.0.0:80 > > + [error] 13868#6132: while reading PROXY protocol, client: 127.0.0.1, server: 0.0.0.0:80 - *1 broken header: "...unsafe..." > > Unfortunatelly errorlog is not configurable at the moment at all. > > 28.09.2022 12:02, Roman Arutyunyan wrote: > > Hi Sergey, > > Thanks for reporting this. The issue indeed needs to be fixed. Attached is > a patch similar to yours that does this. I don't think we need to do anything > beyond just cutting the first line since there's another similar place in > nginx - ngx_http_log_error_handler(), where exactly that is implemented. > > Whether we need to skip special characters when logging to nginx log is > a topic for a bigger discussion and this will require a much bigger patch. > I suggest that we only limit user data to the first line now. > > [..] > > -- > Roman Arutyunyan > > _______________________________________________ > nginx-devel mailing list -- nginx-devel at nginx.org > To unsubscribe send an email to nginx-devel-leave at nginx.org > -------------- next part -------------- An HTML attachment was scrubbed... URL: From arut at nginx.com Wed Sep 28 15:49:38 2022 From: arut at nginx.com (Roman Arutyunyan) Date: Wed, 28 Sep 2022 19:49:38 +0400 Subject: [PATCH] fix weakness by logging of broken header by incorect proxy protocol (IDS/IPS/LOG-analysis) In-Reply-To: <4fdb470d5cf2a6c87ad6aad3d47c9ad9@sebres.de> References: <20220928100230.a77g7sxygrbwei4x@N00W24XTQX> <4fdb470d5cf2a6c87ad6aad3d47c9ad9@sebres.de> Message-ID: <20220928154938.iamloptgwdci4ool@N00W24XTQX> Hi, On Wed, Sep 28, 2022 at 01:07:18PM +0200, Dipl. Ing. Sergey Brester wrote: > > > Sure, this was also my first intention. Just after all I thought the > whole buffer > could be better in order to provide a possibility to debug for someone > searching > for a bug. A possible option would be to log data in hex, but that would make the log less readable. > But there are another aids that would help, so indeed let it > be so. > > As for the rest, well it is surely a subject of different discussion. > However I think > someone who monitoring logs with foreign data is able to ignore wrong > chars > or unsupported surrogates using appropriate encoding facilities, but... > But I would suggest at least to replace a quote-char to provide > opportunity > to bypass the buffer in quotes with something similar this `... header: > "[^"]*" ...` > if parsing of such lines is needed. > > So may be this one either: > > + for (p = buf; p != last; p++) { > + if (*p == '"') { > + *p = '''; continue; > + } > + if (*p == CR || *p == LF) { > + break; > + } > + } I don't think we'll do something like this. Spoiling the buffer would be confusing for people. > Although I don't believe the safe data (like IP, etc) shall take place > after "unsafe" > (foreign) input (especially of variable length), but it is rather a > matter of common > logging format for the error-log. > I mean normally one would rather expect something like that: > > - [error] 13868#6132: *1 broken header: "...unsafe..." while reading > PROXY protocol, client: 127.0.0.1, server: 0.0.0.0:80 > > + [error] 13868#6132: while reading PROXY protocol, client: 127.0.0.1, > server: 0.0.0.0:80 - *1 broken header: "...unsafe..." > > Unfortunatelly errorlog is not configurable at the moment at all. You're right. We try to follow this rule where possible, but everything starting from "while" till the end of line is added automatically by the http log handler ngx_http_log_error(). Changing this would break compatibility with existing log parsing scripts. Also, ngx_http_log_error_handler() logs request line, which can contain unsafe and miltiline data as well. If we move the "while" part to the start of the line, this unsafe request line will move with it. > 28.09.2022 12:02, Roman Arutyunyan wrote: > > > Hi Sergey, > > > > Thanks for reporting this. The issue indeed needs to be fixed. Attached is > > a patch similar to yours that does this. I don't think we need to do anything > > beyond just cutting the first line since there's another similar place in > > nginx - ngx_http_log_error_handler(), where exactly that is implemented. > > > > Whether we need to skip special characters when logging to nginx log is > > a topic for a bigger discussion and this will require a much bigger patch. > > I suggest that we only limit user data to the first line now. > > > > [..] > > > > -- > > Roman Arutyunyan > -- Roman Arutyunyan From mdounin at mdounin.ru Wed Sep 28 18:37:37 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 28 Sep 2022 21:37:37 +0300 Subject: [PATCH 11 of 11] SSL: automatic rotation of session ticket keys In-Reply-To: <384C1C36-43CA-490B-9559-DD77DE6346E6@nginx.com> References: <5c26fe5f6ab0bf4c0d18.1661482878@vm-bsd.mdounin.ru> <384C1C36-43CA-490B-9559-DD77DE6346E6@nginx.com> Message-ID: Hello! On Mon, Sep 26, 2022 at 02:17:18PM +0400, Sergey Kandaurov wrote: > > On 17 Sep 2022, at 01:08, Maxim Dounin wrote: > > > > On Thu, Sep 15, 2022 at 09:50:24AM +0400, Sergey Kandaurov wrote: > > > >>> On 26 Aug 2022, at 07:01, Maxim Dounin wrote: > >>> > >>> # HG changeset patch > >>> # User Maxim Dounin > >>> # Date 1661481958 -10800 > >>> # Fri Aug 26 05:45:58 2022 +0300 > >>> # Node ID 5c26fe5f6ab0bf4c0d18cae8f6f6483348243d4b > >>> # Parent 2487bf5766f79c813b3397b3bb897424c3590445 > >>> SSL: automatic rotation of session ticket keys. > >>> > >>> As long as ssl_session_cache in shared memory is configured, session ticket > >>> keys are now automatically generated in shared memory, and rotated > >>> periodically. > >> > >> It looks odd how session cache is (ab)used to store ticket keys. > >> I understand that it is comfortable to reuse existing shared zone > >> (and not to touch configuration) but probably a more correct way > >> is to create a separate zone to store keys? > >> Something pretty much the same as ssl_session_cache syntax: > >> ssl_session_ticket_key file | shared:name:size > >> > >> What do you think? > > > > I don't think it is a good idea to introduce additional shared > > zones here, especially given that it only needs about 160 bytes of > > shared memory. It really complicates things for users for no real > > reason. > > > > Rather, I was thinking about using nginx_shared_zone, which is > > always available, but it is rather special and will require > > additional integration code. Further, as a single contention > > point it might be a problem, at least unless lock avoidance is > > also implemented (see below). And a single key for all servers > > might not be the best solution either. On the other hand, > > ssl_session_cache in shared memory is readily available and > > logically related, and I see no reasons not to use it to rotate > > ticket keys if it's configured. > > I've been pondering for a while and tend to agree to go with > the ssl_session_cache route for its simplicity. > I share the dislike to introduce new syntax, while > nginx_shared_zone looks unrelated enough to put SSL bits in. I don't think that nginx_shared_zone is unrelated, it's a basic shared zone used for multiple nginx-wide tasks, including mostly module-specific ones, such as stub_status. I don't see why it shouldn't contain some keying material used for SSL ticket keys. But using it certainly would require some additional work, notably a) mutex initialization for proper locking (since there is no shared pool in the zone) and b) some key derivation logic, as we probably want to avoid using identical keys for all server blocks. > > If there will be understanding that we need to rotate ticket keys > > in all cases, even without ssl_session_cache in shared memory > > being configured, the idea of using nginx_shared_zone might be > > revisited. > > Well, an obvious downside is the need to enable a session cache > which might be undesirable in certain memory constrained systems > if you intend to resume only with session tickets. Even though > the impact is reduced by keeping shared memory zone to the minimum, > (re)using session cache to store only ticket keys nonetheless > requires to allocate memory shared zone of at least 8 page sizes. > OTOH, it quite uncommon to have clients that use tickets only. I rather see ticket key rotation as a free feature you'll now get if you have ssl_session_cache enabled. If there will be enough demand for ticket key rotation without ssl_session_cache enabled, using nginx_shared_zone might be revisited, see above. [...] > >>> + > >>> + keys = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_ticket_keys_index); > >>> + if (keys == NULL) { > >>> + return NGX_OK; > >>> + } > >>> + > >>> + key = keys->elts; > >>> + > >>> + if (!key[0].shared) { > >>> + return NGX_OK; > >>> + } > >>> + > >>> + now = ngx_time(); > >>> + expire = now + SSL_CTX_get_timeout(ssl_ctx); > >>> + > >>> + shm_zone = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_cache_index); > >>> + > >>> + cache = shm_zone->data; > >>> + shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; > >>> + > >>> + ngx_shmtx_lock(&shpool->mutex); > >>> + > >>> + key = cache->ticket_keys; > >>> + > >>> + if (key[0].expire == 0) { > >>> + > >>> + /* initialize the current key */ > >>> + > >>> + if (RAND_bytes(buf, 80) != 1) { > >>> + ngx_ssl_error(NGX_LOG_ALERT, log, 0, "RAND_bytes() failed"); > >>> + ngx_shmtx_unlock(&shpool->mutex); > >>> + return NGX_ERROR; > >>> + } > >>> + > >>> + key->shared = 1; > >>> + key->expire = expire; > >>> + key->size = 80; > >>> + ngx_memcpy(key->name, buf, 16); > >>> + ngx_memcpy(key->hmac_key, buf + 16, 32); > >>> + ngx_memcpy(key->aes_key, buf + 48, 32); > >>> + > >>> + ngx_explicit_memzero(&buf, 80); > >>> + > >>> + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, > >>> + "ssl ticket key: \"%*xs\"", > >>> + (size_t) 16, key->name); > >>> + } > >>> + > >>> + if (key[1].expire < now) { > >>> + > >>> + /* > >>> + * if the previous key is no longer needed (or not initialized), > >>> + * replace it with the current key and generate new current key > >>> + */ > >>> + > >>> + key[1] = key[0]; > >>> + > >>> + if (RAND_bytes(buf, 80) != 1) { > >>> + ngx_ssl_error(NGX_LOG_ALERT, log, 0, "RAND_bytes() failed"); > >>> + ngx_shmtx_unlock(&shpool->mutex); > >>> + return NGX_ERROR; > >>> + } > >>> + > >>> + key->shared = 1; > >>> + key->expire = expire; > >>> + key->size = 80; > >>> + ngx_memcpy(key->name, buf, 16); > >>> + ngx_memcpy(key->hmac_key, buf + 16, 32); > >>> + ngx_memcpy(key->aes_key, buf + 48, 32); > >>> + > >>> + ngx_explicit_memzero(&buf, 80); > >>> + > >>> + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, > >>> + "ssl ticket key: \"%*xs\"", > >>> + (size_t) 16, key->name); > >>> + } > >> > >> I wonder if cpu intensive ops can be moved from under the lock. > >> At least, buf[] can be removed if write random data directly > >> to ngx_ssl_ticket_key_t. This eliminates 3 memory copies > >> (likely optimized by compiler to a single) and explicit write. > > > > Micro-optimizations are not something important here: new keys are > > generated once per SSL session timeout, that is, once per 300 > > seconds by default. As such, the code is kept as close as > > possible to the ticket keys reading in > > ngx_ssl_session_ticket_keys(), and not dependant on the > > ngx_ssl_ticket_key_t structure layout. > > Ok, that makes sense. > > > > > What can be interesting here is to avoid locking at all unless we > > are going to update keys (change expiration of the current key, or > > switch to the next key). > > > > To do so without races, the code has to maintains 3 keys: current, > > previous, and next. If a worker will switch to the next key > > earlier, other workers will still be able to decrypt new tickets, > > since they will be encrypted with the next key. > > > > But with the SSL session cache in shared memory we have to lock > > shared zone anyway to create sessions, and this was never seen to > > be a performance bottleneck. > > Good to know this is not a hot path. > Nonetheless, it is good to preserve a status-quo regarding > the locking and/or keep to the minimum its coverage/impact. > > For the record, with this patch and ticket keys rotation enabled, > each ticket operation resembles existing locking in session cache: > - 1 lock per new ticket encryption > - 1 lock per each ticket decryption + 1 lock for possible renew > In TLSv1.3 this is a bit worse: > - 2 locks, each for issuing 2 new tickets > - 2 locks, each for ticket decryption + obligatory ticket renew Note that previously (before the 1st patch from this series) with ssl_session_cache enabled and TLSv1.3 this used to result in 2 sessions being created and both saved to the session cache, with corresponding 2 locks. > With the locking patch, locking acquirement reduces at least to: > - 1 lock total for decryption and renew, as that happens in 1 sec. > - 1 lock for issuing 2 new tickets in TLSv1.3, the same reasons > > In loaded scenarios, working with tickets requires obtaining > at most 1 lock per 1 sec. per each worker process. Correct. > And by the way, while reviewing this patch, I noticed that > OpenSSL doesn't allow a client to gracefully renew TLSv1.2 session > when the client receives a new session ticket in resumed sessions. > In practice, it is visible when client resumes a not yet expired > session encrypted with not a fresh ticket key (after rotation), > which results in sending a new session ticket. > See ssl_update_cache() for the !s->hit condition. > In the opposite, BoringSSL always allows to renew TLSv1.2 sessions. You mean on the client side? Yes, it looks like ngx_ssl_new_client_session() won't be called for such a new session ticket, and updated ticket will be never saved. This might need to be worked around. This should be safe with the key rotation logic introduced in this patch though, given that the previous key is preserved till the last ticket encrypted with it is expected to expire. One of the possible solutions might be to avoid re-encryption of tickets with the new key, as the old key is anyway expected to be available till the session expires. > Additionally, BoringSSL view a bit differs on a session freshness. > Unlike in OpenSSL, BoringSSL treats sessions as fresh only if > the session's time of issuance + timeout points to the future. > In the opposite, OpenSSL resumes sessions expiring in the current second. > See math in ssl_get_prev_session() vs ssl_session_is_time_valid(). > This is an insignificant detail, though. Current code preserves the previous key till key expiration is in the past, and this should be long enough for all cases. > > Given that, and the added code > > complexity, I've dropped the patch which avoids locking, at least > > for now. > > > > This might worth revisiting though. Patch provided below for > > reference. > > > > I see no reasons not to commit it. > Though, it might make sense to let the dust settle down a bit > and commit the locking patch later. Well, if it's already reviewed I see no reasons to delay the commit. [...] -- Maxim Dounin http://mdounin.ru/ From E.Grebenshchikov at F5.com Thu Sep 29 00:02:32 2022 From: E.Grebenshchikov at F5.com (Eugene Grebenschikov) Date: Thu, 29 Sep 2022 00:02:32 +0000 Subject: [nginx-tests] Tests: upstream certificates specified as an empty string. Message-ID: changeset:   1797:c2c188c91488 tag:         tip user:        Eugene Grebenshchikov date:        Wed Sep 28 16:29:50 2022 -0700 summary:     Tests: upstream certificates specified as an empty string. diff -r e1fd234baac0 -r c2c188c91488 grpc_ssl.t --- a/grpc_ssl.t Tue Sep 27 16:11:56 2022 -0700 +++ b/grpc_ssl.t Wed Sep 28 16:29:50 2022 -0700 @@ -29,7 +29,7 @@  $t->{_configure_args} =~ /OpenSSL ([\d\.]+)/;  plan(skip_all => 'OpenSSL too old') unless defined $1 and $1 ge '1.0.2';   -$t->write_file_expand('nginx.conf', <<'EOF')->plan(38); +$t->write_file_expand('nginx.conf', <<'EOF')->plan(39);    %%TEST_GLOBALS%%   @@ -46,6 +46,8 @@          keepalive 1;      }   +    grpc_ssl_session_reuse off; +      server {          listen       127.0.0.1:8081 http2 ssl;          server_name  localhost; @@ -61,6 +63,7 @@          location / {              grpc_pass 127.0.0.1:8082;              add_header X-Connection $connection; +            add_header X-Verify $ssl_client_verify;          }      }   @@ -89,6 +92,13 @@              }          }   +        location /nocert { +            grpc_pass grpcs://127.0.0.1:8081; + +            grpc_ssl_certificate ""; +            grpc_ssl_certificate_key ""; +        } +          location /KeepAlive {              grpc_pass grpcs://u;          } @@ -232,6 +242,14 @@  ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;  is($frame->{headers}{'x-connection'}, $c, 'keepalive - connection reuse');   +# no client certificate + +$f->{http_start}('/nocert'); +$f->{data}('Hello'); +$frames = $f->{http_end}(); +($frame) = grep { $_->{type} eq "HEADERS" } @$frames; +is($frame->{headers}{'x-verify'}, 'NONE', 'request - no client certificate'); +  ###############################################################################    sub grpc { diff -r e1fd234baac0 -r c2c188c91488 proxy_ssl_certificate.t --- a/proxy_ssl_certificate.t Tue Sep 27 16:11:56 2022 -0700 +++ b/proxy_ssl_certificate.t Wed Sep 28 16:29:50 2022 -0700 @@ -24,7 +24,7 @@  select STDOUT; $| = 1;    my $t = Test::Nginx->new()->has(qw/http http_ssl proxy/) - ->has_daemon('openssl')->plan(5); + ->has_daemon('openssl')->plan(6);    $t->write_file_expand('nginx.conf', <<'EOF');   @@ -62,6 +62,12 @@              proxy_ssl_certificate_key 3.example.com.key;              proxy_ssl_password_file password;          } + +        location /nocert { +            proxy_pass https://127.0.0.1:8082/; +            proxy_ssl_certificate ""; +            proxy_ssl_certificate_key ""; +        }      }        server { @@ -139,6 +145,7 @@    like(http_get('/verify'), qr/X-Verify: SUCCESS/ms, 'verify certificate');  like(http_get('/fail'), qr/X-Verify: FAILED/ms, 'fail certificate'); +like(http_get('/nocert'), qr/X-Verify: NONE/ms, 'no certificate');  like(http_get('/encrypted'), qr/X-Verify: SUCCESS/ms, 'with encrypted key');    like(http_get('/verify'), qr!X-Name: /?CN=1.example!, 'valid certificate'); diff -r e1fd234baac0 -r c2c188c91488 stream_proxy_ssl_certificate.t --- a/stream_proxy_ssl_certificate.t Tue Sep 27 16:11:56 2022 -0700 +++ b/stream_proxy_ssl_certificate.t Wed Sep 28 16:29:50 2022 -0700 @@ -24,7 +24,7 @@  select STDOUT; $| = 1;    my $t = Test::Nginx->new()->has(qw/stream stream_ssl http http_ssl/) - ->has_daemon('openssl')->plan(5); + ->has_daemon('openssl')->plan(6);    $t->write_file_expand('nginx.conf', <<'EOF');   @@ -65,6 +65,14 @@          proxy_ssl_certificate_key 3.example.com.key;          proxy_ssl_password_file password;      } + +    server { +        listen      127.0.0.1:8085; +        proxy_pass  127.0.0.1:8080; + +        proxy_ssl_certificate ""; +        proxy_ssl_certificate_key ""; +    }  }    http { @@ -149,6 +157,8 @@   qr/X-Verify: FAILED/ms, 'fail certificate');  like(http_get('/', socket => getconn('127.0.0.1:' . port(8084))),   qr/X-Verify: SUCCESS/ms, 'with encrypted key'); +like(http_get('/', socket => getconn('127.0.0.1:' . port(8085))), + qr/X-Verify: NONE/ms, 'no certificate');    like(http_get('/', socket => getconn('127.0.0.1:' . port(8082))),   qr!X-Name: /?CN=1.example!, 'valid certificate'); diff -r e1fd234baac0 -r c2c188c91488 uwsgi_ssl_certificate.t --- a/uwsgi_ssl_certificate.t Tue Sep 27 16:11:56 2022 -0700 +++ b/uwsgi_ssl_certificate.t Wed Sep 28 16:29:50 2022 -0700 @@ -24,7 +24,7 @@  select STDOUT; $| = 1;    my $t = Test::Nginx->new()->has(qw/http http_ssl uwsgi/) - ->has_daemon('openssl')->plan(5); + ->has_daemon('openssl')->plan(6);    $t->write_file_expand('nginx.conf', <<'EOF');   @@ -62,6 +62,12 @@              uwsgi_ssl_certificate_key 3.example.com.key;              uwsgi_ssl_password_file password;          } + +        location /nocert { +            uwsgi_pass suwsgi://127.0.0.1:8081; +            uwsgi_ssl_certificate ""; +            uwsgi_ssl_certificate_key ""; +        }      }        # stub to implement SSL logic for tests @@ -137,6 +143,7 @@    like(http_get('/verify'), qr/X-Verify: SUCCESS/ms, 'verify certificate');  like(http_get('/fail'), qr/X-Verify: FAILED/ms, 'fail certificate'); +like(http_get('/nocert'), qr/X-Verify: NONE/ms, 'no certificate');  like(http_get('/encrypted'), qr/X-Verify: SUCCESS/ms, 'with encrypted key');    like(http_get('/verify'), qr!X-Name: /?CN=1.example!, 'valid certificate'); From xeioex at nginx.com Thu Sep 29 07:36:13 2022 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 29 Sep 2022 07:36:13 +0000 Subject: [njs] Style. Message-ID: details: https://hg.nginx.org/njs/rev/60cf808fe4ff branches: changeset: 1968:60cf808fe4ff user: Dmitry Volyntsev date: Thu Sep 29 00:35:34 2022 -0700 description: Style. diffstat: src/njs_array.c | 11 ++++++++--- src/njs_object_prop.c | 9 ++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diffs (61 lines): diff -r 1137ad409fee -r 60cf808fe4ff src/njs_array.c --- a/src/njs_array.c Tue Sep 27 16:52:31 2022 -0700 +++ b/src/njs_array.c Thu Sep 29 00:35:34 2022 -0700 @@ -173,7 +173,8 @@ njs_array_convert_to_slow_array(njs_vm_t njs_int_t -njs_array_length_redefine(njs_vm_t *vm, njs_value_t *value, uint32_t length, int writable) +njs_array_length_redefine(njs_vm_t *vm, njs_value_t *value, uint32_t length, + int writable) { njs_object_prop_t *prop; @@ -1631,14 +1632,18 @@ njs_array_indices_handler(const void *fi njs_string_get(val1, &str1); njs_string_get(val2, &str2); - cmp_res = strncmp((const char *) str1.start, (const char *) str2.start, - njs_min(str1.length, str2.length)); + cmp_res = strncmp((const char *) str1.start, (const char *) str2.start, + njs_min(str1.length, str2.length)); + if (cmp_res == 0) { if (str1.length < str2.length) { return -1; + } else if (str1.length > str2.length) { return 1; + } else { + return 0; } } diff -r 1137ad409fee -r 60cf808fe4ff src/njs_object_prop.c --- a/src/njs_object_prop.c Tue Sep 27 16:52:31 2022 -0700 +++ b/src/njs_object_prop.c Thu Sep 29 00:35:34 2022 -0700 @@ -467,9 +467,11 @@ done: if (prev->configurable != 1 && prev->writable != 1 && - !njs_values_strict_equal(&prev->value, &prop->value)) + !njs_values_strict_equal(&prev->value, + &prop->value)) { - njs_type_error(vm, "Cannot redefine property: \"length\""); + njs_type_error(vm, "Cannot redefine " + "property: \"length\""); return NJS_ERROR; } @@ -477,7 +479,8 @@ done: prev->writable = prop->writable; } - return njs_array_length_set(vm, object, prev, &prop->value); + return njs_array_length_set(vm, object, prev, + &prop->value); } } } From pluknet at nginx.com Thu Sep 29 15:09:15 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Thu, 29 Sep 2022 19:09:15 +0400 Subject: [PATCH 01 of 11] SSL: disabled saving tickets to session cache In-Reply-To: References: <2cd8fbeb4edc5a99b725.1661482868@vm-bsd.mdounin.ru> <11914CEC-850E-4A8F-8B2D-BC8C15AC7CA3@nginx.com> Message-ID: <5F135C12-46DB-43F3-8FC9-50FB252152A3@nginx.com> > On 28 Sep 2022, at 00:14, Maxim Dounin wrote: > > Hello! > > On Mon, Sep 26, 2022 at 02:11:17PM +0400, Sergey Kandaurov wrote: > >>> On 17 Sep 2022, at 00:58, Maxim Dounin wrote: >>> >>> On Thu, Sep 15, 2022 at 09:36:31AM +0400, Sergey Kandaurov wrote: >>> >>>>> On 26 Aug 2022, at 07:01, Maxim Dounin wrote: >>>>> >>>>> # HG changeset patch >>>>> # User Maxim Dounin >>>>> # Date 1661481945 -10800 >>>>> # Fri Aug 26 05:45:45 2022 +0300 >>>>> # Node ID 2cd8fbeb4edc5a99b725585edc02a16a8a0c503e >>>>> # Parent 069a4813e8d6d7ec662d282a10f5f7062ebd817f >>>>> SSL: disabled saving tickets to session cache. >>>>> >>>>> OpenSSL for TLSv1.3 tries to save tickets into session cache "because some >>>>> applications just want to know about the creation of a session". To avoid >>>>> trashing session cache with useless data, we do not save such sessions now. >>>>> >>>> >>>> For the record, BoringSSL doesn't seem to call new_session_cb for TLSv1.3 >>>> at all, so there is no way to resume sessions with SSL_OP_NO_TICKET set. >>>> In contrary, OpenSSL emits stateful tickets in this case, which contain >>>> dummy session id used then as a session cache lookup key on server >>>> (much like session ids in TLSv1.2) >>>> >>>> OTOH, without SSL_OP_NO_TICKET set, OpenSSL emits self-containing tickets >>>> with enough info to resume session, so nothing to lookup in session cache. >>>> The latter makes impractical storing something in session cache, except >>>> to use the callback for things like tracking "the creation of a session". >>>> Namely, OpenSSL puts session (i.e. something that SSL_get_session returns) >>>> and supplementary info to session ticket message as the ticket value. >>> >>> It looks like you are trying to introduce "stateful tickets" and >>> "self-containing tickets" terms, which is somewhat confusing >>> unless carefully explained. OpenSSL itself tries to use terms >>> "stateful tickets" and "stateless tickets", with the similar >>> drawbacks. >> >> Indeed, OpenSSL SSL_OP_NO_TICKET documentation is what I refer to. >> While OpenSSL terms may look odd, they are useful to describe the >> difference (and tricks) in TLSv1.3 session resumption with and >> without SSL_OP_NO_TICKET, as implemented to OpenSSL specifically. > > Sure. The problem is that OpenSSL's SSL_OP_NO_TICKET > documentation is far from perfect, and I would rather avoid using > it for anything but explanation of how SSL_OP_NO_TICKET works with > TLSv1.3. > >>> A better explanation would be to follow generic term "session >>> ticket", as originally introduced in RFC 4507 for TLS session >>> resumption without server-side state. >>> >>> In these terms (as always used before introduction of TLSv1.3 and >>> currently used in many places, including nginx own documentation >>> and the source code) there are two basic mechanisms to resume >>> sessions: server-side session cache and session tickets (used to >>> resume sessions without server-side state). >>> >>> Without SSL_OP_NO_TICKET set, OpenSSL uses tickets as long as >>> supported by the client. >>> >>> With SSL_OP_NO_TICKET set, OpenSSL does not use tickets, and uses >>> server-side session cache instead (if configured). >>> >>> The only difference between TLSv1.3 and previous protocols is how >>> session ids are sent to the client if server-side session cache is >>> used. In case of SSL and TLS up to and including TLSv1.2, session >>> ids are sent in the dedicated fields of the ServerHello and >>> ClientHello handshake messages. In case of TLSv1.3, dedicated >>> fields were removed, so session ids are sent in the >>> NewSessionTicket messages ("a database lookup key" in terms of RFC >>> 8446). >>> >>>> With these thoughts in mind, I think log could be clarified to emphasize: >>>> - it's not tickets that are stored in cache >>>> - with SSL_OP_NO_TICKET set TLSv1.3 session are still saved to lookup by id. >>> >>> Hope it is clear enough now. >> >> What I'd like to clarify is the difference between session and (session) >> ticket applied to session cache. Ticket is a container used to envelope >> session state (essentially, a session) and send in the NewSessionTicket >> message "to resume sessions and avoid keeping per-client session state", >> as seen in RFC 8446, 4.6.1 (and somewhat similar in 4507/5077). >> >> struct { >> uint32 ticket_lifetime; >> uint32 ticket_age_add; >> opaque ticket_nonce<0..255>; >> opaque ticket<1..2^16-1>; >> Extension extensions<0..2^16-2>; >> } NewSessionTicket; >> >> So it looks inappropriate to say that tickets are saved in cache. >> OTOH, "ticket" can be seen a correct wording if speaking in terms >> of NewSessionTicket ticket field, or in contrast to "session id" >> synthesized by OpenSSL for the SSL_OP_NO_TICKET case in TLSv1.3. >> In that sense, I'm fine with using "ticket" (not to say changing >> "ticket" to "session" or "session state" brings a tautology). >> However this looks not so important to spend more time on this, >> I'm fine with either case. > > Sure, it would be more correct to say something like "OpenSSL > tries to save sessions into session cache while using tickets for > stateless session resumption" (mostly borrowed from > SSL_OP_NO_TICKET documentation in OpenSSL 1.0.2[1]). I've used > "tickets" instead of "sessions using tickets for stateless session > resumption", as it's clearly shorter and only slightly less correct. > > [1] https://www.openssl.org/docs/man1.0.2/man3/SSL_set_options.html > > Updated with the above wording for clarity (still with the same > summary line though, for simplicity): > > # HG changeset patch > # User Maxim Dounin > # Date 1664290542 -10800 > # Tue Sep 27 17:55:42 2022 +0300 > # Node ID e6b5a2aa0dd91a7f497014359e19458c78f480b3 > # Parent a423e314c22fe99fe9faf28f033c266426993105 > SSL: disabled saving tickets to session cache. > > OpenSSL tries to save TLSv1.3 sessions into session cache even when using > tickets for stateless session resumption, "because some applications just > want to know about the creation of a session". To avoid trashing session > cache with useless data, we do not save such sessions now. > > diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c > --- a/src/event/ngx_event_openssl.c > +++ b/src/event/ngx_event_openssl.c > @@ -3818,6 +3818,23 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_ > ngx_ssl_session_cache_t *cache; > u_char buf[NGX_SSL_MAX_SESSION_SIZE]; > > +#ifdef TLS1_3_VERSION > + > + /* > + * OpenSSL tries to save TLSv1.3 sessions into session cache > + * even when using tickets for stateless session resumption, > + * "because some applications just want to know about the creation > + * of a session"; do not cache such sessions > + */ > + > + if (SSL_version(ssl_conn) == TLS1_3_VERSION > + && (SSL_get_options(ssl_conn) & SSL_OP_NO_TICKET) == 0) > + { > + return 0; > + } > + > +#endif > + > len = i2d_SSL_SESSION(sess, NULL); > > /* do not cache too big session */ > Looks good. -- Sergey Kandaurov From pluknet at nginx.com Thu Sep 29 16:00:03 2022 From: pluknet at nginx.com (Sergey Kandaurov) Date: Thu, 29 Sep 2022 20:00:03 +0400 Subject: [PATCH 11 of 11] SSL: automatic rotation of session ticket keys In-Reply-To: References: <5c26fe5f6ab0bf4c0d18.1661482878@vm-bsd.mdounin.ru> <384C1C36-43CA-490B-9559-DD77DE6346E6@nginx.com> Message-ID: <12290DCD-4B09-4DA0-8057-16172C5F5D28@nginx.com> > On 28 Sep 2022, at 22:37, Maxim Dounin wrote: > > Hello! > > On Mon, Sep 26, 2022 at 02:17:18PM +0400, Sergey Kandaurov wrote: > >>> On 17 Sep 2022, at 01:08, Maxim Dounin wrote: >>> >>> On Thu, Sep 15, 2022 at 09:50:24AM +0400, Sergey Kandaurov wrote: >>> >>>>> On 26 Aug 2022, at 07:01, Maxim Dounin wrote: >>>>> >>>>> # HG changeset patch >>>>> # User Maxim Dounin >>>>> # Date 1661481958 -10800 >>>>> # Fri Aug 26 05:45:58 2022 +0300 >>>>> # Node ID 5c26fe5f6ab0bf4c0d18cae8f6f6483348243d4b >>>>> # Parent 2487bf5766f79c813b3397b3bb897424c3590445 >>>>> SSL: automatic rotation of session ticket keys. >>>>> >>>>> As long as ssl_session_cache in shared memory is configured, session ticket >>>>> keys are now automatically generated in shared memory, and rotated >>>>> periodically. >>>> >>>> It looks odd how session cache is (ab)used to store ticket keys. >>>> I understand that it is comfortable to reuse existing shared zone >>>> (and not to touch configuration) but probably a more correct way >>>> is to create a separate zone to store keys? >>>> Something pretty much the same as ssl_session_cache syntax: >>>> ssl_session_ticket_key file | shared:name:size >>>> >>>> What do you think? >>> >>> I don't think it is a good idea to introduce additional shared >>> zones here, especially given that it only needs about 160 bytes of >>> shared memory. It really complicates things for users for no real >>> reason. >>> >>> Rather, I was thinking about using nginx_shared_zone, which is >>> always available, but it is rather special and will require >>> additional integration code. Further, as a single contention >>> point it might be a problem, at least unless lock avoidance is >>> also implemented (see below). And a single key for all servers >>> might not be the best solution either. On the other hand, >>> ssl_session_cache in shared memory is readily available and >>> logically related, and I see no reasons not to use it to rotate >>> ticket keys if it's configured. >> >> I've been pondering for a while and tend to agree to go with >> the ssl_session_cache route for its simplicity. >> I share the dislike to introduce new syntax, while >> nginx_shared_zone looks unrelated enough to put SSL bits in. > > I don't think that nginx_shared_zone is unrelated, it's a basic > shared zone used for multiple nginx-wide tasks, including mostly > module-specific ones, such as stub_status. I don't see why it > shouldn't contain some keying material used for SSL ticket keys. > Well, from the point of view of a basic shared zone for various tasks, this sounds convincing. If carefully separate storage logic for nginx_shared_zone (i.e. pure allocation in ngx_event_module_init() e.g. as currently done for stub_status) and SSL-related logic that uses this storage, then this approach is quite promising. > But using it certainly would require some additional work, notably > a) mutex initialization for proper locking (since there is no > shared pool in the zone) and b) some key derivation logic, as we > probably want to avoid using identical keys for all server blocks. Agree. > >>> If there will be understanding that we need to rotate ticket keys >>> in all cases, even without ssl_session_cache in shared memory >>> being configured, the idea of using nginx_shared_zone might be >>> revisited. >> >> Well, an obvious downside is the need to enable a session cache >> which might be undesirable in certain memory constrained systems >> if you intend to resume only with session tickets. Even though >> the impact is reduced by keeping shared memory zone to the minimum, >> (re)using session cache to store only ticket keys nonetheless >> requires to allocate memory shared zone of at least 8 page sizes. >> OTOH, it quite uncommon to have clients that use tickets only. > > I rather see ticket key rotation as a free feature you'll now get > if you have ssl_session_cache enabled. If there will be enough > demand for ticket key rotation without ssl_session_cache enabled, > using nginx_shared_zone might be revisited, see above. > Ok. > [...] > >>>>> + >>>>> + keys = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_ticket_keys_index); >>>>> + if (keys == NULL) { >>>>> + return NGX_OK; >>>>> + } >>>>> + >>>>> + key = keys->elts; >>>>> + >>>>> + if (!key[0].shared) { >>>>> + return NGX_OK; >>>>> + } >>>>> + >>>>> + now = ngx_time(); >>>>> + expire = now + SSL_CTX_get_timeout(ssl_ctx); >>>>> + >>>>> + shm_zone = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_cache_index); >>>>> + >>>>> + cache = shm_zone->data; >>>>> + shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; >>>>> + >>>>> + ngx_shmtx_lock(&shpool->mutex); >>>>> + >>>>> + key = cache->ticket_keys; >>>>> + >>>>> + if (key[0].expire == 0) { >>>>> + >>>>> + /* initialize the current key */ >>>>> + >>>>> + if (RAND_bytes(buf, 80) != 1) { >>>>> + ngx_ssl_error(NGX_LOG_ALERT, log, 0, "RAND_bytes() failed"); >>>>> + ngx_shmtx_unlock(&shpool->mutex); >>>>> + return NGX_ERROR; >>>>> + } >>>>> + >>>>> + key->shared = 1; >>>>> + key->expire = expire; >>>>> + key->size = 80; >>>>> + ngx_memcpy(key->name, buf, 16); >>>>> + ngx_memcpy(key->hmac_key, buf + 16, 32); >>>>> + ngx_memcpy(key->aes_key, buf + 48, 32); >>>>> + >>>>> + ngx_explicit_memzero(&buf, 80); >>>>> + >>>>> + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, >>>>> + "ssl ticket key: \"%*xs\"", >>>>> + (size_t) 16, key->name); >>>>> + } >>>>> + >>>>> + if (key[1].expire < now) { >>>>> + >>>>> + /* >>>>> + * if the previous key is no longer needed (or not initialized), >>>>> + * replace it with the current key and generate new current key >>>>> + */ >>>>> + >>>>> + key[1] = key[0]; >>>>> + >>>>> + if (RAND_bytes(buf, 80) != 1) { >>>>> + ngx_ssl_error(NGX_LOG_ALERT, log, 0, "RAND_bytes() failed"); >>>>> + ngx_shmtx_unlock(&shpool->mutex); >>>>> + return NGX_ERROR; >>>>> + } >>>>> + >>>>> + key->shared = 1; >>>>> + key->expire = expire; >>>>> + key->size = 80; >>>>> + ngx_memcpy(key->name, buf, 16); >>>>> + ngx_memcpy(key->hmac_key, buf + 16, 32); >>>>> + ngx_memcpy(key->aes_key, buf + 48, 32); >>>>> + >>>>> + ngx_explicit_memzero(&buf, 80); >>>>> + >>>>> + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, >>>>> + "ssl ticket key: \"%*xs\"", >>>>> + (size_t) 16, key->name); >>>>> + } >>>> >>>> I wonder if cpu intensive ops can be moved from under the lock. >>>> At least, buf[] can be removed if write random data directly >>>> to ngx_ssl_ticket_key_t. This eliminates 3 memory copies >>>> (likely optimized by compiler to a single) and explicit write. >>> >>> Micro-optimizations are not something important here: new keys are >>> generated once per SSL session timeout, that is, once per 300 >>> seconds by default. As such, the code is kept as close as >>> possible to the ticket keys reading in >>> ngx_ssl_session_ticket_keys(), and not dependant on the >>> ngx_ssl_ticket_key_t structure layout. >> >> Ok, that makes sense. >> >>> >>> What can be interesting here is to avoid locking at all unless we >>> are going to update keys (change expiration of the current key, or >>> switch to the next key). >>> >>> To do so without races, the code has to maintains 3 keys: current, >>> previous, and next. If a worker will switch to the next key >>> earlier, other workers will still be able to decrypt new tickets, >>> since they will be encrypted with the next key. >>> >>> But with the SSL session cache in shared memory we have to lock >>> shared zone anyway to create sessions, and this was never seen to >>> be a performance bottleneck. >> >> Good to know this is not a hot path. >> Nonetheless, it is good to preserve a status-quo regarding >> the locking and/or keep to the minimum its coverage/impact. >> >> For the record, with this patch and ticket keys rotation enabled, >> each ticket operation resembles existing locking in session cache: >> - 1 lock per new ticket encryption >> - 1 lock per each ticket decryption + 1 lock for possible renew >> In TLSv1.3 this is a bit worse: >> - 2 locks, each for issuing 2 new tickets >> - 2 locks, each for ticket decryption + obligatory ticket renew > > Note that previously (before the 1st patch from this series) with > ssl_session_cache enabled and TLSv1.3 this used to result in 2 > sessions being created and both saved to the session cache, with > corresponding 2 locks. > >> With the locking patch, locking acquirement reduces at least to: >> - 1 lock total for decryption and renew, as that happens in 1 sec. >> - 1 lock for issuing 2 new tickets in TLSv1.3, the same reasons >> >> In loaded scenarios, working with tickets requires obtaining >> at most 1 lock per 1 sec. per each worker process. > > Correct. > >> And by the way, while reviewing this patch, I noticed that >> OpenSSL doesn't allow a client to gracefully renew TLSv1.2 session >> when the client receives a new session ticket in resumed sessions. >> In practice, it is visible when client resumes a not yet expired >> session encrypted with not a fresh ticket key (after rotation), >> which results in sending a new session ticket. >> See ssl_update_cache() for the !s->hit condition. >> In the opposite, BoringSSL always allows to renew TLSv1.2 sessions. > > You mean on the client side? Yes, it looks like > ngx_ssl_new_client_session() won't be called for such a new > session ticket, and updated ticket will be never saved. This > might need to be worked around. Yes, I mean the client side. > > This should be safe with the key rotation logic introduced in this > patch though, given that the previous key is preserved till the > last ticket encrypted with it is expected to expire. > > One of the possible solutions might be to avoid re-encryption of > tickets with the new key, as the old key is anyway expected to be > available till the session expires. I don't think it's worth the effort. If I got you right, and as far as I understand, re-encrypting the ticket essentially means sending a fresh session (renewal). Avoid doing that will result in eventual session expiration and a full SSL handshake. Please correct me if I'm wrong. > >> Additionally, BoringSSL view a bit differs on a session freshness. >> Unlike in OpenSSL, BoringSSL treats sessions as fresh only if >> the session's time of issuance + timeout points to the future. >> In the opposite, OpenSSL resumes sessions expiring in the current second. >> See math in ssl_get_prev_session() vs ssl_session_is_time_valid(). >> This is an insignificant detail, though. > > Current code preserves the previous key till key expiration is in > the past, and this should be long enough for all cases. > >>> Given that, and the added code >>> complexity, I've dropped the patch which avoids locking, at least >>> for now. >>> >>> This might worth revisiting though. Patch provided below for >>> reference. >>> >> >> I see no reasons not to commit it. >> Though, it might make sense to let the dust settle down a bit >> and commit the locking patch later. > > Well, if it's already reviewed I see no reasons to delay the > commit. Yes, it looks good for me. As well as other patches in the series. -- Sergey Kandaurov From dnj0496 at gmail.com Thu Sep 29 23:47:43 2022 From: dnj0496 at gmail.com (Dk Jack) Date: Thu, 29 Sep 2022 16:47:43 -0700 Subject: Send custom response in req. body filter. Message-ID: Hi, In my module I have a body filter. In the body filter, I respond with a 4XX if the body contains a matching pattern. This is working correctly. However, I would like to send a custom response message when the above situation occurs. I tried doing this by attaching a buffer to the output headers and calling send header before returning from the filter handler as shown below. ----------------------------------------------------------------------- r->err_status = http_status; r->headers_out.status = http_status; r->headers_out.content_length_n = buf->last - buf->pos; ngx_str_set(&r->headers_out.content_type, "text/plain"); rc = ngx_http_send_header(r); ----------------------------------------------------------------------- However, this causes the "header already sent" error to be raised in the error.log. The reason for this is because, ngx_http_core_content_phase calls ngx_http_finalize_request after returning from the body filter. ngx_http_finalize_request calls ngx_http_special_response_handler if the body filter returns any codes greater than NGX_HTTP_SPECIAL_RESPONSE. The ngx_http_special_response_handler in turn calls ngx_http_send_special_response which again tries to send the response header using the stock error message in the ngx_http_error_pages array for the return code in question. The alert is raised since we already sent it. Is there a way to prevent the special_response handler from either sending this message again or use the buffer attached to the request in the body filter? Thanks and regards, Dk. -------------- next part -------------- An HTML attachment was scrubbed... URL: From xeioex at nginx.com Fri Sep 30 00:05:27 2022 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 30 Sep 2022 00:05:27 +0000 Subject: [njs] Removed aligned values which are not required since 0.6.0. Message-ID: details: https://hg.nginx.org/njs/rev/c2a05d35ad72 branches: changeset: 1969:c2a05d35ad72 user: Dmitry Volyntsev date: Thu Sep 29 00:47:43 2022 -0700 description: Removed aligned values which are not required since 0.6.0. diffstat: src/njs_function.c | 11 ++--------- 1 files changed, 2 insertions(+), 9 deletions(-) diffs (27 lines): diff -r 60cf808fe4ff -r c2a05d35ad72 src/njs_function.c --- a/src/njs_function.c Thu Sep 29 00:35:34 2022 -0700 +++ b/src/njs_function.c Thu Sep 29 00:47:43 2022 -0700 @@ -586,21 +586,14 @@ njs_function_call2(njs_vm_t *vm, njs_fun const njs_value_t *this, const njs_value_t *args, njs_uint_t nargs, njs_value_t *retval, njs_bool_t ctor) { - njs_int_t ret; - njs_value_t dst njs_aligned(16); + njs_int_t ret; ret = njs_function_frame(vm, function, this, args, nargs, ctor); if (njs_slow_path(ret != NJS_OK)) { return ret; } - ret = njs_function_frame_invoke(vm, &dst); - - if (ret == NJS_OK) { - *retval = dst; - } - - return ret; + return njs_function_frame_invoke(vm, retval); } From xeioex at nginx.com Fri Sep 30 00:05:29 2022 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 30 Sep 2022 00:05:29 +0000 Subject: [njs] Introduced njs_object_proto_lookup() where appropriate. Message-ID: details: https://hg.nginx.org/njs/rev/7f43ac9cdc30 branches: changeset: 1970:7f43ac9cdc30 user: Dmitry Volyntsev date: Thu Sep 29 16:32:45 2022 -0700 description: Introduced njs_object_proto_lookup() where appropriate. diffstat: src/njs_function.c | 21 +++++---------------- src/njs_string.c | 16 +++------------- 2 files changed, 8 insertions(+), 29 deletions(-) diffs (68 lines): diff -r c2a05d35ad72 -r 7f43ac9cdc30 src/njs_function.c --- a/src/njs_function.c Thu Sep 29 00:47:43 2022 -0700 +++ b/src/njs_function.c Thu Sep 29 16:32:45 2022 -0700 @@ -1302,26 +1302,15 @@ njs_int_t njs_function_instance_length(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { - njs_object_t *proto; njs_function_t *function; - proto = njs_object(value); - - do { - if (njs_fast_path(proto->type == NJS_FUNCTION)) { - break; - } - - proto = proto->__proto__; - } while (proto != NULL); - - if (njs_slow_path(proto == NULL)) { - njs_internal_error(vm, "no function in proto chain"); - return NJS_ERROR; + function = njs_object_proto_lookup(njs_object(value), NJS_FUNCTION, + njs_function_t); + if (njs_slow_path(function == NULL)) { + njs_set_undefined(retval); + return NJS_DECLINED; } - function = (njs_function_t *) proto; - njs_set_number(retval, function->args_count); return NJS_OK; diff -r c2a05d35ad72 -r 7f43ac9cdc30 src/njs_string.c --- a/src/njs_string.c Thu Sep 29 00:47:43 2022 -0700 +++ b/src/njs_string.c Thu Sep 29 16:32:45 2022 -0700 @@ -664,7 +664,6 @@ njs_string_instance_length(njs_vm_t *vm, { size_t size; uintptr_t length; - njs_object_t *proto; njs_object_value_t *ov; /* @@ -674,18 +673,9 @@ njs_string_instance_length(njs_vm_t *vm, length = 0; if (njs_slow_path(njs_is_object(value))) { - proto = njs_object(value); - - do { - if (njs_fast_path(proto->type == NJS_OBJECT_VALUE)) { - break; - } - - proto = proto->__proto__; - } while (proto != NULL); - - if (proto != NULL) { - ov = (njs_object_value_t *) proto; + ov = njs_object_proto_lookup(njs_object(value), NJS_OBJECT_VALUE, + njs_object_value_t); + if (ov != NULL) { value = &ov->value; } } From xeioex at nginx.com Fri Sep 30 00:05:31 2022 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 30 Sep 2022 00:05:31 +0000 Subject: [njs] Fixed njs_assert() macro. Message-ID: details: https://hg.nginx.org/njs/rev/b61a7a4f286e branches: changeset: 1971:b61a7a4f286e user: Dmitry Volyntsev date: Thu Sep 29 16:32:52 2022 -0700 description: Fixed njs_assert() macro. Previously, the argument was always evaluated, even if NJS_DEBUG was undefined. diffstat: src/njs_assert.h | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diffs (14 lines): diff -r 7f43ac9cdc30 -r b61a7a4f286e src/njs_assert.h --- a/src/njs_assert.h Thu Sep 29 16:32:45 2022 -0700 +++ b/src/njs_assert.h Thu Sep 29 16:32:52 2022 -0700 @@ -29,8 +29,8 @@ #else -#define njs_assert(condition) (void) (condition) -#define njs_assert_msg(condition, fmt, ...) (void) (condition) +#define njs_assert(condition) +#define njs_assert_msg(condition, fmt, ...) #endif From mdounin at mdounin.ru Fri Sep 30 09:31:22 2022 From: mdounin at mdounin.ru (Maxim Dounin) Date: Fri, 30 Sep 2022 12:31:22 +0300 Subject: Send custom response in req. body filter. In-Reply-To: References: Message-ID: Hello! On Thu, Sep 29, 2022 at 04:47:43PM -0700, Dk Jack wrote: > Hi, > In my module I have a body filter. In the body filter, I respond with a 4XX > if the body contains a matching pattern. This is working correctly. > > However, I would like to send a custom response message when the above > situation occurs. I tried doing this by attaching a buffer to the output > headers and calling send header before returning from the filter handler as > shown below. > > ----------------------------------------------------------------------- > r->err_status = http_status; > r->headers_out.status = http_status; > r->headers_out.content_length_n = buf->last - buf->pos; > ngx_str_set(&r->headers_out.content_type, "text/plain"); > > rc = ngx_http_send_header(r); > ----------------------------------------------------------------------- > > However, this causes the "header already sent" error to be raised in the > error.log. The reason for this is because, ngx_http_core_content_phase > calls ngx_http_finalize_request after returning from the body filter. > > ngx_http_finalize_request calls ngx_http_special_response_handler if the > body filter returns any codes greater than NGX_HTTP_SPECIAL_RESPONSE. The > ngx_http_special_response_handler in turn calls > ngx_http_send_special_response which again tries to send the response > header using the stock error message in the ngx_http_error_pages array for > the return code in question. The alert is raised since we already sent it. > > Is there a way to prevent the special_response handler from either sending > this message again or use the buffer attached to the request in the body > filter? It is not possible to return a response body from a request body filter, you can only return an error code. If you want to return a custom response, consider configuring an error_page and return an appropriate custom response message from there. -- Maxim Dounin http://mdounin.ru/ From dnj0496 at gmail.com Fri Sep 30 16:15:12 2022 From: dnj0496 at gmail.com (Dk Jack) Date: Fri, 30 Sep 2022 09:15:12 -0700 Subject: Send custom response in req. body filter. In-Reply-To: References: Message-ID: Thanks Dounin, After stepping through the code, I came to the same conclusion but wanted to check just in case I missed something. Thanks for the help. -Dk On Fri, Sep 30, 2022 at 2:32 AM Maxim Dounin wrote: > Hello! > > On Thu, Sep 29, 2022 at 04:47:43PM -0700, Dk Jack wrote: > > > Hi, > > In my module I have a body filter. In the body filter, I respond with a > 4XX > > if the body contains a matching pattern. This is working correctly. > > > > However, I would like to send a custom response message when the above > > situation occurs. I tried doing this by attaching a buffer to the output > > headers and calling send header before returning from the filter handler > as > > shown below. > > > > ----------------------------------------------------------------------- > > r->err_status = http_status; > > r->headers_out.status = http_status; > > r->headers_out.content_length_n = buf->last - buf->pos; > > ngx_str_set(&r->headers_out.content_type, "text/plain"); > > > > rc = ngx_http_send_header(r); > > ----------------------------------------------------------------------- > > > > However, this causes the "header already sent" error to be raised in the > > error.log. The reason for this is because, ngx_http_core_content_phase > > calls ngx_http_finalize_request after returning from the body filter. > > > > ngx_http_finalize_request calls ngx_http_special_response_handler if the > > body filter returns any codes greater than NGX_HTTP_SPECIAL_RESPONSE. The > > ngx_http_special_response_handler in turn calls > > ngx_http_send_special_response which again tries to send the response > > header using the stock error message in the ngx_http_error_pages array > for > > the return code in question. The alert is raised since we already sent > it. > > > > Is there a way to prevent the special_response handler from either > sending > > this message again or use the buffer attached to the request in the body > > filter? > > It is not possible to return a response body from a request body > filter, you can only return an error code. > > If you want to return a custom response, consider configuring an > error_page and return an appropriate custom response message from > there. > > -- > Maxim Dounin > http://mdounin.ru/ > _______________________________________________ > nginx-devel mailing list -- nginx-devel at nginx.org > To unsubscribe send an email to nginx-devel-leave at nginx.org > -------------- next part -------------- An HTML attachment was scrubbed... URL: