diff -Nru nginx-1.18.0/auto/init nginx-1.20.1/auto/init --- nginx-1.18.0/auto/init 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/auto/init 2021-05-25 12:35:38.000000000 +0000 @@ -48,4 +48,6 @@ clean: rm -rf Makefile $NGX_OBJS + +.PHONY: default clean END diff -Nru nginx-1.18.0/auto/install nginx-1.20.1/auto/install --- nginx-1.18.0/auto/install 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/auto/install 2021-05-25 12:35:38.000000000 +0000 @@ -215,4 +215,6 @@ test -f $NGX_PID_PATH.oldbin kill -QUIT \`cat $NGX_PID_PATH.oldbin\` + +.PHONY: build install modules upgrade END diff -Nru nginx-1.18.0/auto/make nginx-1.20.1/auto/make --- nginx-1.18.0/auto/make 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/auto/make 2021-05-25 12:35:38.000000000 +0000 @@ -313,7 +313,7 @@ END fi - done + done fi @@ -343,7 +343,7 @@ $ngx_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX END - done + done fi @@ -373,7 +373,7 @@ $ngx_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX END - done + done fi @@ -399,7 +399,7 @@ $ngx_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX END - done + done fi @@ -431,7 +431,7 @@ $ngx_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX END - done + done fi @@ -502,6 +502,7 @@ for ngx_module in $DYNAMIC_MODULES do eval ngx_module_srcs="\$${ngx_module}_SRCS" + eval ngx_module_shrd="\$${ngx_module}_SHRD" eval eval ngx_module_libs="\\\"\$${ngx_module}_LIBS\\\"" eval ngx_module_modules="\$${ngx_module}_MODULES" @@ -567,7 +568,7 @@ | sed -e "s/\(.*\.\)c/\1$ngx_objext/"` ngx_module_objs= - for ngx_src in $ngx_module_srcs + for ngx_src in $ngx_module_srcs $ngx_module_shrd do case "$ngx_src" in src/*) diff -Nru nginx-1.18.0/auto/module nginx-1.20.1/auto/module --- nginx-1.18.0/auto/module 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/auto/module 2021-05-25 12:35:38.000000000 +0000 @@ -17,7 +17,6 @@ done DYNAMIC_MODULES="$DYNAMIC_MODULES $ngx_module" - eval ${ngx_module}_SRCS=\"$ngx_module_srcs\" eval ${ngx_module}_MODULES=\"$ngx_module_name\" @@ -31,6 +30,30 @@ eval ${ngx_module}_ORDER=\"$ngx_module_order\" fi + srcs= + shrd= + for src in $ngx_module_srcs + do + found=no + for old in $DYNAMIC_MODULES_SRCS + do + if [ $src = $old ]; then + found=yes + break + fi + done + + if [ $found = no ]; then + srcs="$srcs $src" + else + shrd="$shrd $src" + fi + done + eval ${ngx_module}_SRCS=\"$srcs\" + eval ${ngx_module}_SHRD=\"$shrd\" + + DYNAMIC_MODULES_SRCS="$DYNAMIC_MODULES_SRCS $srcs" + if test -n "$ngx_module_incs"; then CORE_INCS="$CORE_INCS $ngx_module_incs" fi @@ -107,7 +130,24 @@ eval ${ngx_module_type}_MODULES=\"\$${ngx_module_type}_MODULES \ $ngx_module_name\" - NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_module_srcs" + srcs= + for src in $ngx_module_srcs + do + found=no + for old in $NGX_ADDON_SRCS + do + if [ $src = $old ]; then + found=yes + break + fi + done + + if [ $found = no ]; then + srcs="$srcs $src" + fi + done + + NGX_ADDON_SRCS="$NGX_ADDON_SRCS $srcs" if test -n "$ngx_module_incs"; then eval ${ngx_var}_INCS=\"\$${ngx_var}_INCS $ngx_module_incs\" diff -Nru nginx-1.18.0/auto/modules nginx-1.20.1/auto/modules --- nginx-1.18.0/auto/modules 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/auto/modules 2021-05-25 12:35:38.000000000 +0000 @@ -985,6 +985,12 @@ ngx_module_srcs=src/mail/ngx_mail_proxy_module.c . auto/module + + ngx_module_name=ngx_mail_realip_module + ngx_module_deps= + ngx_module_srcs=src/mail/ngx_mail_realip_module.c + + . auto/module fi @@ -1118,6 +1124,16 @@ . auto/module fi + + if [ $STREAM_SET = YES ]; then + ngx_module_name=ngx_stream_set_module + ngx_module_deps= + ngx_module_srcs=src/stream/ngx_stream_set_module.c + ngx_module_libs= + ngx_module_link=$STREAM_SET + + . auto/module + fi if [ $STREAM_UPSTREAM_HASH = YES ]; then ngx_module_name=ngx_stream_upstream_hash_module diff -Nru nginx-1.18.0/auto/options nginx-1.20.1/auto/options --- nginx-1.18.0/auto/options 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/auto/options 2021-05-25 12:35:38.000000000 +0000 @@ -124,6 +124,7 @@ STREAM_MAP=YES STREAM_SPLIT_CLIENTS=YES STREAM_RETURN=YES +STREAM_SET=YES STREAM_UPSTREAM_HASH=YES STREAM_UPSTREAM_LEAST_CONN=YES STREAM_UPSTREAM_RANDOM=YES @@ -131,8 +132,10 @@ STREAM_SSL_PREREAD=NO DYNAMIC_MODULES= +DYNAMIC_MODULES_SRCS= NGX_ADDONS= +NGX_ADDON_SRCS= NGX_ADDON_DEPS= DYNAMIC_ADDONS= @@ -324,6 +327,7 @@ --without-stream_split_clients_module) STREAM_SPLIT_CLIENTS=NO ;; --without-stream_return_module) STREAM_RETURN=NO ;; + --without-stream_set_module) STREAM_SET=NO ;; --without-stream_upstream_hash_module) STREAM_UPSTREAM_HASH=NO ;; --without-stream_upstream_least_conn_module) @@ -538,6 +542,7 @@ --without-stream_split_clients_module disable ngx_stream_split_clients_module --without-stream_return_module disable ngx_stream_return_module + --without-stream_set_module disable ngx_stream_set_module --without-stream_upstream_hash_module disable ngx_stream_upstream_hash_module --without-stream_upstream_least_conn_module diff -Nru nginx-1.18.0/auto/os/linux nginx-1.20.1/auto/os/linux --- nginx-1.18.0/auto/os/linux 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/auto/os/linux 2021-05-25 12:35:38.000000000 +0000 @@ -86,6 +86,31 @@ ee.data.ptr = NULL; epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ee)" . auto/feature + + + # eventfd() + + ngx_feature="eventfd()" + ngx_feature_name="NGX_HAVE_EVENTFD" + ngx_feature_run=no + ngx_feature_incs="#include " + ngx_feature_path= + ngx_feature_libs= + ngx_feature_test="(void) eventfd(0, 0)" + . auto/feature + + if [ $ngx_found = yes ]; then + have=NGX_HAVE_SYS_EVENTFD_H . auto/have + fi + + + if [ $ngx_found = no ]; then + + ngx_feature="eventfd() (SYS_eventfd)" + ngx_feature_incs="#include " + ngx_feature_test="(void) SYS_eventfd" + . auto/feature + fi fi diff -Nru nginx-1.18.0/auto/unix nginx-1.20.1/auto/unix --- nginx-1.18.0/auto/unix 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/auto/unix 2021-05-25 12:35:38.000000000 +0000 @@ -582,29 +582,6 @@ END exit 1 fi - -else - - ngx_feature="eventfd()" - ngx_feature_name="NGX_HAVE_EVENTFD" - ngx_feature_run=no - ngx_feature_incs="#include " - ngx_feature_path= - ngx_feature_libs= - ngx_feature_test="(void) eventfd(0, 0)" - . auto/feature - - if [ $ngx_found = yes ]; then - have=NGX_HAVE_SYS_EVENTFD_H . auto/have - fi - - if [ $ngx_found = no ]; then - - ngx_feature="eventfd() (SYS_eventfd)" - ngx_feature_incs="#include " - ngx_feature_test="(void) SYS_eventfd" - . auto/feature - fi fi @@ -727,56 +704,44 @@ . auto/feature -ngx_feature="sys_nerr" -ngx_feature_name="NGX_SYS_NERR" -ngx_feature_run=value -ngx_feature_incs='#include - #include ' +# strerrordesc_np(), introduced in glibc 2.32 + +ngx_feature="strerrordesc_np()" +ngx_feature_name="NGX_HAVE_STRERRORDESC_NP" +ngx_feature_run=no +ngx_feature_incs='#include ' ngx_feature_path= ngx_feature_libs= -ngx_feature_test='printf("%d", sys_nerr);' +ngx_feature_test="char *p; p = strerrordesc_np(0); + if (p == NULL) return 1" . auto/feature if [ $ngx_found = no ]; then - # Cygiwn defines _sys_nerr - ngx_feature="_sys_nerr" + ngx_feature="sys_nerr" ngx_feature_name="NGX_SYS_NERR" ngx_feature_run=value ngx_feature_incs='#include #include ' ngx_feature_path= ngx_feature_libs= - ngx_feature_test='printf("%d", _sys_nerr);' + ngx_feature_test='printf("%d", sys_nerr);' . auto/feature fi if [ $ngx_found = no ]; then - # Solaris has no sys_nerr - ngx_feature='maximum errno' - ngx_feature_name=NGX_SYS_NERR + # Cygiwn defines _sys_nerr + ngx_feature="_sys_nerr" + ngx_feature_name="NGX_SYS_NERR" ngx_feature_run=value ngx_feature_incs='#include - #include #include ' ngx_feature_path= ngx_feature_libs= - ngx_feature_test='int n; - char *p; - for (n = 1; n < 1000; n++) { - errno = 0; - p = strerror(n); - if (errno == EINVAL - || p == NULL - || strncmp(p, "Unknown error", 13) == 0) - { - break; - } - } - printf("%d", n);' + ngx_feature_test='printf("%d", _sys_nerr);' . auto/feature fi diff -Nru nginx-1.18.0/CHANGES nginx-1.20.1/CHANGES --- nginx-1.18.0/CHANGES 2020-04-21 14:09:06.000000000 +0000 +++ nginx-1.20.1/CHANGES 2021-05-25 12:35:44.000000000 +0000 @@ -1,7 +1,241 @@ -Changes with nginx 1.18.0 21 Apr 2020 +Changes with nginx 1.20.1 25 May 2021 - *) 1.18.x stable branch. + *) Security: 1-byte memory overwrite might occur during DNS server + response processing if the "resolver" directive was used, allowing an + attacker who is able to forge UDP packets from the DNS server to + cause worker process crash or, potentially, arbitrary code execution + (CVE-2021-23017). + + +Changes with nginx 1.20.0 20 Apr 2021 + + *) 1.20.x stable branch. + + +Changes with nginx 1.19.10 13 Apr 2021 + + *) Change: the default value of the "keepalive_requests" directive was + changed to 1000. + + *) Feature: the "keepalive_time" directive. + + *) Feature: the $connection_time variable. + + *) Workaround: "gzip filter failed to use preallocated memory" alerts + appeared in logs when using zlib-ng. + + +Changes with nginx 1.19.9 30 Mar 2021 + + *) Bugfix: nginx could not be built with the mail proxy module, but + without the ngx_mail_ssl_module; the bug had appeared in 1.19.8. + + *) Bugfix: "upstream sent response body larger than indicated content + length" errors might occur when working with gRPC backends; the bug + had appeared in 1.19.1. + + *) Bugfix: nginx might not close a connection till keepalive timeout + expiration if the connection was closed by the client while + discarding the request body. + + *) Bugfix: nginx might not detect that a connection was already closed + by the client when waiting for auth_delay or limit_req delay, or when + working with backends. + + *) Bugfix: in the eventport method. + + +Changes with nginx 1.19.8 09 Mar 2021 + + *) Feature: flags in the "proxy_cookie_flags" directive can now contain + variables. + + *) Feature: the "proxy_protocol" parameter of the "listen" directive, + the "proxy_protocol" and "set_real_ip_from" directives in mail proxy. + + *) Bugfix: HTTP/2 connections were immediately closed when using + "keepalive_timeout 0"; the bug had appeared in 1.19.7. + + *) Bugfix: some errors were logged as unknown if nginx was built with + glibc 2.32. + + *) Bugfix: in the eventport method. + + +Changes with nginx 1.19.7 16 Feb 2021 + + *) Change: connections handling in HTTP/2 has been changed to better + match HTTP/1.x; the "http2_recv_timeout", "http2_idle_timeout", and + "http2_max_requests" directives have been removed, the + "keepalive_timeout" and "keepalive_requests" directives should be + used instead. + + *) Change: the "http2_max_field_size" and "http2_max_header_size" + directives have been removed, the "large_client_header_buffers" + directive should be used instead. + + *) Feature: now, if free worker connections are exhausted, nginx starts + closing not only keepalive connections, but also connections in + lingering close. + + *) Bugfix: "zero size buf in output" alerts might appear in logs if an + upstream server returned an incorrect response during unbuffered + proxying; the bug had appeared in 1.19.1. + + *) Bugfix: HEAD requests were handled incorrectly if the "return" + directive was used with the "image_filter" or "xslt_stylesheet" + directives. + + *) Bugfix: in the "add_trailer" directive. + + +Changes with nginx 1.19.6 15 Dec 2020 + + *) Bugfix: "no live upstreams" errors if a "server" inside "upstream" + block was marked as "down". + + *) Bugfix: a segmentation fault might occur in a worker process if HTTPS + was used; the bug had appeared in 1.19.5. + + *) Bugfix: nginx returned the 400 response on requests like + "GET http://example.com?args HTTP/1.0". + + *) Bugfix: in the ngx_http_flv_module and ngx_http_mp4_module. + Thanks to Chris Newton. + + +Changes with nginx 1.19.5 24 Nov 2020 + + *) Feature: the -e switch. + + *) Feature: the same source files can now be specified in different + modules while building addon modules. + + *) Bugfix: SSL shutdown did not work when lingering close was used. + + *) Bugfix: "upstream sent frame for closed stream" errors might occur + when working with gRPC backends. + + *) Bugfix: in request body filters internal API. + + +Changes with nginx 1.19.4 27 Oct 2020 + + *) Feature: the "ssl_conf_command", "proxy_ssl_conf_command", + "grpc_ssl_conf_command", and "uwsgi_ssl_conf_command" directives. + + *) Feature: the "ssl_reject_handshake" directive. + + *) Feature: the "proxy_smtp_auth" directive in mail proxy. + + +Changes with nginx 1.19.3 29 Sep 2020 + + *) Feature: the ngx_stream_set_module. + + *) Feature: the "proxy_cookie_flags" directive. + + *) Feature: the "userid_flags" directive. + + *) Bugfix: the "stale-if-error" cache control extension was erroneously + applied if backend returned a response with status code 500, 502, + 503, 504, 403, 404, or 429. + + *) Bugfix: "[crit] cache file ... has too long header" messages might + appear in logs if caching was used and the backend returned responses + with the "Vary" header line. + + *) Workaround: "[crit] SSL_write() failed" messages might appear in logs + when using OpenSSL 1.1.1. + + *) Bugfix: "SSL_shutdown() failed (SSL: ... bad write retry)" messages + might appear in logs; the bug had appeared in 1.19.2. + + *) Bugfix: a segmentation fault might occur in a worker process when + using HTTP/2 if errors with code 400 were redirected to a proxied + location using the "error_page" directive. + + *) Bugfix: socket leak when using HTTP/2 and subrequests in the njs + module. + + +Changes with nginx 1.19.2 11 Aug 2020 + + *) Change: now nginx starts closing keepalive connections before all + free worker connections are exhausted, and logs a warning about this + to the error log. + + *) Change: optimization of client request body reading when using + chunked transfer encoding. + + *) Bugfix: memory leak if the "ssl_ocsp" directive was used. + + *) Bugfix: "zero size buf in output" alerts might appear in logs if a + FastCGI server returned an incorrect response; the bug had appeared + in 1.19.1. + + *) Bugfix: a segmentation fault might occur in a worker process if + different large_client_header_buffers sizes were used in different + virtual servers. + + *) Bugfix: SSL shutdown might not work. + + *) Bugfix: "SSL_shutdown() failed (SSL: ... bad write retry)" messages + might appear in logs. + + *) Bugfix: in the ngx_http_slice_module. + + *) Bugfix: in the ngx_http_xslt_filter_module. + + +Changes with nginx 1.19.1 07 Jul 2020 + + *) Change: the "lingering_close", "lingering_time", and + "lingering_timeout" directives now work when using HTTP/2. + + *) Change: now extra data sent by a backend are always discarded. + + *) Change: now after receiving a too short response from a FastCGI + server nginx tries to send the available part of the response to the + client, and then closes the client connection. + + *) Change: now after receiving a response with incorrect length from a + gRPC backend nginx stops response processing with an error. + + *) Feature: the "min_free" parameter of the "proxy_cache_path", + "fastcgi_cache_path", "scgi_cache_path", and "uwsgi_cache_path" + directives. + Thanks to Adam Bambuch. + + *) Bugfix: nginx did not delete unix domain listen sockets during + graceful shutdown on the SIGQUIT signal. + + *) Bugfix: zero length UDP datagrams were not proxied. + + *) Bugfix: proxying to uwsgi backends using SSL might not work. + Thanks to Guanzhong Chen. + + *) Bugfix: in error handling when using the "ssl_ocsp" directive. + + *) Bugfix: on XFS and NFS file systems disk cache size might be + calculated incorrectly. + + *) Bugfix: "negative size buf in writer" alerts might appear in logs if + a memcached server returned a malformed response. + + +Changes with nginx 1.19.0 26 May 2020 + + *) Feature: client certificate validation with OCSP. + + *) Bugfix: "upstream sent frame for closed stream" errors might occur + when working with gRPC backends. + + *) Bugfix: OCSP stapling might not work if the "resolver" directive was + not specified. + + *) Bugfix: connections with incorrect HTTP/2 preface were not logged. Changes with nginx 1.17.10 14 Apr 2020 diff -Nru nginx-1.18.0/CHANGES.ru nginx-1.20.1/CHANGES.ru --- nginx-1.18.0/CHANGES.ru 2020-04-21 14:09:05.000000000 +0000 +++ nginx-1.20.1/CHANGES.ru 2021-05-25 12:35:42.000000000 +0000 @@ -1,7 +1,243 @@ -Изменения в nginx 1.18.0 21.04.2020 +Изменения в nginx 1.20.1 25.05.2021 - *) Стабильная ветка 1.18.x. + *) Безопасность: при использовании директивы resolver во время обработки + ответа DNS-сервера могла происходить перезапись одного байта памяти, + что позволяло атакующему, имеющему возможность подделывать UDP-пакеты + от DNS-сервера, вызвать падение рабочего процесса или, потенциально, + выполнение произвольного кода (CVE-2021-23017). + + +Изменения в nginx 1.20.0 20.04.2021 + + *) Стабильная ветка 1.20.x. + + +Изменения в nginx 1.19.10 13.04.2021 + + *) Изменение: в директиве keepalive_requests значение по умолчанию + изменено на 1000. + + *) Добавление: директива keepalive_time. + + *) Добавление: переменная $connection_time. + + *) Изменение: при использовании zlib-ng в логах появлялись сообщения + "gzip filter failed to use preallocated memory". + + +Изменения в nginx 1.19.9 30.03.2021 + + *) Исправление: nginx не собирался с почтовым прокси-сервером, но без + модуля ngx_mail_ssl_module; ошибка появилась в 1.19.8. + + *) Исправление: при работе с gRPC-бэкендами могли возникать ошибки + "upstream sent response body larger than indicated content length"; + ошибка появилась в 1.19.1. + + *) Исправление: если клиент закрывал соединение в момент отбрасывания + тела запроса, nginx мог не закрыть соединение до истечения + keepalive-таймаута. + + *) Исправление: при ожидании задержки limit_req или auth_delay, а также + при работе с бэкендами nginx мог не обнаружить, что соединение уже + закрыто клиентом. + + *) Исправление: в методе обработки соединений eventport. + + +Изменения в nginx 1.19.8 09.03.2021 + + *) Добавление: в директиве proxy_cookie_flags теперь флаги можно + задавать с помощью переменных. + + *) Добавление: параметр proxy_protocol в директиве listen, директивы + proxy_protocol и set_real_ip_from в почтовом прокси-сервере. + + *) Исправление: HTTP/2-соединения сразу закрывались при использовании + "keepalive_timeout 0"; ошибка появилась в 1.19.7. + + *) Исправление: некоторые ошибки логгировались как неизвестные, если + nginx был собран с glibc 2.32. + + *) Исправление: в методе обработки соединений eventport. + + +Изменения в nginx 1.19.7 16.02.2021 + + *) Изменение: обработка соединений в HTTP/2 была изменена и теперь более + соответствует HTTP/1.x; директивы http2_recv_timeout, + http2_idle_timeout и http2_max_requests упразднены, вместо них + следует использовать директивы keepalive_timeout и + keepalive_requests. + + *) Изменение: директивы http2_max_field_size и http2_max_header_size + упразднены, вместо них следует использовать директиву + large_client_header_buffers. + + *) Добавление: теперь при исчерпании свободных соединений nginx + закрывает не только keepalive-соединения, но и соединения в lingering + close. + + *) Исправление: в логах могли появляться сообщения "zero size buf in + output", если бэкенд возвращал некорректный ответ при + небуферизированном проксировании; ошибка появилась в 1.19.1. + + *) Исправление: при использовании директивы return вместе с image_filter + или xslt_stylesheet HEAD-запросы обрабатывались некорректно. + + *) Исправление: в директиве add_trailer. + + +Изменения в nginx 1.19.6 15.12.2020 + + *) Исправление: ошибки "no live upstreams", если server в блоке upstream + был помечен как down. + + *) Исправление: при использовании HTTPS в рабочем процессе мог произойти + segmentation fault; ошибка появилась в 1.19.5. + + *) Исправление: nginx возвращал ошибку 400 на запросы вида + "GET http://example.com?args HTTP/1.0". + + *) Исправление: в модулях ngx_http_flv_module и ngx_http_mp4_module. + Спасибо Chris Newton. + + +Изменения в nginx 1.19.5 24.11.2020 + + *) Добавление: ключ -e. + + *) Добавление: при сборке дополнительных модулей теперь можно указывать + одни и те же исходные файлы в разных модулях. + + *) Исправление: SSL shutdown не работал при закрытии соединений с + ожиданием дополнительных данных (lingering close). + + *) Исправление: при работе с gRPC-бэкендами могли возникать ошибки + "upstream sent frame for closed stream". + + *) Исправление: во внутреннем API для обработки тела запроса. + + +Изменения в nginx 1.19.4 27.10.2020 + + *) Добавление: директивы ssl_conf_command, proxy_ssl_conf_command, + grpc_ssl_conf_command и uwsgi_ssl_conf_command. + + *) Добавление: директива ssl_reject_handshake. + + *) Добавление: директива proxy_smtp_auth в почтовом прокси-сервере. + + +Изменения в nginx 1.19.3 29.09.2020 + + *) Добавление: модуль ngx_stream_set_module. + + *) Добавление: директива proxy_cookie_flags. + + *) Добавление: директива userid_flags. + + *) Исправление: расширение управления кэшированием stale-if-error + ошибочно применялось, если бэкенд возвращал ответ с кодом 500, 502, + 503, 504, 403, 404 или 429. + + *) Исправление: если использовалось кэширование и бэкенд возвращал + ответы с строкой заголовка Vary, в логах могли появляться сообщения + "[crit] cache file ... has too long header". + + *) Изменение: при использовании OpenSSL 1.1.1 в логах могли появляться + сообщения "[crit] SSL_write() failed". + + *) Исправление: в логах могли появляться сообщения "SSL_shutdown() + failed (SSL: ... bad write retry)"; ошибка появилась в 1.19.2. + + *) Исправление: при использовании HTTP/2 в рабочем процессе мог + произойти segmentation fault, если ошибки с кодом 400 с помощью + директивы error_page перенаправлялись в проксируемый location. + + *) Исправление: утечки сокетов при использовании HTTP/2 и подзапросов в + модуле njs. + + +Изменения в nginx 1.19.2 11.08.2020 + + *) Изменение: теперь nginx начинает закрывать keepalive-соединения, не + дожидаясь исчерпания всех свободных соединений, а также пишет об этом + предупреждение в лог ошибок. + + *) Изменение: оптимизация чтения тела запроса при использовании chunked + transfer encoding. + + *) Исправление: утечки памяти при использовании директивы ssl_ocsp. + + *) Исправление: в логах могли появляться сообщения "zero size buf in + output", если FastCGI-сервер возвращал некорректный ответ; ошибка + появилась в 1.19.1. + + *) Исправление: в рабочем процессе мог произойти segmentation fault, + если размеры large_client_header_buffers отличались в разных + виртуальных серверах. + + *) Исправление: SSL shutdown мог не работать. + + *) Исправление: в логах могли появляться сообщения "SSL_shutdown() + failed (SSL: ... bad write retry)". + + *) Исправление: в модуле ngx_http_slice_module. + + *) Исправление: в модуле ngx_http_xslt_filter_module. + + +Изменения в nginx 1.19.1 07.07.2020 + + *) Изменение: директивы lingering_close, lingering_time и + lingering_timeout теперь работают при использовании HTTP/2. + + *) Изменение: теперь лишние данные, присланные бэкендом, всегда + отбрасываются. + + *) Изменение: теперь при получении слишком короткого ответа от + FastCGI-сервера nginx пытается отправить клиенту доступную часть + ответа, после чего закрывает соединение с клиентом. + + *) Изменение: теперь при получении ответа некорректной длины от + gRPC-бэкенда nginx прекращает обработку ответа с ошибкой. + + *) Добавление: параметр min_free в директивах proxy_cache_path, + fastcgi_cache_path, scgi_cache_path и uwsgi_cache_path. + Спасибо Adam Bambuch. + + *) Исправление: nginx не удалял unix domain listen-сокеты при плавном + завершении по сигналу SIGQUIT. + + *) Исправление: UDP-пакеты нулевого размера не проксировались. + + *) Исправление: проксирование на uwsgi-бэкенды с использованием SSL + могло не работать. + Спасибо Guanzhong Chen. + + *) Исправление: в обработке ошибок при использовании директивы ssl_ocsp. + + *) Исправление: при использовании файловых систем XFS и NFS размер кэша + на диске мог считаться некорректно. + + *) Исправление: если сервер memcached возвращал некорректный ответ, в + логах могли появляться сообщения "negative size buf in writer". + + +Изменения в nginx 1.19.0 26.05.2020 + + *) Добавление: проверка клиентских сертификатов с помощью OCSP. + + *) Исправление: при работе с gRPC-бэкендами могли возникать ошибки + "upstream sent frame for closed stream". + + *) Исправление: OCSP stapling мог не работать, если не была указана + директива resolver. + + *) Исправление: соединения с некорректным HTTP/2 preface не + логгировались. Изменения в nginx 1.17.10 14.04.2020 diff -Nru nginx-1.18.0/configure nginx-1.20.1/configure --- nginx-1.18.0/configure 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/configure 2021-05-25 12:35:38.000000000 +0000 @@ -87,6 +87,10 @@ have=NGX_LOCK_PATH value="\"$NGX_LOCK_PATH\"" . auto/define have=NGX_ERROR_LOG_PATH value="\"$NGX_ERROR_LOG_PATH\"" . auto/define +if [ ".$NGX_ERROR_LOG_PATH" = "." ]; then + have=NGX_ERROR_LOG_STDERR . auto/have +fi + have=NGX_HTTP_LOG_PATH value="\"$NGX_HTTP_LOG_PATH\"" . auto/define have=NGX_HTTP_CLIENT_TEMP_PATH value="\"$NGX_HTTP_CLIENT_TEMP_PATH\"" . auto/define diff -Nru nginx-1.18.0/contrib/vim/syntax/nginx.vim nginx-1.20.1/contrib/vim/syntax/nginx.vim --- nginx-1.18.0/contrib/vim/syntax/nginx.vim 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/contrib/vim/syntax/nginx.vim 2021-05-25 12:35:38.000000000 +0000 @@ -141,6 +141,7 @@ syn keyword ngxDirective contained api syn keyword ngxDirective contained auth_basic syn keyword ngxDirective contained auth_basic_user_file +syn keyword ngxDirective contained auth_delay syn keyword ngxDirective contained auth_http syn keyword ngxDirective contained auth_http_header syn keyword ngxDirective contained auth_http_pass_client_cert @@ -267,6 +268,7 @@ syn keyword ngxDirective contained grpc_ssl_certificate syn keyword ngxDirective contained grpc_ssl_certificate_key syn keyword ngxDirective contained grpc_ssl_ciphers +syn keyword ngxDirective contained grpc_ssl_conf_command syn keyword ngxDirective contained grpc_ssl_crl syn keyword ngxDirective contained grpc_ssl_name syn keyword ngxDirective contained grpc_ssl_password_file @@ -332,6 +334,7 @@ syn keyword ngxDirective contained js_access syn keyword ngxDirective contained js_content syn keyword ngxDirective contained js_filter +syn keyword ngxDirective contained js_import syn keyword ngxDirective contained js_include syn keyword ngxDirective contained js_path syn keyword ngxDirective contained js_preread @@ -348,6 +351,7 @@ syn keyword ngxDirective contained least_conn syn keyword ngxDirective contained least_time syn keyword ngxDirective contained limit_conn +syn keyword ngxDirective contained limit_conn_dry_run syn keyword ngxDirective contained limit_conn_log_level syn keyword ngxDirective contained limit_conn_status syn keyword ngxDirective contained limit_conn_zone @@ -444,6 +448,7 @@ syn keyword ngxDirective contained proxy_cache_valid syn keyword ngxDirective contained proxy_connect_timeout syn keyword ngxDirective contained proxy_cookie_domain +syn keyword ngxDirective contained proxy_cookie_flags syn keyword ngxDirective contained proxy_cookie_path syn keyword ngxDirective contained proxy_download_rate syn keyword ngxDirective contained proxy_force_ranges @@ -477,11 +482,13 @@ syn keyword ngxDirective contained proxy_session_drop syn keyword ngxDirective contained proxy_set_body syn keyword ngxDirective contained proxy_set_header +syn keyword ngxDirective contained proxy_smtp_auth syn keyword ngxDirective contained proxy_socket_keepalive syn keyword ngxDirective contained proxy_ssl syn keyword ngxDirective contained proxy_ssl_certificate syn keyword ngxDirective contained proxy_ssl_certificate_key syn keyword ngxDirective contained proxy_ssl_ciphers +syn keyword ngxDirective contained proxy_ssl_conf_command syn keyword ngxDirective contained proxy_ssl_crl syn keyword ngxDirective contained proxy_ssl_name syn keyword ngxDirective contained proxy_ssl_password_file @@ -589,16 +596,21 @@ syn keyword ngxDirective contained ssl_certificate_key syn keyword ngxDirective contained ssl_ciphers syn keyword ngxDirective contained ssl_client_certificate +syn keyword ngxDirective contained ssl_conf_command syn keyword ngxDirective contained ssl_crl syn keyword ngxDirective contained ssl_dhparam syn keyword ngxDirective contained ssl_early_data syn keyword ngxDirective contained ssl_ecdh_curve syn keyword ngxDirective contained ssl_engine syn keyword ngxDirective contained ssl_handshake_timeout +syn keyword ngxDirective contained ssl_ocsp +syn keyword ngxDirective contained ssl_ocsp_cache +syn keyword ngxDirective contained ssl_ocsp_responder syn keyword ngxDirective contained ssl_password_file syn keyword ngxDirective contained ssl_prefer_server_ciphers syn keyword ngxDirective contained ssl_preread syn keyword ngxDirective contained ssl_protocols +syn keyword ngxDirective contained ssl_reject_handshake syn keyword ngxDirective contained ssl_session_cache syn keyword ngxDirective contained ssl_session_ticket_key syn keyword ngxDirective contained ssl_session_tickets @@ -637,6 +649,7 @@ syn keyword ngxDirective contained userid syn keyword ngxDirective contained userid_domain syn keyword ngxDirective contained userid_expires +syn keyword ngxDirective contained userid_flags syn keyword ngxDirective contained userid_mark syn keyword ngxDirective contained userid_name syn keyword ngxDirective contained userid_p3p @@ -687,6 +700,7 @@ syn keyword ngxDirective contained uwsgi_ssl_certificate syn keyword ngxDirective contained uwsgi_ssl_certificate_key syn keyword ngxDirective contained uwsgi_ssl_ciphers +syn keyword ngxDirective contained uwsgi_ssl_conf_command syn keyword ngxDirective contained uwsgi_ssl_crl syn keyword ngxDirective contained uwsgi_ssl_name syn keyword ngxDirective contained uwsgi_ssl_password_file @@ -732,6 +746,7 @@ syn keyword ngxDirective contained zone_sync_ssl_certificate syn keyword ngxDirective contained zone_sync_ssl_certificate_key syn keyword ngxDirective contained zone_sync_ssl_ciphers +syn keyword ngxDirective contained zone_sync_ssl_conf_command syn keyword ngxDirective contained zone_sync_ssl_crl syn keyword ngxDirective contained zone_sync_ssl_name syn keyword ngxDirective contained zone_sync_ssl_password_file @@ -770,6 +785,7 @@ syn keyword ngxDirectiveThirdParty contained auth_gss_force_realm syn keyword ngxDirectiveThirdParty contained auth_gss_format_full syn keyword ngxDirectiveThirdParty contained auth_gss_keytab +syn keyword ngxDirectiveThirdParty contained auth_gss_map_to_local syn keyword ngxDirectiveThirdParty contained auth_gss_realm syn keyword ngxDirectiveThirdParty contained auth_gss_service_name @@ -791,8 +807,8 @@ " AJP protocol proxy " https://github.com/yaoweibin/nginx_ajp_module -syn keyword ngxDirectiveThirdParty contained ajp_buffer_size syn keyword ngxDirectiveThirdParty contained ajp_buffers +syn keyword ngxDirectiveThirdParty contained ajp_buffer_size syn keyword ngxDirectiveThirdParty contained ajp_busy_buffers_size syn keyword ngxDirectiveThirdParty contained ajp_cache syn keyword ngxDirectiveThirdParty contained ajp_cache_key @@ -818,6 +834,7 @@ syn keyword ngxDirectiveThirdParty contained ajp_pass_request_body syn keyword ngxDirectiveThirdParty contained ajp_pass_request_headers syn keyword ngxDirectiveThirdParty contained ajp_read_timeout +syn keyword ngxDirectiveThirdParty contained ajp_secret syn keyword ngxDirectiveThirdParty contained ajp_send_lowat syn keyword ngxDirectiveThirdParty contained ajp_send_timeout syn keyword ngxDirectiveThirdParty contained ajp_store @@ -854,8 +871,8 @@ syn keyword ngxDirectiveThirdParty contained content_handler_type syn keyword ngxDirectiveThirdParty contained handler_code syn keyword ngxDirectiveThirdParty contained handler_name -syn keyword ngxDirectiveThirdParty contained handler_type syn keyword ngxDirectiveThirdParty contained handlers_lazy_init +syn keyword ngxDirectiveThirdParty contained handler_type syn keyword ngxDirectiveThirdParty contained header_filter_code syn keyword ngxDirectiveThirdParty contained header_filter_name syn keyword ngxDirectiveThirdParty contained header_filter_property @@ -871,6 +888,10 @@ syn keyword ngxDirectiveThirdParty contained jvm_path syn keyword ngxDirectiveThirdParty contained jvm_var syn keyword ngxDirectiveThirdParty contained jvm_workers +syn keyword ngxDirectiveThirdParty contained log_handler_code +syn keyword ngxDirectiveThirdParty contained log_handler_name +syn keyword ngxDirectiveThirdParty contained log_handler_property +syn keyword ngxDirectiveThirdParty contained log_handler_type syn keyword ngxDirectiveThirdParty contained max_balanced_tcp_connections syn keyword ngxDirectiveThirdParty contained rewrite_handler_code syn keyword ngxDirectiveThirdParty contained rewrite_handler_name @@ -879,6 +900,7 @@ syn keyword ngxDirectiveThirdParty contained shared_map syn keyword ngxDirectiveThirdParty contained write_page_size + " Certificate Transparency " https://github.com/grahamedgecombe/nginx-ct syn keyword ngxDirectiveThirdParty contained ssl_ct @@ -942,6 +964,7 @@ syn keyword ngxDirectiveThirdParty contained fancyindex_ignore syn keyword ngxDirectiveThirdParty contained fancyindex_localtime syn keyword ngxDirectiveThirdParty contained fancyindex_name_length +syn keyword ngxDirectiveThirdParty contained fancyindex_show_dotfiles syn keyword ngxDirectiveThirdParty contained fancyindex_show_path syn keyword ngxDirectiveThirdParty contained fancyindex_time_format @@ -991,8 +1014,8 @@ syn keyword ngxDirectiveThirdParty contained nchan_benchmark_subscriber_distribution syn keyword ngxDirectiveThirdParty contained nchan_benchmark_subscribers_per_channel syn keyword ngxDirectiveThirdParty contained nchan_benchmark_time -syn keyword ngxDirectiveThirdParty contained nchan_channel_event_string syn keyword ngxDirectiveThirdParty contained nchan_channel_events_channel_id +syn keyword ngxDirectiveThirdParty contained nchan_channel_event_string syn keyword ngxDirectiveThirdParty contained nchan_channel_group syn keyword ngxDirectiveThirdParty contained nchan_channel_group_accounting syn keyword ngxDirectiveThirdParty contained nchan_channel_id @@ -1000,6 +1023,10 @@ syn keyword ngxDirectiveThirdParty contained nchan_channel_timeout syn keyword ngxDirectiveThirdParty contained nchan_deflate_message_for_websocket syn keyword ngxDirectiveThirdParty contained nchan_eventsource_event +syn keyword ngxDirectiveThirdParty contained nchan_eventsource_ping_comment +syn keyword ngxDirectiveThirdParty contained nchan_eventsource_ping_data +syn keyword ngxDirectiveThirdParty contained nchan_eventsource_ping_event +syn keyword ngxDirectiveThirdParty contained nchan_eventsource_ping_interval syn keyword ngxDirectiveThirdParty contained nchan_group_location syn keyword ngxDirectiveThirdParty contained nchan_group_max_channels syn keyword ngxDirectiveThirdParty contained nchan_group_max_messages @@ -1047,10 +1074,10 @@ syn keyword ngxDirectiveThirdParty contained nchan_stub_status syn keyword ngxDirectiveThirdParty contained nchan_sub_channel_id syn keyword ngxDirectiveThirdParty contained nchan_subscribe_existing_channels_only -syn keyword ngxDirectiveThirdParty contained nchan_subscribe_request syn keyword ngxDirectiveThirdParty contained nchan_subscriber syn keyword ngxDirectiveThirdParty contained nchan_subscriber_channel_id syn keyword ngxDirectiveThirdParty contained nchan_subscriber_compound_etag_message_id +syn keyword ngxDirectiveThirdParty contained nchan_subscribe_request syn keyword ngxDirectiveThirdParty contained nchan_subscriber_first_message syn keyword ngxDirectiveThirdParty contained nchan_subscriber_http_raw_stream_separator syn keyword ngxDirectiveThirdParty contained nchan_subscriber_last_message_id @@ -1311,6 +1338,8 @@ syn keyword ngxDirectiveThirdParty contained content_by_lua syn keyword ngxDirectiveThirdParty contained content_by_lua_block syn keyword ngxDirectiveThirdParty contained content_by_lua_file +syn keyword ngxDirectiveThirdParty contained exit_worker_by_lua_block +syn keyword ngxDirectiveThirdParty contained exit_worker_by_lua_file syn keyword ngxDirectiveThirdParty contained header_filter_by_lua syn keyword ngxDirectiveThirdParty contained header_filter_by_lua_block syn keyword ngxDirectiveThirdParty contained header_filter_by_lua_file @@ -1352,6 +1381,7 @@ syn keyword ngxDirectiveThirdParty contained lua_ssl_protocols syn keyword ngxDirectiveThirdParty contained lua_ssl_trusted_certificate syn keyword ngxDirectiveThirdParty contained lua_ssl_verify_depth +syn keyword ngxDirectiveThirdParty contained lua_thread_cache_max_entries syn keyword ngxDirectiveThirdParty contained lua_transform_underscores_in_response_headers syn keyword ngxDirectiveThirdParty contained lua_use_default_type syn keyword ngxDirectiveThirdParty contained rewrite_by_lua @@ -1987,11 +2017,7 @@ " update upstreams' config by restful interface " https://github.com/yzprofile/ngx_http_dyups_module syn keyword ngxDirectiveThirdParty contained dyups_interface -syn keyword ngxDirectiveThirdParty contained dyups_read_msg_log -syn keyword ngxDirectiveThirdParty contained dyups_read_msg_timeout syn keyword ngxDirectiveThirdParty contained dyups_shm_zone_size -syn keyword ngxDirectiveThirdParty contained dyups_trylock -syn keyword ngxDirectiveThirdParty contained dyups_upstream_conf " add given content to the end of the response according to the condition specified " https://github.com/flygoast/ngx_http_footer_if_filter @@ -2271,6 +2297,7 @@ syn keyword ngxDirectiveThirdParty contained testcookie_refresh_encrypt_cookie_key syn keyword ngxDirectiveThirdParty contained testcookie_refresh_status syn keyword ngxDirectiveThirdParty contained testcookie_refresh_template +syn keyword ngxDirectiveThirdParty contained testcookie_samesite syn keyword ngxDirectiveThirdParty contained testcookie_secret syn keyword ngxDirectiveThirdParty contained testcookie_secure_flag syn keyword ngxDirectiveThirdParty contained testcookie_session @@ -2308,31 +2335,105 @@ " https://github.com/flygoast/ngx_http_upstream_ketama_chash syn keyword ngxDirectiveThirdParty contained ketama_chash +" nginx-sticky-module-ng +" https://github.com/ayty-adrianomartins/nginx-sticky-module-ng +syn keyword ngxDirectiveThirdParty contained sticky_no_fallback + +" dynamic linking and call the function of your application +" https://github.com/Taymindis/nginx-link-function +syn keyword ngxDirectiveThirdParty contained ngx_link_func_add_prop +syn keyword ngxDirectiveThirdParty contained ngx_link_func_add_req_header +syn keyword ngxDirectiveThirdParty contained ngx_link_func_ca_cert +syn keyword ngxDirectiveThirdParty contained ngx_link_func_call +syn keyword ngxDirectiveThirdParty contained ngx_link_func_download_link_lib +syn keyword ngxDirectiveThirdParty contained ngx_link_func_lib +syn keyword ngxDirectiveThirdParty contained ngx_link_func_shm_size +syn keyword ngxDirectiveThirdParty contained ngx_link_func_subrequest + +" purge content from FastCGI, proxy, SCGI and uWSGI caches +" https://github.com/torden/ngx_cache_purge +syn keyword ngxDirectiveThirdParty contained cache_purge_response_type + +" set the flags "HttpOnly", "secure" and "SameSite" for cookies +" https://github.com/AirisX/nginx_cookie_flag_module +syn keyword ngxDirectiveThirdParty contained set_cookie_flag + +" Embed websockify into Nginx (convert any tcp connection into websocket) +" https://github.com/tg123/websockify-nginx-module +syn keyword ngxDirectiveThirdParty contained websockify_buffer_size +syn keyword ngxDirectiveThirdParty contained websockify_connect_timeout +syn keyword ngxDirectiveThirdParty contained websockify_pass +syn keyword ngxDirectiveThirdParty contained websockify_read_timeout +syn keyword ngxDirectiveThirdParty contained websockify_send_timeout + +" IP2Location Nginx +" https://github.com/ip2location/ip2location-nginx +syn keyword ngxDirectiveThirdParty contained ip2location_proxy +syn keyword ngxDirectiveThirdParty contained ip2location_proxy_recursive +syn keyword ngxDirectiveThirdParty contained ip2location_areacode +syn keyword ngxDirectiveThirdParty contained ip2location_city +syn keyword ngxDirectiveThirdParty contained ip2location_country_long +syn keyword ngxDirectiveThirdParty contained ip2location_country_short +syn keyword ngxDirectiveThirdParty contained ip2location_domain +syn keyword ngxDirectiveThirdParty contained ip2location_elevation +syn keyword ngxDirectiveThirdParty contained ip2location_iddcode +syn keyword ngxDirectiveThirdParty contained ip2location_isp +syn keyword ngxDirectiveThirdParty contained ip2location_latitude +syn keyword ngxDirectiveThirdParty contained ip2location_longitude +syn keyword ngxDirectiveThirdParty contained ip2location_mcc +syn keyword ngxDirectiveThirdParty contained ip2location_mnc +syn keyword ngxDirectiveThirdParty contained ip2location_mobilebrand +syn keyword ngxDirectiveThirdParty contained ip2location_netspeed +syn keyword ngxDirectiveThirdParty contained ip2location_region +syn keyword ngxDirectiveThirdParty contained ip2location_timezone +syn keyword ngxDirectiveThirdParty contained ip2location_usagetype +syn keyword ngxDirectiveThirdParty contained ip2location_weatherstationcode +syn keyword ngxDirectiveThirdParty contained ip2location_weatherstationname +syn keyword ngxDirectiveThirdParty contained ip2location_zipcode + +" IP2Proxy module for Nginx +" https://github.com/ip2location/ip2proxy-nginx +syn keyword ngxDirectiveThirdParty contained ip2proxy_as +syn keyword ngxDirectiveThirdParty contained ip2proxy_asn +syn keyword ngxDirectiveThirdParty contained ip2proxy_city +syn keyword ngxDirectiveThirdParty contained ip2proxy_country_long +syn keyword ngxDirectiveThirdParty contained ip2proxy_country_short +syn keyword ngxDirectiveThirdParty contained ip2proxy_database +syn keyword ngxDirectiveThirdParty contained ip2proxy_domain +syn keyword ngxDirectiveThirdParty contained ip2proxy_isp +syn keyword ngxDirectiveThirdParty contained ip2proxy_is_proxy +syn keyword ngxDirectiveThirdParty contained ip2proxy_last_seen +syn keyword ngxDirectiveThirdParty contained ip2proxy_proxy +syn keyword ngxDirectiveThirdParty contained ip2proxy_proxy_recursive +syn keyword ngxDirectiveThirdParty contained ip2proxy_proxy_type +syn keyword ngxDirectiveThirdParty contained ip2proxy_region +syn keyword ngxDirectiveThirdParty contained ip2proxy_threat +syn keyword ngxDirectiveThirdParty contained ip2proxy_usage_type " highlight -hi link ngxComment Comment -hi link ngxParamComment Comment -hi link ngxListenComment Comment -hi link ngxVariable Identifier -hi link ngxVariableString PreProc -hi link ngxString String -hi link ngxListenString String - -hi link ngxBoolean Boolean -hi link ngxDirectiveBlock Statement -hi link ngxDirectiveImportant Type -hi link ngxDirectiveListen Type -hi link ngxDirectiveControl Keyword -hi link ngxDirectiveError Constant -hi link ngxDirectiveDeprecated Error -hi link ngxDirective Identifier -hi link ngxDirectiveThirdParty Special -hi link ngxDirectiveThirdPartyDeprecated Error +hi def link ngxComment Comment +hi def link ngxParamComment Comment +hi def link ngxListenComment Comment +hi def link ngxVariable Identifier +hi def link ngxVariableString PreProc +hi def link ngxString String +hi def link ngxListenString String + +hi def link ngxBoolean Boolean +hi def link ngxDirectiveBlock Statement +hi def link ngxDirectiveImportant Type +hi def link ngxDirectiveListen Type +hi def link ngxDirectiveControl Keyword +hi def link ngxDirectiveError Constant +hi def link ngxDirectiveDeprecated Error +hi def link ngxDirective Identifier +hi def link ngxDirectiveThirdParty Special +hi def link ngxDirectiveThirdPartyDeprecated Error -hi link ngxListenOptions Keyword -hi link ngxListenOptionsDeprecated Error +hi def link ngxListenOptions Keyword +hi def link ngxListenOptionsDeprecated Error let b:current_syntax = "nginx" diff -Nru nginx-1.18.0/debian/changelog nginx-1.20.1/debian/changelog --- nginx-1.18.0/debian/changelog 2021-03-12 13:50:54.000000000 +0000 +++ nginx-1.20.1/debian/changelog 2021-06-08 07:08:13.000000000 +0000 @@ -1,15 +1,81 @@ -nginx (1.18.0-6+deb.sury.org+2+ubuntu20.10.1+deb.sury.org+1) groovy; urgency=medium +nginx (1.20.1-1+ubuntu20.10.1+deb.sury.org+1) groovy; urgency=medium * No-change backport to groovy - -- Ondřej Surý Fri, 12 Mar 2021 14:50:54 +0100 + -- Ondřej Surý Tue, 08 Jun 2021 09:08:13 +0200 -nginx (1.18.0-6+deb.sury.org+2) unstable; urgency=medium +nginx (1.20.1-1) unstable; urgency=medium - * Add nginx-common.pref file to downgrade the OpenSSL to distribution - version + * Update d/ for nginx 1.20 + * New upstream version 1.20.1 - -- Ondřej Surý Fri, 12 Mar 2021 14:47:51 +0100 + -- Ondřej Surý Tue, 08 Jun 2021 09:04:11 +0200 + +nginx (1.19.10-1) unstable; urgency=medium + + * New upstream version 1.19.10 + + -- Ondřej Surý Tue, 08 Jun 2021 09:01:38 +0200 + +nginx (1.19.8-0+deb.sury.org+1) unstable; urgency=medium + + * New upstream version 1.19.8 + * Add nginx-common.pref apt preferences configuration file to + downgrade the OpenSSL package(s) to the version provided by + the distribution + + -- Ondřej Surý Tue, 09 Mar 2021 17:44:18 +0100 + +nginx (1.19.7-3) unstable; urgency=medium + + * Downgrade debhelper compat level to 10 + * libhiredis-dev is required at least at 0.13 version + * Add nginx_ct 1.3.2 module + * Add arm64 and ppc64el to list of luajit platforms + * Add ngx_brotli 1.0.0rc module + + -- Ondřej Surý Sat, 06 Mar 2021 10:19:09 +0100 + +nginx (1.19.7-2) unstable; urgency=medium + + * Add ngx_brotli 1.0.0rc module + + -- Ondřej Surý Sat, 06 Mar 2021 09:32:09 +0100 + +nginx (1.19.7-1) unstable; urgency=medium + + * New upstream version 1.19.7 + + -- Ondřej Surý Sun, 28 Feb 2021 10:21:58 +0100 + +nginx (1.19.5-3) unstable; urgency=medium + + * Downgrade http-lua back to 0.10.13 as the new version breaks the nginx + when enabled + + -- Ondřej Surý Thu, 10 Dec 2020 18:50:33 +0100 + +nginx (1.19.5-2) unstable; urgency=medium + + * Add patch for nchan GCC 10 compatibility + * Explicitly configure LUAJIT_INC and LUAJIT_LIB + + -- Ondřej Surý Thu, 10 Dec 2020 05:41:18 +0100 + +nginx (1.19.5-1) unstable; urgency=medium + + * Update the default PPA to mainline + * Remove spurious B-D on dpkg-dev + * New upstream version 1.19.5 + * Update http-echo (0.62), http-lua (0.10.19) and http-fancyindex (0.5.1) + + -- Ondřej Surý Wed, 09 Dec 2020 13:46:22 +0100 + +nginx (1.19.0-1) unstable; urgency=medium + + * New upstream version 1.19.0 + + -- Ondřej Surý Tue, 07 Jul 2020 09:27:58 +0200 nginx (1.18.0-6+deb.sury.org+1) unstable; urgency=medium diff -Nru nginx-1.18.0/debian/gbp.conf nginx-1.20.1/debian/gbp.conf --- nginx-1.18.0/debian/gbp.conf 2021-03-12 13:47:51.000000000 +0000 +++ nginx-1.20.1/debian/gbp.conf 2021-06-08 07:04:11.000000000 +0000 @@ -1,5 +1,5 @@ [DEFAULT] pristine-tar = True -debian-branch = main-1.18 -upstream-branch = upstream-1.18 +debian-branch = main-1.20 +upstream-branch = upstream-1.20 upstream-tag = upstream/%(version)s diff -Nru nginx-1.18.0/debian/modules/control nginx-1.20.1/debian/modules/control --- nginx-1.18.0/debian/modules/control 2021-03-12 13:47:51.000000000 +0000 +++ nginx-1.20.1/debian/modules/control 2021-06-08 07:04:11.000000000 +0000 @@ -13,7 +13,7 @@ Module: http-echo Homepage: https://github.com/agentzh/echo-nginx-module -Version: v0.61 +Version: v0.62 Files-Excluded: .gitignore .gitattributes .travis.yml Module: http-geoip2 @@ -24,7 +24,6 @@ Homepage: https://github.com/openresty/lua-nginx-module Version: 0.10.13 Patch: - openssl-1.1.0.patch discover-luajit-2.1.patch Files-Excluded: .gitignore .gitattributes .travis.yml .github @@ -59,7 +58,7 @@ Module: http-fancyindex Homepage: https://github.com/aperezdc/ngx-fancyindex -Version: 0.4.4 +Version: 0.5.1 Files-Excluded: .gitignore .travis.yml Module: http-subs-filter diff -Nru nginx-1.18.0/debian/modules/http-echo/config nginx-1.20.1/debian/modules/http-echo/config --- nginx-1.18.0/debian/modules/http-echo/config 2021-03-12 13:47:51.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-echo/config 2021-06-08 07:04:11.000000000 +0000 @@ -30,21 +30,24 @@ $ngx_addon_dir/src/ngx_http_echo_foreach.h \ " -# nginx won't have HTTP_POSTPONE_FILTER_MODULE & HTTP_POSTPONE_FILTER_SRCS -# defined since 1.9.11 -if test -z "$HTTP_POSTPONE_FILTER_MODULE"; then - HTTP_POSTPONE_FILTER_MODULE=ngx_http_postpone_filter_module - HTTP_POSTPONE_FILTER_SRCS=src/http/ngx_http_postpone_filter_module.c -fi +# nginx 1.17.0+ unconditionally enables the postpone filter +if [ ! -z "$HTTP_POSTPONE" ]; then + # nginx won't have HTTP_POSTPONE_FILTER_MODULE & HTTP_POSTPONE_FILTER_SRCS + # defined since 1.9.11 + if [ -z "$HTTP_POSTPONE_FILTER_MODULE" ]; then + HTTP_POSTPONE_FILTER_MODULE=ngx_http_postpone_filter_module + HTTP_POSTPONE_FILTER_SRCS=src/http/ngx_http_postpone_filter_module.c + fi -# This module depends upon the postpone filter being activated -if [ $HTTP_POSTPONE != YES ]; then - HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_POSTPONE_FILTER_MODULE" - HTTP_SRCS="$HTTP_SRCS $HTTP_POSTPONE_FILTER_SRCS" - HTTP_POSTPONE=YES + # This module depends upon the postpone filter being activated + if [ "$HTTP_POSTPONE" != YES ]; then + HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_POSTPONE_FILTER_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_POSTPONE_FILTER_SRCS" + HTTP_POSTPONE=YES + fi fi -if test -n "$ngx_module_link"; then +if [ -n "$ngx_module_link" ]; then ngx_module_type=HTTP_AUX_FILTER ngx_module_name=$ngx_addon_name ngx_module_incs= diff -Nru nginx-1.18.0/debian/modules/http-echo/README.markdown nginx-1.20.1/debian/modules/http-echo/README.markdown --- nginx-1.18.0/debian/modules/http-echo/README.markdown 2021-03-12 13:47:51.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-echo/README.markdown 2021-06-08 07:04:11.000000000 +0000 @@ -1606,6 +1606,11 @@ The following versions of Nginx should work with this module: +* **1.16.x** +* **1.15.x** (last tested: 1.15.8) +* **1.14.x** +* **1.13.x** (last tested: 1.13.6) +* **1.12.x** * **1.11.x** (last tested: 1.11.2) * **1.10.x** * **1.9.x** (last tested: 1.9.15) @@ -1809,7 +1814,7 @@ Copyright & License =================== -Copyright (c) 2009-2017, Yichun "agentzh" Zhang (章亦春) , OpenResty Inc. +Copyright (c) 2009-2018, Yichun "agentzh" Zhang (章亦春) , OpenResty Inc. This module is licensed under the terms of the BSD license. diff -Nru nginx-1.18.0/debian/modules/http-echo/src/ngx_http_echo_util.h nginx-1.20.1/debian/modules/http-echo/src/ngx_http_echo_util.h --- nginx-1.18.0/debian/modules/http-echo/src/ngx_http_echo_util.h 2021-03-12 13:47:51.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-echo/src/ngx_http_echo_util.h 2021-06-08 07:04:11.000000000 +0000 @@ -11,7 +11,7 @@ #include "ngx_http_echo_module.h" -#define ngx_http_echo_strcmp_const(a, b) \ +#define ngx_http_echo_strcmp_const(a, b) \ ngx_strncmp(a, b, sizeof(b) - 1) diff -Nru nginx-1.18.0/debian/modules/http-echo/valgrind.suppress nginx-1.20.1/debian/modules/http-echo/valgrind.suppress --- nginx-1.18.0/debian/modules/http-echo/valgrind.suppress 2021-03-12 13:47:51.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-echo/valgrind.suppress 2021-06-08 07:04:11.000000000 +0000 @@ -1,19 +1,5 @@ { - Memcheck:Addr4 - fun:lj_str_new - fun:lua_setfield - fun:ngx_http_lua_cache_store_code -} -{ - - Memcheck:Addr4 - fun:lj_str_new - fun:lua_getfield - fun:ngx_http_lua_cache_load_code -} -{ - Memcheck:Param epoll_ctl(event) fun:epoll_ctl @@ -46,3 +32,22 @@ fun:ngx_single_process_cycle fun:main } +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:ngx_alloc + fun:ngx_set_environment + fun:ngx_single_process_cycle +} +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:ngx_alloc + fun:ngx_set_environment + fun:ngx_worker_process_init + fun:ngx_worker_process_cycle +} diff -Nru nginx-1.18.0/debian/modules/http-fancyindex/CHANGELOG.md nginx-1.20.1/debian/modules/http-fancyindex/CHANGELOG.md --- nginx-1.18.0/debian/modules/http-fancyindex/CHANGELOG.md 2021-03-12 13:47:51.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-fancyindex/CHANGELOG.md 2021-06-08 07:04:11.000000000 +0000 @@ -3,6 +3,34 @@ ## [Unreleased] +## [0.5.1] - 2020-10-26 +### Fixed +- Properly handle optional second argument to `fancyindex_header` and + `fancyindex_footer` + ([#117](https://github.com/aperezdc/ngx-fancyindex/issues/117)). + +## [0.5.0] - 2020-10-24 +### Added +- New option `fancyindex_show_dotfiles`. (Path by Joshua Shaffer + <>.) +- The `fancyindex_header` and `fancyindex_footer` options now support local + files properly, by means of a `local` flag. (Patches by JoungKyun Kim + <> and Adrián Pérez <>.) + +### Changed +- Improved performance of directory entry sorting, which should be quite + noticeable for directories with thousands of files. (Patch by + [Yuxiang Zhang](https://github.com/z4yx).) +- The minimum Nginx version supported by the module is now 0.8.x. + +### Fixed +- Properly escape square brackets in directory entry names when the module + is built with older versions of Nginx. (Patch by Adrián Pérez + <>.) +- Fix directory entry listing not being shown when using the + [nginx-auth-ldap](https://github.com/kvspb/nginx-auth-ldap) module. (Patch + by JoungKyun Kim <>.) + ## [0.4.4] - 2020-02-19 ### Added - New option `fancyindex_hide_parent_dir`, which disables generating @@ -148,7 +176,9 @@ - `NEWS.rst` file, to act as change log. -[Unreleased]: https://github.com/aperezdc/ngx-fancyindex/compare/v0.4.4...HEAD +[Unreleased]: https://github.com/aperezdc/ngx-fancyindex/compare/v0.5.1...HEAD +[0.5.1]: https://github.com/aperezdc/ngx-fancyindex/compare/v0.5.0...v0.5.1 +[0.5.0]: https://github.com/aperezdc/ngx-fancyindex/compare/v0.4.4...v0.5.0 [0.4.4]: https://github.com/aperezdc/ngx-fancyindex/compare/v0.4.3...v0.4.4 [0.4.3]: https://github.com/aperezdc/ngx-fancyindex/compare/v0.4.2...v0.4.3 [0.4.2]: https://github.com/aperezdc/ngx-fancyindex/compare/v0.4.1...v0.4.2 diff -Nru nginx-1.18.0/debian/modules/http-fancyindex/nginx-0.6-support.patch nginx-1.20.1/debian/modules/http-fancyindex/nginx-0.6-support.patch --- nginx-1.18.0/debian/modules/http-fancyindex/nginx-0.6-support.patch 2021-03-12 13:47:51.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-fancyindex/nginx-0.6-support.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -=== modified file 'ngx_http_fancyindex_module.c' ---- ngx_http_fancyindex_module.c 2008-09-11 17:55:52 +0000 -+++ ngx_http_fancyindex_module.c 2008-12-10 01:33:43 +0000 -@@ -383,7 +383,7 @@ - entry->mtime = ngx_de_mtime(&dir); - entry->size = ngx_de_size(&dir); - entry->utf_len = (r->utf8) -- ? ngx_utf8_length(entry->name.data, entry->name.len) -+ ? ngx_utf_length(entry->name.data, entry->name.len) - : len; - } - -@@ -478,8 +478,7 @@ - copy = NGX_HTTP_FANCYINDEX_NAME_LEN + 1; - } - -- b->last = ngx_utf8_cpystrn(b->last, entry[i].name.data, -- copy, entry[i].name.len); -+ b->last = ngx_utf_cpystrn(b->last, entry[i].name.data, copy); - last = b->last; - - } else { - diff -Nru nginx-1.18.0/debian/modules/http-fancyindex/ngx_http_fancyindex_module.c nginx-1.20.1/debian/modules/http-fancyindex/ngx_http_fancyindex_module.c --- nginx-1.18.0/debian/modules/http-fancyindex/ngx_http_fancyindex_module.c 2021-03-12 13:47:51.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-fancyindex/ngx_http_fancyindex_module.c 2021-06-08 07:04:11.000000000 +0000 @@ -140,28 +140,34 @@ #undef DATETIME_CASE } +typedef struct { + ngx_str_t path; + ngx_str_t local; +} ngx_fancyindex_headerfooter_conf_t; /** * Configuration structure for the fancyindex module. The configuration * commands defined in the module do fill in the members of this structure. */ typedef struct { - ngx_flag_t enable; /**< Module is enabled. */ - ngx_uint_t default_sort; /**< Default sort criterion. */ - ngx_flag_t dirs_first; /**< Group directories together first when sorting */ - ngx_flag_t localtime; /**< File mtime dates are sent in local time. */ - ngx_flag_t exact_size; /**< Sizes are sent always in bytes. */ - ngx_uint_t name_length; /**< Maximum length of file names in bytes. */ - ngx_flag_t hide_symlinks;/**< Hide symbolic links in listings. */ - ngx_flag_t show_path; /**< Whether to display or not the path + '' after the header */ - ngx_flag_t hide_parent; /**< Hide parent directory. */ - - ngx_str_t header; /**< File name for header, or empty if none. */ - ngx_str_t footer; /**< File name for footer, or empty if none. */ - ngx_str_t css_href; /**< Link to a CSS stylesheet, or empty if none. */ - ngx_str_t time_format; /**< Format used for file timestamps. */ + ngx_flag_t enable; /**< Module is enabled. */ + ngx_uint_t default_sort; /**< Default sort criterion. */ + ngx_flag_t dirs_first; /**< Group directories together first when sorting */ + ngx_flag_t localtime; /**< File mtime dates are sent in local time. */ + ngx_flag_t exact_size; /**< Sizes are sent always in bytes. */ + ngx_uint_t name_length; /**< Maximum length of file names in bytes. */ + ngx_flag_t hide_symlinks; /**< Hide symbolic links in listings. */ + ngx_flag_t show_path; /**< Whether to display or not the path + '' after the header */ + ngx_flag_t hide_parent; /**< Hide parent directory. */ + ngx_flag_t show_dot_files; /**< Show files that start with a dot.*/ + + ngx_str_t css_href; /**< Link to a CSS stylesheet, or empty if none. */ + ngx_str_t time_format; /**< Format used for file timestamps. */ + + ngx_array_t *ignore; /**< List of files to ignore in listings. */ - ngx_array_t *ignore; /**< List of files to ignore in listings. */ + ngx_fancyindex_headerfooter_conf_t header; + ngx_fancyindex_headerfooter_conf_t footer; } ngx_http_fancyindex_loc_conf_t; #define NGX_HTTP_FANCYINDEX_SORT_CRITERION_NAME 0 @@ -181,6 +187,106 @@ { ngx_null_string, 0 } }; +enum { + NGX_HTTP_FANCYINDEX_HEADERFOOTER_SUBREQUEST, + NGX_HTTP_FANCYINDEX_HEADERFOOTER_LOCAL, +}; + +static ngx_uint_t +headerfooter_kind(const ngx_str_t *value) +{ + static const struct { + ngx_str_t name; + ngx_uint_t value; + } values[] = { + { ngx_string("subrequest"), NGX_HTTP_FANCYINDEX_HEADERFOOTER_SUBREQUEST }, + { ngx_string("local"), NGX_HTTP_FANCYINDEX_HEADERFOOTER_LOCAL }, + }; + + unsigned i; + + for (i = 0; i < sizeof(values) / sizeof(values[0]); i++) { + if (value->len == values[i].name.len && + ngx_strcasecmp(value->data, values[i].name.data) == 0) + { + return values[i].value; + } + } + + return NGX_CONF_UNSET_UINT; +} + +static char* +ngx_fancyindex_conf_set_headerfooter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_fancyindex_headerfooter_conf_t *item = + (void*) (((char*) conf) + cmd->offset); + ngx_str_t *values = cf->args->elts; + + if (item->path.data) + return "is duplicate"; + + item->path = values[1]; + + /* Kind of path. Default is "subrequest". */ + ngx_uint_t kind = NGX_HTTP_FANCYINDEX_HEADERFOOTER_SUBREQUEST; + if (cf->args->nelts == 3) { + kind = headerfooter_kind(&values[2]); + if (kind == NGX_CONF_UNSET_UINT) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unknown header/footer kind \"%V\"", &values[2]); + return NGX_CONF_ERROR; + } + } + + if (kind == NGX_HTTP_FANCYINDEX_HEADERFOOTER_LOCAL) { + ngx_file_t file; + ngx_file_info_t fi; + ssize_t n; + + ngx_memzero(&file, sizeof(ngx_file_t)); + file.log = cf->log; + file.fd = ngx_open_file(item->path.data, NGX_FILE_RDONLY, 0, 0); + if (file.fd == NGX_INVALID_FILE) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, + "cannot open file \"%V\"", &values[1]); + return NGX_CONF_ERROR; + } + + if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) { + ngx_close_file(file.fd); + ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, + "cannot get info for file \"%V\"", &values[1]); + return NGX_CONF_ERROR; + } + + item->local.len = ngx_file_size(&fi); + item->local.data = ngx_pcalloc(cf->pool, item->local.len + 1); + if (item->local.data == NULL) { + ngx_close_file(file.fd); + return NGX_CONF_ERROR; + } + + n = item->local.len; + while (n > 0) { + ssize_t r = ngx_read_file(&file, + item->local.data + file.offset, + n, + file.offset); + if (r == NGX_ERROR) { + ngx_close_file(file.fd); + ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, + "cannot read file \"%V\"", &values[1]); + return NGX_CONF_ERROR; + } + + n -= r; + } + item->local.data[item->local.len] = '\0'; + } + + return NGX_CONF_OK; +} #define NGX_HTTP_FANCYINDEX_PREALLOCATE 50 @@ -229,19 +335,17 @@ -static ngx_int_t ngx_libc_cdecl - ngx_http_fancyindex_cmp_entries_dirs_first(const void *one, const void *two); -static ngx_int_t ngx_libc_cdecl +static int ngx_libc_cdecl ngx_http_fancyindex_cmp_entries_name_desc(const void *one, const void *two); -static ngx_int_t ngx_libc_cdecl +static int ngx_libc_cdecl ngx_http_fancyindex_cmp_entries_size_desc(const void *one, const void *two); -static ngx_int_t ngx_libc_cdecl +static int ngx_libc_cdecl ngx_http_fancyindex_cmp_entries_mtime_desc(const void *one, const void *two); -static ngx_int_t ngx_libc_cdecl +static int ngx_libc_cdecl ngx_http_fancyindex_cmp_entries_name_asc(const void *one, const void *two); -static ngx_int_t ngx_libc_cdecl +static int ngx_libc_cdecl ngx_http_fancyindex_cmp_entries_size_asc(const void *one, const void *two); -static ngx_int_t ngx_libc_cdecl +static int ngx_libc_cdecl ngx_http_fancyindex_cmp_entries_mtime_asc(const void *one, const void *two); static ngx_int_t ngx_http_fancyindex_error(ngx_http_request_t *r, @@ -270,11 +374,6 @@ make_header_buf(ngx_http_request_t *r, const ngx_str_t css_href) ngx_force_inline; -static ngx_inline ngx_buf_t* - make_footer_buf(ngx_http_request_t *r) - ngx_force_inline; - - static ngx_command_t ngx_http_fancyindex_commands[] = { @@ -321,15 +420,15 @@ NULL }, { ngx_string("fancyindex_header"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, - ngx_conf_set_str_slot, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, + ngx_fancyindex_conf_set_headerfooter, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_fancyindex_loc_conf_t, header), NULL }, { ngx_string("fancyindex_footer"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, - ngx_conf_set_str_slot, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, + ngx_fancyindex_conf_set_headerfooter, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_fancyindex_loc_conf_t, footer), NULL }, @@ -362,6 +461,13 @@ offsetof(ngx_http_fancyindex_loc_conf_t, show_path), NULL }, + { ngx_string("fancyindex_show_dotfiles"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_fancyindex_loc_conf_t, show_dot_files), + NULL }, + { ngx_string("fancyindex_hide_parent_dir"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, @@ -444,6 +550,8 @@ switch (*psrc++) { case ':': case '?': + case '[': + case ']': escapes++; break; } @@ -485,6 +593,16 @@ *dst++ = '3'; *dst++ = 'F'; break; + case '[': + *dst++ = '%'; + *dst++ = '5'; + *dst++ = 'B'; + break; + case ']': + *dst++ = '%'; + *dst++ = '5'; + *dst++ = 'D'; + break; default: *dst++ = *buf; } @@ -504,6 +622,7 @@ static ngx_inline ngx_buf_t* make_header_buf(ngx_http_request_t *r, const ngx_str_t css_href) { + ngx_buf_t *b; size_t blen = r->uri.len + ngx_sizeof_ssz(t01_head1) + ngx_sizeof_ssz(t02_head2) @@ -518,9 +637,8 @@ ; } - ngx_buf_t *b = ngx_create_temp_buf(r->pool, blen); - - if (b == NULL) goto bailout; + if ((b = ngx_create_temp_buf(r->pool, blen)) == NULL) + return NULL; b->last = ngx_cpymem_ssz(b->last, t01_head1); @@ -535,31 +653,10 @@ b->last = ngx_cpymem_ssz(b->last, t03_head3); b->last = ngx_cpymem_ssz(b->last, t04_body1); -bailout: return b; } - -static ngx_inline ngx_buf_t* -make_footer_buf(ngx_http_request_t *r) -{ - /* - * TODO: Make this buffer static (i.e. readonly and reusable from - * one request to another. - */ - ngx_buf_t *b = ngx_create_temp_buf(r->pool, ngx_sizeof_ssz(t08_foot1)); - - if (b == NULL) goto bailout; - - b->last = ngx_cpymem_ssz(b->last, t08_foot1); - -bailout: - return b; -} - - - static ngx_inline ngx_int_t make_content_buf( ngx_http_request_t *r, ngx_buf_t **pb, @@ -567,7 +664,7 @@ { ngx_http_fancyindex_entry_t *entry; - ngx_int_t (*sort_cmp_func) (const void*, const void*); + int (*sort_cmp_func)(const void *, const void *); const char *sort_url_args = ""; off_t length; @@ -654,7 +751,7 @@ len = ngx_de_namelen(&dir); - if (ngx_de_name(&dir)[0] == '.') + if (!alcf->show_dot_files && ngx_de_name(&dir)[0] == '.') continue; if (alcf->hide_symlinks && ngx_de_is_link (&dir)) @@ -890,19 +987,41 @@ /* Sort entries, if needed */ if (entries.nelts > 1) { - /* Use ngx_sort for stability */ - ngx_sort(entry, (size_t) entries.nelts, - sizeof(ngx_http_fancyindex_entry_t), - sort_cmp_func); - if (alcf->dirs_first) { - /* Sort directories first */ - ngx_sort(entry, (size_t) entries.nelts, - sizeof(ngx_http_fancyindex_entry_t), - ngx_http_fancyindex_cmp_entries_dirs_first); - } + ngx_http_fancyindex_entry_t *l, *r; + l = entry; + r = entry + entries.nelts - 1; + while (l < r) + { + while (l < r && l->dir) + l++; + while (l < r && !r->dir) + r--; + if (l < r) { + /* Now l points a file while r points a directory */ + ngx_http_fancyindex_entry_t tmp; + tmp = *l; + *l = *r; + *r = tmp; + } + } + if (r->dir) + r++; + + if (r > entry) + /* Sort directories */ + ngx_qsort(entry, (size_t)(r - entry), + sizeof(ngx_http_fancyindex_entry_t), sort_cmp_func); + if (r < entry + entries.nelts) + /* Sort files */ + ngx_qsort(r, (size_t)(entry + entries.nelts - r), + sizeof(ngx_http_fancyindex_entry_t), sort_cmp_func); + } else { + ngx_qsort(entry, (size_t)entries.nelts, + sizeof(ngx_http_fancyindex_entry_t), sort_cmp_func); + } } /* Display the path, if needed */ @@ -1088,19 +1207,19 @@ if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) return rc; - if (alcf->header.len > 0) { + if (alcf->header.path.len > 0 && alcf->header.local.len == 0) { /* URI is configured, make Nginx take care of with a subrequest. */ - sr_uri = &alcf->header; + sr_uri = &alcf->header.path; if (*sr_uri->data != '/') { /* Relative path */ - rel_uri.len = r->uri.len + alcf->header.len; + rel_uri.len = r->uri.len + alcf->header.path.len; rel_uri.data = ngx_palloc(r->pool, rel_uri.len); if (rel_uri.data == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_memcpy(ngx_cpymem(rel_uri.data, r->uri.data, r->uri.len), - alcf->header.data, alcf->header.len); + alcf->header.path.data, alcf->header.path.len); sr_uri = &rel_uri; } @@ -1128,25 +1247,42 @@ } else { add_builtin_header: - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http fancyindex: adding built-in header"); /* Make space before */ out[1].next = out[0].next; out[1].buf = out[0].buf; /* Chain header buffer */ out[0].next = &out[1]; - out[0].buf = make_header_buf(r, alcf->css_href); + if (alcf->header.local.len > 0) { + /* Header buffer is local, make a buffer pointing to the data. */ + out[0].buf = ngx_calloc_buf(r->pool); + if (out[0].buf == NULL) + return NGX_ERROR; + out[0].buf->memory = 1; + out[0].buf->pos = alcf->header.local.data; + out[0].buf->last = alcf->header.local.data + alcf->header.local.len; + } else { + /* Prepare a buffer with the contents of the builtin header. */ + out[0].buf = make_header_buf(r, alcf->css_href); + } } /* If footer is disabled, chain up footer buffer. */ - if (alcf->footer.len == 0) { - ngx_uint_t last = (alcf->header.len == 0) ? 2 : 1; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http fancyindex: adding built-in footer at %i", last); + if (alcf->footer.path.len == 0 || alcf->footer.local.len > 0) { + ngx_uint_t last = (alcf->header.path.len == 0) ? 2 : 1; out[last-1].next = &out[last]; - out[last].buf = make_footer_buf(r); + out[last].buf = ngx_calloc_buf(r->pool); + if (out[last].buf == NULL) + return NGX_ERROR; + + out[last].buf->memory = 1; + if (alcf->footer.local.len > 0) { + out[last].buf->pos = alcf->footer.local.data; + out[last].buf->last = alcf->footer.local.data + alcf->footer.local.len; + } else { + out[last].buf->pos = (u_char*) t08_foot1; + out[last].buf->last = (u_char*) t08_foot1 + sizeof(t08_foot1) - 1; + } out[last-1].buf->last_in_chain = 0; out[last].buf->last_in_chain = 1; @@ -1167,17 +1303,17 @@ return NGX_HTTP_INTERNAL_SERVER_ERROR; /* URI is configured, make Nginx take care of with a subrequest. */ - sr_uri = &alcf->footer; + sr_uri = &alcf->footer.path; if (*sr_uri->data != '/') { /* Relative path */ - rel_uri.len = r->uri.len + alcf->footer.len; + rel_uri.len = r->uri.len + alcf->footer.path.len; rel_uri.data = ngx_palloc(r->pool, rel_uri.len); if (rel_uri.data == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_memcpy(ngx_cpymem(rel_uri.data, r->uri.data, r->uri.len), - alcf->footer.data, alcf->footer.len); + alcf->footer.path.data, alcf->footer.path.len); sr_uri = &rel_uri; } @@ -1202,7 +1338,12 @@ * we get something different from a 404? */ out[0].next = NULL; - out[0].buf = make_footer_buf(r); + out[0].buf = ngx_calloc_buf(r->pool); + if (out[0].buf == NULL) + return NGX_ERROR; + out[0].buf->memory = 1; + out[0].buf->pos = (u_char*) t08_foot1; + out[0].buf->last = (u_char*) t08_foot1 + sizeof(t08_foot1) - 1; out[0].buf->last_in_chain = 1; out[0].buf->last_buf = 1; /* Directly send out the builtin footer */ @@ -1212,25 +1353,8 @@ return (r != r->main) ? rc : ngx_http_send_special(r, NGX_HTTP_LAST); } -static ngx_int_t ngx_libc_cdecl -ngx_http_fancyindex_cmp_entries_dirs_first(const void *one, const void *two) -{ - ngx_http_fancyindex_entry_t *first = (ngx_http_fancyindex_entry_t *) one; - ngx_http_fancyindex_entry_t *second = (ngx_http_fancyindex_entry_t *) two; - - /* move the directories to the start */ - if (first->dir && !second->dir) { - return -1; - } - if (!first->dir && second->dir) { - return 1; - } - - return 0; -} - -static ngx_int_t ngx_libc_cdecl +static int ngx_libc_cdecl ngx_http_fancyindex_cmp_entries_name_desc(const void *one, const void *two) { ngx_http_fancyindex_entry_t *first = (ngx_http_fancyindex_entry_t *) one; @@ -1240,7 +1364,7 @@ } -static ngx_int_t ngx_libc_cdecl +static int ngx_libc_cdecl ngx_http_fancyindex_cmp_entries_size_desc(const void *one, const void *two) { ngx_http_fancyindex_entry_t *first = (ngx_http_fancyindex_entry_t *) one; @@ -1250,7 +1374,7 @@ } -static ngx_int_t ngx_libc_cdecl +static int ngx_libc_cdecl ngx_http_fancyindex_cmp_entries_mtime_desc(const void *one, const void *two) { ngx_http_fancyindex_entry_t *first = (ngx_http_fancyindex_entry_t *) one; @@ -1260,7 +1384,7 @@ } -static ngx_int_t ngx_libc_cdecl +static int ngx_libc_cdecl ngx_http_fancyindex_cmp_entries_name_asc(const void *one, const void *two) { ngx_http_fancyindex_entry_t *first = (ngx_http_fancyindex_entry_t *) one; @@ -1270,7 +1394,7 @@ } -static ngx_int_t ngx_libc_cdecl +static int ngx_libc_cdecl ngx_http_fancyindex_cmp_entries_size_asc(const void *one, const void *two) { ngx_http_fancyindex_entry_t *first = (ngx_http_fancyindex_entry_t *) one; @@ -1280,7 +1404,7 @@ } -static ngx_int_t ngx_libc_cdecl +static int ngx_libc_cdecl ngx_http_fancyindex_cmp_entries_mtime_asc(const void *one, const void *two) { ngx_http_fancyindex_entry_t *first = (ngx_http_fancyindex_entry_t *) one; @@ -1314,25 +1438,26 @@ /* * Set by ngx_pcalloc: - * conf->header.len = 0 - * conf->header.data = NULL - * conf->footer.len = 0 - * conf->footer.data = NULL + * conf->header.*.len = 0 + * conf->header.*.data = NULL + * conf->footer.*.len = 0 + * conf->footer.*.data = NULL * conf->css_href.len = 0 * conf->css_href.data = NULL * conf->time_format.len = 0 * conf->time_format.data = NULL */ - conf->enable = NGX_CONF_UNSET; - conf->default_sort = NGX_CONF_UNSET_UINT; - conf->dirs_first = NGX_CONF_UNSET; - conf->localtime = NGX_CONF_UNSET; - conf->name_length = NGX_CONF_UNSET_UINT; - conf->exact_size = NGX_CONF_UNSET; - conf->ignore = NGX_CONF_UNSET_PTR; - conf->hide_symlinks = NGX_CONF_UNSET; - conf->show_path = NGX_CONF_UNSET; - conf->hide_parent = NGX_CONF_UNSET; + conf->enable = NGX_CONF_UNSET; + conf->default_sort = NGX_CONF_UNSET_UINT; + conf->dirs_first = NGX_CONF_UNSET; + conf->localtime = NGX_CONF_UNSET; + conf->name_length = NGX_CONF_UNSET_UINT; + conf->exact_size = NGX_CONF_UNSET; + conf->ignore = NGX_CONF_UNSET_PTR; + conf->hide_symlinks = NGX_CONF_UNSET; + conf->show_path = NGX_CONF_UNSET; + conf->hide_parent = NGX_CONF_UNSET; + conf->show_dot_files = NGX_CONF_UNSET; return conf; } @@ -1352,10 +1477,14 @@ ngx_conf_merge_value(conf->localtime, prev->localtime, 0); ngx_conf_merge_value(conf->exact_size, prev->exact_size, 1); ngx_conf_merge_value(conf->show_path, prev->show_path, 1); + ngx_conf_merge_value(conf->show_dot_files, prev->show_dot_files, 0); ngx_conf_merge_uint_value(conf->name_length, prev->name_length, 50); - ngx_conf_merge_str_value(conf->header, prev->header, ""); - ngx_conf_merge_str_value(conf->footer, prev->footer, ""); + ngx_conf_merge_str_value(conf->header.path, prev->header.path, ""); + ngx_conf_merge_str_value(conf->header.path, prev->header.local, ""); + ngx_conf_merge_str_value(conf->footer.path, prev->footer.path, ""); + ngx_conf_merge_str_value(conf->footer.path, prev->footer.local, ""); + ngx_conf_merge_str_value(conf->css_href, prev->css_href, ""); ngx_conf_merge_str_value(conf->time_format, prev->time_format, "%Y-%b-%d %H:%M"); @@ -1364,7 +1493,7 @@ ngx_conf_merge_value(conf->hide_parent, prev->hide_parent, 0); /* Just make sure we haven't disabled the show_path directive without providing a custom header */ - if (conf->show_path == 0 && conf->header.len == 0) + if (conf->show_path == 0 && conf->header.path.len == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "FancyIndex : cannot set show_path to off without providing a custom header !"); return NGX_CONF_ERROR; diff -Nru nginx-1.18.0/debian/modules/http-fancyindex/README.rst nginx-1.20.1/debian/modules/http-fancyindex/README.rst --- nginx-1.18.0/debian/modules/http-fancyindex/README.rst 2021-03-12 13:47:51.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-fancyindex/README.rst 2021-06-08 07:04:11.000000000 +0000 @@ -2,8 +2,8 @@ Nginx Fancy Index module ======================== -.. image:: https://travis-ci.org/aperezdc/ngx-fancyindex.svg?branch=master - :target: https://travis-ci.org/aperezdc/ngx-fancyindex +.. image:: https://travis-ci.com/aperezdc/ngx-fancyindex.svg?branch=master + :target: https://travis-ci.com/aperezdc/ngx-fancyindex :alt: Build Status .. contents:: @@ -46,10 +46,8 @@ Other platforms ~~~~~~~~~~~~~~~ -In most other cases you will need the sources for Nginx_. Any version starting from the 0.7 -series onwards will work. Note that the modules *might* compile with -versions in the 0.6 series by applying ``nginx-0.6-support.patch``, but this -is unsupported (YMMV). +In most other cases you will need the sources for Nginx_. Any version starting +from the 0.8 series should work. In order to use the ``fancyindex_header_`` and ``fancyindex_footer_`` directives you will also need the `ngx_http_addition_module `_ @@ -184,13 +182,15 @@ fancyindex_footer ~~~~~~~~~~~~~~~~~ -:Syntax: *fancyindex_footer path* +:Syntax: *fancyindex_footer path* [*subrequest* | *local*] :Default: fancyindex_footer "" :Context: http, server, location :Description: Specifies which file should be inserted at the foot of directory listings. If set to an empty string, the default footer supplied by the module will - be sent. + be sent. The optional parameter indicates whether the *path* is to be + treated as an URI to load using a *subrequest* (the default), or whether + it refers to a *local* file. .. note:: Using this directive needs the ngx_http_addition_module_ built into Nginx. @@ -205,13 +205,15 @@ fancyindex_header ~~~~~~~~~~~~~~~~~ -:Syntax: *fancyindex_header path* +:Syntax: *fancyindex_header path* [*subrequest* | *local*] :Default: fancyindex_header "" :Context: http, server, location :Description: Specifies which file should be inserted at the head of directory listings. If set to an empty string, the default header supplied by the module will - be sent. + be sent. The optional parameter indicates whether the *path* is to be + treated as an URI to load using a *subrequest* (the default), or whether + it refers to a *local* file. .. note:: Using this directive needs the ngx_http_addition_module_ built into Nginx. @@ -229,6 +231,15 @@ .. warning:: This directive can be turned off only if a custom header is provided using fancyindex_header. +fancyindex_show_dotfiles +~~~~~~~~~~~~~~~~~~~~ +:Syntax: *fancyindex_show_dotfiles* [*on* | *off*] +:Default: fancyindex_show_dotfiles off +:Context: http, server, location +:Description: + Whether to list files that are proceeded with a dot. Normal convention is to + hide these. + fancyindex_ignore ~~~~~~~~~~~~~~~~~ :Syntax: *fancyindex_ignore string1 [string2 [... stringN]]* diff -Nru nginx-1.18.0/debian/modules/http-fancyindex/t/07-directory-first.test nginx-1.20.1/debian/modules/http-fancyindex/t/07-directory-first.test --- nginx-1.18.0/debian/modules/http-fancyindex/t/07-directory-first.test 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-fancyindex/t/07-directory-first.test 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,50 @@ +#! /bin/bash +cat <<--- +This test check the output using "fancyindex_directories_first on" +-- +use pup + +for d in "008d" "000d" "004d" ; do + mkdir -p "${TESTDIR}/dir_first/${d}" +done +for f in "005f" "001f" "003f"; do + touch "${TESTDIR}/dir_first/${f}" +done +for d in "006d" "002d" ; do + mkdir -p "${TESTDIR}/dir_first/${d}" +done + +nginx_start 'fancyindex_directories_first on;' +previous='' +cur_type='' +while read -r name ; do + case "$name" in + *Parent*) + ;; + *d*) + echo "dir $name" + [[ "$cur_type" = f ]] && fail 'Directories should come before Files' + cur_type=d + if [[ -z ${previous} ]] ; then + previous=${name} + else + [[ ${previous} < ${name} ]] || fail \ + 'Name %s should come before %s\n' "${previous}" "${name}" + fi + ;; + *f*) + echo "file $name" + [[ -z "$cur_type" ]] && fail 'Directories should come before Files' + if [[ "$cur_type" = d ]] ; then + cur_type=f + previous=${name} + else + [[ ${previous} < ${name} ]] || fail \ + 'Name %s should come before %s\n' "${previous}" "${name}" + fi + ;; + esac +done < <( fetch '/dir_first/' \ + | pup -p body table tbody 'td:nth-child(1)' text{} ) + +nginx_is_running || fail "Nginx died" diff -Nru nginx-1.18.0/debian/modules/http-fancyindex/t/07-show_dotfiles.test nginx-1.20.1/debian/modules/http-fancyindex/t/07-show_dotfiles.test --- nginx-1.18.0/debian/modules/http-fancyindex/t/07-show_dotfiles.test 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-fancyindex/t/07-show_dotfiles.test 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,21 @@ +#! /bin/bash +cat <<--- +Test the option to show dotfiles. +-- +# Turn it on. +nginx_start 'fancyindex_show_dotfiles on;' +on_content=$(fetch /show_dotfiles/) +nginx_stop +if [ $(grep '.okay' <<< "${on_content}") -ne 0 ] ; then + exit 1 +fi + +# Turn it off. +nginx_start +off_content=$(fetch /show_dotfiles/) +nginx_stop +if [ $(grep '.okay' <<< "${on_content}") -eq 0] ; then + exit 1 +fi + +exit 0 diff -Nru nginx-1.18.0/debian/modules/http-fancyindex/t/08-local-footer.test nginx-1.20.1/debian/modules/http-fancyindex/t/08-local-footer.test --- nginx-1.18.0/debian/modules/http-fancyindex/t/08-local-footer.test 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-fancyindex/t/08-local-footer.test 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,17 @@ +#! /bin/bash +cat <<--- +This test checks that a local footer can be included with +"fancyindex_header ... local" +-- +use pup + +cat > "${TESTDIR}/footer" <yes +EOF + +nginx_start "fancyindex_footer \"${TESTDIR}/footer\" local;" + +T=$(fetch / | pup -p body 'div#customfooter' text{}) +[[ $T == yes ]] || fail 'Custom header missing' + +nginx_is_running || fail 'Nginx died' diff -Nru nginx-1.18.0/debian/modules/http-fancyindex/t/09-local-header.test nginx-1.20.1/debian/modules/http-fancyindex/t/09-local-header.test --- nginx-1.18.0/debian/modules/http-fancyindex/t/09-local-header.test 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-fancyindex/t/09-local-header.test 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,17 @@ +#! /bin/bash +cat <<--- +This test checks that a local header can be included with +"fancyindex_header ... local" +-- +use pup + +cat > "${TESTDIR}/header" <yes +EOF + +nginx_start "fancyindex_header \"${TESTDIR}/header\" local;" + +T=$(fetch / | pup -p body 'div#customheader' text{}) +[[ $T == yes ]] || fail 'Custom header missing' + +nginx_is_running || fail 'Nginx died' diff -Nru nginx-1.18.0/debian/modules/http-fancyindex/t/10-local-headerfooter.test nginx-1.20.1/debian/modules/http-fancyindex/t/10-local-headerfooter.test --- nginx-1.18.0/debian/modules/http-fancyindex/t/10-local-headerfooter.test 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-fancyindex/t/10-local-headerfooter.test 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,26 @@ +#! /bin/bash +cat <<--- +This test checks that both a local header and footer can be included with +"fancyindex_{header,footer} ... local" +-- +use pup + +cat > "${TESTDIR}/header" <yes +EOF +cat > "${TESTDIR}/footer" <yes +EOF + +nginx_start "fancyindex_header \"${TESTDIR}/header\" local; + fancyindex_footer \"${TESTDIR}/footer\" local;" + +P=$(fetch /) + +H=$(pup -p body 'div#customheader' text{} <<< "$P") +[[ $H == yes ]] || fail 'Custom header missing' + +F=$(pup -p body 'div#customfooter' text{} <<< "$P") +[[ $F == yes ]] || fail 'Custom footer missing' + +nginx_is_running || fail 'Nginx died' diff -Nru nginx-1.18.0/debian/modules/http-fancyindex/t/11-local-footer-nested.test nginx-1.20.1/debian/modules/http-fancyindex/t/11-local-footer-nested.test --- nginx-1.18.0/debian/modules/http-fancyindex/t/11-local-footer-nested.test 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-fancyindex/t/11-local-footer-nested.test 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,31 @@ +#! /bin/bash +cat <<--- +This test checks that local footers are correctly included in presence of +directives in nested locations: + + fancyindex_footer local; + location /sub { + fancyindex_footer local; + } + +-- +use pup + +echo '
yes
' > "${TESTDIR}/top-footer" +echo '
yes
' > "${TESTDIR}/sub-footer" + +nginx_start "fancyindex_footer \"${TESTDIR}/top-footer\" local; + location /child-directory { + fancyindex_footer \"${TESTDIR}/sub-footer\" local; + }" + +T=$(fetch /) +echo "$T" > "$TESTDIR/top.html" +[[ $(pup -p body 'div#topfooter' text{} <<< "$T") = yes ]] || fail 'Custom header missing at /' +[[ -z $(pup -p body 'div#subfooter' text{} <<< "$T") ]] || fail 'Wrong header at /' + +T=$(fetch /child-directory/) +[[ $(pup -p body 'div#subfooter' text{} <<< "$T") = yes ]] || fail 'Custom header missing at /sub/' +[[ -z $(pup -p body 'div#topfooter' text{} <<< "$T") ]] || fail 'Wrong header at /sub/' + +nginx_is_running || fail 'Nginx died' diff -Nru nginx-1.18.0/debian/modules/http-fancyindex/t/12-local-footer-nested.test nginx-1.20.1/debian/modules/http-fancyindex/t/12-local-footer-nested.test --- nginx-1.18.0/debian/modules/http-fancyindex/t/12-local-footer-nested.test 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-fancyindex/t/12-local-footer-nested.test 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,11 @@ +#! /bin/bash +cat <<--- +This test checks that the configuration file is properly parsed if there +is only one parameter passed to the fancyndex_header and fancyindex_footer +configuration directives. +-- + +nginx_start 'fancyindex_header "/header"; + fancyindex_footer "/footer";' + +nginx_is_running || fail 'Nginx died' diff -Nru nginx-1.18.0/debian/modules/http-fancyindex/t/run nginx-1.20.1/debian/modules/http-fancyindex/t/run --- nginx-1.18.0/debian/modules/http-fancyindex/t/run 2021-03-12 13:47:51.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-fancyindex/t/run 2021-06-08 07:04:11.000000000 +0000 @@ -1,4 +1,4 @@ -#! /bin/bash +#!/bin/bash set -e if [[ $# -lt 1 || $# -gt 2 ]] ; then diff -Nru nginx-1.18.0/debian/modules/http-lua/.mergify.yml nginx-1.20.1/debian/modules/http-lua/.mergify.yml --- nginx-1.18.0/debian/modules/http-lua/.mergify.yml 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-lua/.mergify.yml 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,35 @@ +--- +pull_request_rules: + - name: warn on conflicts + conditions: + - conflict + actions: + comment: + message: This pull request is now in conflict :( + label: + add: + - conflict + - name: remove conflict label if not needed + conditions: + - -conflict + actions: + label: + remove: + - conflict + - name: add label needs-test-cases + conditions: + - files~=^src/ + - -files~=^t/ + actions: + label: + add: + - needs-test-cases + - name: remove label needs-test-cases + conditions: + - label=needs-test-cases + - files~=^src/ + - files~=^t/ + actions: + label: + remove: + - needs-test-cases diff -Nru nginx-1.18.0/debian/modules/http-lua/src/ngx_http_lua_exitworkerby.c nginx-1.20.1/debian/modules/http-lua/src/ngx_http_lua_exitworkerby.c --- nginx-1.18.0/debian/modules/http-lua/src/ngx_http_lua_exitworkerby.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-lua/src/ngx_http_lua_exitworkerby.c 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,111 @@ + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_http_lua_exitworkerby.h" +#include "ngx_http_lua_util.h" + + +void +ngx_http_lua_exit_worker(ngx_cycle_t *cycle) +{ + ngx_http_lua_main_conf_t *lmcf; + ngx_connection_t *c = NULL; + ngx_http_request_t *r = NULL; + ngx_http_lua_ctx_t *ctx; + ngx_http_conf_ctx_t *conf_ctx; + + lmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_lua_module); + if (lmcf == NULL + || lmcf->exit_worker_handler == NULL + || lmcf->lua == NULL +#if !(NGX_WIN32) + || (ngx_process == NGX_PROCESS_HELPER +# ifdef HAVE_PRIVILEGED_PROCESS_PATCH + && !ngx_is_privileged_agent +# endif + ) +#endif /* NGX_WIN32 */ + ) + { + return; + } + + conf_ctx = ((ngx_http_conf_ctx_t *) cycle->conf_ctx[ngx_http_module.index]); + + c = ngx_http_lua_create_fake_connection(NULL); + if (c == NULL) { + goto failed; + } + + c->log = ngx_cycle->log; + + r = ngx_http_lua_create_fake_request(c); + if (r == NULL) { + goto failed; + } + + r->main_conf = conf_ctx->main_conf; + r->srv_conf = conf_ctx->srv_conf; + r->loc_conf = conf_ctx->loc_conf; + + ctx = ngx_http_lua_create_ctx(r); + if (ctx == NULL) { + goto failed; + } + + ctx->context = NGX_HTTP_LUA_CONTEXT_EXIT_WORKER; + ctx->cur_co_ctx = NULL; + + ngx_http_lua_set_req(lmcf->lua, r); + + (void) lmcf->exit_worker_handler(cycle->log, lmcf, lmcf->lua); + + ngx_destroy_pool(c->pool); + return; + +failed: + + if (c) { + ngx_http_lua_close_fake_connection(c); + } + + return; +} + + +ngx_int_t +ngx_http_lua_exit_worker_by_inline(ngx_log_t *log, + ngx_http_lua_main_conf_t *lmcf, lua_State *L) +{ + int status; + + status = luaL_loadbuffer(L, (char *) lmcf->exit_worker_src.data, + lmcf->exit_worker_src.len, "=exit_worker_by_lua") + || ngx_http_lua_do_call(log, L); + + return ngx_http_lua_report(log, L, status, "exit_worker_by_lua"); +} + + +ngx_int_t +ngx_http_lua_exit_worker_by_file(ngx_log_t *log, ngx_http_lua_main_conf_t *lmcf, + lua_State *L) +{ + int status; + + status = luaL_loadfile(L, (char *) lmcf->exit_worker_src.data) + || ngx_http_lua_do_call(log, L); + + return ngx_http_lua_report(log, L, status, "exit_worker_by_lua_file"); +} + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff -Nru nginx-1.18.0/debian/modules/http-lua/src/ngx_http_lua_exitworkerby.h nginx-1.20.1/debian/modules/http-lua/src/ngx_http_lua_exitworkerby.h --- nginx-1.18.0/debian/modules/http-lua/src/ngx_http_lua_exitworkerby.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-lua/src/ngx_http_lua_exitworkerby.h 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,25 @@ + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_HTTP_LUA_EXITWORKERBY_H_INCLUDED_ +#define _NGX_HTTP_LUA_EXITWORKERBY_H_INCLUDED_ + + +#include "ngx_http_lua_common.h" + + +ngx_int_t ngx_http_lua_exit_worker_by_inline(ngx_log_t *log, + ngx_http_lua_main_conf_t *lmcf, lua_State *L); + +ngx_int_t ngx_http_lua_exit_worker_by_file(ngx_log_t *log, + ngx_http_lua_main_conf_t *lmcf, lua_State *L); + +void ngx_http_lua_exit_worker(ngx_cycle_t *cycle); + + +#endif /* _NGX_HTTP_LUA_EXITWORKERBY_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff -Nru nginx-1.18.0/debian/modules/http-lua/src/ngx_http_lua_input_filters.c nginx-1.20.1/debian/modules/http-lua/src/ngx_http_lua_input_filters.c --- nginx-1.18.0/debian/modules/http-lua/src/ngx_http_lua_input_filters.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-lua/src/ngx_http_lua_input_filters.c 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,137 @@ + +/* + * Copyright (C) by OpenResty Inc. + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_http_lua_common.h" + + +ngx_int_t +ngx_http_lua_read_bytes(ngx_buf_t *src, ngx_chain_t *buf_in, size_t *rest, + ssize_t bytes, ngx_log_t *log) +{ + if (bytes == 0) { + return NGX_ERROR; + } + + if ((size_t) bytes >= *rest) { + + buf_in->buf->last += *rest; + src->pos += *rest; + *rest = 0; + + return NGX_OK; + } + + /* bytes < *rest */ + + buf_in->buf->last += bytes; + src->pos += bytes; + *rest -= bytes; + + return NGX_AGAIN; +} + + +ngx_int_t +ngx_http_lua_read_all(ngx_buf_t *src, ngx_chain_t *buf_in, ssize_t bytes, + ngx_log_t *log) +{ + if (bytes == 0) { + return NGX_OK; + } + + buf_in->buf->last += bytes; + src->pos += bytes; + + return NGX_AGAIN; +} + + +ngx_int_t +ngx_http_lua_read_any(ngx_buf_t *src, ngx_chain_t *buf_in, size_t *max, + ssize_t bytes, ngx_log_t *log) +{ + if (bytes == 0) { + return NGX_ERROR; + } + + if (bytes >= (ssize_t) *max) { + bytes = (ssize_t) *max; + } + + buf_in->buf->last += bytes; + src->pos += bytes; + + return NGX_OK; +} + + +ngx_int_t +ngx_http_lua_read_line(ngx_buf_t *src, ngx_chain_t *buf_in, ssize_t bytes, + ngx_log_t *log) +{ + u_char *dst; + u_char c; +#if (NGX_DEBUG) + u_char *begin; +#endif + +#if (NGX_DEBUG) + begin = src->pos; +#endif + + if (bytes == 0) { + return NGX_ERROR; + } + + dd("already read: %p: %.*s", buf_in, + (int) (buf_in->buf->last - buf_in->buf->pos), buf_in->buf->pos); + + dd("data read: %.*s", (int) bytes, src->pos); + + dst = buf_in->buf->last; + + while (bytes--) { + + c = *src->pos++; + + switch (c) { + case '\n': + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0, + "lua read the final line part: \"%*s\"", + src->pos - 1 - begin, begin); + + buf_in->buf->last = dst; + + dd("read a line: %p: %.*s", buf_in, + (int) (buf_in->buf->last - buf_in->buf->pos), buf_in->buf->pos); + + return NGX_OK; + + case '\r': + /* ignore it */ + break; + + default: + *dst++ = c; + break; + } + } + +#if (NGX_DEBUG) + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0, + "lua read partial line data: %*s", dst - begin, begin); +#endif + + buf_in->buf->last = dst; + + return NGX_AGAIN; +} diff -Nru nginx-1.18.0/debian/modules/http-lua/src/ngx_http_lua_input_filters.h nginx-1.20.1/debian/modules/http-lua/src/ngx_http_lua_input_filters.h --- nginx-1.18.0/debian/modules/http-lua/src/ngx_http_lua_input_filters.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-lua/src/ngx_http_lua_input_filters.h 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,29 @@ + +/* + * Copyright (C) by OpenResty Inc. + */ + + +#ifndef _NGX_HTTP_LUA_INPUT_FILTERS_H_INCLUDED_ +#define _NGX_HTTP_LUA_INPUT_FILTERS_H_INCLUDED_ + + +#include "ngx_http_lua_common.h" + + +ngx_int_t ngx_http_lua_read_bytes(ngx_buf_t *src, ngx_chain_t *buf_in, + size_t *rest, ssize_t bytes, ngx_log_t *log); + +ngx_int_t ngx_http_lua_read_all(ngx_buf_t *src, ngx_chain_t *buf_in, + ssize_t bytes, ngx_log_t *log); + +ngx_int_t ngx_http_lua_read_any(ngx_buf_t *src, ngx_chain_t *buf_in, + size_t *max, ssize_t bytes, ngx_log_t *log); + +ngx_int_t ngx_http_lua_read_line(ngx_buf_t *src, ngx_chain_t *buf_in, + ssize_t bytes, ngx_log_t *log); + + +#endif /* _NGX_HTTP_LUA_INPUT_FILTERS_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff -Nru nginx-1.18.0/debian/modules/http-lua/src/ngx_http_lua_pipe.c nginx-1.20.1/debian/modules/http-lua/src/ngx_http_lua_pipe.c --- nginx-1.18.0/debian/modules/http-lua/src/ngx_http_lua_pipe.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-lua/src/ngx_http_lua_pipe.c 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,2494 @@ + +/* + * Copyright (C) by OpenResty Inc. + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_http_lua_common.h" +#include "ngx_http_lua_input_filters.h" +#include "ngx_http_lua_util.h" +#include "ngx_http_lua_pipe.h" +#if (NGX_HTTP_LUA_HAVE_SIGNALFD) +#include +#endif + + +#ifdef HAVE_NGX_LUA_PIPE +static ngx_rbtree_node_t *ngx_http_lua_pipe_lookup_pid(ngx_rbtree_key_t key); +#if !(NGX_HTTP_LUA_HAVE_SIGNALFD) +static void ngx_http_lua_pipe_sigchld_handler(int signo, siginfo_t *siginfo, + void *ucontext); +#endif +static void ngx_http_lua_pipe_sigchld_event_handler(ngx_event_t *ev); +static ssize_t ngx_http_lua_pipe_fd_read(ngx_connection_t *c, u_char *buf, + size_t size); +static ssize_t ngx_http_lua_pipe_fd_write(ngx_connection_t *c, u_char *buf, + size_t size); +static void ngx_http_lua_pipe_close_helper(ngx_http_lua_pipe_t *pipe, + ngx_http_lua_pipe_ctx_t *pipe_ctx, ngx_event_t *ev); +static void ngx_http_lua_pipe_close_stdin(ngx_http_lua_pipe_t *pipe); +static void ngx_http_lua_pipe_close_stdout(ngx_http_lua_pipe_t *pipe); +static void ngx_http_lua_pipe_close_stderr(ngx_http_lua_pipe_t *pipe); +static void ngx_http_lua_pipe_proc_finalize(ngx_http_lua_ffi_pipe_proc_t *proc); +static ngx_int_t ngx_http_lua_pipe_get_lua_ctx(ngx_http_request_t *r, + ngx_http_lua_ctx_t **ctx, u_char *errbuf, size_t *errbuf_size); +static void ngx_http_lua_pipe_put_error(ngx_http_lua_pipe_ctx_t *pipe_ctx, + u_char *errbuf, size_t *errbuf_size); +static void ngx_http_lua_pipe_put_data(ngx_http_lua_pipe_t *pipe, + ngx_http_lua_pipe_ctx_t *pipe_ctx, u_char **buf, size_t *buf_size); +static ngx_int_t ngx_http_lua_pipe_add_input_buffer(ngx_http_lua_pipe_t *pipe, + ngx_http_lua_pipe_ctx_t *pipe_ctx); +static ngx_int_t ngx_http_lua_pipe_read_all(void *data, ssize_t bytes); +static ngx_int_t ngx_http_lua_pipe_read_bytes(void *data, ssize_t bytes); +static ngx_int_t ngx_http_lua_pipe_read_line(void *data, ssize_t bytes); +static ngx_int_t ngx_http_lua_pipe_read_any(void *data, ssize_t bytes); +static ngx_int_t ngx_http_lua_pipe_read(ngx_http_lua_pipe_t *pipe, + ngx_http_lua_pipe_ctx_t *pipe_ctx); +static ngx_int_t ngx_http_lua_pipe_init_ctx( + ngx_http_lua_pipe_ctx_t **pipe_ctx_pt, int fd, ngx_pool_t *pool, + u_char *errbuf, size_t *errbuf_size); +static ngx_int_t ngx_http_lua_pipe_write(ngx_http_lua_pipe_t *pipe, + ngx_http_lua_pipe_ctx_t *pipe_ctx); +static int ngx_http_lua_pipe_read_stdout_retval( + ngx_http_lua_ffi_pipe_proc_t *proc, lua_State *L); +static int ngx_http_lua_pipe_read_stderr_retval( + ngx_http_lua_ffi_pipe_proc_t *proc, lua_State *L); +static int ngx_http_lua_pipe_read_retval_helper( + ngx_http_lua_ffi_pipe_proc_t *proc, lua_State *L, int from_stderr); +static int ngx_http_lua_pipe_write_retval(ngx_http_lua_ffi_pipe_proc_t *proc, + lua_State *L); +static int ngx_http_lua_pipe_wait_retval(ngx_http_lua_ffi_pipe_proc_t *proc, + lua_State *L); +static void ngx_http_lua_pipe_resume_helper(ngx_event_t *ev, + ngx_http_lua_co_ctx_t *wait_co_ctx); +static void ngx_http_lua_pipe_resume_read_stdout_handler(ngx_event_t *ev); +static void ngx_http_lua_pipe_resume_read_stderr_handler(ngx_event_t *ev); +static void ngx_http_lua_pipe_resume_write_handler(ngx_event_t *ev); +static void ngx_http_lua_pipe_resume_wait_handler(ngx_event_t *ev); +static ngx_int_t ngx_http_lua_pipe_resume(ngx_http_request_t *r); +static void ngx_http_lua_pipe_dummy_event_handler(ngx_event_t *ev); +static void ngx_http_lua_pipe_clear_event(ngx_event_t *ev); +static void ngx_http_lua_pipe_proc_read_stdout_cleanup(void *data); +static void ngx_http_lua_pipe_proc_read_stderr_cleanup(void *data); +static void ngx_http_lua_pipe_proc_write_cleanup(void *data); +static void ngx_http_lua_pipe_proc_wait_cleanup(void *data); + + +static ngx_rbtree_t ngx_http_lua_pipe_rbtree; +static ngx_rbtree_node_t ngx_http_lua_pipe_proc_sentinel; + + +#if (NGX_HTTP_LUA_HAVE_SIGNALFD) +static int ngx_http_lua_signalfd; +static struct signalfd_siginfo ngx_http_lua_pipe_notification; + +#define ngx_http_lua_read_sigfd ngx_http_lua_signalfd + +#else +static int ngx_http_lua_sigchldfd[2]; +static u_char ngx_http_lua_pipe_notification[1]; + +#define ngx_http_lua_read_sigfd ngx_http_lua_sigchldfd[0] +#define ngx_http_lua_write_sigfd ngx_http_lua_sigchldfd[1] +#endif + + +static ngx_connection_t *ngx_http_lua_sigfd_conn = NULL; + + +/* The below signals are ignored by Nginx. + * We need to reset them for the spawned child processes. */ +ngx_http_lua_pipe_signal_t ngx_signals[] = { + { SIGSYS, "SIGSYS" }, + { SIGPIPE, "SIGPIPE" }, + { 0, NULL } +}; + + +enum { + PIPE_ERR_CLOSED = 1, + PIPE_ERR_SYSCALL, + PIPE_ERR_NOMEM, + PIPE_ERR_TIMEOUT, + PIPE_ERR_ADD_READ_EV, + PIPE_ERR_ADD_WRITE_EV, + PIPE_ERR_ABORTED, +}; + + +enum { + PIPE_READ_ALL = 0, + PIPE_READ_BYTES, + PIPE_READ_LINE, + PIPE_READ_ANY, +}; + + +#define REASON_EXIT "exit" +#define REASON_SIGNAL "signal" +#define REASON_UNKNOWN "unknown" + +#define REASON_RUNNING_CODE 0 +#define REASON_EXIT_CODE 1 +#define REASON_SIGNAL_CODE 2 +#define REASON_UNKNOWN_CODE 3 + + +void +ngx_http_lua_pipe_init(void) +{ + ngx_rbtree_init(&ngx_http_lua_pipe_rbtree, + &ngx_http_lua_pipe_proc_sentinel, ngx_rbtree_insert_value); +} + + +ngx_int_t +ngx_http_lua_pipe_add_signal_handler(ngx_cycle_t *cycle) +{ + ngx_event_t *rev; +#if (NGX_HTTP_LUA_HAVE_SIGNALFD) + sigset_t set; + +#else + int rc; + struct sigaction sa; +#endif + +#if (NGX_HTTP_LUA_HAVE_SIGNALFD) + if (sigemptyset(&set) != 0) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, + "lua pipe init signal set failed"); + return NGX_ERROR; + } + + if (sigaddset(&set, SIGCHLD) != 0) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, + "lua pipe add SIGCHLD to signal set failed"); + return NGX_ERROR; + } + + if (sigprocmask(SIG_BLOCK, &set, NULL) != 0) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, + "lua pipe block SIGCHLD failed"); + return NGX_ERROR; + } + + ngx_http_lua_signalfd = signalfd(-1, &set, SFD_NONBLOCK|SFD_CLOEXEC); + if (ngx_http_lua_signalfd < 0) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, + "lua pipe create signalfd instance failed"); + return NGX_ERROR; + } + +#else /* !(NGX_HTTP_LUA_HAVE_SIGNALFD) */ +# if (NGX_HTTP_LUA_HAVE_PIPE2) + rc = pipe2(ngx_http_lua_sigchldfd, O_NONBLOCK|O_CLOEXEC); +# else + rc = pipe(ngx_http_lua_sigchldfd); +# endif + + if (rc == -1) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, + "lua pipe init SIGCHLD fd failed"); + return NGX_ERROR; + } + +# if !(NGX_HTTP_LUA_HAVE_PIPE2) + if (ngx_nonblocking(ngx_http_lua_read_sigfd) == -1) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, "lua pipe " + ngx_nonblocking_n " SIGCHLD read fd failed"); + goto failed; + } + + if (ngx_nonblocking(ngx_http_lua_write_sigfd) == -1) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, "lua pipe " + ngx_nonblocking_n " SIGCHLD write fd failed"); + goto failed; + } + + /* it's ok not to set the pipe fd with O_CLOEXEC. This requires + * extra syscall */ +# endif /* !(NGX_HTTP_LUA_HAVE_PIPE2) */ +#endif /* NGX_HTTP_LUA_HAVE_SIGNALFD */ + + ngx_http_lua_sigfd_conn = ngx_get_connection(ngx_http_lua_read_sigfd, + cycle->log); + if (ngx_http_lua_sigfd_conn == NULL) { + goto failed; + } + + ngx_http_lua_sigfd_conn->log = cycle->log; + ngx_http_lua_sigfd_conn->recv = ngx_http_lua_pipe_fd_read; + rev = ngx_http_lua_sigfd_conn->read; + rev->log = ngx_http_lua_sigfd_conn->log; + rev->handler = ngx_http_lua_pipe_sigchld_event_handler; + +#ifdef HAVE_SOCKET_CLOEXEC_PATCH + rev->skip_socket_leak_check = 1; +#endif + + if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + goto failed; + } + +#if !(NGX_HTTP_LUA_HAVE_SIGNALFD) + ngx_memzero(&sa, sizeof(struct sigaction)); + sa.sa_sigaction = ngx_http_lua_pipe_sigchld_handler; + sa.sa_flags = SA_SIGINFO; + + if (sigemptyset(&sa.sa_mask) != 0) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, + "lua pipe init signal mask failed"); + goto failed; + } + + if (sigaction(SIGCHLD, &sa, NULL) == -1) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, + "lua pipe sigaction(SIGCHLD) failed"); + goto failed; + } +#endif + + return NGX_OK; + +failed: + + if (ngx_http_lua_sigfd_conn != NULL) { + ngx_close_connection(ngx_http_lua_sigfd_conn); + ngx_http_lua_sigfd_conn = NULL; + } + + if (close(ngx_http_lua_read_sigfd) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "lua pipe close the read sigfd failed"); + } + +#if !(NGX_HTTP_LUA_HAVE_SIGNALFD) + if (close(ngx_http_lua_write_sigfd) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "lua pipe close the write sigfd failed"); + } +#endif + + return NGX_ERROR; +} + + +static ngx_rbtree_node_t * +ngx_http_lua_pipe_lookup_pid(ngx_rbtree_key_t key) +{ + ngx_rbtree_node_t *node, *sentinel; + + node = ngx_http_lua_pipe_rbtree.root; + sentinel = ngx_http_lua_pipe_rbtree.sentinel; + + while (node != sentinel) { + if (key < node->key) { + node = node->left; + continue; + } + + if (key > node->key) { + node = node->right; + continue; + } + + return node; + } + + return NULL; +} + + +#if !(NGX_HTTP_LUA_HAVE_SIGNALFD) +static void +ngx_http_lua_pipe_sigchld_handler(int signo, siginfo_t *siginfo, + void *ucontext) +{ + ngx_err_t err, saved_err; + ngx_int_t n; + + saved_err = ngx_errno; + + for ( ;; ) { + n = write(ngx_http_lua_write_sigfd, ngx_http_lua_pipe_notification, + sizeof(ngx_http_lua_pipe_notification)); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0, + "lua pipe SIGCHLD fd write siginfo:%p", siginfo); + + if (n >= 0) { + break; + } + + err = ngx_errno; + + if (err != NGX_EINTR) { + if (err != NGX_EAGAIN) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, err, + "lua pipe SIGCHLD fd write failed"); + } + + break; + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, err, + "lua pipe SIGCHLD fd write was interrupted"); + } + + ngx_set_errno(saved_err); +} +#endif + + +static void +ngx_http_lua_pipe_sigchld_event_handler(ngx_event_t *ev) +{ + int n; + int status; + ngx_pid_t pid; + ngx_connection_t *c = ev->data; + ngx_rbtree_node_t *node; + ngx_http_lua_pipe_node_t *pipe_node; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0, + "lua pipe reaping children"); + + for ( ;; ) { +#if (NGX_HTTP_LUA_HAVE_SIGNALFD) + n = c->recv(c, (u_char *) &ngx_http_lua_pipe_notification, +#else + n = c->recv(c, ngx_http_lua_pipe_notification, +#endif + sizeof(ngx_http_lua_pipe_notification)); + + if (n <= 0) { + if (n == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe SIGCHLD fd read failed"); + } + + break; + } + + for ( ;; ) { + pid = waitpid(-1, &status, WNOHANG); + + if (pid == 0) { + break; + } + + if (pid < 0) { + if (ngx_errno != NGX_ECHILD) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe waitpid failed"); + } + + break; + } + + /* This log is ported from Nginx's signal handler since we override + * or block it in this implementation. */ + ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, + "signal %d (SIGCHLD) received from %P", + SIGCHLD, pid); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe SIGCHLD fd read pid:%P status:%d", pid, + status); + + node = ngx_http_lua_pipe_lookup_pid(pid); + if (node != NULL) { + pipe_node = (ngx_http_lua_pipe_node_t *) &node->color; + if (pipe_node->wait_co_ctx != NULL) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe resume process:%p waiting for %P", + pipe_node->proc, pid); + + /* + * We need the extra parentheses around the first argument + * of ngx_post_event() just to work around macro issues in + * nginx cores older than 1.7.12 (exclusive). + */ + ngx_post_event((&pipe_node->wait_co_ctx->sleep), + &ngx_posted_events); + } + + pipe_node->proc->pipe->dead = 1; + + if (WIFSIGNALED(status)) { + pipe_node->status = WTERMSIG(status); + pipe_node->reason_code = REASON_SIGNAL_CODE; + + } else if (WIFEXITED(status)) { + pipe_node->status = WEXITSTATUS(status); + pipe_node->reason_code = REASON_EXIT_CODE; + + } else { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, + "lua pipe unknown exit status %d from " + "process %P", status, pid); + pipe_node->status = status; + pipe_node->reason_code = REASON_UNKNOWN_CODE; + } + } + } + } +} + + +static ssize_t +ngx_http_lua_pipe_fd_read(ngx_connection_t *c, u_char *buf, size_t size) +{ + ssize_t n; + ngx_err_t err; + ngx_event_t *rev; + + rev = c->read; + + do { + n = read(c->fd, buf, size); + + err = ngx_errno; + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "read: fd:%d %z of %uz", c->fd, n, size); + + if (n == 0) { + rev->ready = 0; + rev->eof = 1; + return 0; + } + + if (n > 0) { + if ((size_t) n < size + && !(ngx_event_flags & NGX_USE_GREEDY_EVENT)) + { + rev->ready = 0; + } + + return n; + } + + if (err == NGX_EAGAIN || err == NGX_EINTR) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "read() not ready"); + n = NGX_AGAIN; + + } else { + n = ngx_connection_error(c, err, "read() failed"); + break; + } + + } while (err == NGX_EINTR); + + rev->ready = 0; + + if (n == NGX_ERROR) { + rev->error = 1; + } + + return n; +} + + +static ssize_t +ngx_http_lua_pipe_fd_write(ngx_connection_t *c, u_char *buf, size_t size) +{ + ssize_t n; + ngx_err_t err; + ngx_event_t *wev; + + wev = c->write; + + do { + n = write(c->fd, buf, size); + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "write: fd:%d %z of %uz", c->fd, n, size); + + if (n >= 0) { + if ((size_t) n != size) { + wev->ready = 0; + } + + return n; + } + + err = ngx_errno; + + if (err == NGX_EAGAIN || err == NGX_EINTR) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "write() not ready"); + n = NGX_AGAIN; + + } else if (err != NGX_EPIPE) { + n = ngx_connection_error(c, err, "write() failed"); + break; + } + + } while (err == NGX_EINTR); + + wev->ready = 0; + + if (n == NGX_ERROR) { + wev->error = 1; + } + + return n; +} + + +int +ngx_http_lua_ffi_pipe_spawn(ngx_http_lua_ffi_pipe_proc_t *proc, + const char *file, const char **argv, int merge_stderr, size_t buffer_size, + const char **environ, u_char *errbuf, size_t *errbuf_size) +{ + int rc; + int in[2]; + int out[2]; + int err[2]; + int stdin_fd, stdout_fd, stderr_fd; + int errlog_fd, temp_errlog_fd; + ngx_pid_t pid; + ssize_t pool_size; + ngx_pool_t *pool; + ngx_uint_t i; + ngx_listening_t *ls; + ngx_http_lua_pipe_t *pp; + ngx_rbtree_node_t *node; + ngx_http_lua_pipe_node_t *pipe_node; + struct sigaction sa; + ngx_http_lua_pipe_signal_t *sig; + sigset_t set; + +#if !(NGX_HTTP_LUA_HAVE_EXECVPE) + if (environ != NULL) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, + "environ option not supported") + - errbuf; + return NGX_ERROR; + } +#endif + + pool_size = ngx_align(NGX_MIN_POOL_SIZE + buffer_size * 2, + NGX_POOL_ALIGNMENT); + + pool = ngx_create_pool(pool_size, ngx_cycle->log); + if (pool == NULL) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "no memory") + - errbuf; + return NGX_ERROR; + } + + pp = ngx_pcalloc(pool, sizeof(ngx_http_lua_pipe_t) + + offsetof(ngx_rbtree_node_t, color) + + sizeof(ngx_http_lua_pipe_node_t)); + if (pp == NULL) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "no memory") + - errbuf; + goto free_pool; + } + + rc = pipe(in); + if (rc == -1) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "pipe failed: %s", + strerror(errno)) + - errbuf; + goto free_pool; + } + + rc = pipe(out); + if (rc == -1) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "pipe failed: %s", + strerror(errno)) + - errbuf; + goto close_in_fd; + } + + if (!merge_stderr) { + rc = pipe(err); + if (rc == -1) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, + "pipe failed: %s", strerror(errno)) + - errbuf; + goto close_in_out_fd; + } + } + + pid = fork(); + if (pid == -1) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "fork failed: %s", + strerror(errno)) + - errbuf; + goto close_in_out_err_fd; + } + + if (pid == 0) { + +#if (NGX_HAVE_CPU_AFFINITY) + /* reset the CPU affinity mask */ + ngx_uint_t log_level; + ngx_cpuset_t child_cpu_affinity; + + if (ngx_process == NGX_PROCESS_WORKER + && ngx_get_cpu_affinity(ngx_worker) != NULL) + { + CPU_ZERO(&child_cpu_affinity); + + for (i = 0; i < (ngx_uint_t) ngx_min(ngx_ncpu, CPU_SETSIZE); i++) { + CPU_SET(i, &child_cpu_affinity); + } + + log_level = ngx_cycle->log->log_level; + ngx_cycle->log->log_level = NGX_LOG_WARN; + ngx_setaffinity(&child_cpu_affinity, ngx_cycle->log); + ngx_cycle->log->log_level = log_level; + } +#endif + + /* reset the handler of ignored signals to the default */ + for (sig = ngx_signals; sig->signo != 0; sig++) { + ngx_memzero(&sa, sizeof(struct sigaction)); + sa.sa_handler = SIG_DFL; + + if (sigemptyset(&sa.sa_mask) != 0) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child init signal mask failed"); + exit(EXIT_FAILURE); + } + + if (sigaction(sig->signo, &sa, NULL) == -1) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child reset signal handler for %s " + "failed", sig->signame); + exit(EXIT_FAILURE); + } + } + + /* reset signal mask */ + if (sigemptyset(&set) != 0) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child init signal set failed"); + exit(EXIT_FAILURE); + } + + if (sigprocmask(SIG_SETMASK, &set, NULL) != 0) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child reset signal mask failed"); + exit(EXIT_FAILURE); + } + + /* close listening socket fd */ + ls = ngx_cycle->listening.elts; + for (i = 0; i < ngx_cycle->listening.nelts; i++) { + if (ngx_close_socket(ls[i].fd) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_socket_errno, + "lua pipe child " ngx_close_socket_n + " %V failed", &ls[i].addr_text); + } + } + + /* close and dup pipefd */ + if (close(in[1]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "lua pipe child failed to close the in[1] " + "pipe fd"); + } + + if (close(out[0]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "lua pipe child failed to close the out[0] " + "pipe fd"); + } + + if (ngx_cycle->log->file && ngx_cycle->log->file->fd == STDERR_FILENO) { + errlog_fd = ngx_cycle->log->file->fd; + temp_errlog_fd = dup(errlog_fd); + + if (temp_errlog_fd == -1) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child dup errlog fd failed"); + exit(EXIT_FAILURE); + } + + if (ngx_cloexec(temp_errlog_fd) == -1) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child new errlog fd " ngx_cloexec_n + " failed"); + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe child dup old errlog fd %d to new fd %d", + ngx_cycle->log->file->fd, temp_errlog_fd); + + ngx_cycle->log->file->fd = temp_errlog_fd; + } + + if (dup2(in[0], STDIN_FILENO) == -1) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child dup2 stdin failed"); + exit(EXIT_FAILURE); + } + + if (dup2(out[1], STDOUT_FILENO) == -1) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child dup2 stdout failed"); + exit(EXIT_FAILURE); + } + + if (merge_stderr) { + if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child dup2 stderr failed"); + exit(EXIT_FAILURE); + } + + } else { + if (close(err[0]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "lua pipe child failed to close the err[0] " + "pipe fd"); + } + + if (dup2(err[1], STDERR_FILENO) == -1) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child dup2 stderr failed"); + exit(EXIT_FAILURE); + } + } + +#if (NGX_HTTP_LUA_HAVE_EXECVPE) + if (environ != NULL) { + if (execvpe(file, (char * const *) argv, (char * const *) environ) + == -1) + { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child execvpe() failed while " + "executing %s", file); + } + + } else { + if (execvp(file, (char * const *) argv) == -1) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child execvp() failed while " + "executing %s", file); + } + } + +#else + if (execvp(file, (char * const *) argv) == -1) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe child execvp() failed while executing %s", + file); + } +#endif + + exit(EXIT_FAILURE); + } + + /* parent process */ + if (close(in[0]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "lua pipe: failed to close the in[0] pipe fd"); + } + + stdin_fd = in[1]; + + if (ngx_nonblocking(stdin_fd) == -1) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, + ngx_nonblocking_n " failed: %s", + strerror(errno)) + - errbuf; + goto close_in_out_err_fd; + } + + pp->stdin_fd = stdin_fd; + + if (close(out[1]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "lua pipe: failed to close the out[1] pipe fd"); + } + + stdout_fd = out[0]; + + if (ngx_nonblocking(stdout_fd) == -1) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, + ngx_nonblocking_n " failed: %s", + strerror(errno)) + - errbuf; + goto close_in_out_err_fd; + } + + pp->stdout_fd = stdout_fd; + + if (!merge_stderr) { + if (close(err[1]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "lua pipe: failed to close the err[1] pipe fd"); + } + + stderr_fd = err[0]; + + if (ngx_nonblocking(stderr_fd) == -1) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, + ngx_nonblocking_n " failed: %s", + strerror(errno)) + - errbuf; + goto close_in_out_err_fd; + } + + pp->stderr_fd = stderr_fd; + } + + node = (ngx_rbtree_node_t *) (pp + 1); + node->key = pid; + pipe_node = (ngx_http_lua_pipe_node_t *) &node->color; + pipe_node->proc = proc; + ngx_rbtree_insert(&ngx_http_lua_pipe_rbtree, node); + + pp->node = node; + pp->pool = pool; + pp->merge_stderr = merge_stderr; + pp->buffer_size = buffer_size; + + proc->_pid = pid; + proc->pipe = pp; + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe spawn process:%p pid:%P merge_stderr:%d " + "buffer_size:%uz", proc, pid, merge_stderr, buffer_size); + return NGX_OK; + +close_in_out_err_fd: + + if (!merge_stderr) { + if (close(err[0]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "failed to close the err[0] pipe fd"); + } + + if (close(err[1]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "failed to close the err[1] pipe fd"); + } + } + +close_in_out_fd: + + if (close(out[0]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "failed to close the out[0] pipe fd"); + } + + if (close(out[1]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "failed to close the out[1] pipe fd"); + } + +close_in_fd: + + if (close(in[0]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "failed to close the in[0] pipe fd"); + } + + if (close(in[1]) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "failed to close the in[1] pipe fd"); + } + +free_pool: + + ngx_destroy_pool(pool); + return NGX_ERROR; +} + + +static void +ngx_http_lua_pipe_close_helper(ngx_http_lua_pipe_t *pipe, + ngx_http_lua_pipe_ctx_t *pipe_ctx, ngx_event_t *ev) +{ + if (ev->handler != ngx_http_lua_pipe_dummy_event_handler) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe abort blocking operation pipe_ctx:%p ev:%p", + pipe_ctx, ev); + + if (pipe->dead) { + pipe_ctx->err_type = PIPE_ERR_CLOSED; + + } else { + pipe_ctx->err_type = PIPE_ERR_ABORTED; + } + + ngx_post_event(ev, &ngx_posted_events); + return; + } + + ngx_close_connection(pipe_ctx->c); + pipe_ctx->c = NULL; +} + + +static void +ngx_http_lua_pipe_close_stdin(ngx_http_lua_pipe_t *pipe) +{ + ngx_event_t *wev; + + if (pipe->stdin_ctx == NULL) { + if (pipe->stdin_fd != -1) { + if (close(pipe->stdin_fd) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "failed to close the stdin pipe fd"); + } + + pipe->stdin_fd = -1; + } + + } else if (pipe->stdin_ctx->c != NULL) { + wev = pipe->stdin_ctx->c->write; + ngx_http_lua_pipe_close_helper(pipe, pipe->stdin_ctx, wev); + } +} + + +static void +ngx_http_lua_pipe_close_stdout(ngx_http_lua_pipe_t *pipe) +{ + ngx_event_t *rev; + + if (pipe->stdout_ctx == NULL) { + if (pipe->stdout_fd != -1) { + if (close(pipe->stdout_fd) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "failed to close the stdout pipe fd"); + } + + pipe->stdout_fd = -1; + } + + } else if (pipe->stdout_ctx->c != NULL) { + rev = pipe->stdout_ctx->c->read; + ngx_http_lua_pipe_close_helper(pipe, pipe->stdout_ctx, rev); + } +} + + +static void +ngx_http_lua_pipe_close_stderr(ngx_http_lua_pipe_t *pipe) +{ + ngx_event_t *rev; + + if (pipe->stderr_ctx == NULL) { + if (pipe->stderr_fd != -1) { + if (close(pipe->stderr_fd) == -1) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "failed to close the stderr pipe fd"); + } + + pipe->stderr_fd = -1; + } + + } else if (pipe->stderr_ctx->c != NULL) { + rev = pipe->stderr_ctx->c->read; + ngx_http_lua_pipe_close_helper(pipe, pipe->stderr_ctx, rev); + } +} + + +int +ngx_http_lua_ffi_pipe_proc_shutdown_stdin(ngx_http_lua_ffi_pipe_proc_t *proc, + u_char *errbuf, size_t *errbuf_size) +{ + ngx_http_lua_pipe_t *pipe; + + pipe = proc->pipe; + if (pipe == NULL || pipe->closed) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "closed") - errbuf; + return NGX_ERROR; + } + + ngx_http_lua_pipe_close_stdin(pipe); + + return NGX_OK; +} + + +int +ngx_http_lua_ffi_pipe_proc_shutdown_stdout(ngx_http_lua_ffi_pipe_proc_t *proc, + u_char *errbuf, size_t *errbuf_size) +{ + ngx_http_lua_pipe_t *pipe; + + pipe = proc->pipe; + if (pipe == NULL || pipe->closed) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "closed") - errbuf; + return NGX_ERROR; + } + + ngx_http_lua_pipe_close_stdout(pipe); + + return NGX_OK; +} + + +int +ngx_http_lua_ffi_pipe_proc_shutdown_stderr(ngx_http_lua_ffi_pipe_proc_t *proc, + u_char *errbuf, size_t *errbuf_size) +{ + ngx_http_lua_pipe_t *pipe; + + pipe = proc->pipe; + if (pipe == NULL || pipe->closed) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "closed") - errbuf; + return NGX_ERROR; + } + + if (pipe->merge_stderr) { + /* stdout is used internally as stderr when merge_stderr is true */ + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "merged to stdout") + - errbuf; + return NGX_ERROR; + } + + ngx_http_lua_pipe_close_stderr(pipe); + + return NGX_OK; +} + + +static void +ngx_http_lua_pipe_proc_finalize(ngx_http_lua_ffi_pipe_proc_t *proc) +{ + ngx_http_lua_pipe_t *pipe; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe finalize process:%p pid:%P", + proc, proc->_pid); + pipe = proc->pipe; + + if (pipe->node) { + ngx_rbtree_delete(&ngx_http_lua_pipe_rbtree, pipe->node); + pipe->node = NULL; + } + + pipe->dead = 1; + + ngx_http_lua_pipe_close_stdin(pipe); + ngx_http_lua_pipe_close_stdout(pipe); + + if (!pipe->merge_stderr) { + ngx_http_lua_pipe_close_stderr(pipe); + } + + pipe->closed = 1; +} + + +void +ngx_http_lua_ffi_pipe_proc_destroy(ngx_http_lua_ffi_pipe_proc_t *proc) +{ + ngx_http_lua_pipe_t *pipe; + + pipe = proc->pipe; + if (pipe == NULL) { + return; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe destroy process:%p pid:%P", proc, proc->_pid); + + if (!pipe->dead) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe kill process:%p pid:%P", proc, proc->_pid); + + if (kill(proc->_pid, SIGKILL) == -1) { + if (ngx_errno != ESRCH) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "lua pipe failed to kill process:%p pid:%P", + proc, proc->_pid); + } + } + } + + ngx_http_lua_pipe_proc_finalize(proc); + ngx_destroy_pool(pipe->pool); + proc->pipe = NULL; +} + + +static ngx_int_t +ngx_http_lua_pipe_get_lua_ctx(ngx_http_request_t *r, + ngx_http_lua_ctx_t **ctx, u_char *errbuf, size_t *errbuf_size) +{ + int rc; + + *ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return NGX_HTTP_LUA_FFI_NO_REQ_CTX; + } + + rc = ngx_http_lua_ffi_check_context(*ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE, + errbuf, errbuf_size); + if (rc != NGX_OK) { + return NGX_HTTP_LUA_FFI_BAD_CONTEXT; + } + + return NGX_OK; +} + + +static void +ngx_http_lua_pipe_put_error(ngx_http_lua_pipe_ctx_t *pipe_ctx, u_char *errbuf, + size_t *errbuf_size) +{ + switch (pipe_ctx->err_type) { + + case PIPE_ERR_CLOSED: + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "closed") - errbuf; + break; + + case PIPE_ERR_SYSCALL: + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "%s", + strerror(pipe_ctx->pipe_errno)) + - errbuf; + break; + + case PIPE_ERR_NOMEM: + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "no memory") + - errbuf; + break; + + case PIPE_ERR_TIMEOUT: + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "timeout") + - errbuf; + break; + + case PIPE_ERR_ADD_READ_EV: + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, + "failed to add read event") + - errbuf; + break; + + case PIPE_ERR_ADD_WRITE_EV: + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, + "failed to add write event") + - errbuf; + break; + + case PIPE_ERR_ABORTED: + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "aborted") - errbuf; + break; + + default: + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, + "unexpected err type: %d", pipe_ctx->err_type); + ngx_http_lua_assert(NULL); + } +} + + +static void +ngx_http_lua_pipe_put_data(ngx_http_lua_pipe_t *pipe, + ngx_http_lua_pipe_ctx_t *pipe_ctx, u_char **buf, size_t *buf_size) +{ + size_t size = 0; + size_t chunk_size; + size_t nbufs; + u_char *p; + ngx_buf_t *b; + ngx_chain_t *cl; + ngx_chain_t **ll; + + nbufs = 0; + ll = NULL; + + for (cl = pipe_ctx->bufs_in; cl; cl = cl->next) { + b = cl->buf; + chunk_size = b->last - b->pos; + + if (cl->next) { + ll = &cl->next; + } + + size += chunk_size; + + nbufs++; + } + + if (*buf_size < size) { + *buf = NULL; + *buf_size = size; + + return; + } + + *buf_size = size; + + p = *buf; + for (cl = pipe_ctx->bufs_in; cl; cl = cl->next) { + b = cl->buf; + chunk_size = b->last - b->pos; + p = ngx_cpymem(p, b->pos, chunk_size); + } + + if (nbufs > 1 && ll) { + *ll = pipe->free_bufs; + pipe->free_bufs = pipe_ctx->bufs_in; + pipe_ctx->bufs_in = pipe_ctx->buf_in; + } + + if (pipe_ctx->buffer.pos == pipe_ctx->buffer.last) { + pipe_ctx->buffer.pos = pipe_ctx->buffer.start; + pipe_ctx->buffer.last = pipe_ctx->buffer.start; + } + + if (pipe_ctx->bufs_in) { + pipe_ctx->buf_in->buf->last = pipe_ctx->buffer.pos; + pipe_ctx->buf_in->buf->pos = pipe_ctx->buffer.pos; + } +} + + +static ngx_int_t +ngx_http_lua_pipe_add_input_buffer(ngx_http_lua_pipe_t *pipe, + ngx_http_lua_pipe_ctx_t *pipe_ctx) +{ + ngx_chain_t *cl; + + cl = ngx_http_lua_chain_get_free_buf(ngx_cycle->log, pipe->pool, + &pipe->free_bufs, + pipe->buffer_size); + + if (cl == NULL) { + pipe_ctx->err_type = PIPE_ERR_NOMEM; + return NGX_ERROR; + } + + pipe_ctx->buf_in->next = cl; + pipe_ctx->buf_in = cl; + pipe_ctx->buffer = *cl->buf; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_lua_pipe_read_all(void *data, ssize_t bytes) +{ + ngx_http_lua_pipe_ctx_t *pipe_ctx = data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "lua pipe read all"); + return ngx_http_lua_read_all(&pipe_ctx->buffer, pipe_ctx->buf_in, bytes, + ngx_cycle->log); +} + + +static ngx_int_t +ngx_http_lua_pipe_read_bytes(void *data, ssize_t bytes) +{ + ngx_int_t rc; + ngx_http_lua_pipe_ctx_t *pipe_ctx = data; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe read bytes %z", bytes); + + rc = ngx_http_lua_read_bytes(&pipe_ctx->buffer, pipe_ctx->buf_in, + &pipe_ctx->rest, bytes, ngx_cycle->log); + if (rc == NGX_ERROR) { + pipe_ctx->err_type = PIPE_ERR_CLOSED; + return NGX_ERROR; + } + + return rc; +} + + +static ngx_int_t +ngx_http_lua_pipe_read_line(void *data, ssize_t bytes) +{ + ngx_int_t rc; + ngx_http_lua_pipe_ctx_t *pipe_ctx = data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe read line"); + rc = ngx_http_lua_read_line(&pipe_ctx->buffer, pipe_ctx->buf_in, bytes, + ngx_cycle->log); + if (rc == NGX_ERROR) { + pipe_ctx->err_type = PIPE_ERR_CLOSED; + return NGX_ERROR; + } + + return rc; +} + + +static ngx_int_t +ngx_http_lua_pipe_read_any(void *data, ssize_t bytes) +{ + ngx_int_t rc; + ngx_http_lua_pipe_ctx_t *pipe_ctx = data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "lua pipe read any"); + rc = ngx_http_lua_read_any(&pipe_ctx->buffer, pipe_ctx->buf_in, + &pipe_ctx->rest, bytes, ngx_cycle->log); + if (rc == NGX_ERROR) { + pipe_ctx->err_type = PIPE_ERR_CLOSED; + return NGX_ERROR; + } + + return rc; +} + + +static ngx_int_t +ngx_http_lua_pipe_read(ngx_http_lua_pipe_t *pipe, + ngx_http_lua_pipe_ctx_t *pipe_ctx) +{ + int rc; + int read; + size_t size; + ssize_t n; + ngx_buf_t *b; + ngx_event_t *rev; + ngx_connection_t *c; + + c = pipe_ctx->c; + rev = c->read; + b = &pipe_ctx->buffer; + read = 0; + + for ( ;; ) { + size = b->last - b->pos; + + if (size || pipe_ctx->eof) { + rc = pipe_ctx->input_filter(pipe_ctx->input_filter_ctx, size); + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_OK) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe read done pipe:%p", pipe_ctx); + return NGX_OK; + } + + /* rc == NGX_AGAIN */ + continue; + } + + if (read && !rev->ready) { + break; + } + + size = b->end - b->last; + + if (size == 0) { + rc = ngx_http_lua_pipe_add_input_buffer(pipe, pipe_ctx); + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + b = &pipe_ctx->buffer; + size = (size_t) (b->end - b->last); + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe try to read data %uz pipe:%p", + size, pipe_ctx); + + n = c->recv(c, b->last, size); + read = 1; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe read data returned %z pipe:%p", n, pipe_ctx); + + if (n == NGX_AGAIN) { + break; + } + + if (n == 0) { + pipe_ctx->eof = 1; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe closed pipe:%p", pipe_ctx); + continue; + } + + if (n == NGX_ERROR) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, ngx_errno, + "lua pipe read data error pipe:%p", pipe_ctx); + + pipe_ctx->err_type = PIPE_ERR_SYSCALL; + pipe_ctx->pipe_errno = ngx_errno; + return NGX_ERROR; + } + + b->last += n; + } + + return NGX_AGAIN; +} + + +static ngx_int_t +ngx_http_lua_pipe_init_ctx(ngx_http_lua_pipe_ctx_t **pipe_ctx_pt, int fd, + ngx_pool_t *pool, u_char *errbuf, size_t *errbuf_size) +{ + ngx_connection_t *c; + + if (fd == -1) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "closed") - errbuf; + return NGX_ERROR; + } + + *pipe_ctx_pt = ngx_pcalloc(pool, sizeof(ngx_http_lua_pipe_ctx_t)); + if (*pipe_ctx_pt == NULL) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "no memory") + - errbuf; + return NGX_ERROR; + } + + c = ngx_get_connection(fd, ngx_cycle->log); + if (c == NULL) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "no connection") + - errbuf; + return NGX_ERROR; + } + + c->log = ngx_cycle->log; + c->recv = ngx_http_lua_pipe_fd_read; + c->read->handler = ngx_http_lua_pipe_dummy_event_handler; + c->read->log = c->log; + +#ifdef HAVE_SOCKET_CLOEXEC_PATCH + c->read->skip_socket_leak_check = 1; +#endif + + c->send = ngx_http_lua_pipe_fd_write; + c->write->handler = ngx_http_lua_pipe_dummy_event_handler; + c->write->log = c->log; + (*pipe_ctx_pt)->c = c; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe init pipe ctx:%p fd:*%d", *pipe_ctx_pt, fd); + + return NGX_OK; +} + + +int +ngx_http_lua_ffi_pipe_proc_read(ngx_http_request_t *r, + ngx_http_lua_ffi_pipe_proc_t *proc, int from_stderr, int reader_type, + size_t length, u_char **buf, size_t *buf_size, u_char *errbuf, + size_t *errbuf_size) +{ + int rc; + ngx_msec_t timeout; + ngx_event_t *rev; + ngx_connection_t *c; + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_co_ctx_t *wait_co_ctx; + ngx_http_lua_pipe_ctx_t *pipe_ctx; + + rc = ngx_http_lua_pipe_get_lua_ctx(r, &ctx, errbuf, errbuf_size); + if (rc != NGX_OK) { + return rc; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua pipe read process:%p pid:%P", proc, proc->_pid); + + pipe = proc->pipe; + if (pipe == NULL || pipe->closed) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "closed") - errbuf; + return NGX_ERROR; + } + + if (pipe->merge_stderr && from_stderr) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "merged to stdout") + - errbuf; + return NGX_ERROR; + } + + if (from_stderr) { + if (pipe->stderr_ctx == NULL) { + if (ngx_http_lua_pipe_init_ctx(&pipe->stderr_ctx, pipe->stderr_fd, + pipe->pool, errbuf, + errbuf_size) + != NGX_OK) + { + return NGX_ERROR; + } + + } else { + pipe->stderr_ctx->err_type = 0; + } + + pipe_ctx = pipe->stderr_ctx; + + } else { + if (pipe->stdout_ctx == NULL) { + if (ngx_http_lua_pipe_init_ctx(&pipe->stdout_ctx, pipe->stdout_fd, + pipe->pool, errbuf, + errbuf_size) + != NGX_OK) + { + return NGX_ERROR; + } + + } else { + pipe->stdout_ctx->err_type = 0; + } + + pipe_ctx = pipe->stdout_ctx; + } + + c = pipe_ctx->c; + if (c == NULL) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "closed") - errbuf; + return NGX_ERROR; + } + + rev = c->read; + if (rev->handler != ngx_http_lua_pipe_dummy_event_handler) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "pipe busy reading") + - errbuf; + return NGX_ERROR; + } + + pipe_ctx->input_filter_ctx = pipe_ctx; + + switch (reader_type) { + + case PIPE_READ_ALL: + pipe_ctx->input_filter = ngx_http_lua_pipe_read_all; + break; + + case PIPE_READ_BYTES: + pipe_ctx->input_filter = ngx_http_lua_pipe_read_bytes; + break; + + case PIPE_READ_LINE: + pipe_ctx->input_filter = ngx_http_lua_pipe_read_line; + break; + + case PIPE_READ_ANY: + pipe_ctx->input_filter = ngx_http_lua_pipe_read_any; + break; + + default: + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, + "unexpected reader_type: %d", reader_type); + ngx_http_lua_assert(NULL); + } + + pipe_ctx->rest = length; + + if (pipe_ctx->bufs_in == NULL) { + pipe_ctx->bufs_in = + ngx_http_lua_chain_get_free_buf(ngx_cycle->log, pipe->pool, + &pipe->free_bufs, + pipe->buffer_size); + + if (pipe_ctx->bufs_in == NULL) { + pipe_ctx->err_type = PIPE_ERR_NOMEM; + goto error; + } + + pipe_ctx->buf_in = pipe_ctx->bufs_in; + pipe_ctx->buffer = *pipe_ctx->buf_in->buf; + } + + rc = ngx_http_lua_pipe_read(pipe, pipe_ctx); + if (rc == NGX_ERROR) { + goto error; + } + + if (rc == NGX_OK) { + ngx_http_lua_pipe_put_data(pipe, pipe_ctx, buf, buf_size); + return NGX_OK; + } + + /* rc == NGX_AGAIN */ + wait_co_ctx = ctx->cur_co_ctx; + + c->data = wait_co_ctx; + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + pipe_ctx->err_type = PIPE_ERR_ADD_READ_EV; + goto error; + } + + wait_co_ctx->data = proc; + + if (from_stderr) { + rev->handler = ngx_http_lua_pipe_resume_read_stderr_handler; + wait_co_ctx->cleanup = ngx_http_lua_pipe_proc_read_stderr_cleanup; + timeout = proc->stderr_read_timeout; + + } else { + rev->handler = ngx_http_lua_pipe_resume_read_stdout_handler; + wait_co_ctx->cleanup = ngx_http_lua_pipe_proc_read_stdout_cleanup; + timeout = proc->stdout_read_timeout; + } + + if (timeout > 0) { + ngx_add_timer(rev, timeout); + ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua pipe add timer for reading: %d(ms) process:%p " + "pid:%P pipe:%p ev:%p", timeout, proc, proc->_pid, pipe, + rev); + } + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua pipe read yielding process:%p pid:%P pipe:%p", proc, + proc->_pid, pipe); + + return NGX_AGAIN; + +error: + + if (pipe_ctx->bufs_in) { + ngx_http_lua_pipe_put_data(pipe, pipe_ctx, buf, buf_size); + ngx_http_lua_pipe_put_error(pipe_ctx, errbuf, errbuf_size); + return NGX_DECLINED; + } + + ngx_http_lua_pipe_put_error(pipe_ctx, errbuf, errbuf_size); + + return NGX_ERROR; +} + + +/* + * ngx_http_lua_ffi_pipe_get_read_result should only be called just after + * ngx_http_lua_ffi_pipe_proc_read, so we omit most of the sanity check already + * done in ngx_http_lua_ffi_pipe_proc_read. + */ +int +ngx_http_lua_ffi_pipe_get_read_result(ngx_http_request_t *r, + ngx_http_lua_ffi_pipe_proc_t *proc, int from_stderr, u_char **buf, + size_t *buf_size, u_char *errbuf, size_t *errbuf_size) +{ + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_pipe_ctx_t *pipe_ctx; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua pipe get read result process:%p pid:%P", proc, + proc->_pid); + + pipe = proc->pipe; + pipe_ctx = from_stderr ? pipe->stderr_ctx : pipe->stdout_ctx; + + if (!pipe_ctx->err_type) { + ngx_http_lua_pipe_put_data(pipe, pipe_ctx, buf, buf_size); + return NGX_OK; + } + + if (pipe_ctx->bufs_in) { + ngx_http_lua_pipe_put_data(pipe, pipe_ctx, buf, buf_size); + ngx_http_lua_pipe_put_error(pipe_ctx, errbuf, errbuf_size); + return NGX_DECLINED; + } + + ngx_http_lua_pipe_put_error(pipe_ctx, errbuf, errbuf_size); + + return NGX_ERROR; +} + + +static ngx_int_t +ngx_http_lua_pipe_write(ngx_http_lua_pipe_t *pipe, + ngx_http_lua_pipe_ctx_t *pipe_ctx) +{ + size_t size; + ngx_int_t n; + ngx_buf_t *b; + ngx_connection_t *c; + + c = pipe_ctx->c; + b = pipe_ctx->buf_in->buf; + + for ( ;; ) { + size = b->last - b->pos; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe try to write data %uz pipe:%p", size, + pipe_ctx); + + n = c->send(c, b->pos, size); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe write returned %i pipe:%p", n, pipe_ctx); + + if (n >= 0) { + b->pos += n; + + if (b->pos == b->last) { + b->pos = b->start; + b->last = b->start; + + if (!pipe->free_bufs) { + pipe->free_bufs = pipe_ctx->buf_in; + + } else { + pipe->free_bufs->next = pipe_ctx->buf_in; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe write done pipe:%p", pipe_ctx); + return NGX_OK; + } + + continue; + } + + /* NGX_ERROR || NGX_AGAIN */ + break; + } + + if (n == NGX_ERROR) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, ngx_errno, + "lua pipe write data error pipe:%p", pipe_ctx); + + if (ngx_errno == NGX_EPIPE) { + pipe_ctx->err_type = PIPE_ERR_CLOSED; + + } else { + pipe_ctx->err_type = PIPE_ERR_SYSCALL; + pipe_ctx->pipe_errno = ngx_errno; + } + + return NGX_ERROR; + } + + return NGX_AGAIN; +} + + +ssize_t +ngx_http_lua_ffi_pipe_proc_write(ngx_http_request_t *r, + ngx_http_lua_ffi_pipe_proc_t *proc, const u_char *data, size_t len, + u_char *errbuf, size_t *errbuf_size) +{ + int rc; + ngx_buf_t *b; + ngx_msec_t timeout; + ngx_chain_t *cl; + ngx_event_t *wev; + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_co_ctx_t *wait_co_ctx; + ngx_http_lua_pipe_ctx_t *pipe_ctx; + + rc = ngx_http_lua_pipe_get_lua_ctx(r, &ctx, errbuf, errbuf_size); + if (rc != NGX_OK) { + return rc; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua pipe write process:%p pid:%P", proc, proc->_pid); + + pipe = proc->pipe; + if (pipe == NULL || pipe->closed) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "closed") - errbuf; + return NGX_ERROR; + } + + if (pipe->stdin_ctx == NULL) { + if (ngx_http_lua_pipe_init_ctx(&pipe->stdin_ctx, pipe->stdin_fd, + pipe->pool, errbuf, + errbuf_size) + != NGX_OK) + { + return NGX_ERROR; + } + + } else { + pipe->stdin_ctx->err_type = 0; + } + + pipe_ctx = pipe->stdin_ctx; + if (pipe_ctx->c == NULL) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "closed") - errbuf; + return NGX_ERROR; + } + + wev = pipe_ctx->c->write; + if (wev->handler != ngx_http_lua_pipe_dummy_event_handler) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "pipe busy writing") + - errbuf; + return NGX_ERROR; + } + + pipe_ctx->rest = len; + + cl = ngx_http_lua_chain_get_free_buf(ngx_cycle->log, pipe->pool, + &pipe->free_bufs, len); + if (cl == NULL) { + pipe_ctx->err_type = PIPE_ERR_NOMEM; + goto error; + } + + pipe_ctx->buf_in = cl; + b = pipe_ctx->buf_in->buf; + b->last = ngx_copy(b->last, data, len); + + rc = ngx_http_lua_pipe_write(pipe, pipe_ctx); + if (rc == NGX_ERROR) { + goto error; + } + + if (rc == NGX_OK) { + return len; + } + + /* rc == NGX_AGAIN */ + wait_co_ctx = ctx->cur_co_ctx; + pipe_ctx->c->data = wait_co_ctx; + + wev->handler = ngx_http_lua_pipe_resume_write_handler; + if (ngx_handle_write_event(wev, 0) != NGX_OK) { + pipe_ctx->err_type = PIPE_ERR_ADD_WRITE_EV; + goto error; + } + + wait_co_ctx->data = proc; + wait_co_ctx->cleanup = ngx_http_lua_pipe_proc_write_cleanup; + timeout = proc->write_timeout; + + if (timeout > 0) { + ngx_add_timer(wev, timeout); + ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua pipe add timer for writing: %d(ms) process:%p " + "pid:%P pipe:%p ev:%p", timeout, proc, proc->_pid, pipe, + wev); + } + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua pipe write yielding process:%p pid:%P pipe:%p", proc, + proc->_pid, pipe); + + return NGX_AGAIN; + +error: + + ngx_http_lua_pipe_put_error(pipe_ctx, errbuf, errbuf_size); + return NGX_ERROR; +} + + +/* + * ngx_http_lua_ffi_pipe_get_write_result should only be called just after + * ngx_http_lua_ffi_pipe_proc_write, so we omit most of the sanity check + * already done in ngx_http_lua_ffi_pipe_proc_write. + */ +ssize_t +ngx_http_lua_ffi_pipe_get_write_result(ngx_http_request_t *r, + ngx_http_lua_ffi_pipe_proc_t *proc, u_char *errbuf, size_t *errbuf_size) +{ + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_pipe_ctx_t *pipe_ctx; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua pipe get write result process:%p pid:%P", proc, + proc->_pid); + + pipe = proc->pipe; + pipe_ctx = pipe->stdin_ctx; + + if (pipe_ctx->err_type) { + ngx_http_lua_pipe_put_error(pipe_ctx, errbuf, errbuf_size); + return NGX_ERROR; + } + + return pipe_ctx->rest; +} + + +int +ngx_http_lua_ffi_pipe_proc_wait(ngx_http_request_t *r, + ngx_http_lua_ffi_pipe_proc_t *proc, char **reason, int *status, + u_char *errbuf, size_t *errbuf_size) +{ + int rc; + ngx_rbtree_node_t *node; + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_co_ctx_t *wait_co_ctx; + ngx_http_lua_pipe_node_t *pipe_node; + + rc = ngx_http_lua_pipe_get_lua_ctx(r, &ctx, errbuf, errbuf_size); + if (rc != NGX_OK) { + return rc; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua pipe wait process:%p pid:%P", proc, proc->_pid); + + pipe = proc->pipe; + if (pipe == NULL || pipe->closed) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "exited") - errbuf; + return NGX_ERROR; + } + + node = pipe->node; + pipe_node = (ngx_http_lua_pipe_node_t *) &node->color; + if (pipe_node->wait_co_ctx) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "pipe busy waiting") + - errbuf; + return NGX_ERROR; + } + + if (pipe_node->reason_code == REASON_RUNNING_CODE) { + wait_co_ctx = ctx->cur_co_ctx; + wait_co_ctx->data = proc; + ngx_memzero(&wait_co_ctx->sleep, sizeof(ngx_event_t)); + wait_co_ctx->sleep.handler = ngx_http_lua_pipe_resume_wait_handler; + wait_co_ctx->sleep.data = wait_co_ctx; + wait_co_ctx->sleep.log = r->connection->log; + wait_co_ctx->cleanup = ngx_http_lua_pipe_proc_wait_cleanup; + + pipe_node->wait_co_ctx = wait_co_ctx; + + if (proc->wait_timeout > 0) { + ngx_add_timer(&wait_co_ctx->sleep, proc->wait_timeout); + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua pipe add timer for waiting: %d(ms) process:%p " + "pid:%P ev:%p", proc->wait_timeout, proc, + proc->_pid, &wait_co_ctx->sleep); + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua pipe wait yielding process:%p pid:%P", proc, + proc->_pid); + + return NGX_AGAIN; + } + + *status = pipe_node->status; + + switch (pipe_node->reason_code) { + + case REASON_EXIT_CODE: + *reason = REASON_EXIT; + break; + + case REASON_SIGNAL_CODE: + *reason = REASON_SIGNAL; + break; + + default: + *reason = REASON_UNKNOWN; + } + + ngx_http_lua_pipe_proc_finalize(proc); + + if (*status == 0) { + return NGX_OK; + } + + return NGX_DECLINED; +} + + +int +ngx_http_lua_ffi_pipe_proc_kill(ngx_http_lua_ffi_pipe_proc_t *proc, int signal, + u_char *errbuf, size_t *errbuf_size) +{ + ngx_pid_t pid; + ngx_http_lua_pipe_t *pipe; + + pipe = proc->pipe; + + if (pipe == NULL || pipe->dead) { + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "exited") - errbuf; + return NGX_ERROR; + } + + pid = proc->_pid; + + if (kill(pid, signal) == -1) { + switch (ngx_errno) { + case EINVAL: + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "invalid signal") + - errbuf; + break; + + case ESRCH: + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "exited") + - errbuf; + break; + + default: + *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "%s", + strerror(ngx_errno)) + - errbuf; + } + + return NGX_ERROR; + } + + return NGX_OK; +} + + +static int +ngx_http_lua_pipe_read_stdout_retval(ngx_http_lua_ffi_pipe_proc_t *proc, + lua_State *L) +{ + return ngx_http_lua_pipe_read_retval_helper(proc, L, 0); +} + + +static int +ngx_http_lua_pipe_read_stderr_retval(ngx_http_lua_ffi_pipe_proc_t *proc, + lua_State *L) +{ + return ngx_http_lua_pipe_read_retval_helper(proc, L, 1); +} + + +static int +ngx_http_lua_pipe_read_retval_helper(ngx_http_lua_ffi_pipe_proc_t *proc, + lua_State *L, int from_stderr) +{ + int rc; + ngx_msec_t timeout; + ngx_event_t *rev; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_pipe_ctx_t *pipe_ctx; + + pipe = proc->pipe; + if (from_stderr) { + pipe_ctx = pipe->stderr_ctx; + + } else { + pipe_ctx = pipe->stdout_ctx; + } + + if (pipe->timeout) { + pipe->timeout = 0; + pipe_ctx->err_type = PIPE_ERR_TIMEOUT; + return 0; + } + + if (pipe_ctx->err_type == PIPE_ERR_ABORTED) { + ngx_close_connection(pipe_ctx->c); + pipe_ctx->c = NULL; + return 0; + } + + rc = ngx_http_lua_pipe_read(pipe, pipe_ctx); + if (rc != NGX_AGAIN) { + return 0; + } + + rev = pipe_ctx->c->read; + + if (from_stderr) { + rev->handler = ngx_http_lua_pipe_resume_read_stderr_handler; + timeout = proc->stderr_read_timeout; + + } else { + rev->handler = ngx_http_lua_pipe_resume_read_stdout_handler; + timeout = proc->stdout_read_timeout; + } + + if (timeout > 0) { + ngx_add_timer(rev, timeout); + ngx_log_debug5(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe add timer for reading: %d(ms) proc:%p " + "pid:%P pipe:%p ev:%p", timeout, proc, proc->_pid, pipe, + rev); + } + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe read yielding process:%p pid:%P pipe:%p", proc, + proc->_pid, pipe); + + return NGX_AGAIN; +} + + +static int +ngx_http_lua_pipe_write_retval(ngx_http_lua_ffi_pipe_proc_t *proc, + lua_State *L) +{ + int rc; + ngx_msec_t timeout; + ngx_event_t *wev; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_pipe_ctx_t *pipe_ctx; + + pipe = proc->pipe; + pipe_ctx = pipe->stdin_ctx; + + if (pipe->timeout) { + pipe->timeout = 0; + pipe_ctx->err_type = PIPE_ERR_TIMEOUT; + return 0; + } + + if (pipe_ctx->err_type == PIPE_ERR_ABORTED) { + ngx_close_connection(pipe_ctx->c); + pipe_ctx->c = NULL; + return 0; + } + + rc = ngx_http_lua_pipe_write(pipe, pipe_ctx); + if (rc != NGX_AGAIN) { + return 0; + } + + wev = pipe_ctx->c->write; + wev->handler = ngx_http_lua_pipe_resume_write_handler; + timeout = proc->write_timeout; + + if (timeout > 0) { + ngx_add_timer(wev, timeout); + ngx_log_debug5(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe add timer for writing: %d(ms) proc:%p " + "pid:%P pipe:%p ev:%p", timeout, proc, proc->_pid, pipe, + wev); + } + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe write yielding process:%p pid:%P pipe:%p", proc, + proc->_pid, pipe); + + return NGX_AGAIN; +} + + +static int +ngx_http_lua_pipe_wait_retval(ngx_http_lua_ffi_pipe_proc_t *proc, lua_State *L) +{ + int nret; + ngx_rbtree_node_t *node; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_pipe_node_t *pipe_node; + + pipe = proc->pipe; + node = pipe->node; + pipe_node = (ngx_http_lua_pipe_node_t *) &node->color; + pipe_node->wait_co_ctx = NULL; + + if (pipe->timeout) { + pipe->timeout = 0; + lua_pushnil(L); + lua_pushliteral(L, "timeout"); + return 2; + } + + ngx_http_lua_pipe_proc_finalize(pipe_node->proc); + + if (pipe_node->status == 0) { + lua_pushboolean(L, 1); + lua_pushliteral(L, REASON_EXIT); + lua_pushinteger(L, pipe_node->status); + nret = 3; + + } else { + lua_pushboolean(L, 0); + + switch (pipe_node->reason_code) { + + case REASON_EXIT_CODE: + lua_pushliteral(L, REASON_EXIT); + break; + + case REASON_SIGNAL_CODE: + lua_pushliteral(L, REASON_SIGNAL); + break; + + default: + lua_pushliteral(L, REASON_UNKNOWN); + } + + lua_pushinteger(L, pipe_node->status); + nret = 3; + } + + return nret; +} + + +static void +ngx_http_lua_pipe_resume_helper(ngx_event_t *ev, + ngx_http_lua_co_ctx_t *wait_co_ctx) +{ + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_ffi_pipe_proc_t *proc; + + if (ev->timedout) { + proc = wait_co_ctx->data; + pipe = proc->pipe; + pipe->timeout = 1; + ev->timedout = 0; + } + + ngx_http_lua_pipe_clear_event(ev); + + r = ngx_http_lua_get_req(wait_co_ctx->co); + c = r->connection; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + ngx_http_lua_assert(ctx != NULL); + + ctx->cur_co_ctx = wait_co_ctx; + + if (ctx->entered_content_phase) { + (void) ngx_http_lua_pipe_resume(r); + + } else { + ctx->resume_handler = ngx_http_lua_pipe_resume; + ngx_http_core_run_phases(r); + } + + ngx_http_run_posted_requests(c); +} + + +static void +ngx_http_lua_pipe_resume_read_stdout_handler(ngx_event_t *ev) +{ + ngx_connection_t *c = ev->data; + ngx_http_lua_co_ctx_t *wait_co_ctx; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_ffi_pipe_proc_t *proc; + + wait_co_ctx = c->data; + proc = wait_co_ctx->data; + pipe = proc->pipe; + pipe->retval_handler = ngx_http_lua_pipe_read_stdout_retval; + ngx_http_lua_pipe_resume_helper(ev, wait_co_ctx); +} + + +static void +ngx_http_lua_pipe_resume_read_stderr_handler(ngx_event_t *ev) +{ + ngx_connection_t *c = ev->data; + ngx_http_lua_co_ctx_t *wait_co_ctx; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_ffi_pipe_proc_t *proc; + + wait_co_ctx = c->data; + proc = wait_co_ctx->data; + pipe = proc->pipe; + pipe->retval_handler = ngx_http_lua_pipe_read_stderr_retval; + ngx_http_lua_pipe_resume_helper(ev, wait_co_ctx); +} + + +static void +ngx_http_lua_pipe_resume_write_handler(ngx_event_t *ev) +{ + ngx_connection_t *c = ev->data; + ngx_http_lua_co_ctx_t *wait_co_ctx; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_ffi_pipe_proc_t *proc; + + wait_co_ctx = c->data; + proc = wait_co_ctx->data; + pipe = proc->pipe; + pipe->retval_handler = ngx_http_lua_pipe_write_retval; + ngx_http_lua_pipe_resume_helper(ev, wait_co_ctx); +} + + +static void +ngx_http_lua_pipe_resume_wait_handler(ngx_event_t *ev) +{ + ngx_http_lua_co_ctx_t *wait_co_ctx = ev->data; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_ffi_pipe_proc_t *proc; + + proc = wait_co_ctx->data; + pipe = proc->pipe; + pipe->retval_handler = ngx_http_lua_pipe_wait_retval; + ngx_http_lua_pipe_resume_helper(ev, wait_co_ctx); +} + + +static ngx_int_t +ngx_http_lua_pipe_resume(ngx_http_request_t *r) +{ + int nret; + lua_State *vm; + ngx_int_t rc; + ngx_uint_t nreqs; + ngx_connection_t *c; + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_pipe_t *pipe; + ngx_http_lua_ffi_pipe_proc_t *proc; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return NGX_ERROR; + } + + ctx->resume_handler = ngx_http_lua_wev_handler; + ctx->cur_co_ctx->cleanup = NULL; + + proc = ctx->cur_co_ctx->data; + pipe = proc->pipe; + nret = pipe->retval_handler(proc, ctx->cur_co_ctx->co); + if (nret == NGX_AGAIN) { + return NGX_DONE; + } + + c = r->connection; + vm = ngx_http_lua_get_lua_vm(r, ctx); + nreqs = c->requests; + + rc = ngx_http_lua_run_thread(vm, r, ctx, nret); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua run thread returned %d", rc); + + if (rc == NGX_AGAIN) { + return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + if (rc == NGX_DONE) { + ngx_http_lua_finalize_request(r, NGX_DONE); + return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + /* rc == NGX_ERROR || rc >= NGX_OK */ + + if (ctx->entered_content_phase) { + ngx_http_lua_finalize_request(r, rc); + return NGX_DONE; + } + + return rc; +} + + +static void +ngx_http_lua_pipe_dummy_event_handler(ngx_event_t *ev) +{ + /* do nothing */ +} + + +static void +ngx_http_lua_pipe_clear_event(ngx_event_t *ev) +{ + ev->handler = ngx_http_lua_pipe_dummy_event_handler; + + if (ev->timer_set) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "lua pipe del timer for ev:%p", ev); + ngx_del_timer(ev); + } + + if (ev->posted) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "lua pipe del posted event for ev:%p", ev); + ngx_delete_posted_event(ev); + } +} + + +static void +ngx_http_lua_pipe_proc_read_stdout_cleanup(void *data) +{ + ngx_event_t *rev; + ngx_connection_t *c; + ngx_http_lua_co_ctx_t *wait_co_ctx = data; + ngx_http_lua_ffi_pipe_proc_t *proc; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe proc read stdout cleanup"); + + proc = wait_co_ctx->data; + c = proc->pipe->stdout_ctx->c; + if (c) { + rev = c->read; + ngx_http_lua_pipe_clear_event(rev); + } + + wait_co_ctx->cleanup = NULL; +} + + +static void +ngx_http_lua_pipe_proc_read_stderr_cleanup(void *data) +{ + ngx_event_t *rev; + ngx_connection_t *c; + ngx_http_lua_co_ctx_t *wait_co_ctx = data; + ngx_http_lua_ffi_pipe_proc_t *proc; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe proc read stderr cleanup"); + + proc = wait_co_ctx->data; + c = proc->pipe->stderr_ctx->c; + if (c) { + rev = c->read; + ngx_http_lua_pipe_clear_event(rev); + } + + wait_co_ctx->cleanup = NULL; +} + + +static void +ngx_http_lua_pipe_proc_write_cleanup(void *data) +{ + ngx_event_t *wev; + ngx_connection_t *c; + ngx_http_lua_co_ctx_t *wait_co_ctx = data; + ngx_http_lua_ffi_pipe_proc_t *proc; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe proc write cleanup"); + + proc = wait_co_ctx->data; + c = proc->pipe->stdin_ctx->c; + if (c) { + wev = c->write; + ngx_http_lua_pipe_clear_event(wev); + } + + wait_co_ctx->cleanup = NULL; +} + + +static void +ngx_http_lua_pipe_proc_wait_cleanup(void *data) +{ + ngx_rbtree_node_t *node; + ngx_http_lua_co_ctx_t *wait_co_ctx = data; + ngx_http_lua_pipe_node_t *pipe_node; + ngx_http_lua_ffi_pipe_proc_t *proc; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua pipe proc wait cleanup"); + + proc = wait_co_ctx->data; + node = proc->pipe->node; + pipe_node = (ngx_http_lua_pipe_node_t *) &node->color; + pipe_node->wait_co_ctx = NULL; + + ngx_http_lua_pipe_clear_event(&wait_co_ctx->sleep); + + wait_co_ctx->cleanup = NULL; +} + + +#endif /* HAVE_NGX_LUA_PIPE */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff -Nru nginx-1.18.0/debian/modules/http-lua/src/ngx_http_lua_pipe.h nginx-1.20.1/debian/modules/http-lua/src/ngx_http_lua_pipe.h --- nginx-1.18.0/debian/modules/http-lua/src/ngx_http_lua_pipe.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-lua/src/ngx_http_lua_pipe.h 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,94 @@ + +/* + * Copyright (C) by OpenResty Inc. + */ + + +#ifndef _NGX_HTTP_LUA_PIPE_H_INCLUDED_ +#define _NGX_HTTP_LUA_PIPE_H_INCLUDED_ + + +#include "ngx_http_lua_common.h" + + +typedef ngx_int_t (*ngx_http_lua_pipe_input_filter)(void *data, ssize_t bytes); + + +typedef struct { + ngx_connection_t *c; + ngx_http_lua_pipe_input_filter input_filter; + void *input_filter_ctx; + size_t rest; + ngx_chain_t *buf_in; + ngx_chain_t *bufs_in; + ngx_buf_t buffer; + ngx_err_t pipe_errno; + unsigned err_type:16; + unsigned eof:1; +} ngx_http_lua_pipe_ctx_t; + + +typedef struct ngx_http_lua_pipe_s ngx_http_lua_pipe_t; + + +typedef struct { + ngx_pid_t _pid; + ngx_msec_t write_timeout; + ngx_msec_t stdout_read_timeout; + ngx_msec_t stderr_read_timeout; + ngx_msec_t wait_timeout; + /* pipe hides the implementation from the Lua binding */ + ngx_http_lua_pipe_t *pipe; +} ngx_http_lua_ffi_pipe_proc_t; + + +typedef int (*ngx_http_lua_pipe_retval_handler)( + ngx_http_lua_ffi_pipe_proc_t *proc, lua_State *L); + + +struct ngx_http_lua_pipe_s { + ngx_pool_t *pool; + ngx_chain_t *free_bufs; + ngx_rbtree_node_t *node; + int stdin_fd; + int stdout_fd; + int stderr_fd; + ngx_http_lua_pipe_ctx_t *stdin_ctx; + ngx_http_lua_pipe_ctx_t *stdout_ctx; + ngx_http_lua_pipe_ctx_t *stderr_ctx; + ngx_http_lua_pipe_retval_handler retval_handler; + size_t buffer_size; + unsigned closed:1; + unsigned dead:1; + unsigned timeout:1; + unsigned merge_stderr:1; +}; + + +typedef struct { + u_char color; + u_char reason_code; + int status; + ngx_http_lua_co_ctx_t *wait_co_ctx; + ngx_http_lua_ffi_pipe_proc_t *proc; +} ngx_http_lua_pipe_node_t; + + +typedef struct { + int signo; + char *signame; +} ngx_http_lua_pipe_signal_t; + + +#if !(NGX_WIN32) && defined(HAVE_SOCKET_CLOEXEC_PATCH) +#define HAVE_NGX_LUA_PIPE 1 + + +void ngx_http_lua_pipe_init(void); +ngx_int_t ngx_http_lua_pipe_add_signal_handler(ngx_cycle_t *cycle); +#endif + + +#endif /* _NGX_HTTP_LUA_PIPE_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff -Nru nginx-1.18.0/debian/modules/http-lua/t/030-uri-args-with-ctrl.t nginx-1.20.1/debian/modules/http-lua/t/030-uri-args-with-ctrl.t --- nginx-1.18.0/debian/modules/http-lua/t/030-uri-args-with-ctrl.t 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-lua/t/030-uri-args-with-ctrl.t 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,137 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua; + +log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2 + 3); + +no_root_location(); + +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: rewrite args (string with \r) +--- config + location /foo { + rewrite_by_lua_block { + ngx.req.set_uri_args("a\rb") + } + proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/echo; + } + location /echo { + content_by_lua_block { + ngx.say(ngx.var.request_uri); + } + } +--- request +GET /foo?world +--- error_code: 200 +--- response_body +/echo?a%0Db + + + +=== TEST 2: rewrite args (string with \n) +--- config + location /foo { + rewrite_by_lua_block { + ngx.req.set_uri_args("a\nb") + } + proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/echo; + } + location /echo { + content_by_lua_block { + ngx.say(ngx.var.request_uri); + } + } +--- request +GET /foo?world +--- response_body +/echo?a%0Ab + + + +=== TEST 3: rewrite args (string with \0) +--- config + location /foo { + rewrite_by_lua_block { + ngx.req.set_uri_args("a\0b") + } + proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/echo; + } + location /echo { + content_by_lua_block { + ngx.say(ngx.var.request_uri); + } + } +--- request +GET /foo?world +--- response_body +/echo?a%00b + + + +=== TEST 4: rewrite args (string arg with 'lang=中文') +ngx.req.set_uri_args with string argument should be carefully encoded. +For backward compatibility, we are allowed to pass such parameters. +--- config + location /foo { + rewrite_by_lua_block { + ngx.req.set_uri_args("lang=中文") + } + content_by_lua_block { + ngx.say(ngx.var.arg_lang) + } + } +--- request +GET /foo?world +--- response_body +中文 +--- no_error_log +[error] + + + +=== TEST 5: rewrite args (string arg with '语言=chinese') +ngx.req.set_uri_args with string argument should be carefully encoded. +For backward compatibility, we are allowed to pass such parameters. +--- config + location /foo { + rewrite_by_lua_block { + ngx.req.set_uri_args("语言=chinese") + } + content_by_lua_block { + ngx.say(ngx.var.arg_语言) + } + } +--- request +GET /foo?world +--- response_body +chinese +--- no_error_log +[error] + + + +=== TEST 6: rewrite args (string arg with '语言=中文') +ngx.req.set_uri_args with string argument should be carefully encoded. +For backward compatibility, we are allowed to pass such parameters. +--- config + location /foo { + rewrite_by_lua_block { + ngx.req.set_uri_args("语言=中文") + } + content_by_lua_block { + ngx.say(ngx.var.arg_语言) + } + } +--- request +GET /foo?world +--- response_body +中文 +--- no_error_log +[error] diff -Nru nginx-1.18.0/debian/modules/http-lua/t/156-slow-network.t nginx-1.20.1/debian/modules/http-lua/t/156-slow-network.t --- nginx-1.18.0/debian/modules/http-lua/t/156-slow-network.t 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-lua/t/156-slow-network.t 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,138 @@ +BEGIN { + if (!defined $ENV{LD_PRELOAD}) { + $ENV{LD_PRELOAD} = ''; + } + + if ($ENV{LD_PRELOAD} !~ /\bmockeagain\.so\b/) { + $ENV{LD_PRELOAD} = "mockeagain.so $ENV{LD_PRELOAD}"; + } + + if ($ENV{MOCKEAGAIN} eq 'r') { + $ENV{MOCKEAGAIN} = 'rw'; + + } else { + $ENV{MOCKEAGAIN} = 'w'; + } + + $ENV{TEST_NGINX_EVENT_TYPE} = 'poll'; +} + +use Test::Nginx::Socket::Lua; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 4); + +add_block_preprocessor(sub { + my $block = shift; + + if (!defined $block->error_log) { + $block->set_value("no_error_log", "[error]"); + } + + if (!defined $block->request) { + $block->set_value("request", "GET /t"); + } + +}); + + +log_level("debug"); +no_long_string(); +#no_diff(); +run_tests(); + +__DATA__ + +=== TEST 1: receiveany returns anything once socket receives +--- config + server_tokens off; + location = /t { + set $port $TEST_NGINX_SERVER_PORT; + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(500) + assert(sock:connect("127.0.0.1", ngx.var.port)) + local req = { + 'GET /foo HTTP/1.0\r\n', + 'Host: localhost\r\n', + 'Connection: close\r\n\r\n', + } + local ok, err = sock:send(req) + if not ok then + ngx.say("send request failed: ", err) + return + end + + + -- skip http header + while true do + local data, err, _ = sock:receive('*l') + if err then + ngx.say('unexpected error occurs when receiving http head: ' .. err) + return + end + if #data == 0 then -- read last line of head + break + end + end + + -- receive http body + while true do + local data, err = sock:receiveany(1024) + if err then + if err ~= 'closed' then + ngx.say('unexpected err: ', err) + end + break + end + ngx.say(data) + end + + sock:close() + } + } + + location = /foo { + content_by_lua_block { + local resp = { + '1', + 'hello', + } + + local length = 0 + for _, v in ipairs(resp) do + length = length + #v + end + + -- flush http header + ngx.header['Content-Length'] = length + ngx.flush(true) + ngx.sleep(0.01) + + -- send http body bytes by bytes + for _, v in ipairs(resp) do + ngx.print(v) + ngx.flush(true) + ngx.sleep(0.01) + end + } + } + +--- response_body +1 +h +e +l +l +o +--- grep_error_log eval +qr/lua tcp socket read any/ +--- grep_error_log_out +lua tcp socket read any +lua tcp socket read any +lua tcp socket read any +lua tcp socket read any +lua tcp socket read any +lua tcp socket read any +lua tcp socket read any diff -Nru nginx-1.18.0/debian/modules/http-lua/t/157-socket-keepalive-hup.t nginx-1.20.1/debian/modules/http-lua/t/157-socket-keepalive-hup.t --- nginx-1.18.0/debian/modules/http-lua/t/157-socket-keepalive-hup.t 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-lua/t/157-socket-keepalive-hup.t 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,91 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +our $SkipReason; + +BEGIN { + if ($ENV{TEST_NGINX_CHECK_LEAK}) { + $SkipReason = "unavailable for the hup tests"; + + } else { + $ENV{TEST_NGINX_USE_HUP} = 1; + undef $ENV{TEST_NGINX_USE_STAP}; + } +} + +use Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : (); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 8); + +#no_diff(); +no_long_string(); + +worker_connections(1024); +run_tests(); + +__DATA__ + +=== TEST 1: exiting +--- config + location /t { + set $port $TEST_NGINX_SERVER_PORT; + + content_by_lua_block { + local f, err = io.open("$TEST_NGINX_SERVER_ROOT/logs/nginx.pid", "r") + if not f then + ngx.say("failed to open nginx.pid: ", err) + return + end + + local pid = f:read() + -- ngx.say("master pid: [", pid, "]") + + f:close() + + local i = 0 + local port = ngx.var.port + + local function f(premature) + print("timer prematurely expired: ", premature) + + local sock = ngx.socket.tcp() + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + print("failed to connect: ", err) + return + end + + local ok, err = sock:setkeepalive() + if not ok then + print("failed to setkeepalive: ", err) + return + end + + print("setkeepalive successfully") + end + local ok, err = ngx.timer.at(3, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.say("registered timer") + os.execute("kill -HUP " .. pid) + } + } +--- request +GET /t + +--- response_body +registered timer + +--- wait: 0.3 +--- no_error_log +[error] +[alert] +[crit] +--- error_log +timer prematurely expired: true +setkeepalive successfully +lua tcp socket set keepalive while process exiting, closing connection diff -Nru nginx-1.18.0/debian/modules/http-lua/t/158-global-var.t nginx-1.20.1/debian/modules/http-lua/t/158-global-var.t --- nginx-1.18.0/debian/modules/http-lua/t/158-global-var.t 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-lua/t/158-global-var.t 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,508 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua; + +log_level('debug'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3 + 14); + +our $HtmlDir = html_dir; + +$ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); + +no_long_string(); + +sub read_file { + my $infile = shift; + open my $in, $infile + or die "cannot open $infile for reading: $!"; + my $cert = do { local $/; <$in> }; + close $in; + $cert; +} + +our $TestCertificate = read_file("t/cert/test.crt"); +our $TestCertificateKey = read_file("t/cert/test.key"); + +add_block_preprocessor(sub { + my $block = shift; + + if (!defined $block->error_log) { + $block->set_value("no_error_log", "[error]"); + } + + if (!defined $block->request) { + $block->set_value("request", "GET /t"); + } + +}); + +run_tests(); + +__DATA__ + +=== TEST 1: set_by_lua +--- config + location /t { + set_by_lua_block $res { + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + return foo + } + echo $res; + } +--- response_body_like chomp +\A[12]\n\z +--- grep_error_log eval +qr/(old foo: \d+|\[\w+\].*?writing a global Lua variable \('[^'\s]+'\)|set_by_lua:\d+: in main chunk, )/ +--- grep_error_log_out eval +[qr/\A\[warn\] .*?writing a global Lua variable \('foo'\) +set_by_lua:3: in main chunk, \n\z/, "old foo: 1\n"] + + + +=== TEST 2: rewrite_by_lua +--- config + location /t { + rewrite_by_lua_block { + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + ngx.say(foo) + } + } +--- response_body_like chomp +\A[12]\n\z +--- grep_error_log eval +qr/(old foo: \d+|\[\w+\].*?writing a global Lua variable \('[^'\s]+'\)|\w+_by_lua\(.*?\):\d+: in main chunk, )/ +--- grep_error_log_out eval +[qr/\A\[warn\] .*?writing a global Lua variable \('foo'\) +rewrite_by_lua\(nginx\.conf:48\):3: in main chunk, \n\z/, "old foo: 1\n"] + + + +=== TEST 3: access_by_lua +--- config + location /t { + access_by_lua_block { + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + ngx.say(foo) + } + } +--- response_body_like chomp +\A[12]\n\z +--- grep_error_log eval +qr/(old foo: \d+|\[\w+\].*?writing a global Lua variable \('[^'\s]+'\)|\w+_by_lua\(.*?\):\d+: in main chunk, )/ +--- grep_error_log_out eval +[qr/\A\[warn\] .*?writing a global Lua variable \('foo'\) +access_by_lua\(nginx\.conf:48\):3: in main chunk, \n\z/, "old foo: 1\n"] + + + +=== TEST 4: content_by_lua +--- config + location /t { + content_by_lua_block { + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + ngx.say(foo) + } + } +--- response_body_like chomp +\A[12]\n\z +--- grep_error_log eval +qr/(old foo: \d+|\[\w+\].*?writing a global Lua variable \('[^'\s]+'\)|\w+_by_lua\(.*?\):\d+: in main chunk, )/ +--- grep_error_log_out eval +[qr/\A\[warn\] .*?writing a global Lua variable \('foo'\) +content_by_lua\(nginx\.conf:48\):3: in main chunk, \n\z/, "old foo: 1\n"] + + + +=== TEST 5: header_filter_by_lua +--- config + location /t { + content_by_lua_block { + ngx.say(foo) + } + header_filter_by_lua_block { + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + } + } +--- response_body_like chomp +\A(?:nil|1)\n\z +--- grep_error_log eval +qr/(old foo: \d+|\[\w+\].*?writing a global Lua variable \('[^'\s]+'\)|\w+_by_lua:\d+: in main chunk, )/ +--- grep_error_log_out eval +[qr/\A\[warn\] .*?writing a global Lua variable \('foo'\) +header_filter_by_lua:3: in main chunk, \n\z/, "old foo: 1\n"] + + + +=== TEST 6: body_filter_by_lua +--- config + location /t { + content_by_lua_block { + ngx.say(foo) + } + body_filter_by_lua_block { + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + } + } +--- response_body_like chomp +\A(?:nil|2)\n\z +--- grep_error_log eval +qr/(old foo: \d+|\[\w+\].*?writing a global Lua variable \('[^'\s]+'\)|\w+_by_lua:\d+: in main chunk, )/ +--- grep_error_log_out eval +[qr/\[warn\] .*?writing a global Lua variable \('foo'\) +body_filter_by_lua:3: in main chunk, +old foo: 1\n\z/, "old foo: 2\nold foo: 3\n"] + + + +=== TEST 7: log_by_lua +--- config + location /t { + content_by_lua_block { + ngx.say(foo) + } + log_by_lua_block { + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + } + } +--- response_body_like chomp +\A(?:nil|1)\n\z +--- grep_error_log eval +qr/(old foo: \d+|\[\w+\].*?writing a global Lua variable \('[^'\s]+'\)|\w+_by_lua\(.*?\):\d+: in main chunk)/ +--- grep_error_log_out eval +[qr/\A\[warn\] .*?writing a global Lua variable \('foo'\) +log_by_lua\(nginx\.conf:50\):3: in main chunk\n\z/, "old foo: 1\n"] + + + +=== TEST 8: ssl_certificate_by_lua +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate_by_lua_block { + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + } + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + content_by_lua_block { + ngx.say("foo: ", foo) + } + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + -- ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + -- ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + -- ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + local m, err = ngx.re.match(line, "^foo: (.*)$", "jo") + if err then + ngx.say("failed to match line: ", err) + end + + if m and m[1] then + ngx.print(m[1]) + end + end + + local ok, err = sock:close() + ngx.say("done") + end -- do + } + } + +--- response_body_like chomp +\A[12]done\n\z +--- grep_error_log eval +qr/(old foo: \d+|\[\w+\].*?writing a global Lua variable \('[^'\s]+'\)|\w+_by_lua:\d+: in main chunk)/ +--- grep_error_log_out eval +[qr/\A\[warn\] .*?writing a global Lua variable \('foo'\) +ssl_certificate_by_lua:3: in main chunk\n\z/, "old foo: 1\n"] + + + +=== TEST 9: timer +--- config + location /t { + content_by_lua_block { + local function f() + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + end + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.sleep(0.01) + ngx.say(foo) + } + } +--- response_body_like chomp +\A[12]\n\z +--- grep_error_log eval +qr/(old foo: \d+|\[\w+\].*?writing a global Lua variable \('[^'\s]+'\)|\w+_by_lua\(.*?\):\d+: in\b)/ +--- grep_error_log_out eval +[qr/\A\[warn\] .*?writing a global Lua variable \('foo'\) +content_by_lua\(nginx\.conf:56\):4: in\n\z/, "old foo: 1\n"] + + + +=== TEST 10: init_by_lua +--- http_config + init_by_lua_block { + foo = 1 + } +--- config + location /t { + content_by_lua_block { + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + ngx.say(foo) + } + } +--- response_body_like chomp +\A[23]\n\z +--- grep_error_log eval: qr/old foo: \d+/ +--- grep_error_log_out eval +["old foo: 1\n", "old foo: 2\n"] + + + +=== TEST 11: init_worker_by_lua +--- http_config + init_worker_by_lua_block { + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + } +--- config + location /t { + content_by_lua_block { + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + ngx.say(foo) + } + } +--- response_body_like chomp +\A[23]\n\z +--- grep_error_log eval: qr/old foo: \d+/ +--- grep_error_log_out eval +["old foo: 1\n", "old foo: 2\n"] + + + +=== TEST 12: init_by_lua + init_worker_by_lua +--- http_config + init_by_lua_block { + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + } + init_worker_by_lua_block { + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + } +--- config + location /t { + content_by_lua_block { + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + ngx.say(foo) + } + } +--- response_body_like chomp +\A[34]\n\z +--- grep_error_log eval: qr/old foo: \d+/ +--- grep_error_log_out eval +["old foo: 1\nold foo: 2\n", "old foo: 3\n"] + + + +=== TEST 13: don't show warn messages in init/init_worker +--- http_config + init_by_lua_block { + foo = 1 + } + + init_worker_by_lua_block { + bar = 2 + } +--- config + location /t { + content_by_lua_block { + ngx.say(foo) + ngx.say(bar) + } + } +--- response_body +1 +2 +--- no_error_log +setting global variable + + + +=== TEST 14: uthread +--- config + location /t { + content_by_lua_block { + local function f() + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + end + local ok, err = ngx.thread.spawn(f) + if not ok then + ngx.say("failed to set timer: ", err) + return + end + ngx.sleep(0.01) + ngx.say(foo) + } + } +--- response_body_like chomp +\A[12]\n\z +--- grep_error_log eval +qr/(old foo: \d+|writing a global Lua variable \('\w+'\))/ +--- grep_error_log_out eval +["writing a global Lua variable \('foo'\)\n", "old foo: 1\n"] + + + +=== TEST 15: balancer_by_lua +--- http_config + upstream backend { + server 0.0.0.1; + balancer_by_lua_block { + if not foo then + foo = 1 + else + ngx.log(ngx.WARN, "old foo: ", foo) + foo = foo + 1 + end + } + } +--- config + location = /t { + proxy_pass http://backend; + } +--- response_body_like: 502 Bad Gateway +--- error_code: 502 +--- error_log eval +qr/\[crit\].*?\Qconnect() to 0.0.0.1:80 failed\E/ +--- grep_error_log eval: qr/(old foo: \d+|writing a global Lua variable \('\w+'\))/ +--- grep_error_log_out eval +["writing a global Lua variable \('foo'\)\n", "old foo: 1\n"] diff -Nru nginx-1.18.0/debian/modules/http-lua/t/159-sa-restart.t nginx-1.20.1/debian/modules/http-lua/t/159-sa-restart.t --- nginx-1.18.0/debian/modules/http-lua/t/159-sa-restart.t 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-lua/t/159-sa-restart.t 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,180 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua; + +add_block_preprocessor(sub { + my $block = shift; + + my $http_config = $block->http_config || ''; + + $http_config .= <<_EOC_; + init_by_lua_block { + function test_sa_restart() + local signals = { + --"HUP", + --"INFO", + --"XCPU", + --"USR1", + --"USR2", + "ALRM", + --"INT", + "IO", + "CHLD", + --"WINCH", + } + + for _, signame in ipairs(signals) do + local cmd = string.format("kill -s %s %d && sleep 0.01", + signame, ngx.worker.pid()) + local err = select(2, io.popen(cmd):read("*a")) + if err then + error("SIG" .. signame .. " caused: " .. err) + end + end + end + } +_EOC_ + + $block->set_value("http_config", $http_config); + + if (!defined $block->config) { + my $config = <<_EOC_; + location /t { + echo ok; + } +_EOC_ + + $block->set_value("config", $config); + } + + if (!defined $block->request) { + $block->set_value("request", "GET /t"); + } + + if (!defined $block->response_body) { + $block->set_value("ignore_response_body"); + } + + if (!defined $block->no_error_log) { + $block->set_value("no_error_log", "[error]"); + } +}); + +plan tests => repeat_each() * (blocks() * 2 + 1); + +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: lua_sa_restart default - sets SA_RESTART in init_worker_by_lua* +--- http_config + init_worker_by_lua_block { + test_sa_restart() + } + + + +=== TEST 2: lua_sa_restart off - does not set SA_RESTART +--- http_config + lua_sa_restart off; + + init_worker_by_lua_block { + test_sa_restart() + } +--- no_error_log +[crit] +--- error_log +Interrupted system call + + + +=== TEST 3: lua_sa_restart on (default) - sets SA_RESTART if no init_worker_by_lua* phase is defined +--- config + location /t { + content_by_lua_block { + test_sa_restart() + } + } + + + +=== TEST 4: lua_sa_restart on (default) - SA_RESTART is effective in rewrite_by_lua* +--- config + location /t { + rewrite_by_lua_block { + test_sa_restart() + } + + echo ok; + } + + + +=== TEST 5: lua_sa_restart on (default) - SA_RESTART is effective in access_by_lua* +--- config + location /t { + access_by_lua_block { + test_sa_restart() + } + + echo ok; + } + + + +=== TEST 6: lua_sa_restart on (default) - SA_RESTART is effective in content_by_lua* +--- config + location /t { + content_by_lua_block { + test_sa_restart() + } + } + + + +=== TEST 7: lua_sa_restart on (default) - SA_RESTART is effective in header_filter_by_lua* +--- config + location /t { + echo ok; + + header_filter_by_lua_block { + test_sa_restart() + } + } + + + +=== TEST 8: lua_sa_restart on (default) - SA_RESTART is effective in body_filter_by_lua* +--- config + location /t { + echo ok; + + body_filter_by_lua_block { + test_sa_restart() + } + } + + + +=== TEST 9: lua_sa_restart on (default) - SA_RESTART is effective in log_by_lua* +--- config + location /t { + echo ok; + + log_by_lua_block { + test_sa_restart() + } + } + + + +=== TEST 10: lua_sa_restart on (default) - SA_RESTART is effective in timer phase +--- config + location /t { + echo ok; + + log_by_lua_block { + ngx.timer.at(0, test_sa_restart) + } + } diff -Nru nginx-1.18.0/debian/modules/http-lua/t/160-disable-init-by-lua.t nginx-1.20.1/debian/modules/http-lua/t/160-disable-init-by-lua.t --- nginx-1.18.0/debian/modules/http-lua/t/160-disable-init-by-lua.t 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-lua/t/160-disable-init-by-lua.t 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,194 @@ +use Test::Nginx::Socket::Lua; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +$ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); + +my $html_dir = $ENV{TEST_NGINX_HTML_DIR}; +my $http_config = <<_EOC_; + init_by_lua_block { + function set_up_ngx_tmp_conf(conf) + if conf == nil then + conf = [[ + events { + worker_connections 64; + } + http { + init_by_lua_block { + ngx.log(ngx.ERR, "run init_by_lua") + } + } + ]] + end + + assert(os.execute("mkdir -p $html_dir/logs")) + + local conf_file = "$html_dir/nginx.conf" + local f, err = io.open(conf_file, "w") + if not f then + ngx.log(ngx.ERR, err) + return + end + + assert(f:write(conf)) + + return conf_file + end + + function get_ngx_bin_path() + local ffi = require "ffi" + ffi.cdef[[char **ngx_argv;]] + return ffi.string(ffi.C.ngx_argv[0]) + end + } +_EOC_ + +add_block_preprocessor(sub { + my $block = shift; + + if (!defined $block->http_config) { + $block->set_value("http_config", $http_config); + } + + if (!defined $block->request) { + $block->set_value("request", "GET /t"); + } +}); + +env_to_nginx("PATH"); +log_level("warn"); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: ensure init_by_lua* is not run in signaller process +--- config + location = /t { + content_by_lua_block { + local conf_file = set_up_ngx_tmp_conf() + local nginx = get_ngx_bin_path() + + local cmd = nginx .. " -p $TEST_NGINX_HTML_DIR -c " .. conf_file .. " -s reopen" + local p, err = io.popen(cmd) + if not p then + ngx.log(ngx.ERR, err) + return + end + + local out, err = p:read("*a") + if not out then + ngx.log(ngx.ERR, err) + + else + ngx.log(ngx.WARN, out) + end + } + } +--- error_log +failed (2: No such file or directory) +--- no_error_log eval +qr/\[error\] .*? init_by_lua:\d+: run init_by_lua/ + + + +=== TEST 2: init_by_lua* does not run when testing Nginx configuration +--- config + location = /t { + content_by_lua_block { + local conf_file = set_up_ngx_tmp_conf() + local nginx = get_ngx_bin_path() + + local cmd = nginx .. " -p $TEST_NGINX_HTML_DIR -c " .. conf_file .. " -t" + local p, err = io.popen(cmd) + if not p then + ngx.log(ngx.ERR, err) + return + end + + local out, err = p:read("*a") + if not out then + ngx.log(ngx.ERR, err) + + else + ngx.log(ngx.WARN, out) + end + + local cmd = nginx .. " -p $TEST_NGINX_HTML_DIR -c " .. conf_file .. " -T" + local p, err = io.popen(cmd) + if not p then + ngx.log(ngx.ERR, err) + return + end + + local out, err = p:read("*a") + if not out then + ngx.log(ngx.ERR, err) + + else + ngx.log(ngx.WARN, out) + end + } + } +--- error_log +test is successful +--- no_error_log eval +qr/\[error\] .*? init_by_lua:\d+: run init_by_lua/ + + + +=== TEST 3: init_by_lua* does not run when testing Nginx configuration which contains 'lua_shared_dict' (GitHub #1462) +--- config + location = /t { + content_by_lua_block { + local conf = [[ + events { + worker_connections 64; + } + http { + lua_shared_dict test 64k; + init_by_lua_block { + ngx.log(ngx.ERR, "run init_by_lua with lua_shared_dict") + } + } + ]] + local conf_file = set_up_ngx_tmp_conf(conf) + local nginx = get_ngx_bin_path() + + local cmd = nginx .. " -p $TEST_NGINX_HTML_DIR -c " .. conf_file .. " -t" + local p, err = io.popen(cmd) + if not p then + ngx.log(ngx.ERR, err) + return + end + + local out, err = p:read("*a") + if not out then + ngx.log(ngx.ERR, err) + + else + ngx.log(ngx.WARN, out) + end + + local cmd = nginx .. " -p $TEST_NGINX_HTML_DIR -c " .. conf_file .. " -T" + local p, err = io.popen(cmd) + if not p then + ngx.log(ngx.ERR, err) + return + end + + local out, err = p:read("*a") + if not out then + ngx.log(ngx.ERR, err) + + else + ngx.log(ngx.WARN, out) + end + } + } +--- error_log +test is successful +--- no_error_log eval +qr/\[error\] .*? init_by_lua:\d+: run init_by_lua with lua_shared_dict/ diff -Nru nginx-1.18.0/debian/modules/http-lua/t/161-load-resty-core.t nginx-1.20.1/debian/modules/http-lua/t/161-load-resty-core.t --- nginx-1.18.0/debian/modules/http-lua/t/161-load-resty-core.t 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-lua/t/161-load-resty-core.t 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,166 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +our $HtmlDir = html_dir; + +add_block_preprocessor(sub { + my $block = shift; + + if (!defined $block->request) { + $block->set_value("request", "GET /t"); + } + + if (!defined $block->no_error_log) { + $block->set_value("no_error_log", "[error]"); + } +}); + +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: resty.core is automatically loaded in the Lua VM +--- config + location = /t { + content_by_lua_block { + local loaded_resty_core = package.loaded["resty.core"] + local resty_core = require "resty.core" + + ngx.say("resty.core loaded: ", loaded_resty_core == resty_core) + } + } +--- response_body +resty.core loaded: true + + + +=== TEST 2: resty.core is automatically loaded in the Lua VM when 'lua_shared_dict' is used +--- http_config + lua_shared_dict dogs 128k; +--- config + location = /t { + content_by_lua_block { + local loaded_resty_core = package.loaded["resty.core"] + local resty_core = require "resty.core" + + ngx.say("resty.core loaded: ", loaded_resty_core == resty_core) + } + } +--- response_body +resty.core loaded: true + + + +=== TEST 3: resty.core is automatically loaded in the Lua VM with 'lua_code_cache off' +--- http_config + lua_code_cache off; +--- config + location = /t { + content_by_lua_block { + local loaded_resty_core = package.loaded["resty.core"] + local resty_core = require "resty.core" + + ngx.say("resty.core loaded: ", loaded_resty_core == resty_core) + } + } +--- response_body +resty.core loaded: true + + + +=== TEST 4: resty.core loading honors the lua_package_path directive +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;;';" +--- config + location = /t { + content_by_lua_block { + local loaded_resty_core = package.loaded["resty.core"] + local resty_core = require "resty.core" + + ngx.say("resty.core loaded: ", loaded_resty_core == resty_core) + + resty_core.go() + } + } +--- response_body +resty.core loaded: true +loaded from html dir +--- user_files +>>> resty/core.lua +return { + go = function () + ngx.say("loaded from html dir") + end +} + + + +=== TEST 5: resty.core not loading aborts the initialization +--- http_config eval + "lua_package_path '$::HtmlDir/?.lua;';" +--- config + location = /t { + return 200; + } +--- must_die +--- error_log eval +qr/\[alert\] .*? failed to load the 'resty\.core' module .*? \(reason: module 'resty\.core' not found:/ + + + +=== TEST 6: resty.core not loading produces an error with 'lua_code_cache off' +--- http_config + lua_code_cache off; + + init_by_lua_block { + package.path = "" + } +--- config + location = /t { + content_by_lua_block { + ngx.say("ok") + } + } +--- error_code: 500 +--- error_log eval +qr/\[error\] .*? failed to load the 'resty\.core' module .*? \(reason: module 'resty\.core' not found:/ +--- no_error_log eval +qr/\[alert\] .*? failed to load the 'resty\.core' module/ + + + +=== TEST 7: lua_load_resty_core logs a deprecation warning when specified (on) +--- http_config + lua_load_resty_core on; +--- config + location = /t { + return 200; + } +--- grep_error_log eval: qr/\[warn\] .*? lua_load_resty_core is deprecated.*/ +--- grep_error_log_out eval +[ +qr/\[warn\] .*? lua_load_resty_core is deprecated \(the lua-resty-core library is required since ngx_lua v0\.10\.16\) in .*?nginx\.conf:\d+/, +"" +] + + + +=== TEST 8: lua_load_resty_core logs a deprecation warning when specified (off) +--- http_config + lua_load_resty_core off; +--- config + location = /t { + return 200; + } +--- grep_error_log eval: qr/\[warn\] .*? lua_load_resty_core is deprecated.*/ +--- grep_error_log_out eval +[ +qr/\[warn\] .*? lua_load_resty_core is deprecated \(the lua-resty-core library is required since ngx_lua v0\.10\.16\) in .*?nginx\.conf:\d+/, +"" +] diff -Nru nginx-1.18.0/debian/modules/http-lua/t/162-exit-worker.t nginx-1.20.1/debian/modules/http-lua/t/162-exit-worker.t --- nginx-1.18.0/debian/modules/http-lua/t/162-exit-worker.t 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-lua/t/162-exit-worker.t 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,195 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua; + +master_on(); +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2 + 2) + 12; + +#log_level("warn"); +no_long_string(); +our $HtmlDir = html_dir; + +run_tests(); + +__DATA__ + +=== TEST 1: simple exit_worker_by_lua_block +--- http_config + exit_worker_by_lua_block { + ngx.log(ngx.NOTICE, "log from exit_worker_by_lua_block") + } +--- config + location /t { + echo "ok"; + } +--- request +GET /t +--- response_body +ok +--- shutdown_error_log +log from exit_worker_by_lua_block + + + +=== TEST 2: simple exit_worker_by_lua_file +--- http_config + exit_worker_by_lua_file html/exit_worker.lua; +--- config + location /t { + echo "ok"; + } +--- user_files +>>> exit_worker.lua +ngx.log(ngx.NOTICE, "log from exit_worker_by_lua_file") +--- request +GET /t +--- response_body +ok +--- shutdown_error_log +log from exit_worker_by_lua_file + + + +=== TEST 3: exit_worker_by_lua (require a global table) +--- http_config eval + qq{lua_package_path '$::HtmlDir/?.lua;;'; + exit_worker_by_lua_block { + foo = require("foo") + ngx.log(ngx.NOTICE, foo.bar) + }} +--- config + location /t { + content_by_lua_block { + foo = require("foo") + foo.bar = "hello, world" + ngx.say("ok") + } + } +--- user_files +>>> foo.lua +return {} +--- request +GET /t +--- response_body +ok +--- shutdown_error_log +hello, world + + + +=== TEST 4: exit_worker_by_lua single process ngx.timer not work +--- http_config + exit_worker_by_lua_block { + local function bar() + ngx.log(ngx.ERR, "run the timer!" + end + + local ok, err = ngx.timer.at(0, bar) + if not ok then + ngx.log(ngx.ERR, "failed to create timer: ", err) + else + ngx.log(ngx.NOTICE, "success") + end + } +--- config + location /t { + echo "ok"; + } +--- request +GET /t +--- response_body +ok +--- no_error_log +[error] + + + +=== TEST 5: exit_worker_by_lua use shdict +--- http_config + lua_shared_dict dog 1m; + exit_worker_by_lua_block { + local dog = ngx.shared.dog + local val, err = dog:get("foo") + if not val then + ngx.log(ngx.ERR, "failed get shdict: ", err) + else + ngx.log(ngx.NOTICE, "get val: ", val) + end + } +--- config + location /t { + content_by_lua_block { + local dog = ngx.shared.dog + dog:set("foo", 100) + ngx.say("ok") + } + } +--- request +GET /t +--- response_body +ok +--- shutdown_error_log +get val: 100 + + + +=== TEST 6: skip in cache processes (with exit worker and privileged agent) +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;"; + + proxy_cache_path /tmp/cache levels=1:2 keys_zone=cache:1m; + + init_by_lua_block { + assert(require "ngx.process".enable_privileged_agent()) + } + + exit_worker_by_lua_block { + local process = require "ngx.process" + ngx.log(ngx.INFO, "hello from exit worker by lua, process type: ", process.type()) + } +--- config + location = /t { + return 200; + } +--- request + GET /t +--- no_error_log +[error] +--- shutdown_error_log eval +[ +qr/cache loader process \d+ exited/, +qr/cache manager process \d+ exited/, +qr/hello from exit worker by lua, process type: worker/, +qr/hello from exit worker by lua, process type: privileged agent/, +qr/privileged agent process \d+ exited/, +] + + + +=== TEST 7: skipin cache processes (with init worker but without privileged agent) +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;"; + + proxy_cache_path /tmp/cache levels=1:2 keys_zone=cache:1m; + + exit_worker_by_lua_block { + local process = require "ngx.process" + ngx.log(ngx.INFO, "hello from exit worker by lua, process type: ", process.type()) + } +--- config + location = /t { + return 200; + } +--- request + GET /t +--- no_error_log +[error] +start privileged agent process +--- shutdown_error_log eval +[ +qr/cache loader process \d+ exited/, +qr/cache manager process \d+ exited/, +qr/hello from exit worker by lua, process type: worker/, +] diff -Nru nginx-1.18.0/debian/modules/http-lua/t/162-static-module-location.t nginx-1.20.1/debian/modules/http-lua/t/162-static-module-location.t --- nginx-1.18.0/debian/modules/http-lua/t/162-static-module-location.t 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-lua/t/162-static-module-location.t 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,95 @@ +use Test::Nginx::Socket::Lua; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +our $HtmlDir = html_dir; + +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: decoded url contains '\0' and '\r\n' +--- config + server_tokens off; + location = /t { + rewrite_by_lua_block { + ngx.req.read_body(); + local args, _ = ngx.req.get_post_args(); + ngx.req.set_uri(args["url"], true, true); + } + } +--- request +POST /t +url=%00%0a%0dset-cookie:1234567 +--- error_code: 301 +--- response_headers +Location: %00%0A%0Dset-cookie:1234567/ +--- response_body_like +.*301 Moved Permanently.* + + + +=== TEST 2: uri contain chinese characters +--- config + server_tokens off; +--- user_files +>>> t/中文/foo.txt +Hello, world +--- request +GET /t/中文 +--- error_code: 301 +--- response_headers +Location: http://localhost:1984/t/%E4%B8%AD%E6%96%87/ +--- response_body_like +.*301 Moved Permanently.* + + + +=== TEST 3: uri contain chinese characters with args +--- config + server_tokens off; +--- user_files +>>> t/中文/foo.txt +Hello, world +--- request +GET /t/中文?q=name +--- error_code: 301 +--- response_headers +Location: http://localhost:1984/t/%E4%B8%AD%E6%96%87/?q=name +--- response_body_like +.*301 Moved Permanently.* + + + +=== TEST 4: uri already encoded +--- config + server_tokens off; +--- user_files +>>> t/中文/foo.txt +Hello, world +--- request +GET /t/%E4%B8%AD%E6%96%87 +--- error_code: 301 +--- response_headers +Location: http://localhost:1984/t/%E4%B8%AD%E6%96%87/ +--- response_body_like +.*301 Moved Permanently.* + + + +=== TEST 5: uri already encoded with args +--- config + server_tokens off; +--- user_files +>>> t/中文/foo.txt +Hello, world +--- request +GET /t/%E4%B8%AD%E6%96%87?q=name +--- error_code: 301 +--- response_headers +Location: http://localhost:1984/t/%E4%B8%AD%E6%96%87/?q=name +--- response_body_like +.*301 Moved Permanently.* diff -Nru nginx-1.18.0/debian/modules/http-lua/t/163-exit-worker-hup.t nginx-1.20.1/debian/modules/http-lua/t/163-exit-worker-hup.t --- nginx-1.18.0/debian/modules/http-lua/t/163-exit-worker-hup.t 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-lua/t/163-exit-worker-hup.t 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,86 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +our $SkipReason; + +BEGIN { + if ($ENV{TEST_NGINX_CHECK_LEAK}) { + $SkipReason = "unavailable for the hup tests"; + + } else { + $ENV{TEST_NGINX_USE_HUP} = 1; + undef $ENV{TEST_NGINX_USE_STAP}; + } +} + +use Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : (); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2 + 1) + 2; + +no_long_string(); + +worker_connections(1024); +run_tests(); + +__DATA__ + +=== TEST 1: simple exit_worker_by_lua_block with hup +--- http_config + exit_worker_by_lua_block { + ngx.log(ngx.NOTICE, "log from exit_worker_by_lua_block") + } +--- config + location /t { + content_by_lua_block { + ngx.say("ok") + } + } +--- request +GET /t +--- response_body +ok +--- shutdown_error_log +log from exit_worker_by_lua_block + + + +=== TEST 2: exit after worker_shutdown_timeout +--- main_config + worker_shutdown_timeout 1; +--- http_config + exit_worker_by_lua_block { + ngx.log(ngx.NOTICE, "log from exit_worker_by_lua_block") + } + + server { + listen 12345; + + location = /t { + echo 'hello world'; + } + } +--- config + location /t { + content_by_lua_block { + ngx.timer.at(0, function () + local sock = ngx.socket.tcp() + sock:connect("127.0.0.1", 12345) + local reader = sock:receiveuntil("unknow") + ngx.log(ngx.NOTICE, "reading to block the exiting") + reader() + end) + + ngx.sleep(0) + + ngx.say("ok") + } + } +--- request +GET /t +--- response_body +ok +--- error_log +reading to block the exiting +--- shutdown_error_log +log from exit_worker_by_lua_block diff -Nru nginx-1.18.0/debian/modules/http-lua/t/163-signal.t nginx-1.20.1/debian/modules/http-lua/t/163-signal.t --- nginx-1.18.0/debian/modules/http-lua/t/163-signal.t 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-lua/t/163-signal.t 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,59 @@ +# vi:ft= + +our $SkipReason; + +BEGIN { + if ($ENV{TEST_NGINX_USE_HUP}) { + $SkipReason = "unavailable under hup test mode"; + } +} + +use Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : (); + +plan tests => 2 * blocks(); + +no_long_string(); + + +run_tests(); + +__DATA__ + +=== TEST 1: SIGHUP followed by SIGQUIT +--- config + location = /t { + content_by_lua_block { + local pid = ngx.worker.pid() + os.execute("kill -HUP " .. pid) + ngx.sleep(0.01) + + os.execute("kill -QUIT " .. pid) + } + } +--- request +GET /t +--- ignore_response +--- wait: 0.1 +--- error_log eval +qr/\[notice\] \d+#\d+: exit$/ +--- no_error_log eval +qr/\[notice\] \d+#\d+: reconfiguring/ + + + +=== TEST 2: exit after receiving SIGHUP in single process mode +--- config + location = /t { + content_by_lua_block { + local pid = ngx.worker.pid() + os.execute("kill -HUP " .. pid) + } + } +--- request +GET /t +--- ignore_response +--- wait: 0.1 +--- error_log eval +qr/\[notice\] \d+#\d+: exit$/ +--- no_error_log eval +qr/\[notice\] \d+#\d+: reconfiguring/ diff -Nru nginx-1.18.0/debian/modules/http-lua/t/164-say.t nginx-1.20.1/debian/modules/http-lua/t/164-say.t --- nginx-1.18.0/debian/modules/http-lua/t/164-say.t 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-lua/t/164-say.t 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,111 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua; + +repeat_each(2); + +plan tests => blocks() * repeat_each() * 2; + +run_tests(); + +__DATA__ + +=== TEST 1: ngx.say (integer) +--- config + location /lua { + content_by_lua_block { + ngx.say(2) + } + } +--- request +GET /lua +--- response_body +2 + + + +=== TEST 2: ngx.say (floating point number) +the maximum number of significant digits is 14 in lua +--- config + location /lua { + content_by_lua_block { + ngx.say(3.1415926) + ngx.say(3.14159265357939723846) + } + } +--- request +GET /lua +--- response_body +3.1415926 +3.1415926535794 + + + +=== TEST 3: ngx.say (table with number) +--- config + location /lua { + content_by_lua_block { + local data = {123," ", 3.1415926} + ngx.say(data) + } + } +--- request +GET /lua +--- response_body +123 3.1415926 + + + +=== TEST 4: ngx.say min int32 -2147483648 +--- config + location /lua { + content_by_lua_block { + ngx.say(-2147483648) + } + } +--- request +GET /lua +--- response_body +-2147483648 + + + +=== TEST 5: ngx.say big integer 2147483647 +--- config + location /lua { + content_by_lua_block { + ngx.say(2147483647) + } + } +--- request +GET /lua +--- response_body +2147483647 + + + +=== TEST 6: ngx.say big integer -9223372036854775808 +--- config + location /lua { + content_by_lua_block { + ngx.say(-9223372036854775808) + } + } +--- request +GET /lua +--- response_body +-9.2233720368548e+18 + + + +=== TEST 7: ngx.say big integer 18446744073709551615 +--- config + location /lua { + content_by_lua_block { + ngx.say(18446744073709551615) + } + } +--- request +GET /lua +--- response_body +1.844674407371e+19 diff -Nru nginx-1.18.0/debian/modules/http-lua/t/165-thread-cache.t nginx-1.20.1/debian/modules/http-lua/t/165-thread-cache.t --- nginx-1.18.0/debian/modules/http-lua/t/165-thread-cache.t 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/modules/http-lua/t/165-thread-cache.t 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,79 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua; + +#worker_connections(1014); +#master_on(); +#workers(2); +#log_level('warn'); + +repeat_each(2); +#repeat_each(1); + +plan tests => repeat_each() * (blocks() * 4); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: thread cache size == 1 +--- http_config + lua_thread_cache_max_entries 1; + +--- config + location /lua { + # NOTE: the newline escape sequence must be double-escaped, as nginx config + # parser will unescape first! + content_by_lua ' + local ok, err = ngx.print("Hello, Lua!\\n") + if not ok then + ngx.log(ngx.ERR, "print failed: ", err) + end + '; + } +--- request +GET /lua +--- response_body +Hello, Lua! +--- no_error_log +[error] +--- grep_error_log eval: qr/lua caching unused lua thread|lua reusing cached lua thread/ +--- grep_error_log_out eval +[ + "lua caching unused lua thread\n", + "lua reusing cached lua thread +lua caching unused lua thread +", +] + + + +=== TEST 2: thread cache size == 0 +--- http_config + lua_thread_cache_max_entries 0; + +--- config + location /lua { + # NOTE: the newline escape sequence must be double-escaped, as nginx config + # parser will unescape first! + content_by_lua ' + local ok, err = ngx.print("Hello, Lua!\\n") + if not ok then + ngx.log(ngx.ERR, "print failed: ", err) + end + '; + } +--- request +GET /lua +--- response_body +Hello, Lua! +--- no_error_log +[error] +--- grep_error_log eval: qr/lua caching unused lua thread|lua reusing cached lua thread/ +--- grep_error_log_out eval +[ + "", + "", +] diff -Nru nginx-1.18.0/debian/modules/patches/http-lua/series nginx-1.20.1/debian/modules/patches/http-lua/series --- nginx-1.18.0/debian/modules/patches/http-lua/series 2021-03-12 13:47:51.000000000 +0000 +++ nginx-1.20.1/debian/modules/patches/http-lua/series 2021-06-08 07:04:11.000000000 +0000 @@ -1,2 +1 @@ discover-luajit-2.1.patch -CVE-2020-11724.patch diff -Nru nginx-1.18.0/debian/nginx-common.NEWS nginx-1.20.1/debian/nginx-common.NEWS --- nginx-1.18.0/debian/nginx-common.NEWS 2021-03-12 13:47:51.000000000 +0000 +++ nginx-1.20.1/debian/nginx-common.NEWS 2021-06-08 07:04:11.000000000 +0000 @@ -1,4 +1,4 @@ -nginx (1.18.0-6+deb.sury.org+2) unstable; urgency=medium +nginx (1.19.8-0+deb.sury.org+1) unstable; urgency=medium * The custom src:openssl packages were introduced to upgrade the cryptographic functions for PHP, Apache2 and NGINX, but the situation diff -Nru nginx-1.18.0/debian/patches/0001-Make-sure-signature-stays-the-same-in-all-nginx-buil.patch nginx-1.20.1/debian/patches/0001-Make-sure-signature-stays-the-same-in-all-nginx-buil.patch --- nginx-1.18.0/debian/patches/0001-Make-sure-signature-stays-the-same-in-all-nginx-buil.patch 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/patches/0001-Make-sure-signature-stays-the-same-in-all-nginx-buil.patch 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,30 @@ +From: Christos Trochalakis +Date: Wed, 30 Mar 2016 09:47:11 +0300 +Subject: Make sure signature stays the same in all nginx builds + +NGX_HTTP_HEADERS is part of nginx signature. When a dyn +modules is loaded the signature of the module is compared +to the one of the nginx binary. + +dyn modules are build from nginx-full, so in order to make +them loadable in other flavors we need to make sure all the +binaries share the same signature. +--- + configure | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/configure b/configure +index 474d69e..b7c507a 100755 +--- a/configure ++++ b/configure +@@ -58,6 +58,10 @@ if [ "$NGX_PLATFORM" != win32 ]; then + . auto/unix + fi + ++# Debian ++# Make sure signature stays the same on all nginx flavors ++have=NGX_HTTP_HEADERS . auto/have ++ + . auto/threads + . auto/modules + . auto/lib/conf diff -Nru nginx-1.18.0/debian/patches/0002-define_gnu_source-on-other-glibc-based-platforms.patch nginx-1.20.1/debian/patches/0002-define_gnu_source-on-other-glibc-based-platforms.patch --- nginx-1.18.0/debian/patches/0002-define_gnu_source-on-other-glibc-based-platforms.patch 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/debian/patches/0002-define_gnu_source-on-other-glibc-based-platforms.patch 2021-06-08 07:04:11.000000000 +0000 @@ -0,0 +1,29 @@ +From: Steven Chamberlain +Date: Sat, 16 Jul 2016 23:52:50 +0100 +Subject: Use _GNU_SOURCE on GNU/kFreeBSD + +Define _GNU_SOURCE not only on GNU/Hurd, but also other glibc-based +platforms including GNU/kFreeBSD. +--- + src/os/unix/ngx_posix_config.h | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/os/unix/ngx_posix_config.h b/src/os/unix/ngx_posix_config.h +index 2a8c413..03f7e0a 100644 +--- a/src/os/unix/ngx_posix_config.h ++++ b/src/os/unix/ngx_posix_config.h +@@ -21,10 +21,13 @@ + #endif + + +-#if (NGX_GNU_HURD) ++#if defined(__GLIBC__) + #ifndef _GNU_SOURCE + #define _GNU_SOURCE /* accept4() */ + #endif ++#endif ++ ++#if (NGX_GNU_HURD) + #define _FILE_OFFSET_BITS 64 + #endif + diff -Nru nginx-1.18.0/debian/patches/0002-Make-sure-signature-stays-the-same-in-all-nginx-buil.patch nginx-1.20.1/debian/patches/0002-Make-sure-signature-stays-the-same-in-all-nginx-buil.patch --- nginx-1.18.0/debian/patches/0002-Make-sure-signature-stays-the-same-in-all-nginx-buil.patch 2021-03-12 13:47:51.000000000 +0000 +++ nginx-1.20.1/debian/patches/0002-Make-sure-signature-stays-the-same-in-all-nginx-buil.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -From: Christos Trochalakis -Date: Wed, 30 Mar 2016 09:47:11 +0300 -Subject: Make sure signature stays the same in all nginx builds - -NGX_HTTP_HEADERS is part of nginx signature. When a dyn -modules is loaded the signature of the module is compared -to the one of the nginx binary. - -dyn modules are build from nginx-full, so in order to make -them loadable in other flavors we need to make sure all the -binaries share the same signature. ---- - configure | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/configure b/configure -index ceff15e..3816fa1 100755 ---- a/configure -+++ b/configure -@@ -58,6 +58,10 @@ if [ "$NGX_PLATFORM" != win32 ]; then - . auto/unix - fi - -+# Debian -+# Make sure signature stays the same on all nginx flavors -+have=NGX_HTTP_HEADERS . auto/have -+ - . auto/threads - . auto/modules - . auto/lib/conf diff -Nru nginx-1.18.0/debian/patches/0003-define_gnu_source-on-other-glibc-based-platforms.patch nginx-1.20.1/debian/patches/0003-define_gnu_source-on-other-glibc-based-platforms.patch --- nginx-1.18.0/debian/patches/0003-define_gnu_source-on-other-glibc-based-platforms.patch 2021-03-12 13:47:51.000000000 +0000 +++ nginx-1.20.1/debian/patches/0003-define_gnu_source-on-other-glibc-based-platforms.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -Date: Sat, 16 Jul 2016 23:52:50 +0100 -From: Steven Chamberlain -Subject: Use _GNU_SOURCE on GNU/kFreeBSD - -Define _GNU_SOURCE not only on GNU/Hurd, but also other glibc-based -platforms including GNU/kFreeBSD. ---- a/src/os/unix/ngx_posix_config.h -+++ b/src/os/unix/ngx_posix_config.h -@@ -21,10 +21,13 @@ - #endif - - --#if (NGX_GNU_HURD) -+#if defined(__GLIBC__) - #ifndef _GNU_SOURCE - #define _GNU_SOURCE /* accept4() */ - #endif -+#endif -+ -+#if (NGX_GNU_HURD) - #define _FILE_OFFSET_BITS 64 - #endif - diff -Nru nginx-1.18.0/debian/patches/CVE-2019-20372.patch nginx-1.20.1/debian/patches/CVE-2019-20372.patch --- nginx-1.18.0/debian/patches/CVE-2019-20372.patch 2021-03-12 13:47:51.000000000 +0000 +++ nginx-1.20.1/debian/patches/CVE-2019-20372.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -From 8bffc01d084b4881e3eed2052c115b8f04268cb9 Mon Sep 17 00:00:00 2001 -From: Ruslan Ermilov -Date: Mon, 23 Dec 2019 15:45:46 +0300 -Subject: [PATCH] Discard request body when redirecting to a URL via - error_page. - -Reported by Bert JW Regeer and Francisco Oca Gonzalez. ---- - src/http/ngx_http_special_response.c | 6 ++++++ - 1 file changed, 6 insertions(+) - ---- a/src/http/ngx_http_special_response.c -+++ b/src/http/ngx_http_special_response.c -@@ -629,6 +629,12 @@ - r->keepalive = 0; - } - -+ r->expect_tested = 1; -+ -+ if (ngx_http_discard_request_body(r) != NGX_OK) { -+ r->keepalive = 0; -+ } -+ - location = ngx_list_push(&r->headers_out.headers); - - if (location == NULL) { diff -Nru nginx-1.18.0/debian/patches/series nginx-1.20.1/debian/patches/series --- nginx-1.18.0/debian/patches/series 2021-03-12 13:47:51.000000000 +0000 +++ nginx-1.20.1/debian/patches/series 2021-06-08 07:04:11.000000000 +0000 @@ -1,3 +1,2 @@ -0002-Make-sure-signature-stays-the-same-in-all-nginx-buil.patch -0003-define_gnu_source-on-other-glibc-based-platforms.patch -CVE-2019-20372.patch +0001-Make-sure-signature-stays-the-same-in-all-nginx-buil.patch +0002-define_gnu_source-on-other-glibc-based-platforms.patch diff -Nru nginx-1.18.0/debian/rules nginx-1.20.1/debian/rules --- nginx-1.18.0/debian/rules 2021-03-12 13:47:51.000000000 +0000 +++ nginx-1.20.1/debian/rules 2021-06-08 07:04:11.000000000 +0000 @@ -1,10 +1,14 @@ #!/usr/bin/make -f +include /usr/share/dpkg/default.mk export DH_VERBOSE=1 export DEB_BUILD_MAINT_OPTIONS=hardening=+all debian_cflags:=$(shell dpkg-buildflags --get CFLAGS) -fPIC $(shell dpkg-buildflags --get CPPFLAGS) debian_ldflags:=$(shell dpkg-buildflags --get LDFLAGS) -fPIC +export LUAJIT_INC := $(subst -I,,$(shell pkg-config --cflags luajit)) +export LUAJIT_LIB := /usr + FLAVOURS := core light extras DYN_MODS := \ brotli \ diff -Nru nginx-1.18.0/debian/watch nginx-1.20.1/debian/watch --- nginx-1.18.0/debian/watch 2021-03-12 13:47:51.000000000 +0000 +++ nginx-1.20.1/debian/watch 2021-06-08 07:04:11.000000000 +0000 @@ -1,3 +1,3 @@ version=3 opts=pgpsigurlmangle=s/$/.asc/ \ -https://nginx.org/download/nginx-(1\.18\.\d+)\.tar\.gz +https://nginx.org/download/nginx-(1\.20\.\d+)\.tar\.gz diff -Nru nginx-1.18.0/LICENSE nginx-1.20.1/LICENSE --- nginx-1.18.0/LICENSE 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/LICENSE 2021-05-25 12:35:38.000000000 +0000 @@ -1,6 +1,6 @@ /* - * Copyright (C) 2002-2019 Igor Sysoev - * Copyright (C) 2011-2019 Nginx, Inc. + * Copyright (C) 2002-2021 Igor Sysoev + * Copyright (C) 2011-2021 Nginx, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff -Nru nginx-1.18.0/man/nginx.8 nginx-1.20.1/man/nginx.8 --- nginx-1.18.0/man/nginx.8 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/man/nginx.8 2021-05-25 12:35:38.000000000 +0000 @@ -25,7 +25,7 @@ .\" SUCH DAMAGE. .\" .\" -.Dd December 5, 2019 +.Dd November 5, 2020 .Dt NGINX 8 .Os .Sh NAME @@ -35,6 +35,7 @@ .Nm .Op Fl ?hqTtVv .Op Fl c Ar file +.Op Fl e Ar file .Op Fl g Ar directives .Op Fl p Ar prefix .Op Fl s Ar signal @@ -54,6 +55,12 @@ .It Fl c Ar file Use an alternative configuration .Ar file . +.It Fl e Ar file +Use an alternative error log +.Ar file . +Special value +.Cm stderr +indicates that the standard error output should be used. .It Fl g Ar directives Set global configuration directives. See @@ -198,10 +205,10 @@ started in 2002, with the first public release on October 4, 2004. .Sh AUTHORS .An -nosplit -.An Igor Sysoev Aq igor@sysoev.ru . +.An Igor Sysoev Aq Mt igor@sysoev.ru . .Pp This manual page was originally written by -.An Sergey A. Osokin Aq osa@FreeBSD.org.ru +.An Sergey A. Osokin Aq Mt osa@FreeBSD.org.ru as a result of compiling many .Nm documents from all over the world. diff -Nru nginx-1.18.0/src/core/nginx.c nginx-1.20.1/src/core/nginx.c --- nginx-1.18.0/src/core/nginx.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/core/nginx.c 2021-05-25 12:35:38.000000000 +0000 @@ -183,6 +183,7 @@ static ngx_uint_t ngx_show_version; static ngx_uint_t ngx_show_configure; static u_char *ngx_prefix; +static u_char *ngx_error_log; static u_char *ngx_conf_file; static u_char *ngx_conf_params; static char *ngx_signal; @@ -230,7 +231,7 @@ ngx_pid = ngx_getpid(); ngx_parent = ngx_getppid(); - log = ngx_log_init(ngx_prefix); + log = ngx_log_init(ngx_prefix, ngx_error_log); if (log == NULL) { return 1; } @@ -393,9 +394,9 @@ if (ngx_show_help) { ngx_write_stderr( - "Usage: nginx [-?hvVtTq] [-s signal] [-c filename] " - "[-p prefix] [-g directives]" NGX_LINEFEED - NGX_LINEFEED + "Usage: nginx [-?hvVtTq] [-s signal] [-p prefix]" NGX_LINEFEED + " [-e filename] [-c filename] [-g directives]" + NGX_LINEFEED NGX_LINEFEED "Options:" NGX_LINEFEED " -?,-h : this help" NGX_LINEFEED " -v : show version and exit" NGX_LINEFEED @@ -414,6 +415,12 @@ #else " -p prefix : set prefix path (default: NONE)" NGX_LINEFEED #endif + " -e filename : set error log file (default: " +#ifdef NGX_ERROR_LOG_STDERR + "stderr)" NGX_LINEFEED +#else + NGX_ERROR_LOG_PATH ")" NGX_LINEFEED +#endif " -c filename : set configuration file (default: " NGX_CONF_PATH ")" NGX_LINEFEED " -g directives : set global directives out of configuration " @@ -492,6 +499,7 @@ ngx_memzero(ls, sizeof(ngx_listening_t)); ls->fd = (ngx_socket_t) s; + ls->inherited = 1; } } @@ -799,6 +807,24 @@ ngx_log_stderr(0, "option \"-p\" requires directory name"); return NGX_ERROR; + case 'e': + if (*p) { + ngx_error_log = p; + + } else if (argv[++i]) { + ngx_error_log = (u_char *) argv[i]; + + } else { + ngx_log_stderr(0, "option \"-e\" requires file name"); + return NGX_ERROR; + } + + if (ngx_strcmp(ngx_error_log, "stderr") == 0) { + ngx_error_log = (u_char *) ""; + } + + goto next; + case 'c': if (*p) { ngx_conf_file = p; @@ -991,6 +1017,14 @@ } } + if (ngx_error_log) { + cycle->error_log.len = ngx_strlen(ngx_error_log); + cycle->error_log.data = ngx_error_log; + + } else { + ngx_str_set(&cycle->error_log, NGX_ERROR_LOG_PATH); + } + if (ngx_conf_params) { cycle->conf_param.len = ngx_strlen(ngx_conf_params); cycle->conf_param.data = ngx_conf_params; diff -Nru nginx-1.18.0/src/core/nginx.h nginx-1.20.1/src/core/nginx.h --- nginx-1.18.0/src/core/nginx.h 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/core/nginx.h 2021-05-25 12:35:38.000000000 +0000 @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1018000 -#define NGINX_VERSION "1.18.0" +#define nginx_version 1020001 +#define NGINX_VERSION "1.20.1" #define NGINX_VER "nginx/" NGINX_VERSION #ifdef NGX_BUILD diff -Nru nginx-1.18.0/src/core/ngx_buf.h nginx-1.20.1/src/core/ngx_buf.h --- nginx-1.18.0/src/core/ngx_buf.h 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/core/ngx_buf.h 2021-05-25 12:35:38.000000000 +0000 @@ -125,20 +125,20 @@ #define NGX_CHAIN_ERROR (ngx_chain_t *) NGX_ERROR -#define ngx_buf_in_memory(b) (b->temporary || b->memory || b->mmap) -#define ngx_buf_in_memory_only(b) (ngx_buf_in_memory(b) && !b->in_file) +#define ngx_buf_in_memory(b) ((b)->temporary || (b)->memory || (b)->mmap) +#define ngx_buf_in_memory_only(b) (ngx_buf_in_memory(b) && !(b)->in_file) #define ngx_buf_special(b) \ - ((b->flush || b->last_buf || b->sync) \ - && !ngx_buf_in_memory(b) && !b->in_file) + (((b)->flush || (b)->last_buf || (b)->sync) \ + && !ngx_buf_in_memory(b) && !(b)->in_file) #define ngx_buf_sync_only(b) \ - (b->sync \ - && !ngx_buf_in_memory(b) && !b->in_file && !b->flush && !b->last_buf) + ((b)->sync && !ngx_buf_in_memory(b) \ + && !(b)->in_file && !(b)->flush && !(b)->last_buf) #define ngx_buf_size(b) \ - (ngx_buf_in_memory(b) ? (off_t) (b->last - b->pos): \ - (b->file_last - b->file_pos)) + (ngx_buf_in_memory(b) ? (off_t) ((b)->last - (b)->pos): \ + ((b)->file_last - (b)->file_pos)) ngx_buf_t *ngx_create_temp_buf(ngx_pool_t *pool, size_t size); ngx_chain_t *ngx_create_chain_of_bufs(ngx_pool_t *pool, ngx_bufs_t *bufs); @@ -149,8 +149,8 @@ ngx_chain_t *ngx_alloc_chain_link(ngx_pool_t *pool); #define ngx_free_chain(pool, cl) \ - cl->next = pool->chain; \ - pool->chain = cl + (cl)->next = (pool)->chain; \ + (pool)->chain = (cl) diff -Nru nginx-1.18.0/src/core/ngx_conf_file.c nginx-1.20.1/src/core/ngx_conf_file.c --- nginx-1.18.0/src/core/ngx_conf_file.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/core/ngx_conf_file.c 2021-05-25 12:35:38.000000000 +0000 @@ -1137,7 +1137,7 @@ a = (ngx_array_t **) (p + cmd->offset); - if (*a == NULL) { + if (*a == NGX_CONF_UNSET_PTR || *a == NULL) { *a = ngx_array_create(cf->pool, 4, sizeof(ngx_keyval_t)); if (*a == NULL) { return NGX_CONF_ERROR; diff -Nru nginx-1.18.0/src/core/ngx_connection.c nginx-1.20.1/src/core/ngx_connection.c --- nginx-1.18.0/src/core/ngx_connection.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/core/ngx_connection.c 2021-05-25 12:35:38.000000000 +0000 @@ -1070,7 +1070,8 @@ if (ls[i].sockaddr->sa_family == AF_UNIX && ngx_process <= NGX_PROCESS_MASTER - && ngx_new_binary == 0) + && ngx_new_binary == 0 + && (!ls[i].inherited || ngx_getppid() != ngx_parent)) { u_char *name = ls[i].addr_text.data + sizeof("unix:") - 1; @@ -1106,12 +1107,9 @@ return NULL; } - c = ngx_cycle->free_connections; + ngx_drain_connections((ngx_cycle_t *) ngx_cycle); - if (c == NULL) { - ngx_drain_connections((ngx_cycle_t *) ngx_cycle); - c = ngx_cycle->free_connections; - } + c = ngx_cycle->free_connections; if (c == NULL) { ngx_log_error(NGX_LOG_ALERT, log, 0, @@ -1297,6 +1295,22 @@ ngx_queue_t *q; ngx_connection_t *c; + if (cycle->free_connection_n > cycle->connection_n / 16 + || cycle->reusable_connections_n == 0) + { + return; + } + + if (cycle->connections_reuse_time != ngx_time()) { + cycle->connections_reuse_time = ngx_time(); + + ngx_log_error(NGX_LOG_WARN, cycle->log, 0, + "%ui worker_connections are not enough, " + "reusing connections", + cycle->connection_n); + } + + c = NULL; n = ngx_max(ngx_min(32, cycle->reusable_connections_n / 8), 1); for (i = 0; i < n; i++) { @@ -1312,6 +1326,21 @@ c->close = 1; c->read->handler(c->read); + } + + if (cycle->free_connection_n == 0 && c && c->reusable) { + + /* + * if no connections were freed, try to reuse the last + * connection again: this should free it as long as + * previous reuse moved it to lingering close + */ + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0, + "reusing connection again"); + + c->close = 1; + c->read->handler(c->read); } } diff -Nru nginx-1.18.0/src/core/ngx_connection.h nginx-1.20.1/src/core/ngx_connection.h --- nginx-1.18.0/src/core/ngx_connection.h 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/core/ngx_connection.h 2021-05-25 12:35:38.000000000 +0000 @@ -45,8 +45,6 @@ size_t pool_size; /* should be here because of the AcceptEx() preread */ size_t post_accept_buffer_size; - /* should be here because of the deferred accept */ - ngx_msec_t post_accept_timeout; ngx_listening_t *previous; ngx_connection_t *connection; @@ -164,6 +162,7 @@ ngx_atomic_uint_t number; + ngx_msec_t start_time; ngx_uint_t requests; unsigned buffered:8; diff -Nru nginx-1.18.0/src/core/ngx_cycle.c nginx-1.20.1/src/core/ngx_cycle.c --- nginx-1.18.0/src/core/ngx_cycle.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/core/ngx_cycle.c 2021-05-25 12:35:38.000000000 +0000 @@ -96,6 +96,15 @@ return NULL; } + cycle->error_log.len = old_cycle->error_log.len; + cycle->error_log.data = ngx_pnalloc(pool, old_cycle->error_log.len + 1); + if (cycle->error_log.data == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + ngx_cpystrn(cycle->error_log.data, old_cycle->error_log.data, + old_cycle->error_log.len + 1); + cycle->conf_file.len = old_cycle->conf_file.len; cycle->conf_file.data = ngx_pnalloc(pool, old_cycle->conf_file.len + 1); if (cycle->conf_file.data == NULL) { @@ -520,6 +529,7 @@ == NGX_OK) { nls[n].fd = ls[i].fd; + nls[n].inherited = ls[i].inherited; nls[n].previous = &ls[i]; ls[i].remain = 1; @@ -1008,6 +1018,7 @@ ngx_create_pidfile(ngx_str_t *name, ngx_log_t *log) { size_t len; + ngx_int_t rc; ngx_uint_t create; ngx_file_t file; u_char pid[NGX_INT64_LEN + 2]; @@ -1032,11 +1043,13 @@ return NGX_ERROR; } + rc = NGX_OK; + if (!ngx_test_config) { len = ngx_snprintf(pid, NGX_INT64_LEN + 2, "%P%N", ngx_pid) - pid; if (ngx_write_file(&file, pid, len, 0) == NGX_ERROR) { - return NGX_ERROR; + rc = NGX_ERROR; } } @@ -1045,7 +1058,7 @@ ngx_close_file_n " \"%s\" failed", file.name.data); } - return NGX_OK; + return rc; } diff -Nru nginx-1.18.0/src/core/ngx_cycle.h nginx-1.20.1/src/core/ngx_cycle.h --- nginx-1.18.0/src/core/ngx_cycle.h 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/core/ngx_cycle.h 2021-05-25 12:35:38.000000000 +0000 @@ -55,6 +55,7 @@ ngx_queue_t reusable_connections_queue; ngx_uint_t reusable_connections_n; + time_t connections_reuse_time; ngx_array_t listening; ngx_array_t paths; @@ -79,6 +80,7 @@ ngx_str_t conf_param; ngx_str_t conf_prefix; ngx_str_t prefix; + ngx_str_t error_log; ngx_str_t lock_file; ngx_str_t hostname; }; diff -Nru nginx-1.18.0/src/core/ngx_log.c nginx-1.20.1/src/core/ngx_log.c --- nginx-1.18.0/src/core/ngx_log.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/core/ngx_log.c 2021-05-25 12:35:38.000000000 +0000 @@ -315,7 +315,7 @@ ngx_log_t * -ngx_log_init(u_char *prefix) +ngx_log_init(u_char *prefix, u_char *error_log) { u_char *p, *name; size_t nlen, plen; @@ -323,13 +323,11 @@ ngx_log.file = &ngx_log_file; ngx_log.log_level = NGX_LOG_NOTICE; - name = (u_char *) NGX_ERROR_LOG_PATH; - - /* - * we use ngx_strlen() here since BCC warns about - * condition is always false and unreachable code - */ + if (error_log == NULL) { + error_log = (u_char *) NGX_ERROR_LOG_PATH; + } + name = error_log; nlen = ngx_strlen(name); if (nlen == 0) { @@ -369,7 +367,7 @@ *p++ = '/'; } - ngx_cpystrn(p, (u_char *) NGX_ERROR_LOG_PATH, nlen + 1); + ngx_cpystrn(p, error_log, nlen + 1); p = name; } @@ -403,8 +401,7 @@ ngx_int_t ngx_log_open_default(ngx_cycle_t *cycle) { - ngx_log_t *log; - static ngx_str_t error_log = ngx_string(NGX_ERROR_LOG_PATH); + ngx_log_t *log; if (ngx_log_get_file_log(&cycle->new_log) != NULL) { return NGX_OK; @@ -425,7 +422,7 @@ log->log_level = NGX_LOG_ERR; - log->file = ngx_conf_open_file(cycle, &error_log); + log->file = ngx_conf_open_file(cycle, &cycle->error_log); if (log->file == NULL) { return NGX_ERROR; } diff -Nru nginx-1.18.0/src/core/ngx_log.h nginx-1.20.1/src/core/ngx_log.h --- nginx-1.18.0/src/core/ngx_log.h 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/core/ngx_log.h 2021-05-25 12:35:38.000000000 +0000 @@ -228,7 +228,7 @@ /*********************************/ -ngx_log_t *ngx_log_init(u_char *prefix); +ngx_log_t *ngx_log_init(u_char *prefix, u_char *error_log); void ngx_cdecl ngx_log_abort(ngx_err_t err, const char *fmt, ...); void ngx_cdecl ngx_log_stderr(ngx_err_t err, const char *fmt, ...); u_char *ngx_log_errno(u_char *buf, u_char *last, ngx_err_t err); diff -Nru nginx-1.18.0/src/core/ngx_resolver.c nginx-1.20.1/src/core/ngx_resolver.c --- nginx-1.18.0/src/core/ngx_resolver.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/core/ngx_resolver.c 2021-05-25 12:35:38.000000000 +0000 @@ -1563,13 +1563,28 @@ do { n = ngx_udp_recv(c, buf, NGX_RESOLVER_UDP_SIZE); - if (n < 0) { - return; + if (n == NGX_AGAIN) { + break; + } + + if (n == NGX_ERROR) { + goto failed; } ngx_resolver_process_response(rec->resolver, buf, n, 0); } while (rev->ready); + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + goto failed; + } + + return; + +failed: + + ngx_close_connection(rec->udp); + rec->udp = NULL; } @@ -1783,6 +1798,12 @@ i = sizeof(ngx_resolver_hdr_t); while (i < (ngx_uint_t) n) { + + if (buf[i] & 0xc0) { + err = "unexpected compression pointer in DNS response"; + goto done; + } + if (buf[i] == '\0') { goto found; } @@ -1918,7 +1939,7 @@ if (rn == NULL) { ngx_log_error(r->log_level, r->log, 0, - "unexpected response for %V", &name); + "unexpected DNS response for %V", &name); ngx_resolver_free(r, name.data); goto failed; } @@ -1930,7 +1951,7 @@ if (rn->query6 == NULL || rn->naddrs6 != (u_short) -1) { ngx_log_error(r->log_level, r->log, 0, - "unexpected response for %V", &name); + "unexpected DNS response for %V", &name); ngx_resolver_free(r, name.data); goto failed; } @@ -1949,7 +1970,7 @@ if (rn->query == NULL || rn->naddrs != (u_short) -1) { ngx_log_error(r->log_level, r->log, 0, - "unexpected response for %V", &name); + "unexpected DNS response for %V", &name); ngx_resolver_free(r, name.data); goto failed; } @@ -1964,7 +1985,7 @@ if (ident != qident) { ngx_log_error(r->log_level, r->log, 0, - "wrong ident %ui response for %V, expect %ui", + "wrong ident %ui in DNS response for %V, expect %ui", ident, &name, qident); ngx_resolver_free(r, name.data); goto failed; @@ -2149,7 +2170,7 @@ if (class != 1) { ngx_log_error(r->log_level, r->log, 0, - "unexpected RR class %ui", class); + "unexpected RR class %ui in DNS response", class); goto failed; } @@ -2218,7 +2239,7 @@ default: ngx_log_error(r->log_level, r->log, 0, - "unexpected RR type %ui", type); + "unexpected RR type %ui in DNS response", type); } i += len; @@ -2567,7 +2588,7 @@ if (rn == NULL || rn->query == NULL) { ngx_log_error(r->log_level, r->log, 0, - "unexpected response for %V", &name); + "unexpected DNS response for %V", &name); ngx_resolver_free(r, name.data); goto failed; } @@ -2581,7 +2602,7 @@ if (ident != qident) { ngx_log_error(r->log_level, r->log, 0, - "wrong ident %ui response for %V, expect %ui", + "wrong ident %ui in DNS response for %V, expect %ui", ident, &name, qident); ngx_resolver_free(r, name.data); goto failed; @@ -2691,7 +2712,7 @@ if (class != 1) { ngx_log_error(r->log_level, r->log, 0, - "unexpected RR class %ui", class); + "unexpected RR class %ui in DNS response", class); goto failed; } @@ -2734,7 +2755,7 @@ default: ngx_log_error(r->log_level, r->log, 0, - "unexpected RR type %ui", type); + "unexpected RR type %ui in DNS response", type); } i += len; @@ -3165,7 +3186,7 @@ if (rn == NULL || rn->query == NULL) { ngx_log_error(r->log_level, r->log, 0, - "unexpected response for %V", &name); + "unexpected DNS response for %V", &name); ngx_resolver_free(r, name.data); goto failed; } @@ -3174,7 +3195,7 @@ if (ident != qident) { ngx_log_error(r->log_level, r->log, 0, - "wrong ident %ui response for %V, expect %ui", + "wrong ident %ui in DNS response for %V, expect %ui", ident, &name, qident); ngx_resolver_free(r, name.data); goto failed; @@ -3256,7 +3277,7 @@ if (class != 1) { ngx_log_error(r->log_level, r->log, 0, - "unexpected RR class %ui", class); + "unexpected RR class %ui in DNS response", class); goto failed; } @@ -3283,7 +3304,7 @@ default: ngx_log_error(r->log_level, r->log, 0, - "unexpected RR type %ui", type); + "unexpected RR type %ui in DNS response", type); } i += len; @@ -3924,11 +3945,11 @@ { char *err; u_char *p, *dst; - ssize_t len; + size_t len; ngx_uint_t i, n; p = src; - len = -1; + len = 0; /* * compression pointers allow to create endless loop, so we set limit; @@ -3943,6 +3964,16 @@ } if (n & 0xc0) { + if ((n & 0xc0) != 0xc0) { + err = "invalid label type in DNS response"; + goto invalid; + } + + if (p >= last) { + err = "name is out of DNS response"; + goto invalid; + } + n = ((n & 0x3f) << 8) + *p; p = &buf[n]; @@ -3952,12 +3983,12 @@ } if (p >= last) { - err = "name is out of response"; + err = "name is out of DNS response"; goto invalid; } } - err = "compression pointers loop"; + err = "compression pointers loop in DNS response"; invalid: @@ -3971,7 +4002,7 @@ return NGX_OK; } - if (len == -1) { + if (len == 0) { ngx_str_null(name); return NGX_OK; } @@ -3983,30 +4014,23 @@ name->data = dst; - n = *src++; - for ( ;; ) { + n = *src++; + + if (n == 0) { + name->len = dst - name->data - 1; + return NGX_OK; + } + if (n & 0xc0) { n = ((n & 0x3f) << 8) + *src; src = &buf[n]; - n = *src++; - } else { ngx_strlow(dst, src, n); dst += n; src += n; - - n = *src++; - - if (n != 0) { - *dst++ = '.'; - } - } - - if (n == 0) { - name->len = dst - name->data; - return NGX_OK; + *dst++ = '.'; } } } @@ -4444,6 +4468,8 @@ c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1); + c->start_time = ngx_current_msec; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, &rec->log, 0, "connect to %V, fd:%d #%uA", &rec->server, s, c->number); @@ -4530,6 +4556,8 @@ c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1); + c->start_time = ngx_current_msec; + if (ngx_add_conn) { if (ngx_add_conn(c) == NGX_ERROR) { goto failed; diff -Nru nginx-1.18.0/src/core/ngx_string.c nginx-1.20.1/src/core/ngx_string.c --- nginx-1.18.0/src/core/ngx_string.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/core/ngx_string.c 2021-05-25 12:35:38.000000000 +0000 @@ -11,6 +11,8 @@ static u_char *ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64, u_char zero, ngx_uint_t hexadecimal, ngx_uint_t width); +static u_char *ngx_sprintf_str(u_char *buf, u_char *last, u_char *src, + size_t len, ngx_uint_t hexadecimal); static void ngx_encode_base64_internal(ngx_str_t *dst, ngx_str_t *src, const u_char *basis, ngx_uint_t padding); static ngx_int_t ngx_decode_base64_internal(ngx_str_t *dst, ngx_str_t *src, @@ -101,10 +103,10 @@ * %M ngx_msec_t * %r rlim_t * %p void * - * %V ngx_str_t * - * %v ngx_variable_value_t * - * %s null-terminated string - * %*s length and string + * %[x|X]V ngx_str_t * + * %[x|X]v ngx_variable_value_t * + * %[x|X]s null-terminated string + * %*[x|X]s length and string * %Z '\0' * %N '\n' * %c char @@ -165,7 +167,7 @@ u_char *p, zero; int d; double f; - size_t len, slen; + size_t slen; int64_t i64; uint64_t ui64, frac; ngx_msec_t ms; @@ -250,8 +252,7 @@ case 'V': v = va_arg(args, ngx_str_t *); - len = ngx_min(((size_t) (last - buf)), v->len); - buf = ngx_cpymem(buf, v->data, len); + buf = ngx_sprintf_str(buf, last, v->data, v->len, hex); fmt++; continue; @@ -259,8 +260,7 @@ case 'v': vv = va_arg(args, ngx_variable_value_t *); - len = ngx_min(((size_t) (last - buf)), vv->len); - buf = ngx_cpymem(buf, vv->data, len); + buf = ngx_sprintf_str(buf, last, vv->data, vv->len, hex); fmt++; continue; @@ -268,16 +268,7 @@ case 's': p = va_arg(args, u_char *); - if (slen == (size_t) -1) { - while (*p && buf < last) { - *buf++ = *p++; - } - - } else { - len = ngx_min(((size_t) (last - buf)), slen); - buf = ngx_cpymem(buf, p, len); - } - + buf = ngx_sprintf_str(buf, last, p, slen, hex); fmt++; continue; @@ -576,6 +567,64 @@ } +static u_char * +ngx_sprintf_str(u_char *buf, u_char *last, u_char *src, size_t len, + ngx_uint_t hexadecimal) +{ + static u_char hex[] = "0123456789abcdef"; + static u_char HEX[] = "0123456789ABCDEF"; + + if (hexadecimal == 0) { + + if (len == (size_t) -1) { + while (*src && buf < last) { + *buf++ = *src++; + } + + } else { + len = ngx_min((size_t) (last - buf), len); + buf = ngx_cpymem(buf, src, len); + } + + } else if (hexadecimal == 1) { + + if (len == (size_t) -1) { + + while (*src && buf < last - 1) { + *buf++ = hex[*src >> 4]; + *buf++ = hex[*src++ & 0xf]; + } + + } else { + + while (len-- && buf < last - 1) { + *buf++ = hex[*src >> 4]; + *buf++ = hex[*src++ & 0xf]; + } + } + + } else { /* hexadecimal == 2 */ + + if (len == (size_t) -1) { + + while (*src && buf < last - 1) { + *buf++ = HEX[*src >> 4]; + *buf++ = HEX[*src++ & 0xf]; + } + + } else { + + while (len-- && buf < last - 1) { + *buf++ = HEX[*src >> 4]; + *buf++ = HEX[*src++ & 0xf]; + } + } + } + + return buf; +} + + /* * We use ngx_strcasecmp()/ngx_strncasecmp() for 7-bit ASCII strings only, * and implement our own ngx_strcasecmp()/ngx_strncasecmp() diff -Nru nginx-1.18.0/src/event/modules/ngx_eventport_module.c nginx-1.20.1/src/event/modules/ngx_eventport_module.c --- nginx-1.18.0/src/event/modules/ngx_eventport_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/event/modules/ngx_eventport_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -399,7 +399,7 @@ return NGX_ERROR; } - } else { + } else if (ev->active) { ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0, "eventport del event: fd:%d", c->fd); diff -Nru nginx-1.18.0/src/event/ngx_event_accept.c nginx-1.20.1/src/event/ngx_event_accept.c --- nginx-1.18.0/src/event/ngx_event_accept.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/event/ngx_event_accept.c 2021-05-25 12:35:38.000000000 +0000 @@ -256,6 +256,8 @@ c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1); + c->start_time = ngx_current_msec; + #if (NGX_STAT_STUB) (void) ngx_atomic_fetch_add(ngx_stat_handled, 1); #endif diff -Nru nginx-1.18.0/src/event/ngx_event.c nginx-1.20.1/src/event/ngx_event.c --- nginx-1.18.0/src/event/ngx_event.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/event/ngx_event.c 2021-05-25 12:35:38.000000000 +0000 @@ -257,9 +257,7 @@ ngx_shmtx_unlock(&ngx_accept_mutex); } - if (delta) { - ngx_event_expire_timers(); - } + ngx_event_expire_timers(); ngx_event_process_posted(cycle, &ngx_posted_events); } @@ -318,7 +316,7 @@ return NGX_OK; } - if (rev->oneshot && !rev->ready) { + if (rev->oneshot && rev->ready) { if (ngx_del_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { return NGX_ERROR; } diff -Nru nginx-1.18.0/src/event/ngx_event_connect.c nginx-1.20.1/src/event/ngx_event_connect.c --- nginx-1.18.0/src/event/ngx_event_connect.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/event/ngx_event_connect.c 2021-05-25 12:35:38.000000000 +0000 @@ -193,6 +193,8 @@ c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1); + c->start_time = ngx_current_msec; + if (ngx_add_conn) { if (ngx_add_conn(c) == NGX_ERROR) { goto failed; diff -Nru nginx-1.18.0/src/event/ngx_event_openssl.c nginx-1.20.1/src/event/ngx_event_openssl.c --- nginx-1.18.0/src/event/ngx_event_openssl.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/event/ngx_event_openssl.c 2021-05-25 12:35:38.000000000 +0000 @@ -83,7 +83,7 @@ #if OPENSSL_VERSION_NUMBER > 0x10100000L const #endif - ASN1_TIME *asn1time); + ASN1_TIME *asn1time, ngx_log_t *log); static void *ngx_openssl_create_conf(ngx_cycle_t *cycle); static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -130,6 +130,7 @@ int ngx_ssl_server_conf_index; int ngx_ssl_session_cache_index; int ngx_ssl_session_ticket_keys_index; +int ngx_ssl_ocsp_index; int ngx_ssl_certificate_index; int ngx_ssl_next_certificate_index; int ngx_ssl_certificate_name_index; @@ -213,6 +214,13 @@ return NGX_ERROR; } + ngx_ssl_ocsp_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); + if (ngx_ssl_ocsp_index == -1) { + ngx_ssl_error(NGX_LOG_ALERT, log, 0, + "SSL_CTX_get_ex_new_index() failed"); + return NGX_ERROR; + } + ngx_ssl_certificate_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); if (ngx_ssl_certificate_index == -1) { @@ -912,6 +920,9 @@ ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_int_t depth) { + SSL_CTX_set_verify(ssl->ctx, SSL_CTX_get_verify_mode(ssl->ctx), + ngx_ssl_verify_callback); + SSL_CTX_set_verify_depth(ssl->ctx, depth); if (cert->len == 0) { @@ -1003,26 +1014,52 @@ c = ngx_ssl_get_connection(ssl_conn); + if (!(c->log->log_level & NGX_LOG_DEBUG_EVENT)) { + return 1; + } + cert = X509_STORE_CTX_get_current_cert(x509_store); err = X509_STORE_CTX_get_error(x509_store); depth = X509_STORE_CTX_get_error_depth(x509_store); sname = X509_get_subject_name(cert); - subject = sname ? X509_NAME_oneline(sname, NULL, 0) : "(none)"; + + if (sname) { + subject = X509_NAME_oneline(sname, NULL, 0); + if (subject == NULL) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, + "X509_NAME_oneline() failed"); + } + + } else { + subject = NULL; + } iname = X509_get_issuer_name(cert); - issuer = iname ? X509_NAME_oneline(iname, NULL, 0) : "(none)"; + + if (iname) { + issuer = X509_NAME_oneline(iname, NULL, 0); + if (issuer == NULL) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, + "X509_NAME_oneline() failed"); + } + + } else { + issuer = NULL; + } ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0, "verify:%d, error:%d, depth:%d, " "subject:\"%s\", issuer:\"%s\"", - ok, err, depth, subject, issuer); + ok, err, depth, + subject ? subject : "(none)", + issuer ? issuer : "(none)"); - if (sname) { + if (subject) { OPENSSL_free(subject); } - if (iname) { + if (issuer) { OPENSSL_free(issuer); } #endif @@ -1460,6 +1497,78 @@ ngx_int_t +ngx_ssl_conf_commands(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *commands) +{ + if (commands == NULL) { + return NGX_OK; + } + +#ifdef SSL_CONF_FLAG_FILE + { + int type; + u_char *key, *value; + ngx_uint_t i; + ngx_keyval_t *cmd; + SSL_CONF_CTX *cctx; + + cctx = SSL_CONF_CTX_new(); + if (cctx == NULL) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CONF_CTX_new() failed"); + return NGX_ERROR; + } + + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SERVER); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CLIENT); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CERTIFICATE); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SHOW_ERRORS); + + SSL_CONF_CTX_set_ssl_ctx(cctx, ssl->ctx); + + cmd = commands->elts; + for (i = 0; i < commands->nelts; i++) { + + key = cmd[i].key.data; + type = SSL_CONF_cmd_value_type(cctx, (char *) key); + + if (type == SSL_CONF_TYPE_FILE || type == SSL_CONF_TYPE_DIR) { + if (ngx_conf_full_name(cf->cycle, &cmd[i].value, 1) != NGX_OK) { + SSL_CONF_CTX_free(cctx); + return NGX_ERROR; + } + } + + value = cmd[i].value.data; + + if (SSL_CONF_cmd(cctx, (char *) key, (char *) value) <= 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CONF_cmd(\"%s\", \"%s\") failed", key, value); + SSL_CONF_CTX_free(cctx); + return NGX_ERROR; + } + } + + if (SSL_CONF_CTX_finish(cctx) != 1) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CONF_finish() failed"); + SSL_CONF_CTX_free(cctx); + return NGX_ERROR; + } + + SSL_CONF_CTX_free(cctx); + + return NGX_OK; + } +#else + ngx_log_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CONF_cmd() is not available on this platform"); + return NGX_ERROR; +#endif +} + + +ngx_int_t ngx_ssl_client_session_cache(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_uint_t enable) { if (!enable) { @@ -1594,6 +1703,7 @@ { int n, sslerr; ngx_err_t err; + ngx_int_t rc; #ifdef SSL_READ_EARLY_DATA_SUCCESS if (c->ssl->try_early_data) { @@ -1601,6 +1711,10 @@ } #endif + if (c->ssl->in_ocsp) { + return ngx_ssl_ocsp_validate(c); + } + ngx_ssl_clear_error(c->log); n = SSL_do_handshake(c->ssl->connection); @@ -1621,8 +1735,6 @@ ngx_ssl_handshake_log(c); #endif - c->ssl->handshaked = 1; - c->recv = ngx_ssl_recv; c->send = ngx_ssl_write; c->recv_chain = ngx_ssl_recv_chain; @@ -1641,6 +1753,20 @@ #endif #endif + rc = ngx_ssl_ocsp_validate(c); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_AGAIN) { + c->read->handler = ngx_ssl_handshake_handler; + c->write->handler = ngx_ssl_handshake_handler; + return NGX_AGAIN; + } + + c->ssl->handshaked = 1; + return NGX_OK; } @@ -1693,6 +1819,13 @@ return NGX_ERROR; } + if (c->ssl->handshake_rejected) { + ngx_connection_error(c, err, "handshake rejected"); + ERR_clear_error(); + + return NGX_ERROR; + } + c->read->error = 1; ngx_ssl_connection_error(c, sslerr, err, "SSL_do_handshake() failed"); @@ -1710,6 +1843,7 @@ u_char buf; size_t readbytes; ngx_err_t err; + ngx_int_t rc; ngx_ssl_clear_error(c->log); @@ -1744,7 +1878,6 @@ c->ssl->early_buf = buf; c->ssl->early_preread = 1; - c->ssl->handshaked = 1; c->ssl->in_early = 1; c->recv = ngx_ssl_recv; @@ -1752,6 +1885,20 @@ c->recv_chain = ngx_ssl_recv_chain; c->send_chain = ngx_ssl_send_chain; + rc = ngx_ssl_ocsp_validate(c); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_AGAIN) { + c->read->handler = ngx_ssl_handshake_handler; + c->write->handler = ngx_ssl_handshake_handler; + return NGX_AGAIN; + } + + c->ssl->handshaked = 1; + return NGX_OK; } @@ -1827,6 +1974,10 @@ #endif SSL_CIPHER *cipher; + if (!(c->log->log_level & NGX_LOG_DEBUG_EVENT)) { + return; + } + cipher = SSL_get_current_cipher(c->ssl->connection); if (cipher) { @@ -2531,6 +2682,18 @@ sslerr = SSL_get_error(c->ssl->connection, n); + if (sslerr == SSL_ERROR_ZERO_RETURN) { + + /* + * OpenSSL 1.1.1 fails to return SSL_ERROR_SYSCALL if an error + * happens during SSL_write() after close_notify alert from the + * peer, and returns SSL_ERROR_ZERO_RETURN instead, + * https://git.openssl.org/?p=openssl.git;a=commitdiff;h=8051ab2 + */ + + sslerr = SSL_ERROR_SYSCALL; + } + err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr); @@ -2732,8 +2895,11 @@ ngx_int_t ngx_ssl_shutdown(ngx_connection_t *c) { - int n, sslerr, mode; - ngx_err_t err; + int n, sslerr, mode; + ngx_err_t err; + ngx_uint_t tries; + + ngx_ssl_ocsp_cleanup(c); if (SSL_in_init(c->ssl->connection)) { /* @@ -2744,11 +2910,12 @@ SSL_free(c->ssl->connection); c->ssl = NULL; + c->recv = ngx_recv; return NGX_OK; } - if (c->timedout) { + if (c->timedout || c->error || c->buffered) { mode = SSL_RECEIVED_SHUTDOWN|SSL_SENT_SHUTDOWN; SSL_set_quiet_shutdown(c->ssl->connection, 1); @@ -2772,55 +2939,81 @@ ngx_ssl_clear_error(c->log); - n = SSL_shutdown(c->ssl->connection); + tries = 2; - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_shutdown: %d", n); + for ( ;; ) { - sslerr = 0; + /* + * For bidirectional shutdown, SSL_shutdown() needs to be called + * twice: first call sends the "close notify" alert and returns 0, + * second call waits for the peer's "close notify" alert. + */ - /* before 0.9.8m SSL_shutdown() returned 0 instead of -1 on errors */ + n = SSL_shutdown(c->ssl->connection); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_shutdown: %d", n); + + if (n == 1) { + SSL_free(c->ssl->connection); + c->ssl = NULL; + c->recv = ngx_recv; + + return NGX_OK; + } + + if (n == 0 && tries-- > 1) { + continue; + } + + /* before 0.9.8m SSL_shutdown() returned 0 instead of -1 on errors */ - if (n != 1 && ERR_peek_error()) { sslerr = SSL_get_error(c->ssl->connection, n); ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr); - } - if (n == 1 || sslerr == 0 || sslerr == SSL_ERROR_ZERO_RETURN) { - SSL_free(c->ssl->connection); - c->ssl = NULL; + if (sslerr == SSL_ERROR_WANT_READ || sslerr == SSL_ERROR_WANT_WRITE) { + c->read->handler = ngx_ssl_shutdown_handler; + c->write->handler = ngx_ssl_shutdown_handler; - return NGX_OK; - } + if (sslerr == SSL_ERROR_WANT_READ) { + c->read->ready = 0; - if (sslerr == SSL_ERROR_WANT_READ || sslerr == SSL_ERROR_WANT_WRITE) { - c->read->handler = ngx_ssl_shutdown_handler; - c->write->handler = ngx_ssl_shutdown_handler; + } else { + c->write->ready = 0; + } - if (ngx_handle_read_event(c->read, 0) != NGX_OK) { - return NGX_ERROR; - } + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + return NGX_ERROR; + } - if (ngx_handle_write_event(c->write, 0) != NGX_OK) { - return NGX_ERROR; - } + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { + return NGX_ERROR; + } + + ngx_add_timer(c->read, 3000); - if (sslerr == SSL_ERROR_WANT_READ) { - ngx_add_timer(c->read, 30000); + return NGX_AGAIN; } - return NGX_AGAIN; - } + if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) { + SSL_free(c->ssl->connection); + c->ssl = NULL; + c->recv = ngx_recv; - err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; + return NGX_OK; + } - ngx_ssl_connection_error(c, sslerr, err, "SSL_shutdown() failed"); + err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; - SSL_free(c->ssl->connection); - c->ssl = NULL; + ngx_ssl_connection_error(c, sslerr, err, "SSL_shutdown() failed"); - return NGX_ERROR; + SSL_free(c->ssl->connection); + c->ssl = NULL; + c->recv = ngx_recv; + + return NGX_ERROR; + } } @@ -3202,8 +3395,9 @@ } } - if (SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index) == NULL) { - + if (SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index) == NULL + && certificates != NULL) + { /* * If certificates are loaded dynamically, we use certificate * names as specified in the configuration (with variables). @@ -3897,9 +4091,6 @@ ngx_ssl_session_ticket_key_t *key; const EVP_MD *digest; const EVP_CIPHER *cipher; -#if (NGX_DEBUG) - u_char buf[32]; -#endif c = ngx_ssl_get_connection(ssl_conn); ssl_ctx = c->ssl->session_ctx; @@ -3921,8 +4112,8 @@ /* encrypt session ticket */ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, - "ssl session ticket encrypt, key: \"%*s\" (%s session)", - ngx_hex_dump(buf, key[0].name, 16) - buf, buf, + "ssl session ticket encrypt, key: \"%*xs\" (%s session)", + (size_t) 16, key[0].name, SSL_session_reused(ssl_conn) ? "reused" : "new"); if (key[0].size == 48) { @@ -3968,17 +4159,16 @@ } ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, - "ssl session ticket decrypt, key: \"%*s\" not found", - ngx_hex_dump(buf, name, 16) - buf, buf); + "ssl session ticket decrypt, key: \"%*xs\" not found", + (size_t) 16, name); return 0; found: ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, - "ssl session ticket decrypt, key: \"%*s\"%s", - ngx_hex_dump(buf, key[i].name, 16) - buf, buf, - (i == 0) ? " (default)" : ""); + "ssl session ticket decrypt, key: \"%*xs\"%s", + (size_t) 16, key[i].name, (i == 0) ? " (default)" : ""); if (key[i].size == 48) { cipher = EVP_aes_128_cbc(); @@ -4635,11 +4825,13 @@ bio = BIO_new(BIO_s_mem()); if (bio == NULL) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "BIO_new() failed"); X509_free(cert); return NGX_ERROR; } if (X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253) < 0) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "X509_NAME_print_ex() failed"); goto failed; } @@ -4687,11 +4879,13 @@ bio = BIO_new(BIO_s_mem()); if (bio == NULL) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "BIO_new() failed"); X509_free(cert); return NGX_ERROR; } if (X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253) < 0) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "X509_NAME_print_ex() failed"); goto failed; } @@ -4740,6 +4934,11 @@ } p = X509_NAME_oneline(name, NULL, 0); + if (p == NULL) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "X509_NAME_oneline() failed"); + X509_free(cert); + return NGX_ERROR; + } for (len = 0; p[len]; len++) { /* void */ } @@ -4783,6 +4982,11 @@ } p = X509_NAME_oneline(name, NULL, 0); + if (p == NULL) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "X509_NAME_oneline() failed"); + X509_free(cert); + return NGX_ERROR; + } for (len = 0; p[len]; len++) { /* void */ } @@ -4819,6 +5023,7 @@ bio = BIO_new(BIO_s_mem()); if (bio == NULL) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "BIO_new() failed"); X509_free(cert); return NGX_ERROR; } @@ -4857,6 +5062,7 @@ } if (!X509_digest(cert, EVP_sha1(), buf, &len)) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "X509_digest() failed"); X509_free(cert); return NGX_ERROR; } @@ -4894,11 +5100,14 @@ rc = SSL_get_verify_result(c->ssl->connection); if (rc == X509_V_OK) { - ngx_str_set(s, "SUCCESS"); - return NGX_OK; - } + if (ngx_ssl_ocsp_get_status(c, &str) == NGX_OK) { + ngx_str_set(s, "SUCCESS"); + return NGX_OK; + } - str = X509_verify_cert_error_string(rc); + } else { + str = X509_verify_cert_error_string(rc); + } s->data = ngx_pnalloc(pool, sizeof("FAILED:") - 1 + ngx_strlen(str)); if (s->data == NULL) { @@ -4927,6 +5136,7 @@ bio = BIO_new(BIO_s_mem()); if (bio == NULL) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "BIO_new() failed"); X509_free(cert); return NGX_ERROR; } @@ -4971,6 +5181,7 @@ bio = BIO_new(BIO_s_mem()); if (bio == NULL) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "BIO_new() failed"); X509_free(cert); return NGX_ERROR; } @@ -5013,9 +5224,9 @@ } #if OPENSSL_VERSION_NUMBER > 0x10100000L - end = ngx_ssl_parse_time(X509_get0_notAfter(cert)); + end = ngx_ssl_parse_time(X509_get0_notAfter(cert), c->log); #else - end = ngx_ssl_parse_time(X509_get_notAfter(cert)); + end = ngx_ssl_parse_time(X509_get_notAfter(cert), c->log); #endif if (end == (time_t) NGX_ERROR) { @@ -5050,7 +5261,7 @@ #if OPENSSL_VERSION_NUMBER > 0x10100000L const #endif - ASN1_TIME *asn1time) + ASN1_TIME *asn1time, ngx_log_t *log) { BIO *bio; char *value; @@ -5066,6 +5277,7 @@ bio = BIO_new(BIO_s_mem()); if (bio == NULL) { + ngx_ssl_error(NGX_LOG_ALERT, log, 0, "BIO_new() failed"); return NGX_ERROR; } diff -Nru nginx-1.18.0/src/event/ngx_event_openssl.h nginx-1.20.1/src/event/ngx_event_openssl.h --- nginx-1.18.0/src/event/ngx_event_openssl.h 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/event/ngx_event_openssl.h 2021-05-25 12:35:38.000000000 +0000 @@ -64,6 +64,9 @@ #endif +typedef struct ngx_ssl_ocsp_s ngx_ssl_ocsp_t; + + struct ngx_ssl_s { SSL_CTX *ctx; ngx_log_t *log; @@ -87,9 +90,12 @@ ngx_event_handler_pt saved_read_handler; ngx_event_handler_pt saved_write_handler; + ngx_ssl_ocsp_t *ocsp; + u_char early_buf; unsigned handshaked:1; + unsigned handshake_rejected:1; unsigned renegotiation:1; unsigned buffer:1; unsigned no_wait_shutdown:1; @@ -97,6 +103,7 @@ unsigned handshake_buffer_set:1; unsigned try_early_data:1; unsigned in_early:1; + unsigned in_ocsp:1; unsigned early_preread:1; unsigned write_blocked:1; }; @@ -180,6 +187,14 @@ ngx_str_t *file, ngx_str_t *responder, ngx_uint_t verify); ngx_int_t ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_resolver_t *resolver, ngx_msec_t resolver_timeout); +ngx_int_t ngx_ssl_ocsp(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder, + 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); RSA *ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export, int key_length); ngx_array_t *ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file); @@ -189,6 +204,9 @@ ngx_int_t ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name); ngx_int_t ngx_ssl_early_data(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_uint_t enable); +ngx_int_t ngx_ssl_conf_commands(ngx_conf_t *cf, ngx_ssl_t *ssl, + ngx_array_t *commands); + ngx_int_t ngx_ssl_client_session_cache(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_uint_t enable); ngx_int_t ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx, @@ -197,6 +215,7 @@ ngx_int_t ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *paths); ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data); + ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags); @@ -281,6 +300,7 @@ extern int ngx_ssl_server_conf_index; extern int ngx_ssl_session_cache_index; extern int ngx_ssl_session_ticket_keys_index; +extern int ngx_ssl_ocsp_index; extern int ngx_ssl_certificate_index; extern int ngx_ssl_next_certificate_index; extern int ngx_ssl_certificate_name_index; diff -Nru nginx-1.18.0/src/event/ngx_event_openssl_stapling.c nginx-1.20.1/src/event/ngx_event_openssl_stapling.c --- nginx-1.18.0/src/event/ngx_event_openssl_stapling.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/event/ngx_event_openssl_stapling.c 2021-05-25 12:35:38.000000000 +0000 @@ -22,6 +22,7 @@ ngx_msec_t resolver_timeout; ngx_addr_t *addrs; + ngx_uint_t naddrs; ngx_str_t host; ngx_str_t uri; in_port_t port; @@ -30,6 +31,7 @@ X509 *cert; X509 *issuer; + STACK_OF(X509) *chain; u_char *name; @@ -41,15 +43,66 @@ } ngx_ssl_stapling_t; +typedef struct { + ngx_addr_t *addrs; + ngx_uint_t naddrs; + + ngx_str_t host; + ngx_str_t uri; + in_port_t port; + ngx_uint_t depth; + + ngx_shm_zone_t *shm_zone; + + ngx_resolver_t *resolver; + ngx_msec_t resolver_timeout; +} ngx_ssl_ocsp_conf_t; + + +typedef struct { + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; + ngx_queue_t expire_queue; +} ngx_ssl_ocsp_cache_t; + + +typedef struct { + ngx_str_node_t node; + ngx_queue_t queue; + int status; + time_t valid; +} ngx_ssl_ocsp_cache_node_t; + + typedef struct ngx_ssl_ocsp_ctx_s ngx_ssl_ocsp_ctx_t; + +struct ngx_ssl_ocsp_s { + STACK_OF(X509) *certs; + ngx_uint_t ncert; + + int cert_status; + ngx_int_t status; + + ngx_ssl_ocsp_conf_t *conf; + ngx_ssl_ocsp_ctx_t *ctx; +}; + + struct ngx_ssl_ocsp_ctx_s { + SSL_CTX *ssl_ctx; + X509 *cert; X509 *issuer; + STACK_OF(X509) *chain; + + int status; + time_t valid; u_char *name; ngx_uint_t naddrs; + ngx_uint_t naddr; ngx_addr_t *addrs; ngx_str_t host; @@ -64,17 +117,20 @@ void (*handler)(ngx_ssl_ocsp_ctx_t *ctx); void *data; + ngx_str_t key; ngx_buf_t *request; ngx_buf_t *response; ngx_peer_connection_t peer; + ngx_shm_zone_t *shm_zone; + ngx_int_t (*process)(ngx_ssl_ocsp_ctx_t *ctx); ngx_uint_t state; ngx_uint_t code; ngx_uint_t count; - + ngx_uint_t flags; ngx_uint_t done; u_char *header_name_start; @@ -105,8 +161,14 @@ static void ngx_ssl_stapling_cleanup(void *data); -static ngx_ssl_ocsp_ctx_t *ngx_ssl_ocsp_start(void); +static void ngx_ssl_ocsp_validate_next(ngx_connection_t *c); +static void ngx_ssl_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx); +static ngx_int_t ngx_ssl_ocsp_responder(ngx_connection_t *c, + ngx_ssl_ocsp_ctx_t *ctx); + +static ngx_ssl_ocsp_ctx_t *ngx_ssl_ocsp_start(ngx_log_t *log); static void ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx); +static void ngx_ssl_ocsp_next(ngx_ssl_ocsp_ctx_t *ctx); static void ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx); static void ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve); static void ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx); @@ -120,6 +182,11 @@ static ngx_int_t ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx); static ngx_int_t ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx); static ngx_int_t ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx); +static ngx_int_t ngx_ssl_ocsp_verify(ngx_ssl_ocsp_ctx_t *ctx); + +static ngx_int_t ngx_ssl_ocsp_cache_lookup(ngx_ssl_ocsp_ctx_t *ctx); +static ngx_int_t ngx_ssl_ocsp_cache_store(ngx_ssl_ocsp_ctx_t *ctx); +static ngx_int_t ngx_ssl_ocsp_create_key(ngx_ssl_ocsp_ctx_t *ctx); static u_char *ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len); @@ -173,6 +240,18 @@ return NGX_ERROR; } +#ifdef SSL_CTRL_SELECT_CURRENT_CERT + /* OpenSSL 1.0.2+ */ + SSL_CTX_select_current_cert(ssl->ctx, cert); +#endif + +#ifdef SSL_CTRL_GET_EXTRA_CHAIN_CERTS + /* OpenSSL 1.0.1+ */ + SSL_CTX_get_extra_chain_certs(ssl->ctx, &staple->chain); +#else + staple->chain = ssl->ctx->extra_certs; +#endif + staple->ssl_ctx = ssl->ctx; staple->timeout = 60000; staple->verify = verify; @@ -289,29 +368,16 @@ X509 *cert, *issuer; X509_STORE *store; X509_STORE_CTX *store_ctx; - STACK_OF(X509) *chain; cert = staple->cert; -#ifdef SSL_CTRL_SELECT_CURRENT_CERT - /* OpenSSL 1.0.2+ */ - SSL_CTX_select_current_cert(ssl->ctx, cert); -#endif - -#ifdef SSL_CTRL_GET_EXTRA_CHAIN_CERTS - /* OpenSSL 1.0.1+ */ - SSL_CTX_get_extra_chain_certs(ssl->ctx, &chain); -#else - chain = ssl->ctx->extra_certs; -#endif - - n = sk_X509_num(chain); + n = sk_X509_num(staple->chain); ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0, "SSL get issuer: %d extra certs", n); for (i = 0; i < n; i++) { - issuer = sk_X509_value(chain, i); + issuer = sk_X509_value(staple->chain, i); if (X509_check_issued(issuer, cert) == X509_V_OK) { #if OPENSSL_VERSION_NUMBER >= 0x10100001L X509_up_ref(issuer); @@ -462,6 +528,7 @@ } staple->addrs = u.addrs; + staple->naddrs = u.naddrs; staple->host = u.host; staple->uri = u.uri; staple->port = u.port; @@ -559,16 +626,20 @@ staple->loading = 1; - ctx = ngx_ssl_ocsp_start(); + ctx = ngx_ssl_ocsp_start(ngx_cycle->log); if (ctx == NULL) { return; } + ctx->ssl_ctx = staple->ssl_ctx; ctx->cert = staple->cert; ctx->issuer = staple->issuer; + ctx->chain = staple->chain; ctx->name = staple->name; + ctx->flags = (staple->verify ? OCSP_TRUSTOTHER : OCSP_NOVERIFY); ctx->addrs = staple->addrs; + ctx->naddrs = staple->naddrs; ctx->host = staple->host; ctx->uri = staple->uri; ctx->port = staple->port; @@ -589,137 +660,27 @@ static void ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx) { - int n; - size_t len; - time_t now, valid; - ngx_str_t response; - X509_STORE *store; - const u_char *p; - STACK_OF(X509) *chain; - OCSP_CERTID *id; - OCSP_RESPONSE *ocsp; - OCSP_BASICRESP *basic; - ngx_ssl_stapling_t *staple; - ASN1_GENERALIZEDTIME *thisupdate, *nextupdate; + time_t now; + ngx_str_t response; + ngx_ssl_stapling_t *staple; staple = ctx->data; now = ngx_time(); - ocsp = NULL; - basic = NULL; - id = NULL; - if (ctx->code != 200) { - goto error; - } - - /* check the response */ - - len = ctx->response->last - ctx->response->pos; - p = ctx->response->pos; - - ocsp = d2i_OCSP_RESPONSE(NULL, &p, len); - if (ocsp == NULL) { - ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, - "d2i_OCSP_RESPONSE() failed"); - goto error; - } - - n = OCSP_response_status(ocsp); - - if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) { - ngx_log_error(NGX_LOG_ERR, ctx->log, 0, - "OCSP response not successful (%d: %s)", - n, OCSP_response_status_str(n)); - goto error; - } - - basic = OCSP_response_get1_basic(ocsp); - if (basic == NULL) { - ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, - "OCSP_response_get1_basic() failed"); - goto error; - } - - store = SSL_CTX_get_cert_store(staple->ssl_ctx); - if (store == NULL) { - ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0, - "SSL_CTX_get_cert_store() failed"); - goto error; - } - -#ifdef SSL_CTRL_SELECT_CURRENT_CERT - /* OpenSSL 1.0.2+ */ - SSL_CTX_select_current_cert(staple->ssl_ctx, ctx->cert); -#endif - -#ifdef SSL_CTRL_GET_EXTRA_CHAIN_CERTS - /* OpenSSL 1.0.1+ */ - SSL_CTX_get_extra_chain_certs(staple->ssl_ctx, &chain); -#else - chain = staple->ssl_ctx->extra_certs; -#endif - - if (OCSP_basic_verify(basic, chain, store, - staple->verify ? OCSP_TRUSTOTHER : OCSP_NOVERIFY) - != 1) - { - ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, - "OCSP_basic_verify() failed"); - goto error; - } - - id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer); - if (id == NULL) { - ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0, - "OCSP_cert_to_id() failed"); - goto error; - } - - if (OCSP_resp_find_status(basic, id, &n, NULL, NULL, - &thisupdate, &nextupdate) - != 1) - { - ngx_log_error(NGX_LOG_ERR, ctx->log, 0, - "certificate status not found in the OCSP response"); + if (ngx_ssl_ocsp_verify(ctx) != NGX_OK) { goto error; } - if (n != V_OCSP_CERTSTATUS_GOOD) { + if (ctx->status != V_OCSP_CERTSTATUS_GOOD) { ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "certificate status \"%s\" in the OCSP response", - OCSP_cert_status_str(n)); - goto error; - } - - if (OCSP_check_validity(thisupdate, nextupdate, 300, -1) != 1) { - ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, - "OCSP_check_validity() failed"); + OCSP_cert_status_str(ctx->status)); goto error; } - if (nextupdate) { - valid = ngx_ssl_stapling_time(nextupdate); - if (valid == (time_t) NGX_ERROR) { - ngx_log_error(NGX_LOG_ERR, ctx->log, 0, - "invalid nextUpdate time in certificate status"); - goto error; - } - - } else { - valid = NGX_MAX_TIME_T_VALUE; - } - - OCSP_CERTID_free(id); - OCSP_BASICRESP_free(basic); - OCSP_RESPONSE_free(ocsp); - - id = NULL; - basic = NULL; - ocsp = NULL; - /* copy the response to memory not in ctx->pool */ - response.len = len; + response.len = ctx->response->last - ctx->response->pos; response.data = ngx_alloc(response.len, ctx->log); if (response.data == NULL) { @@ -728,16 +689,12 @@ ngx_memcpy(response.data, ctx->response->pos, response.len); - ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0, - "ssl ocsp response, %s, %uz", - OCSP_cert_status_str(n), response.len); - if (staple->staple.data) { ngx_free(staple->staple.data); } staple->staple = response; - staple->valid = valid; + staple->valid = ctx->valid; /* * refresh before the response expires, @@ -745,7 +702,7 @@ */ staple->loading = 0; - staple->refresh = ngx_max(ngx_min(valid - 300, now + 3600), now + 300); + staple->refresh = ngx_max(ngx_min(ctx->valid - 300, now + 3600), now + 300); ngx_ssl_ocsp_done(ctx); return; @@ -755,18 +712,6 @@ staple->loading = 0; staple->refresh = now + 300; - if (id) { - OCSP_CERTID_free(id); - } - - if (basic) { - OCSP_BASICRESP_free(basic); - } - - if (ocsp) { - OCSP_RESPONSE_free(ocsp); - } - ngx_ssl_ocsp_done(ctx); } @@ -820,366 +765,905 @@ } -static ngx_ssl_ocsp_ctx_t * -ngx_ssl_ocsp_start(void) +ngx_int_t +ngx_ssl_ocsp(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder, + ngx_uint_t depth, ngx_shm_zone_t *shm_zone) { - ngx_log_t *log; - ngx_pool_t *pool; - ngx_ssl_ocsp_ctx_t *ctx; - - pool = ngx_create_pool(2048, ngx_cycle->log); - if (pool == NULL) { - return NULL; - } + ngx_url_t u; + ngx_ssl_ocsp_conf_t *ocf; - ctx = ngx_pcalloc(pool, sizeof(ngx_ssl_ocsp_ctx_t)); - if (ctx == NULL) { - ngx_destroy_pool(pool); - return NULL; + ocf = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_ocsp_conf_t)); + if (ocf == NULL) { + return NGX_ERROR; } - log = ngx_palloc(pool, sizeof(ngx_log_t)); - if (log == NULL) { - ngx_destroy_pool(pool); - return NULL; - } + ocf->depth = depth; + ocf->shm_zone = shm_zone; - ctx->pool = pool; + if (responder->len) { + ngx_memzero(&u, sizeof(ngx_url_t)); - *log = *ctx->pool->log; + u.url = *responder; + u.default_port = 80; + u.uri_part = 1; - ctx->pool->log = log; - ctx->log = log; + if (u.url.len > 7 + && ngx_strncasecmp(u.url.data, (u_char *) "http://", 7) == 0) + { + u.url.len -= 7; + u.url.data += 7; - log->handler = ngx_ssl_ocsp_log_error; - log->data = ctx; - log->action = "requesting certificate status"; + } else { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "invalid URL prefix in OCSP responder \"%V\" " + "in \"ssl_ocsp_responder\"", &u.url); + return NGX_ERROR; + } - return ctx; -} + if (ngx_parse_url(cf->pool, &u) != NGX_OK) { + if (u.err) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "%s in OCSP responder \"%V\" " + "in \"ssl_ocsp_responder\"", u.err, &u.url); + } + return NGX_ERROR; + } -static void -ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx) -{ - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, - "ssl ocsp done"); + ocf->addrs = u.addrs; + ocf->naddrs = u.naddrs; + ocf->host = u.host; + ocf->uri = u.uri; + ocf->port = u.port; + } - if (ctx->peer.connection) { - ngx_close_connection(ctx->peer.connection); + if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_ocsp_index, ocf) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_set_ex_data() failed"); + return NGX_ERROR; } - ngx_destroy_pool(ctx->pool); + return NGX_OK; } -static void -ngx_ssl_ocsp_error(ngx_ssl_ocsp_ctx_t *ctx) +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_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, - "ssl ocsp error"); + ngx_ssl_ocsp_conf_t *ocf; - ctx->code = 0; - ctx->handler(ctx); + ocf = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_ocsp_index); + ocf->resolver = resolver; + ocf->resolver_timeout = resolver_timeout; + + return NGX_OK; } -static void -ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx) +ngx_int_t +ngx_ssl_ocsp_validate(ngx_connection_t *c) { - ngx_resolver_ctx_t *resolve, temp; + X509 *cert; + SSL_CTX *ssl_ctx; + ngx_int_t rc; + X509_STORE *store; + X509_STORE_CTX *store_ctx; + STACK_OF(X509) *chain; + ngx_ssl_ocsp_t *ocsp; + ngx_ssl_ocsp_conf_t *ocf; - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, - "ssl ocsp request"); + if (c->ssl->in_ocsp) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + return NGX_ERROR; + } - if (ngx_ssl_ocsp_create_request(ctx) != NGX_OK) { - ngx_ssl_ocsp_error(ctx); - return; - } + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { + return NGX_ERROR; + } - if (ctx->resolver) { - /* resolve OCSP responder hostname */ + return NGX_AGAIN; + } - temp.name = ctx->host; + ssl_ctx = SSL_get_SSL_CTX(c->ssl->connection); - resolve = ngx_resolve_start(ctx->resolver, &temp); - if (resolve == NULL) { - ngx_ssl_ocsp_error(ctx); - return; - } + ocf = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_ocsp_index); + if (ocf == NULL) { + return NGX_OK; + } - if (resolve == NGX_NO_RESOLVER) { - ngx_log_error(NGX_LOG_WARN, ctx->log, 0, - "no resolver defined to resolve %V", &ctx->host); - goto connect; - } + if (SSL_get_verify_result(c->ssl->connection) != X509_V_OK) { + return NGX_OK; + } - resolve->name = ctx->host; - resolve->handler = ngx_ssl_ocsp_resolve_handler; - resolve->data = ctx; - resolve->timeout = ctx->resolver_timeout; - - if (ngx_resolve_name(resolve) != NGX_OK) { - ngx_ssl_ocsp_error(ctx); - return; - } + cert = SSL_get_peer_certificate(c->ssl->connection); + if (cert == NULL) { + return NGX_OK; + } - return; + ocsp = ngx_pcalloc(c->pool, sizeof(ngx_ssl_ocsp_t)); + if (ocsp == NULL) { + X509_free(cert); + return NGX_ERROR; } -connect: + c->ssl->ocsp = ocsp; - ngx_ssl_ocsp_connect(ctx); -} + ocsp->status = NGX_AGAIN; + ocsp->cert_status = V_OCSP_CERTSTATUS_GOOD; + ocsp->conf = ocf; +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined LIBRESSL_VERSION_NUMBER) -static void -ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve) -{ - ngx_ssl_ocsp_ctx_t *ctx = resolve->data; + ocsp->certs = SSL_get0_verified_chain(c->ssl->connection); - u_char *p; - size_t len; - socklen_t socklen; - ngx_uint_t i; - struct sockaddr *sockaddr; + if (ocsp->certs) { + ocsp->certs = X509_chain_up_ref(ocsp->certs); + if (ocsp->certs == NULL) { + X509_free(cert); + return NGX_ERROR; + } + } - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, - "ssl ocsp resolve handler"); +#endif - if (resolve->state) { - ngx_log_error(NGX_LOG_ERR, ctx->log, 0, - "%V could not be resolved (%i: %s)", - &resolve->name, resolve->state, - ngx_resolver_strerror(resolve->state)); - goto failed; - } + if (ocsp->certs == NULL) { + store = SSL_CTX_get_cert_store(ssl_ctx); + if (store == NULL) { + ngx_ssl_error(NGX_LOG_ERR, c->log, 0, + "SSL_CTX_get_cert_store() failed"); + X509_free(cert); + return NGX_ERROR; + } -#if (NGX_DEBUG) - { - u_char text[NGX_SOCKADDR_STRLEN]; - ngx_str_t addr; + store_ctx = X509_STORE_CTX_new(); + if (store_ctx == NULL) { + ngx_ssl_error(NGX_LOG_ERR, c->log, 0, + "X509_STORE_CTX_new() failed"); + X509_free(cert); + return NGX_ERROR; + } - addr.data = text; + chain = SSL_get_peer_cert_chain(c->ssl->connection); - for (i = 0; i < resolve->naddrs; i++) { - addr.len = ngx_sock_ntop(resolve->addrs[i].sockaddr, - resolve->addrs[i].socklen, - text, NGX_SOCKADDR_STRLEN, 0); + if (X509_STORE_CTX_init(store_ctx, store, cert, chain) == 0) { + ngx_ssl_error(NGX_LOG_ERR, c->log, 0, + "X509_STORE_CTX_init() failed"); + X509_STORE_CTX_free(store_ctx); + X509_free(cert); + return NGX_ERROR; + } - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0, - "name was resolved to %V", &addr); + rc = X509_verify_cert(store_ctx); + if (rc <= 0) { + ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "X509_verify_cert() failed"); + X509_STORE_CTX_free(store_ctx); + X509_free(cert); + return NGX_ERROR; + } + ocsp->certs = X509_STORE_CTX_get1_chain(store_ctx); + if (ocsp->certs == NULL) { + ngx_ssl_error(NGX_LOG_ERR, c->log, 0, + "X509_STORE_CTX_get1_chain() failed"); + X509_STORE_CTX_free(store_ctx); + X509_free(cert); + return NGX_ERROR; + } + + X509_STORE_CTX_free(store_ctx); } - } -#endif - ctx->naddrs = resolve->naddrs; - ctx->addrs = ngx_pcalloc(ctx->pool, ctx->naddrs * sizeof(ngx_addr_t)); + X509_free(cert); - if (ctx->addrs == NULL) { - goto failed; + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "ssl ocsp validate, certs:%d", sk_X509_num(ocsp->certs)); + + ngx_ssl_ocsp_validate_next(c); + + if (ocsp->status == NGX_AGAIN) { + c->ssl->in_ocsp = 1; + return NGX_AGAIN; } - for (i = 0; i < resolve->naddrs; i++) { + return NGX_OK; +} - socklen = resolve->addrs[i].socklen; - sockaddr = ngx_palloc(ctx->pool, socklen); - if (sockaddr == NULL) { - goto failed; - } +static void +ngx_ssl_ocsp_validate_next(ngx_connection_t *c) +{ + ngx_int_t rc; + ngx_uint_t n; + ngx_ssl_ocsp_t *ocsp; + ngx_ssl_ocsp_ctx_t *ctx; + ngx_ssl_ocsp_conf_t *ocf; - ngx_memcpy(sockaddr, resolve->addrs[i].sockaddr, socklen); - ngx_inet_set_port(sockaddr, ctx->port); + ocsp = c->ssl->ocsp; + ocf = ocsp->conf; - ctx->addrs[i].sockaddr = sockaddr; - ctx->addrs[i].socklen = socklen; + n = sk_X509_num(ocsp->certs); - p = ngx_pnalloc(ctx->pool, NGX_SOCKADDR_STRLEN); - if (p == NULL) { - goto failed; + for ( ;; ) { + + if (ocsp->ncert == n - 1 || (ocf->depth == 2 && ocsp->ncert == 1)) { + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "ssl ocsp validated, certs:%ui", ocsp->ncert); + rc = NGX_OK; + goto done; } - len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1); + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "ssl ocsp validate cert:%ui", ocsp->ncert); - ctx->addrs[i].name.len = len; - ctx->addrs[i].name.data = p; - } + ctx = ngx_ssl_ocsp_start(c->log); + if (ctx == NULL) { + rc = NGX_ERROR; + goto done; + } - ngx_resolve_name_done(resolve); + ocsp->ctx = ctx; - ngx_ssl_ocsp_connect(ctx); - return; + ctx->ssl_ctx = SSL_get_SSL_CTX(c->ssl->connection); + ctx->cert = sk_X509_value(ocsp->certs, ocsp->ncert); + ctx->issuer = sk_X509_value(ocsp->certs, ocsp->ncert + 1); + ctx->chain = ocsp->certs; -failed: + ctx->resolver = ocf->resolver; + ctx->resolver_timeout = ocf->resolver_timeout; - ngx_resolve_name_done(resolve); - ngx_ssl_ocsp_error(ctx); -} + ctx->handler = ngx_ssl_ocsp_handler; + ctx->data = c; + ctx->shm_zone = ocf->shm_zone; -static void -ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx) -{ - ngx_int_t rc; + ctx->addrs = ocf->addrs; + ctx->naddrs = ocf->naddrs; + ctx->host = ocf->host; + ctx->uri = ocf->uri; + ctx->port = ocf->port; - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, - "ssl ocsp connect"); + rc = ngx_ssl_ocsp_responder(c, ctx); + if (rc != NGX_OK) { + goto done; + } - /* TODO: use all ip addresses */ + if (ctx->uri.len == 0) { + ngx_str_set(&ctx->uri, "/"); + } - ctx->peer.sockaddr = ctx->addrs[0].sockaddr; - ctx->peer.socklen = ctx->addrs[0].socklen; - ctx->peer.name = &ctx->addrs[0].name; - ctx->peer.get = ngx_event_get_peer; - ctx->peer.log = ctx->log; - ctx->peer.log_error = NGX_ERROR_ERR; + ocsp->ncert++; - rc = ngx_event_connect_peer(&ctx->peer); + rc = ngx_ssl_ocsp_cache_lookup(ctx); - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, - "ssl ocsp connect peer done"); + if (rc == NGX_ERROR) { + goto done; + } - if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) { - ngx_ssl_ocsp_error(ctx); - return; - } + if (rc == NGX_DECLINED) { + break; + } - ctx->peer.connection->data = ctx; - ctx->peer.connection->pool = ctx->pool; + /* rc == NGX_OK */ - ctx->peer.connection->read->handler = ngx_ssl_ocsp_read_handler; - ctx->peer.connection->write->handler = ngx_ssl_ocsp_write_handler; + if (ctx->status != V_OCSP_CERTSTATUS_GOOD) { + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0, + "ssl ocsp cached status \"%s\"", + OCSP_cert_status_str(ctx->status)); + ocsp->cert_status = ctx->status; + goto done; + } - ctx->process = ngx_ssl_ocsp_process_status_line; + ocsp->ctx = NULL; + ngx_ssl_ocsp_done(ctx); + } - ngx_add_timer(ctx->peer.connection->read, ctx->timeout); - ngx_add_timer(ctx->peer.connection->write, ctx->timeout); + ngx_ssl_ocsp_request(ctx); + return; - if (rc == NGX_OK) { - ngx_ssl_ocsp_write_handler(ctx->peer.connection->write); - return; +done: + + ocsp->status = rc; + + if (c->ssl->in_ocsp) { + c->ssl->handshaked = 1; + c->ssl->handler(c); } } static void -ngx_ssl_ocsp_write_handler(ngx_event_t *wev) +ngx_ssl_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx) { - ssize_t n, size; - ngx_connection_t *c; - ngx_ssl_ocsp_ctx_t *ctx; - - c = wev->data; - ctx = c->data; + ngx_int_t rc; + ngx_ssl_ocsp_t *ocsp; + ngx_connection_t *c; - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, wev->log, 0, - "ssl ocsp write handler"); + c = ctx->data; + ocsp = c->ssl->ocsp; + ocsp->ctx = NULL; - if (wev->timedout) { - ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT, - "OCSP responder timed out"); - ngx_ssl_ocsp_error(ctx); - return; + rc = ngx_ssl_ocsp_verify(ctx); + if (rc != NGX_OK) { + goto done; } - size = ctx->request->last - ctx->request->pos; - - n = ngx_send(c, ctx->request->pos, size); + rc = ngx_ssl_ocsp_cache_store(ctx); + if (rc != NGX_OK) { + goto done; + } - if (n == NGX_ERROR) { - ngx_ssl_ocsp_error(ctx); - return; + if (ctx->status != V_OCSP_CERTSTATUS_GOOD) { + ocsp->cert_status = ctx->status; + goto done; } - if (n > 0) { - ctx->request->pos += n; + ngx_ssl_ocsp_done(ctx); - if (n == size) { - wev->handler = ngx_ssl_ocsp_dummy_handler; + ngx_ssl_ocsp_validate_next(c); - if (wev->timer_set) { - ngx_del_timer(wev); - } + return; - if (ngx_handle_write_event(wev, 0) != NGX_OK) { - ngx_ssl_ocsp_error(ctx); - } +done: - return; - } - } + ocsp->status = rc; + ngx_ssl_ocsp_done(ctx); - if (!wev->timer_set) { - ngx_add_timer(wev, ctx->timeout); + if (c->ssl->in_ocsp) { + c->ssl->handshaked = 1; + c->ssl->handler(c); } } -static void -ngx_ssl_ocsp_read_handler(ngx_event_t *rev) +static ngx_int_t +ngx_ssl_ocsp_responder(ngx_connection_t *c, ngx_ssl_ocsp_ctx_t *ctx) { - ssize_t n, size; - ngx_int_t rc; - ngx_connection_t *c; - ngx_ssl_ocsp_ctx_t *ctx; + char *s; + ngx_str_t responder; + ngx_url_t u; + STACK_OF(OPENSSL_STRING) *aia; - c = rev->data; - ctx = c->data; + if (ctx->host.len) { + return NGX_OK; + } - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, 0, - "ssl ocsp read handler"); + /* extract OCSP responder URL from certificate */ - if (rev->timedout) { - ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT, - "OCSP responder timed out"); - ngx_ssl_ocsp_error(ctx); - return; + aia = X509_get1_ocsp(ctx->cert); + if (aia == NULL) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "no OCSP responder URL in certificate"); + return NGX_ERROR; } - if (ctx->response == NULL) { - ctx->response = ngx_create_temp_buf(ctx->pool, 16384); - if (ctx->response == NULL) { - ngx_ssl_ocsp_error(ctx); - return; - } +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + s = sk_OPENSSL_STRING_value(aia, 0); +#else + s = sk_value(aia, 0); +#endif + if (s == NULL) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "no OCSP responder URL in certificate"); + X509_email_free(aia); + return NGX_ERROR; } - for ( ;; ) { + responder.len = ngx_strlen(s); + responder.data = ngx_palloc(ctx->pool, responder.len); + if (responder.data == NULL) { + X509_email_free(aia); + return NGX_ERROR; + } - size = ctx->response->end - ctx->response->last; + ngx_memcpy(responder.data, s, responder.len); + X509_email_free(aia); - n = ngx_recv(c, ctx->response->last, size); + ngx_memzero(&u, sizeof(ngx_url_t)); - if (n > 0) { - ctx->response->last += n; + u.url = responder; + u.default_port = 80; + u.uri_part = 1; + u.no_resolve = 1; - rc = ctx->process(ctx); + if (u.url.len > 7 + && ngx_strncasecmp(u.url.data, (u_char *) "http://", 7) == 0) + { + u.url.len -= 7; + u.url.data += 7; - if (rc == NGX_ERROR) { - ngx_ssl_ocsp_error(ctx); - return; - } + } else { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "invalid URL prefix in OCSP responder \"%V\" " + "in certificate", &u.url); + return NGX_ERROR; + } - continue; + if (ngx_parse_url(ctx->pool, &u) != NGX_OK) { + if (u.err) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "%s in OCSP responder \"%V\" in certificate", + u.err, &u.url); } - if (n == NGX_AGAIN) { + return NGX_ERROR; + } - if (ngx_handle_read_event(rev, 0) != NGX_OK) { - ngx_ssl_ocsp_error(ctx); - } + if (u.host.len == 0) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "empty host in OCSP responder in certificate"); + return NGX_ERROR; + } - return; - } + ctx->addrs = u.addrs; + ctx->naddrs = u.naddrs; + ctx->host = u.host; + ctx->uri = u.uri; + ctx->port = u.port; - break; - } + return NGX_OK; +} - ctx->done = 1; - rc = ctx->process(ctx); +ngx_int_t +ngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s) +{ + ngx_ssl_ocsp_t *ocsp; + + ocsp = c->ssl->ocsp; + if (ocsp == NULL) { + return NGX_OK; + } + + if (ocsp->status == NGX_ERROR) { + *s = "certificate status request failed"; + return NGX_DECLINED; + } + + switch (ocsp->cert_status) { + + case V_OCSP_CERTSTATUS_GOOD: + return NGX_OK; + + case V_OCSP_CERTSTATUS_REVOKED: + *s = "certificate revoked"; + break; + + default: /* V_OCSP_CERTSTATUS_UNKNOWN */ + *s = "certificate status unknown"; + } + + return NGX_DECLINED; +} + + +void +ngx_ssl_ocsp_cleanup(ngx_connection_t *c) +{ + ngx_ssl_ocsp_t *ocsp; + + ocsp = c->ssl->ocsp; + if (ocsp == NULL) { + return; + } + + if (ocsp->ctx) { + ngx_ssl_ocsp_done(ocsp->ctx); + ocsp->ctx = NULL; + } + + if (ocsp->certs) { + sk_X509_pop_free(ocsp->certs, X509_free); + ocsp->certs = NULL; + } +} + + +static ngx_ssl_ocsp_ctx_t * +ngx_ssl_ocsp_start(ngx_log_t *log) +{ + ngx_pool_t *pool; + ngx_ssl_ocsp_ctx_t *ctx; + + pool = ngx_create_pool(2048, log); + if (pool == NULL) { + return NULL; + } + + ctx = ngx_pcalloc(pool, sizeof(ngx_ssl_ocsp_ctx_t)); + if (ctx == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + log = ngx_palloc(pool, sizeof(ngx_log_t)); + if (log == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + ctx->pool = pool; + + *log = *ctx->pool->log; + + ctx->pool->log = log; + ctx->log = log; + + log->handler = ngx_ssl_ocsp_log_error; + log->data = ctx; + log->action = "requesting certificate status"; + + return ctx; +} + + +static void +ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx) +{ + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, + "ssl ocsp done"); + + if (ctx->peer.connection) { + ngx_close_connection(ctx->peer.connection); + } + + ngx_destroy_pool(ctx->pool); +} + + +static void +ngx_ssl_ocsp_error(ngx_ssl_ocsp_ctx_t *ctx) +{ + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, + "ssl ocsp error"); + + ctx->code = 0; + ctx->handler(ctx); +} + + +static void +ngx_ssl_ocsp_next(ngx_ssl_ocsp_ctx_t *ctx) +{ + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, + "ssl ocsp next"); + + if (++ctx->naddr >= ctx->naddrs) { + ngx_ssl_ocsp_error(ctx); + return; + } + + ctx->request->pos = ctx->request->start; + + if (ctx->response) { + ctx->response->last = ctx->response->pos; + } + + if (ctx->peer.connection) { + ngx_close_connection(ctx->peer.connection); + ctx->peer.connection = NULL; + } + + ctx->state = 0; + ctx->count = 0; + ctx->done = 0; + + ngx_ssl_ocsp_connect(ctx); +} + + +static void +ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx) +{ + ngx_resolver_ctx_t *resolve, temp; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, + "ssl ocsp request"); + + if (ngx_ssl_ocsp_create_request(ctx) != NGX_OK) { + ngx_ssl_ocsp_error(ctx); + return; + } + + if (ctx->resolver) { + /* resolve OCSP responder hostname */ + + temp.name = ctx->host; + + resolve = ngx_resolve_start(ctx->resolver, &temp); + if (resolve == NULL) { + ngx_ssl_ocsp_error(ctx); + return; + } + + if (resolve == NGX_NO_RESOLVER) { + if (ctx->naddrs == 0) { + ngx_log_error(NGX_LOG_ERR, ctx->log, 0, + "no resolver defined to resolve %V", &ctx->host); + + ngx_ssl_ocsp_error(ctx); + return; + } + + ngx_log_error(NGX_LOG_WARN, ctx->log, 0, + "no resolver defined to resolve %V", &ctx->host); + goto connect; + } + + resolve->name = ctx->host; + resolve->handler = ngx_ssl_ocsp_resolve_handler; + resolve->data = ctx; + resolve->timeout = ctx->resolver_timeout; + + if (ngx_resolve_name(resolve) != NGX_OK) { + ngx_ssl_ocsp_error(ctx); + return; + } + + return; + } + +connect: + + ngx_ssl_ocsp_connect(ctx); +} + + +static void +ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve) +{ + ngx_ssl_ocsp_ctx_t *ctx = resolve->data; + + u_char *p; + size_t len; + socklen_t socklen; + ngx_uint_t i; + struct sockaddr *sockaddr; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, + "ssl ocsp resolve handler"); + + if (resolve->state) { + ngx_log_error(NGX_LOG_ERR, ctx->log, 0, + "%V could not be resolved (%i: %s)", + &resolve->name, resolve->state, + ngx_resolver_strerror(resolve->state)); + goto failed; + } + +#if (NGX_DEBUG) + { + u_char text[NGX_SOCKADDR_STRLEN]; + ngx_str_t addr; + + addr.data = text; + + for (i = 0; i < resolve->naddrs; i++) { + addr.len = ngx_sock_ntop(resolve->addrs[i].sockaddr, + resolve->addrs[i].socklen, + text, NGX_SOCKADDR_STRLEN, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0, + "name was resolved to %V", &addr); + + } + } +#endif + + ctx->naddrs = resolve->naddrs; + ctx->addrs = ngx_pcalloc(ctx->pool, ctx->naddrs * sizeof(ngx_addr_t)); + + if (ctx->addrs == NULL) { + goto failed; + } + + for (i = 0; i < resolve->naddrs; i++) { + + socklen = resolve->addrs[i].socklen; + + sockaddr = ngx_palloc(ctx->pool, socklen); + if (sockaddr == NULL) { + goto failed; + } + + ngx_memcpy(sockaddr, resolve->addrs[i].sockaddr, socklen); + ngx_inet_set_port(sockaddr, ctx->port); + + ctx->addrs[i].sockaddr = sockaddr; + ctx->addrs[i].socklen = socklen; + + p = ngx_pnalloc(ctx->pool, NGX_SOCKADDR_STRLEN); + if (p == NULL) { + goto failed; + } + + len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1); + + ctx->addrs[i].name.len = len; + ctx->addrs[i].name.data = p; + } + + ngx_resolve_name_done(resolve); + + ngx_ssl_ocsp_connect(ctx); + return; + +failed: + + ngx_resolve_name_done(resolve); + ngx_ssl_ocsp_error(ctx); +} + + +static void +ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx) +{ + ngx_int_t rc; + ngx_addr_t *addr; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0, + "ssl ocsp connect %ui/%ui", ctx->naddr, ctx->naddrs); + + addr = &ctx->addrs[ctx->naddr]; + + ctx->peer.sockaddr = addr->sockaddr; + ctx->peer.socklen = addr->socklen; + ctx->peer.name = &addr->name; + ctx->peer.get = ngx_event_get_peer; + ctx->peer.log = ctx->log; + ctx->peer.log_error = NGX_ERROR_ERR; + + rc = ngx_event_connect_peer(&ctx->peer); + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, + "ssl ocsp connect peer done"); + + if (rc == NGX_ERROR) { + ngx_ssl_ocsp_error(ctx); + return; + } + + if (rc == NGX_BUSY || rc == NGX_DECLINED) { + ngx_ssl_ocsp_next(ctx); + return; + } + + ctx->peer.connection->data = ctx; + ctx->peer.connection->pool = ctx->pool; + + ctx->peer.connection->read->handler = ngx_ssl_ocsp_read_handler; + ctx->peer.connection->write->handler = ngx_ssl_ocsp_write_handler; + + ctx->process = ngx_ssl_ocsp_process_status_line; + + if (ctx->timeout) { + ngx_add_timer(ctx->peer.connection->read, ctx->timeout); + ngx_add_timer(ctx->peer.connection->write, ctx->timeout); + } + + if (rc == NGX_OK) { + ngx_ssl_ocsp_write_handler(ctx->peer.connection->write); + return; + } +} + + +static void +ngx_ssl_ocsp_write_handler(ngx_event_t *wev) +{ + ssize_t n, size; + ngx_connection_t *c; + ngx_ssl_ocsp_ctx_t *ctx; + + c = wev->data; + ctx = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, wev->log, 0, + "ssl ocsp write handler"); + + if (wev->timedout) { + ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT, + "OCSP responder timed out"); + ngx_ssl_ocsp_next(ctx); + return; + } + + size = ctx->request->last - ctx->request->pos; + + n = ngx_send(c, ctx->request->pos, size); + + if (n == NGX_ERROR) { + ngx_ssl_ocsp_next(ctx); + return; + } + + if (n > 0) { + ctx->request->pos += n; + + if (n == size) { + wev->handler = ngx_ssl_ocsp_dummy_handler; + + if (wev->timer_set) { + ngx_del_timer(wev); + } + + if (ngx_handle_write_event(wev, 0) != NGX_OK) { + ngx_ssl_ocsp_error(ctx); + } + + return; + } + } + + if (!wev->timer_set && ctx->timeout) { + ngx_add_timer(wev, ctx->timeout); + } +} + + +static void +ngx_ssl_ocsp_read_handler(ngx_event_t *rev) +{ + ssize_t n, size; + ngx_int_t rc; + ngx_connection_t *c; + ngx_ssl_ocsp_ctx_t *ctx; + + c = rev->data; + ctx = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, 0, + "ssl ocsp read handler"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT, + "OCSP responder timed out"); + ngx_ssl_ocsp_next(ctx); + return; + } + + if (ctx->response == NULL) { + ctx->response = ngx_create_temp_buf(ctx->pool, 16384); + if (ctx->response == NULL) { + ngx_ssl_ocsp_error(ctx); + return; + } + } + + for ( ;; ) { + + size = ctx->response->end - ctx->response->last; + + n = ngx_recv(c, ctx->response->last, size); + + if (n > 0) { + ctx->response->last += n; + + rc = ctx->process(ctx); + + if (rc == NGX_ERROR) { + ngx_ssl_ocsp_next(ctx); + return; + } + + continue; + } + + if (n == NGX_AGAIN) { + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_ssl_ocsp_error(ctx); + } + + return; + } + + break; + } + + ctx->done = 1; + + rc = ctx->process(ctx); if (rc == NGX_DONE) { /* ctx->handler() was called */ @@ -1189,7 +1673,7 @@ ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "OCSP responder prematurely closed connection"); - ngx_ssl_ocsp_error(ctx); + ngx_ssl_ocsp_next(ctx); } @@ -1484,43 +1968,273 @@ break; } - if (ch < '0' || ch > '9') { - return NGX_ERROR; + if (ch < '0' || ch > '9') { + return NGX_ERROR; + } + + ctx->code = ctx->code * 10 + (ch - '0'); + + if (++ctx->count == 3) { + state = sw_space_after_status; + ctx->header_start = p - 2; + } + + break; + + /* space or end of line */ + case sw_space_after_status: + switch (ch) { + case ' ': + state = sw_status_text; + break; + case '.': /* IIS may send 403.1, 403.2, etc */ + state = sw_status_text; + break; + case CR: + state = sw_almost_done; + break; + case LF: + ctx->header_end = p; + goto done; + default: + return NGX_ERROR; + } + break; + + /* any text until end of line */ + case sw_status_text: + switch (ch) { + case CR: + state = sw_almost_done; + break; + case LF: + ctx->header_end = p; + goto done; + } + break; + + /* end of status line */ + case sw_almost_done: + switch (ch) { + case LF: + ctx->header_end = p - 1; + goto done; + default: + return NGX_ERROR; + } + } + } + + b->pos = p; + ctx->state = state; + + return NGX_AGAIN; + +done: + + b->pos = p + 1; + ctx->state = sw_start; + + return NGX_OK; +} + + +static ngx_int_t +ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx) +{ + size_t len; + ngx_int_t rc; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, + "ssl ocsp process headers"); + + for ( ;; ) { + rc = ngx_ssl_ocsp_parse_header_line(ctx); + + if (rc == NGX_OK) { + + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, ctx->log, 0, + "ssl ocsp header \"%*s: %*s\"", + ctx->header_name_end - ctx->header_name_start, + ctx->header_name_start, + ctx->header_end - ctx->header_start, + ctx->header_start); + + len = ctx->header_name_end - ctx->header_name_start; + + if (len == sizeof("Content-Type") - 1 + && ngx_strncasecmp(ctx->header_name_start, + (u_char *) "Content-Type", + sizeof("Content-Type") - 1) + == 0) + { + len = ctx->header_end - ctx->header_start; + + if (len != sizeof("application/ocsp-response") - 1 + || ngx_strncasecmp(ctx->header_start, + (u_char *) "application/ocsp-response", + sizeof("application/ocsp-response") - 1) + != 0) + { + ngx_log_error(NGX_LOG_ERR, ctx->log, 0, + "OCSP responder sent invalid " + "\"Content-Type\" header: \"%*s\"", + ctx->header_end - ctx->header_start, + ctx->header_start); + return NGX_ERROR; + } + + continue; + } + + /* TODO: honor Content-Length */ + + continue; + } + + if (rc == NGX_DONE) { + break; + } + + if (rc == NGX_AGAIN) { + return NGX_AGAIN; + } + + /* rc == NGX_ERROR */ + + ngx_log_error(NGX_LOG_ERR, ctx->log, 0, + "OCSP responder sent invalid response"); + + return NGX_ERROR; + } + + ctx->process = ngx_ssl_ocsp_process_body; + return ctx->process(ctx); +} + + +static ngx_int_t +ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx) +{ + u_char c, ch, *p; + enum { + sw_start = 0, + sw_name, + sw_space_before_value, + sw_value, + sw_space_after_value, + sw_almost_done, + sw_header_almost_done + } state; + + state = ctx->state; + + for (p = ctx->response->pos; p < ctx->response->last; p++) { + ch = *p; + +#if 0 + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ctx->log, 0, + "s:%d in:'%02Xd:%c'", state, ch, ch); +#endif + + switch (state) { + + /* first char */ + case sw_start: + + switch (ch) { + case CR: + ctx->header_end = p; + state = sw_header_almost_done; + break; + case LF: + ctx->header_end = p; + goto header_done; + default: + state = sw_name; + ctx->header_name_start = p; + + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'z') { + break; + } + + if (ch >= '0' && ch <= '9') { + break; + } + + return NGX_ERROR; + } + break; + + /* header name */ + case sw_name: + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'z') { + break; + } + + if (ch == ':') { + ctx->header_name_end = p; + state = sw_space_before_value; + break; + } + + if (ch == '-') { + break; + } + + if (ch >= '0' && ch <= '9') { + break; } - ctx->code = ctx->code * 10 + (ch - '0'); + if (ch == CR) { + ctx->header_name_end = p; + ctx->header_start = p; + ctx->header_end = p; + state = sw_almost_done; + break; + } - if (++ctx->count == 3) { - state = sw_space_after_status; - ctx->header_start = p - 2; + if (ch == LF) { + ctx->header_name_end = p; + ctx->header_start = p; + ctx->header_end = p; + goto done; } - break; + return NGX_ERROR; - /* space or end of line */ - case sw_space_after_status: + /* space* before header value */ + case sw_space_before_value: switch (ch) { case ' ': - state = sw_status_text; - break; - case '.': /* IIS may send 403.1, 403.2, etc */ - state = sw_status_text; break; case CR: + ctx->header_start = p; + ctx->header_end = p; state = sw_almost_done; break; case LF: + ctx->header_start = p; ctx->header_end = p; goto done; default: - return NGX_ERROR; + ctx->header_start = p; + state = sw_value; + break; } break; - /* any text until end of line */ - case sw_status_text: + /* header value */ + case sw_value: switch (ch) { + case ' ': + ctx->header_end = p; + state = sw_space_after_value; + break; case CR: + ctx->header_end = p; state = sw_almost_done; break; case LF: @@ -1529,305 +2243,429 @@ } break; - /* end of status line */ + /* space* before end of header line */ + case sw_space_after_value: + switch (ch) { + case ' ': + break; + case CR: + state = sw_almost_done; + break; + case LF: + goto done; + default: + state = sw_value; + break; + } + break; + + /* end of header line */ case sw_almost_done: switch (ch) { case LF: - ctx->header_end = p - 1; goto done; default: return NGX_ERROR; } + + /* end of header */ + case sw_header_almost_done: + switch (ch) { + case LF: + goto header_done; + default: + return NGX_ERROR; + } } } - b->pos = p; + ctx->response->pos = p; ctx->state = state; return NGX_AGAIN; done: - b->pos = p + 1; + ctx->response->pos = p + 1; ctx->state = sw_start; return NGX_OK; + +header_done: + + ctx->response->pos = p + 1; + ctx->state = sw_start; + + return NGX_DONE; } static ngx_int_t -ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx) +ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx) { - size_t len; - ngx_int_t rc; - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, - "ssl ocsp process headers"); + "ssl ocsp process body"); - for ( ;; ) { - rc = ngx_ssl_ocsp_parse_header_line(ctx); + if (ctx->done) { + ctx->handler(ctx); + return NGX_DONE; + } - if (rc == NGX_OK) { + return NGX_AGAIN; +} - ngx_log_debug4(NGX_LOG_DEBUG_EVENT, ctx->log, 0, - "ssl ocsp header \"%*s: %*s\"", - ctx->header_name_end - ctx->header_name_start, - ctx->header_name_start, - ctx->header_end - ctx->header_start, - ctx->header_start); - len = ctx->header_name_end - ctx->header_name_start; +static ngx_int_t +ngx_ssl_ocsp_verify(ngx_ssl_ocsp_ctx_t *ctx) +{ + int n; + size_t len; + X509_STORE *store; + const u_char *p; + OCSP_CERTID *id; + OCSP_RESPONSE *ocsp; + OCSP_BASICRESP *basic; + ASN1_GENERALIZEDTIME *thisupdate, *nextupdate; - if (len == sizeof("Content-Type") - 1 - && ngx_strncasecmp(ctx->header_name_start, - (u_char *) "Content-Type", - sizeof("Content-Type") - 1) - == 0) - { - len = ctx->header_end - ctx->header_start; + ocsp = NULL; + basic = NULL; + id = NULL; - if (len != sizeof("application/ocsp-response") - 1 - || ngx_strncasecmp(ctx->header_start, - (u_char *) "application/ocsp-response", - sizeof("application/ocsp-response") - 1) - != 0) - { - ngx_log_error(NGX_LOG_ERR, ctx->log, 0, - "OCSP responder sent invalid " - "\"Content-Type\" header: \"%*s\"", - ctx->header_end - ctx->header_start, - ctx->header_start); - return NGX_ERROR; - } + if (ctx->code != 200) { + goto error; + } - continue; - } + /* check the response */ - /* TODO: honor Content-Length */ + len = ctx->response->last - ctx->response->pos; + p = ctx->response->pos; - continue; + ocsp = d2i_OCSP_RESPONSE(NULL, &p, len); + if (ocsp == NULL) { + ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, + "d2i_OCSP_RESPONSE() failed"); + goto error; + } + + n = OCSP_response_status(ocsp); + + if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) { + ngx_log_error(NGX_LOG_ERR, ctx->log, 0, + "OCSP response not successful (%d: %s)", + n, OCSP_response_status_str(n)); + goto error; + } + + basic = OCSP_response_get1_basic(ocsp); + if (basic == NULL) { + ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, + "OCSP_response_get1_basic() failed"); + goto error; + } + + store = SSL_CTX_get_cert_store(ctx->ssl_ctx); + if (store == NULL) { + ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0, + "SSL_CTX_get_cert_store() failed"); + goto error; + } + + if (OCSP_basic_verify(basic, ctx->chain, store, ctx->flags) != 1) { + ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, + "OCSP_basic_verify() failed"); + goto error; + } + + id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer); + if (id == NULL) { + ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0, + "OCSP_cert_to_id() failed"); + goto error; + } + + if (OCSP_resp_find_status(basic, id, &ctx->status, NULL, NULL, + &thisupdate, &nextupdate) + != 1) + { + ngx_log_error(NGX_LOG_ERR, ctx->log, 0, + "certificate status not found in the OCSP response"); + goto error; + } + + if (OCSP_check_validity(thisupdate, nextupdate, 300, -1) != 1) { + ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, + "OCSP_check_validity() failed"); + goto error; + } + + if (nextupdate) { + ctx->valid = ngx_ssl_stapling_time(nextupdate); + if (ctx->valid == (time_t) NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, ctx->log, 0, + "invalid nextUpdate time in certificate status"); + goto error; } - if (rc == NGX_DONE) { - break; - } + } else { + ctx->valid = NGX_MAX_TIME_T_VALUE; + } + + OCSP_CERTID_free(id); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(ocsp); + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0, + "ssl ocsp response, %s, %uz", + OCSP_cert_status_str(ctx->status), len); + + return NGX_OK; + +error: + + if (id) { + OCSP_CERTID_free(id); + } + + if (basic) { + OCSP_BASICRESP_free(basic); + } + + if (ocsp) { + OCSP_RESPONSE_free(ocsp); + } + + return NGX_ERROR; +} + + +ngx_int_t +ngx_ssl_ocsp_cache_init(ngx_shm_zone_t *shm_zone, void *data) +{ + size_t len; + ngx_slab_pool_t *shpool; + ngx_ssl_ocsp_cache_t *cache; + + if (data) { + shm_zone->data = data; + return NGX_OK; + } + + shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + + if (shm_zone->shm.exists) { + shm_zone->data = shpool->data; + return NGX_OK; + } + + cache = ngx_slab_alloc(shpool, sizeof(ngx_ssl_ocsp_cache_t)); + if (cache == NULL) { + return NGX_ERROR; + } + + shpool->data = cache; + shm_zone->data = cache; - if (rc == NGX_AGAIN) { - return NGX_AGAIN; - } + ngx_rbtree_init(&cache->rbtree, &cache->sentinel, + ngx_str_rbtree_insert_value); - /* rc == NGX_ERROR */ + ngx_queue_init(&cache->expire_queue); - ngx_log_error(NGX_LOG_ERR, ctx->log, 0, - "OCSP responder sent invalid response"); + len = sizeof(" in OCSP cache \"\"") + shm_zone->shm.name.len; + shpool->log_ctx = ngx_slab_alloc(shpool, len); + if (shpool->log_ctx == NULL) { return NGX_ERROR; } - ctx->process = ngx_ssl_ocsp_process_body; - return ctx->process(ctx); + ngx_sprintf(shpool->log_ctx, " in OCSP cache \"%V\"%Z", + &shm_zone->shm.name); + + shpool->log_nomem = 0; + + return NGX_OK; } static ngx_int_t -ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx) +ngx_ssl_ocsp_cache_lookup(ngx_ssl_ocsp_ctx_t *ctx) { - u_char c, ch, *p; - enum { - sw_start = 0, - sw_name, - sw_space_before_value, - sw_value, - sw_space_after_value, - sw_almost_done, - sw_header_almost_done - } state; + uint32_t hash; + ngx_shm_zone_t *shm_zone; + ngx_slab_pool_t *shpool; + ngx_ssl_ocsp_cache_t *cache; + ngx_ssl_ocsp_cache_node_t *node; - state = ctx->state; + shm_zone = ctx->shm_zone; - for (p = ctx->response->pos; p < ctx->response->last; p++) { - ch = *p; + if (shm_zone == NULL) { + return NGX_DECLINED; + } -#if 0 - ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ctx->log, 0, - "s:%d in:'%02Xd:%c'", state, ch, ch); -#endif + if (ngx_ssl_ocsp_create_key(ctx) != NGX_OK) { + return NGX_ERROR; + } - switch (state) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, "ssl ocsp cache lookup"); - /* first char */ - case sw_start: + cache = shm_zone->data; + shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + hash = ngx_hash_key(ctx->key.data, ctx->key.len); - switch (ch) { - case CR: - ctx->header_end = p; - state = sw_header_almost_done; - break; - case LF: - ctx->header_end = p; - goto header_done; - default: - state = sw_name; - ctx->header_name_start = p; + ngx_shmtx_lock(&shpool->mutex); - c = (u_char) (ch | 0x20); - if (c >= 'a' && c <= 'z') { - break; - } + node = (ngx_ssl_ocsp_cache_node_t *) + ngx_str_rbtree_lookup(&cache->rbtree, &ctx->key, hash); - if (ch >= '0' && ch <= '9') { - break; - } + if (node) { + if (node->valid > ngx_time()) { + ctx->status = node->status; + ngx_shmtx_unlock(&shpool->mutex); - return NGX_ERROR; - } - break; + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0, + "ssl ocsp cache hit, %s", + OCSP_cert_status_str(ctx->status)); - /* header name */ - case sw_name: - c = (u_char) (ch | 0x20); - if (c >= 'a' && c <= 'z') { - break; - } + return NGX_OK; + } - if (ch == ':') { - ctx->header_name_end = p; - state = sw_space_before_value; - break; - } + ngx_queue_remove(&node->queue); + ngx_rbtree_delete(&cache->rbtree, &node->node.node); + ngx_slab_free_locked(shpool, node); - if (ch == '-') { - break; - } + ngx_shmtx_unlock(&shpool->mutex); - if (ch >= '0' && ch <= '9') { - break; - } + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, + "ssl ocsp cache expired"); - if (ch == CR) { - ctx->header_name_end = p; - ctx->header_start = p; - ctx->header_end = p; - state = sw_almost_done; - break; - } + return NGX_DECLINED; + } - if (ch == LF) { - ctx->header_name_end = p; - ctx->header_start = p; - ctx->header_end = p; - goto done; - } + ngx_shmtx_unlock(&shpool->mutex); - return NGX_ERROR; + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, "ssl ocsp cache miss"); - /* space* before header value */ - case sw_space_before_value: - switch (ch) { - case ' ': - break; - case CR: - ctx->header_start = p; - ctx->header_end = p; - state = sw_almost_done; - break; - case LF: - ctx->header_start = p; - ctx->header_end = p; - goto done; - default: - ctx->header_start = p; - state = sw_value; - break; - } - break; + return NGX_DECLINED; +} - /* header value */ - case sw_value: - switch (ch) { - case ' ': - ctx->header_end = p; - state = sw_space_after_value; - break; - case CR: - ctx->header_end = p; - state = sw_almost_done; - break; - case LF: - ctx->header_end = p; - goto done; - } - break; - /* space* before end of header line */ - case sw_space_after_value: - switch (ch) { - case ' ': - break; - case CR: - state = sw_almost_done; - break; - case LF: - goto done; - default: - state = sw_value; - break; - } - break; +static ngx_int_t +ngx_ssl_ocsp_cache_store(ngx_ssl_ocsp_ctx_t *ctx) +{ + time_t now, valid; + uint32_t hash; + ngx_queue_t *q; + ngx_shm_zone_t *shm_zone; + ngx_slab_pool_t *shpool; + ngx_ssl_ocsp_cache_t *cache; + ngx_ssl_ocsp_cache_node_t *node; - /* end of header line */ - case sw_almost_done: - switch (ch) { - case LF: - goto done; - default: - return NGX_ERROR; - } + shm_zone = ctx->shm_zone; - /* end of header */ - case sw_header_almost_done: - switch (ch) { - case LF: - goto header_done; - default: - return NGX_ERROR; - } - } + if (shm_zone == NULL) { + return NGX_OK; } - ctx->response->pos = p; - ctx->state = state; + valid = ctx->valid; - return NGX_AGAIN; + now = ngx_time(); -done: + if (valid < now) { + return NGX_OK; + } - ctx->response->pos = p + 1; - ctx->state = sw_start; + if (valid == NGX_MAX_TIME_T_VALUE) { + valid = now + 3600; + } - return NGX_OK; + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0, + "ssl ocsp cache store, valid:%T", valid - now); -header_done: + cache = shm_zone->data; + shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + hash = ngx_hash_key(ctx->key.data, ctx->key.len); - ctx->response->pos = p + 1; - ctx->state = sw_start; + ngx_shmtx_lock(&shpool->mutex); - return NGX_DONE; + node = ngx_slab_calloc_locked(shpool, + sizeof(ngx_ssl_ocsp_cache_node_t) + ctx->key.len); + if (node == NULL) { + + if (!ngx_queue_empty(&cache->expire_queue)) { + q = ngx_queue_last(&cache->expire_queue); + node = ngx_queue_data(q, ngx_ssl_ocsp_cache_node_t, queue); + + ngx_rbtree_delete(&cache->rbtree, &node->node.node); + ngx_queue_remove(q); + ngx_slab_free_locked(shpool, node); + + node = ngx_slab_alloc_locked(shpool, + sizeof(ngx_ssl_ocsp_cache_node_t) + ctx->key.len); + } + + if (node == NULL) { + ngx_shmtx_unlock(&shpool->mutex); + ngx_log_error(NGX_LOG_ALERT, ctx->log, 0, + "could not allocate new entry%s", shpool->log_ctx); + return NGX_ERROR; + } + } + + node->node.str.len = ctx->key.len; + node->node.str.data = (u_char *) node + sizeof(ngx_ssl_ocsp_cache_node_t); + ngx_memcpy(node->node.str.data, ctx->key.data, ctx->key.len); + node->node.node.key = hash; + node->status = ctx->status; + node->valid = valid; + + ngx_rbtree_insert(&cache->rbtree, &node->node.node); + ngx_queue_insert_head(&cache->expire_queue, &node->queue); + + ngx_shmtx_unlock(&shpool->mutex); + + return NGX_OK; } static ngx_int_t -ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx) +ngx_ssl_ocsp_create_key(ngx_ssl_ocsp_ctx_t *ctx) { - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, - "ssl ocsp process body"); + u_char *p; + X509_NAME *name; + ASN1_INTEGER *serial; - if (ctx->done) { - ctx->handler(ctx); - return NGX_DONE; + p = ngx_pnalloc(ctx->pool, 60); + if (p == NULL) { + return NGX_ERROR; } - return NGX_AGAIN; + ctx->key.data = p; + ctx->key.len = 60; + + name = X509_get_subject_name(ctx->issuer); + if (X509_NAME_digest(name, EVP_sha1(), p, NULL) == 0) { + return NGX_ERROR; + } + + p += 20; + + if (X509_pubkey_digest(ctx->issuer, EVP_sha1(), p, NULL) == 0) { + return NGX_ERROR; + } + + p += 20; + + serial = X509_get_serialNumber(ctx->cert); + if (serial->length > 20) { + return NGX_ERROR; + } + + p = ngx_cpymem(p, serial->data, serial->length); + ngx_memzero(p, 20 - serial->length); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0, + "ssl ocsp key %xV", &ctx->key); + + return NGX_OK; } @@ -1889,6 +2727,52 @@ { return NGX_OK; } + + +ngx_int_t +ngx_ssl_ocsp(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder, + ngx_uint_t depth, ngx_shm_zone_t *shm_zone) +{ + ngx_log_error(NGX_LOG_EMERG, ssl->log, 0, + "\"ssl_ocsp\" is not supported on this platform"); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_ssl_ocsp_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl, + ngx_resolver_t *resolver, ngx_msec_t resolver_timeout) +{ + return NGX_OK; +} + + +ngx_int_t +ngx_ssl_ocsp_validate(ngx_connection_t *c) +{ + return NGX_OK; +} + + +ngx_int_t +ngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s) +{ + return NGX_OK; +} + + +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) +{ + return NGX_OK; +} #endif diff -Nru nginx-1.18.0/src/event/ngx_event_pipe.c nginx-1.20.1/src/event/ngx_event_pipe.c --- nginx-1.18.0/src/event/ngx_event_pipe.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/event/ngx_event_pipe.c 2021-05-25 12:35:38.000000000 +0000 @@ -960,6 +960,22 @@ return NGX_OK; } + if (p->upstream_done) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0, + "input data after close"); + return NGX_OK; + } + + if (p->length == 0) { + p->upstream_done = 1; + + ngx_log_error(NGX_LOG_WARN, p->log, 0, + "upstream sent more data than specified in " + "\"Content-Length\" header"); + + return NGX_OK; + } + cl = ngx_chain_get_free_buf(p->pool, &p->free); if (cl == NULL) { return NGX_ERROR; @@ -987,6 +1003,18 @@ return NGX_OK; } + if (b->last - b->pos > p->length) { + + ngx_log_error(NGX_LOG_WARN, p->log, 0, + "upstream sent more data than specified in " + "\"Content-Length\" header"); + + b->last = b->pos + p->length; + p->upstream_done = 1; + + return NGX_OK; + } + p->length -= b->last - b->pos; return NGX_OK; diff -Nru nginx-1.18.0/src/event/ngx_event_udp.c nginx-1.20.1/src/event/ngx_event_udp.c --- nginx-1.18.0/src/event/ngx_event_udp.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/event/ngx_event_udp.c 2021-05-25 12:35:38.000000000 +0000 @@ -363,6 +363,8 @@ c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1); + c->start_time = ngx_current_msec; + #if (NGX_STAT_STUB) (void) ngx_atomic_fetch_add(ngx_stat_handled, 1); #endif diff -Nru nginx-1.18.0/src/http/modules/ngx_http_fastcgi_module.c nginx-1.20.1/src/http/modules/ngx_http_fastcgi_module.c --- nginx-1.18.0/src/http/modules/ngx_http_fastcgi_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/modules/ngx_http_fastcgi_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -81,12 +81,15 @@ size_t length; size_t padding; + off_t rest; + ngx_chain_t *free; ngx_chain_t *busy; unsigned fastcgi_stdout:1; unsigned large_stderr:1; unsigned header_sent:1; + unsigned closed:1; ngx_array_t *split_parts; @@ -2075,13 +2078,31 @@ static ngx_int_t ngx_http_fastcgi_input_filter_init(void *data) { - ngx_http_request_t *r = data; + ngx_http_request_t *r = data; + + ngx_http_upstream_t *u; + ngx_http_fastcgi_ctx_t *f; ngx_http_fastcgi_loc_conf_t *flcf; + u = r->upstream; + + f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module); flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); - r->upstream->pipe->length = flcf->keep_conn ? - (off_t) sizeof(ngx_http_fastcgi_header_t) : -1; + u->pipe->length = flcf->keep_conn ? + (off_t) sizeof(ngx_http_fastcgi_header_t) : -1; + + if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT + || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED) + { + f->rest = 0; + + } else if (r->method == NGX_HTTP_HEAD) { + f->rest = -2; + + } else { + f->rest = u->headers_in.content_length_n; + } return NGX_OK; } @@ -2106,6 +2127,15 @@ f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module); flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); + if (p->upstream_done || f->closed) { + r->upstream->keepalive = 0; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0, + "http fastcgi data after close"); + + return NGX_OK; + } + b = NULL; prev = &buf->shadow; @@ -2128,13 +2158,25 @@ if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) { f->state = ngx_http_fastcgi_st_padding; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0, + "http fastcgi closed stdout"); + + if (f->rest > 0) { + ngx_log_error(NGX_LOG_ERR, p->log, 0, + "upstream prematurely closed " + "FastCGI stdout"); + + p->upstream_error = 1; + p->upstream_eof = 0; + f->closed = 1; + + break; + } + if (!flcf->keep_conn) { p->upstream_done = 1; } - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0, - "http fastcgi closed stdout"); - continue; } @@ -2143,6 +2185,18 @@ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0, "http fastcgi sent end request"); + if (f->rest > 0) { + ngx_log_error(NGX_LOG_ERR, p->log, 0, + "upstream prematurely closed " + "FastCGI request"); + + p->upstream_error = 1; + p->upstream_eof = 0; + f->closed = 1; + + break; + } + if (!flcf->keep_conn) { p->upstream_done = 1; break; @@ -2252,6 +2306,18 @@ break; } + if (f->rest == -2) { + f->rest = r->upstream->headers_in.content_length_n; + } + + if (f->rest == 0) { + ngx_log_error(NGX_LOG_WARN, p->log, 0, + "upstream sent more data than specified in " + "\"Content-Length\" header"); + p->upstream_done = 1; + break; + } + cl = ngx_chain_get_free_buf(p->pool, &p->free); if (cl == NULL) { return NGX_ERROR; @@ -2289,15 +2355,27 @@ f->pos += f->length; b->last = f->pos; - continue; + } else { + f->length -= f->last - f->pos; + f->pos = f->last; + b->last = f->last; } - f->length -= f->last - f->pos; + if (f->rest > 0) { - b->last = f->last; + if (b->last - b->pos > f->rest) { + ngx_log_error(NGX_LOG_WARN, p->log, 0, + "upstream sent more data than specified in " + "\"Content-Length\" header"); - break; + b->last = b->pos + f->rest; + p->upstream_done = 1; + break; + } + + f->rest -= b->last - b->pos; + } } if (flcf->keep_conn) { @@ -2391,6 +2469,14 @@ if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) { + if (f->rest > 0) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream prematurely closed " + "FastCGI request"); + u->error = 1; + break; + } + if (f->pos + f->padding < f->last) { u->length = 0; break; @@ -2486,6 +2572,14 @@ break; } + if (f->rest == 0) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "upstream sent more data than specified in " + "\"Content-Length\" header"); + u->length = 0; + break; + } + cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs); if (cl == NULL) { return NGX_ERROR; @@ -2510,13 +2604,27 @@ f->pos += f->length; b->last = f->pos; - continue; + } else { + f->length -= f->last - f->pos; + f->pos = f->last; + b->last = f->last; } - f->length -= f->last - f->pos; - b->last = f->last; + if (f->rest > 0) { + + if (b->last - b->pos > f->rest) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "upstream sent more data than specified in " + "\"Content-Length\" header"); - break; + b->last = b->pos + f->rest; + u->length = 0; + + break; + } + + f->rest -= b->last - b->pos; + } } return NGX_OK; diff -Nru nginx-1.18.0/src/http/modules/ngx_http_flv_module.c nginx-1.20.1/src/http/modules/ngx_http_flv_module.c --- nginx-1.18.0/src/http/modules/ngx_http_flv_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/modules/ngx_http_flv_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -156,12 +156,6 @@ } if (!of.is_file) { - - if (ngx_close_file(of.fd) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, - ngx_close_file_n " \"%s\" failed", path.data); - } - return NGX_DECLINED; } diff -Nru nginx-1.18.0/src/http/modules/ngx_http_grpc_module.c nginx-1.20.1/src/http/modules/ngx_http_grpc_module.c --- nginx-1.18.0/src/http/modules/ngx_http_grpc_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/modules/ngx_http_grpc_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -40,6 +40,7 @@ ngx_str_t ssl_certificate; ngx_str_t ssl_certificate_key; ngx_array_t *ssl_passwords; + ngx_array_t *ssl_conf_commands; #endif } ngx_http_grpc_loc_conf_t; @@ -84,6 +85,8 @@ ngx_uint_t pings; ngx_uint_t settings; + off_t length; + ssize_t send_window; size_t recv_window; @@ -120,6 +123,7 @@ unsigned end_stream:1; unsigned done:1; unsigned status:1; + unsigned rst:1; ngx_http_request_t *request; @@ -205,6 +209,8 @@ #if (NGX_HTTP_SSL) static char *ngx_http_grpc_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_grpc_ssl_conf_command_check(ngx_conf_t *cf, void *post, + void *data); static ngx_int_t ngx_http_grpc_set_ssl(ngx_conf_t *cf, ngx_http_grpc_loc_conf_t *glcf); #endif @@ -239,6 +245,9 @@ { ngx_null_string, 0 } }; +static ngx_conf_post_t ngx_http_grpc_ssl_conf_command_post = + { ngx_http_grpc_ssl_conf_command_check }; + #endif @@ -435,6 +444,13 @@ 0, NULL }, + { ngx_string("grpc_ssl_conf_command"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, + ngx_conf_set_keyval_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_grpc_loc_conf_t, ssl_conf_commands), + &ngx_http_grpc_ssl_conf_command_post }, + #endif ngx_null_command @@ -1125,20 +1141,11 @@ f->flags |= NGX_HTTP_V2_END_HEADERS_FLAG; -#if (NGX_DEBUG) - if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) { - u_char buf[512]; - size_t n, m; - - n = ngx_min(b->last - b->pos, 256); - m = ngx_hex_dump(buf, b->pos, n) - buf; - - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc header: %*s%s, len: %uz", - m, buf, b->last - b->pos > 256 ? "..." : "", - b->last - b->pos); - } -#endif + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "grpc header: %*xs%s, len: %uz", + (size_t) ngx_min(b->last - b->pos, 256), b->pos, + b->last - b->pos > 256 ? "..." : "", + b->last - b->pos); if (r->request_body_no_buffering) { @@ -1205,6 +1212,7 @@ ctx->end_stream = 0; ctx->done = 0; ctx->status = 0; + ctx->rst = 0; ctx->connection = NULL; return NGX_OK; @@ -1587,20 +1595,11 @@ u = r->upstream; b = &u->buffer; -#if (NGX_DEBUG) - if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) { - u_char buf[512]; - size_t n, m; - - n = ngx_min(b->last - b->pos, 256); - m = ngx_hex_dump(buf, b->pos, n) - buf; - - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc response: %*s%s, len: %uz", - m, buf, b->last - b->pos > 256 ? "..." : "", - b->last - b->pos); - } -#endif + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "grpc response: %*xs%s, len: %uz", + (size_t) ngx_min(b->last - b->pos, 256), + b->pos, b->last - b->pos > 256 ? "..." : "", + b->last - b->pos); ctx = ngx_http_grpc_get_ctx(r); @@ -1951,10 +1950,29 @@ r = ctx->request; u = r->upstream; - u->length = 1; + if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT + || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED + || r->method == NGX_HTTP_HEAD) + { + ctx->length = 0; + + } else { + ctx->length = u->headers_in.content_length_n; + } if (ctx->end_stream) { + + if (ctx->length > 0) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream prematurely closed stream"); + return NGX_ERROR; + } + u->length = 0; + ctx->done = 1; + + } else { + u->length = 1; } return NGX_OK; @@ -1997,6 +2015,12 @@ if (ctx->done) { + if (ctx->length > 0) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream prematurely closed stream"); + return NGX_ERROR; + } + /* * We have finished parsing the response and the * remaining control frames. If there are unsent @@ -2088,7 +2112,10 @@ return NGX_ERROR; } - if (ctx->stream_id && ctx->done) { + if (ctx->stream_id && ctx->done + && ctx->type != NGX_HTTP_V2_RST_STREAM_FRAME + && ctx->type != NGX_HTTP_V2_WINDOW_UPDATE_FRAME) + { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "upstream sent frame for closed stream %ui", ctx->stream_id); @@ -2131,11 +2158,21 @@ return NGX_ERROR; } - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream rejected request with error %ui", - ctx->error); + if (ctx->error || !ctx->done) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream rejected request with error %ui", + ctx->error); + return NGX_ERROR; + } - return NGX_ERROR; + if (ctx->rst) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent frame for closed stream %ui", + ctx->stream_id); + return NGX_ERROR; + } + + ctx->rst = 1; } if (ctx->type == NGX_HTTP_V2_GOAWAY_FRAME) { @@ -2402,6 +2439,18 @@ b->pos = b->last; buf->last = b->pos; + if (ctx->length != -1) { + + if (buf->last - buf->pos > ctx->length) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent response body larger " + "than indicated content length"); + return NGX_ERROR; + } + + ctx->length -= buf->last - buf->pos; + } + return NGX_AGAIN; } @@ -2409,6 +2458,18 @@ buf->last = b->pos; ctx->rest = ctx->padding; + if (ctx->length != -1) { + + if (buf->last - buf->pos > ctx->length) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent response body larger " + "than indicated content length"); + return NGX_ERROR; + } + + ctx->length -= buf->last - buf->pos; + } + done: if (ctx->padding) { @@ -4272,7 +4333,6 @@ * conf->upstream.hide_headers_hash = { NULL, 0 }; * conf->upstream.ssl_name = NULL; * - * conf->headers_source = NULL; * conf->headers.lengths = NULL; * conf->headers.values = NULL; * conf->headers.hash = { NULL, 0 }; @@ -4308,6 +4368,7 @@ conf->upstream.ssl_verify = NGX_CONF_UNSET; conf->ssl_verify_depth = NGX_CONF_UNSET_UINT; conf->ssl_passwords = NGX_CONF_UNSET_PTR; + conf->ssl_conf_commands = NGX_CONF_UNSET_PTR; #endif /* the hardcoded values */ @@ -4325,6 +4386,8 @@ conf->upstream.pass_trailers = 1; conf->upstream.preserve_output = 1; + conf->headers_source = NGX_CONF_UNSET_PTR; + ngx_str_set(&conf->upstream.module, "grpc"); return conf; @@ -4416,6 +4479,9 @@ prev->ssl_certificate_key, ""); ngx_conf_merge_ptr_value(conf->ssl_passwords, prev->ssl_passwords, NULL); + ngx_conf_merge_ptr_value(conf->ssl_conf_commands, + prev->ssl_conf_commands, NULL); + if (conf->ssl && ngx_http_grpc_set_ssl(cf, conf) != NGX_OK) { return NGX_CONF_ERROR; } @@ -4455,9 +4521,10 @@ clcf->handler = ngx_http_grpc_handler; } - if (conf->headers_source == NULL) { + ngx_conf_merge_ptr_value(conf->headers_source, prev->headers_source, NULL); + + if (conf->headers_source == prev->headers_source) { conf->headers = prev->headers; - conf->headers_source = prev->headers_source; conf->host_set = prev->host_set; } @@ -4782,6 +4849,17 @@ } +static char * +ngx_http_grpc_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data) +{ +#ifndef SSL_CONF_FLAG_FILE + return "is not supported on this platform"; +#else + return NGX_CONF_OK; +#endif +} + + static ngx_int_t ngx_http_grpc_set_ssl(ngx_conf_t *cf, ngx_http_grpc_loc_conf_t *glcf) { @@ -4872,6 +4950,12 @@ #endif + if (ngx_ssl_conf_commands(cf, glcf->upstream.ssl, glcf->ssl_conf_commands) + != NGX_OK) + { + return NGX_ERROR; + } + return NGX_OK; } diff -Nru nginx-1.18.0/src/http/modules/ngx_http_gzip_filter_module.c nginx-1.20.1/src/http/modules/ngx_http_gzip_filter_module.c --- nginx-1.18.0/src/http/modules/ngx_http_gzip_filter_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/modules/ngx_http_gzip_filter_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -56,7 +56,7 @@ unsigned done:1; unsigned nomem:1; unsigned buffering:1; - unsigned intel:1; + unsigned zlib_ng:1; size_t zin; size_t zout; @@ -213,7 +213,7 @@ static ngx_http_output_header_filter_pt ngx_http_next_header_filter; static ngx_http_output_body_filter_pt ngx_http_next_body_filter; -static ngx_uint_t ngx_http_gzip_assume_intel; +static ngx_uint_t ngx_http_gzip_assume_zlib_ng; static ngx_int_t @@ -501,18 +501,21 @@ * 8K is for zlib deflate_state, it takes * *) 5816 bytes on i386 and sparc64 (32-bit mode) * *) 5920 bytes on amd64 and sparc64 + * + * A zlib variant from Intel (https://github.com/jtkukunas/zlib) + * uses additional 16-byte padding in one of window-sized buffers. */ - if (!ngx_http_gzip_assume_intel) { - ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9)); + if (!ngx_http_gzip_assume_zlib_ng) { + ctx->allocated = 8192 + 16 + (1 << (wbits + 2)) + + (1 << (memlevel + 9)); } else { /* - * A zlib variant from Intel, https://github.com/jtkukunas/zlib. - * It can force window bits to 13 for fast compression level, - * on processors with SSE 4.2 it uses 64K hash instead of scaling - * it from the specified memory level, and also introduces - * 16-byte padding in one out of the two window-sized buffers. + * Another zlib variant, https://github.com/zlib-ng/zlib-ng. + * It forces window bits to 13 for fast compression level, + * uses 16-byte padding in one of window-sized buffers, and + * uses 128K hash. */ if (conf->level == 1) { @@ -520,9 +523,8 @@ } ctx->allocated = 8192 + 16 + (1 << (wbits + 2)) - + (1 << (ngx_max(memlevel, 8) + 8)) - + (1 << (memlevel + 8)); - ctx->intel = 1; + + 131072 + (1 << (memlevel + 8)); + ctx->zlib_ng = 1; } } @@ -945,13 +947,13 @@ return p; } - if (ctx->intel) { + if (ctx->zlib_ng) { ngx_log_error(NGX_LOG_ALERT, ctx->request->connection->log, 0, "gzip filter failed to use preallocated memory: " "%ud of %ui", items * size, ctx->allocated); } else { - ngx_http_gzip_assume_intel = 1; + ngx_http_gzip_assume_zlib_ng = 1; } p = ngx_palloc(ctx->request->pool, items * size); diff -Nru nginx-1.18.0/src/http/modules/ngx_http_limit_req_module.c nginx-1.20.1/src/http/modules/ngx_http_limit_req_module.c --- nginx-1.18.0/src/http/modules/ngx_http_limit_req_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/modules/ngx_http_limit_req_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -69,6 +69,8 @@ ngx_uint_t hash, ngx_str_t *key, ngx_uint_t *ep, ngx_uint_t account); static ngx_msec_t ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits, ngx_uint_t n, ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit); +static void ngx_http_limit_req_unlock(ngx_http_limit_req_limit_t *limits, + ngx_uint_t n); static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n); @@ -223,6 +225,7 @@ ctx = limit->shm_zone->data; if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) { + ngx_http_limit_req_unlock(limits, n); return NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -270,21 +273,7 @@ &limit->shm_zone->shm.name); } - while (n--) { - ctx = limits[n].shm_zone->data; - - if (ctx->node == NULL) { - continue; - } - - ngx_shmtx_lock(&ctx->shpool->mutex); - - ctx->node->count--; - - ngx_shmtx_unlock(&ctx->shpool->mutex); - - ctx->node = NULL; - } + ngx_http_limit_req_unlock(limits, n); if (lrcf->dry_run) { r->main->limit_req_status = NGX_HTTP_LIMIT_REQ_REJECTED_DRY_RUN; @@ -321,8 +310,13 @@ r->main->limit_req_status = NGX_HTTP_LIMIT_REQ_DELAYED; - if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; + if (r->connection->read->ready) { + ngx_post_event(r->connection->read, &ngx_posted_events); + + } else { + if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } } r->read_event_handler = ngx_http_test_reading; @@ -612,6 +606,29 @@ } +static void +ngx_http_limit_req_unlock(ngx_http_limit_req_limit_t *limits, ngx_uint_t n) +{ + ngx_http_limit_req_ctx_t *ctx; + + while (n--) { + ctx = limits[n].shm_zone->data; + + if (ctx->node == NULL) { + continue; + } + + ngx_shmtx_lock(&ctx->shpool->mutex); + + ctx->node->count--; + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + ctx->node = NULL; + } +} + + static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n) { diff -Nru nginx-1.18.0/src/http/modules/ngx_http_memcached_module.c nginx-1.20.1/src/http/modules/ngx_http_memcached_module.c --- nginx-1.18.0/src/http/modules/ngx_http_memcached_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/modules/ngx_http_memcached_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -485,10 +485,11 @@ if (u->length == (ssize_t) ctx->rest) { - if (ngx_strncmp(b->last, + if (bytes > u->length + || ngx_strncmp(b->last, ngx_http_memcached_end + NGX_HTTP_MEMCACHED_END - ctx->rest, bytes) - != 0) + != 0) { ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0, "memcached sent invalid trailer"); @@ -540,7 +541,9 @@ last += (size_t) (u->length - NGX_HTTP_MEMCACHED_END); - if (ngx_strncmp(last, ngx_http_memcached_end, b->last - last) != 0) { + if (bytes > u->length + || ngx_strncmp(last, ngx_http_memcached_end, b->last - last) != 0) + { ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0, "memcached sent invalid trailer"); diff -Nru nginx-1.18.0/src/http/modules/ngx_http_mp4_module.c nginx-1.20.1/src/http/modules/ngx_http_mp4_module.c --- nginx-1.18.0/src/http/modules/ngx_http_mp4_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/modules/ngx_http_mp4_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -521,12 +521,6 @@ } if (!of.is_file) { - - if (ngx_close_file(of.fd) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, - ngx_close_file_n " \"%s\" failed", path.data); - } - return NGX_DECLINED; } diff -Nru nginx-1.18.0/src/http/modules/ngx_http_proxy_module.c nginx-1.20.1/src/http/modules/ngx_http_proxy_module.c --- nginx-1.18.0/src/http/modules/ngx_http_proxy_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/modules/ngx_http_proxy_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -10,6 +10,19 @@ #include +#define NGX_HTTP_PROXY_COOKIE_SECURE 0x0001 +#define NGX_HTTP_PROXY_COOKIE_SECURE_ON 0x0002 +#define NGX_HTTP_PROXY_COOKIE_SECURE_OFF 0x0004 +#define NGX_HTTP_PROXY_COOKIE_HTTPONLY 0x0008 +#define NGX_HTTP_PROXY_COOKIE_HTTPONLY_ON 0x0010 +#define NGX_HTTP_PROXY_COOKIE_HTTPONLY_OFF 0x0020 +#define NGX_HTTP_PROXY_COOKIE_SAMESITE 0x0040 +#define NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT 0x0080 +#define NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX 0x0100 +#define NGX_HTTP_PROXY_COOKIE_SAMESITE_NONE 0x0200 +#define NGX_HTTP_PROXY_COOKIE_SAMESITE_OFF 0x0400 + + typedef struct { ngx_array_t caches; /* ngx_http_file_cache_t * */ } ngx_http_proxy_main_conf_t; @@ -18,7 +31,7 @@ typedef struct ngx_http_proxy_rewrite_s ngx_http_proxy_rewrite_t; typedef ngx_int_t (*ngx_http_proxy_rewrite_pt)(ngx_http_request_t *r, - ngx_table_elt_t *h, size_t prefix, size_t len, + ngx_str_t *value, size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr); struct ngx_http_proxy_rewrite_s { @@ -36,6 +49,19 @@ typedef struct { + union { + ngx_http_complex_value_t complex; +#if (NGX_PCRE) + ngx_http_regex_t *regex; +#endif + } cookie; + + ngx_array_t flags_values; + ngx_uint_t regex; +} ngx_http_proxy_cookie_flags_t; + + +typedef struct { ngx_str_t key_start; ngx_str_t schema; ngx_str_t host_header; @@ -72,6 +98,7 @@ ngx_array_t *redirects; ngx_array_t *cookie_domains; ngx_array_t *cookie_paths; + ngx_array_t *cookie_flags; ngx_http_complex_value_t *method; ngx_str_t location; @@ -100,6 +127,7 @@ ngx_str_t ssl_certificate; ngx_str_t ssl_certificate_key; ngx_array_t *ssl_passwords; + ngx_array_t *ssl_conf_commands; #endif } ngx_http_proxy_loc_conf_t; @@ -158,10 +186,16 @@ ngx_table_elt_t *h, size_t prefix); static ngx_int_t ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, ngx_table_elt_t *h); +static ngx_int_t ngx_http_proxy_parse_cookie(ngx_str_t *value, + ngx_array_t *attrs); static ngx_int_t ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r, - ngx_table_elt_t *h, u_char *value, ngx_array_t *rewrites); + ngx_str_t *value, ngx_array_t *rewrites); +static ngx_int_t ngx_http_proxy_rewrite_cookie_flags(ngx_http_request_t *r, + ngx_array_t *attrs, ngx_array_t *flags); +static ngx_int_t ngx_http_proxy_edit_cookie_flags(ngx_http_request_t *r, + ngx_array_t *attrs, ngx_uint_t flags); static ngx_int_t ngx_http_proxy_rewrite(ngx_http_request_t *r, - ngx_table_elt_t *h, size_t prefix, size_t len, ngx_str_t *replacement); + ngx_str_t *value, size_t prefix, size_t len, ngx_str_t *replacement); static ngx_int_t ngx_http_proxy_add_variables(ngx_conf_t *cf); static void *ngx_http_proxy_create_main_conf(ngx_conf_t *cf); @@ -180,6 +214,8 @@ void *conf); static char *ngx_http_proxy_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_proxy_cookie_flags(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static char *ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); #if (NGX_HTTP_CACHE) @@ -194,6 +230,10 @@ #endif static char *ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data); +#if (NGX_HTTP_SSL) +static char *ngx_http_proxy_ssl_conf_command_check(ngx_conf_t *cf, void *post, + void *data); +#endif static ngx_int_t ngx_http_proxy_rewrite_regex(ngx_conf_t *cf, ngx_http_proxy_rewrite_t *pr, ngx_str_t *regex, ngx_uint_t caseless); @@ -239,6 +279,9 @@ { ngx_null_string, 0 } }; +static ngx_conf_post_t ngx_http_proxy_ssl_conf_command_post = + { ngx_http_proxy_ssl_conf_command_check }; + #endif @@ -282,6 +325,13 @@ 0, NULL }, + { ngx_string("proxy_cookie_flags"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234, + ngx_http_proxy_cookie_flags, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + { ngx_string("proxy_store"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_http_proxy_store, @@ -722,6 +772,13 @@ 0, NULL }, + { ngx_string("proxy_ssl_conf_command"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, + ngx_conf_set_keyval_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, ssl_conf_commands), + &ngx_http_proxy_ssl_conf_command_post }, + #endif ngx_null_command @@ -845,6 +902,36 @@ }; +static ngx_conf_bitmask_t ngx_http_proxy_cookie_flags_masks[] = { + + { ngx_string("secure"), + NGX_HTTP_PROXY_COOKIE_SECURE|NGX_HTTP_PROXY_COOKIE_SECURE_ON }, + + { ngx_string("nosecure"), + NGX_HTTP_PROXY_COOKIE_SECURE|NGX_HTTP_PROXY_COOKIE_SECURE_OFF }, + + { ngx_string("httponly"), + NGX_HTTP_PROXY_COOKIE_HTTPONLY|NGX_HTTP_PROXY_COOKIE_HTTPONLY_ON }, + + { ngx_string("nohttponly"), + NGX_HTTP_PROXY_COOKIE_HTTPONLY|NGX_HTTP_PROXY_COOKIE_HTTPONLY_OFF }, + + { ngx_string("samesite=strict"), + NGX_HTTP_PROXY_COOKIE_SAMESITE|NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT }, + + { ngx_string("samesite=lax"), + NGX_HTTP_PROXY_COOKIE_SAMESITE|NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX }, + + { ngx_string("samesite=none"), + NGX_HTTP_PROXY_COOKIE_SAMESITE|NGX_HTTP_PROXY_COOKIE_SAMESITE_NONE }, + + { ngx_string("nosamesite"), + NGX_HTTP_PROXY_COOKIE_SAMESITE|NGX_HTTP_PROXY_COOKIE_SAMESITE_OFF }, + + { ngx_null_string, 0 } +}; + + static ngx_int_t ngx_http_proxy_handler(ngx_http_request_t *r) { @@ -906,7 +993,7 @@ u->rewrite_redirect = ngx_http_proxy_rewrite_redirect; } - if (plcf->cookie_domains || plcf->cookie_paths) { + if (plcf->cookie_domains || plcf->cookie_paths || plcf->cookie_flags) { u->rewrite_cookie = ngx_http_proxy_rewrite_cookie; } @@ -2015,6 +2102,25 @@ return NGX_OK; } + if (p->upstream_done) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0, + "http proxy data after close"); + return NGX_OK; + } + + if (p->length == 0) { + + ngx_log_error(NGX_LOG_WARN, p->log, 0, + "upstream sent more data than specified in " + "\"Content-Length\" header"); + + r = p->input_ctx; + r->upstream->keepalive = 0; + p->upstream_done = 1; + + return NGX_OK; + } + cl = ngx_chain_get_free_buf(p->pool, &p->free); if (cl == NULL) { return NGX_ERROR; @@ -2042,20 +2148,23 @@ return NGX_OK; } + if (b->last - b->pos > p->length) { + + ngx_log_error(NGX_LOG_WARN, p->log, 0, + "upstream sent more data than specified in " + "\"Content-Length\" header"); + + b->last = b->pos + p->length; + p->upstream_done = 1; + + return NGX_OK; + } + p->length -= b->last - b->pos; if (p->length == 0) { r = p->input_ctx; - p->upstream_done = 1; r->upstream->keepalive = !r->upstream->headers_in.connection_close; - - } else if (p->length < 0) { - r = p->input_ctx; - p->upstream_done = 1; - - ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, - "upstream sent more data than specified in " - "\"Content-Length\" header"); } return NGX_OK; @@ -2082,6 +2191,23 @@ return NGX_ERROR; } + if (p->upstream_done) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0, + "http proxy data after close"); + return NGX_OK; + } + + if (p->length == 0) { + + ngx_log_error(NGX_LOG_WARN, p->log, 0, + "upstream sent data after final chunk"); + + r->upstream->keepalive = 0; + p->upstream_done = 1; + + return NGX_OK; + } + b = NULL; prev = &buf->shadow; @@ -2144,9 +2270,15 @@ /* a whole response has been parsed successfully */ - p->upstream_done = 1; + p->length = 0; r->upstream->keepalive = !r->upstream->headers_in.connection_close; + if (buf->pos != buf->last) { + ngx_log_error(NGX_LOG_WARN, p->log, 0, + "upstream sent data after final chunk"); + r->upstream->keepalive = 0; + } + break; } @@ -2161,13 +2293,13 @@ /* invalid response */ - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + ngx_log_error(NGX_LOG_ERR, p->log, 0, "upstream sent invalid chunked response"); return NGX_ERROR; } - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, p->log, 0, "http proxy chunked state %ui, length %O", ctx->chunked.state, p->length); @@ -2202,6 +2334,13 @@ u = r->upstream; + if (u->length == 0) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "upstream sent more data than specified in " + "\"Content-Length\" header"); + return NGX_OK; + } + for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) { ll = &cl->next; } @@ -2227,6 +2366,18 @@ return NGX_OK; } + if (bytes > u->length) { + + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "upstream sent more data than specified in " + "\"Content-Length\" header"); + + cl->buf->last = cl->buf->pos + u->length; + u->length = 0; + + return NGX_OK; + } + u->length -= bytes; if (u->length == 0) { @@ -2313,6 +2464,12 @@ u->keepalive = !u->headers_in.connection_close; u->length = 0; + if (buf->pos != buf->last) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "upstream sent data after final chunk"); + u->keepalive = 0; + } + break; } @@ -2521,7 +2678,7 @@ len = h->value.len - prefix; for (i = 0; i < plcf->redirects->nelts; i++) { - rc = pr[i].handler(r, h, prefix, len, &pr[i]); + rc = pr[i].handler(r, &h->value, prefix, len, &pr[i]); if (rc != NGX_DECLINED) { return rc; @@ -2535,27 +2692,43 @@ static ngx_int_t ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, ngx_table_elt_t *h) { - size_t prefix; u_char *p; + size_t len; ngx_int_t rc, rv; + ngx_str_t *key, *value; + ngx_uint_t i; + ngx_array_t attrs; + ngx_keyval_t *attr; ngx_http_proxy_loc_conf_t *plcf; - p = (u_char *) ngx_strchr(h->value.data, ';'); - if (p == NULL) { - return NGX_DECLINED; + if (ngx_array_init(&attrs, r->pool, 2, sizeof(ngx_keyval_t)) != NGX_OK) { + return NGX_ERROR; } - prefix = p + 1 - h->value.data; + if (ngx_http_proxy_parse_cookie(&h->value, &attrs) != NGX_OK) { + return NGX_ERROR; + } + + attr = attrs.elts; + + if (attr[0].value.data == NULL) { + return NGX_DECLINED; + } rv = NGX_DECLINED; plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); - if (plcf->cookie_domains) { - p = ngx_strcasestrn(h->value.data + prefix, "domain=", 7 - 1); + for (i = 1; i < attrs.nelts; i++) { - if (p) { - rc = ngx_http_proxy_rewrite_cookie_value(r, h, p + 7, + key = &attr[i].key; + value = &attr[i].value; + + if (plcf->cookie_domains && key->len == 6 + && ngx_strncasecmp(key->data, (u_char *) "domain", 6) == 0 + && value->data) + { + rc = ngx_http_proxy_rewrite_cookie_value(r, value, plcf->cookie_domains); if (rc == NGX_ERROR) { return NGX_ERROR; @@ -2565,13 +2738,12 @@ rv = rc; } } - } - - if (plcf->cookie_paths) { - p = ngx_strcasestrn(h->value.data + prefix, "path=", 5 - 1); - if (p) { - rc = ngx_http_proxy_rewrite_cookie_value(r, h, p + 5, + if (plcf->cookie_paths && key->len == 4 + && ngx_strncasecmp(key->data, (u_char *) "path", 4) == 0 + && value->data) + { + rc = ngx_http_proxy_rewrite_cookie_value(r, value, plcf->cookie_paths); if (rc == NGX_ERROR) { return NGX_ERROR; @@ -2583,30 +2755,153 @@ } } - return rv; + if (plcf->cookie_flags) { + rc = ngx_http_proxy_rewrite_cookie_flags(r, &attrs, + plcf->cookie_flags); + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc != NGX_DECLINED) { + rv = rc; + } + + attr = attrs.elts; + } + + if (rv != NGX_OK) { + return rv; + } + + len = 0; + + for (i = 0; i < attrs.nelts; i++) { + + if (attr[i].key.data == NULL) { + continue; + } + + if (i > 0) { + len += 2; + } + + len += attr[i].key.len; + + if (attr[i].value.data) { + len += 1 + attr[i].value.len; + } + } + + p = ngx_pnalloc(r->pool, len + 1); + if (p == NULL) { + return NGX_ERROR; + } + + h->value.data = p; + h->value.len = len; + + for (i = 0; i < attrs.nelts; i++) { + + if (attr[i].key.data == NULL) { + continue; + } + + if (i > 0) { + *p++ = ';'; + *p++ = ' '; + } + + p = ngx_cpymem(p, attr[i].key.data, attr[i].key.len); + + if (attr[i].value.data) { + *p++ = '='; + p = ngx_cpymem(p, attr[i].value.data, attr[i].value.len); + } + } + + *p = '\0'; + + return NGX_OK; } static ngx_int_t -ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r, ngx_table_elt_t *h, - u_char *value, ngx_array_t *rewrites) +ngx_http_proxy_parse_cookie(ngx_str_t *value, ngx_array_t *attrs) { - size_t len, prefix; - u_char *p; - ngx_int_t rc; - ngx_uint_t i; - ngx_http_proxy_rewrite_t *pr; + u_char *start, *end, *p, *last; + ngx_str_t name, val; + ngx_keyval_t *attr; + + start = value->data; + end = value->data + value->len; + + for ( ;; ) { + + last = (u_char *) ngx_strchr(start, ';'); + + if (last == NULL) { + last = end; + } + + while (start < last && *start == ' ') { start++; } - prefix = value - h->value.data; + for (p = start; p < last && *p != '='; p++) { /* void */ } - p = (u_char *) ngx_strchr(value, ';'); + name.data = start; + name.len = p - start; - len = p ? (size_t) (p - value) : (h->value.len - prefix); + while (name.len && name.data[name.len - 1] == ' ') { + name.len--; + } + + if (p < last) { + + p++; + + while (p < last && *p == ' ') { p++; } + + val.data = p; + val.len = last - val.data; + + while (val.len && val.data[val.len - 1] == ' ') { + val.len--; + } + + } else { + ngx_str_null(&val); + } + + attr = ngx_array_push(attrs); + if (attr == NULL) { + return NGX_ERROR; + } + + attr->key = name; + attr->value = val; + + if (last == end) { + break; + } + + start = last + 1; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r, ngx_str_t *value, + ngx_array_t *rewrites) +{ + ngx_int_t rc; + ngx_uint_t i; + ngx_http_proxy_rewrite_t *pr; pr = rewrites->elts; for (i = 0; i < rewrites->nelts; i++) { - rc = pr[i].handler(r, h, prefix, len, &pr[i]); + rc = pr[i].handler(r, value, 0, value->len, &pr[i]); if (rc != NGX_DECLINED) { return rc; @@ -2618,8 +2913,236 @@ static ngx_int_t -ngx_http_proxy_rewrite_complex_handler(ngx_http_request_t *r, - ngx_table_elt_t *h, size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr) +ngx_http_proxy_rewrite_cookie_flags(ngx_http_request_t *r, ngx_array_t *attrs, + ngx_array_t *flags) +{ + ngx_str_t pattern, value; +#if (NGX_PCRE) + ngx_int_t rc; +#endif + ngx_uint_t i, m, f, nelts; + ngx_keyval_t *attr; + ngx_conf_bitmask_t *mask; + ngx_http_complex_value_t *flags_values; + ngx_http_proxy_cookie_flags_t *pcf; + + attr = attrs->elts; + pcf = flags->elts; + + for (i = 0; i < flags->nelts; i++) { + +#if (NGX_PCRE) + if (pcf[i].regex) { + rc = ngx_http_regex_exec(r, pcf[i].cookie.regex, &attr[0].key); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_OK) { + break; + } + + /* NGX_DECLINED */ + + continue; + } +#endif + + if (ngx_http_complex_value(r, &pcf[i].cookie.complex, &pattern) + != NGX_OK) + { + return NGX_ERROR; + } + + if (pattern.len == attr[0].key.len + && ngx_strncasecmp(attr[0].key.data, pattern.data, pattern.len) + == 0) + { + break; + } + } + + if (i == flags->nelts) { + return NGX_DECLINED; + } + + nelts = pcf[i].flags_values.nelts; + flags_values = pcf[i].flags_values.elts; + + mask = ngx_http_proxy_cookie_flags_masks; + f = 0; + + for (i = 0; i < nelts; i++) { + + if (ngx_http_complex_value(r, &flags_values[i], &value) != NGX_OK) { + return NGX_ERROR; + } + + if (value.len == 0) { + continue; + } + + for (m = 0; mask[m].name.len != 0; m++) { + + if (mask[m].name.len != value.len + || ngx_strncasecmp(mask[m].name.data, value.data, value.len) + != 0) + { + continue; + } + + f |= mask[m].mask; + + break; + } + + if (mask[m].name.len == 0) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "invalid proxy_cookie_flags flag \"%V\"", &value); + } + } + + if (f == 0) { + return NGX_DECLINED; + } + + return ngx_http_proxy_edit_cookie_flags(r, attrs, f); +} + + +static ngx_int_t +ngx_http_proxy_edit_cookie_flags(ngx_http_request_t *r, ngx_array_t *attrs, + ngx_uint_t flags) +{ + ngx_str_t *key, *value; + ngx_uint_t i; + ngx_keyval_t *attr; + + attr = attrs->elts; + + for (i = 1; i < attrs->nelts; i++) { + key = &attr[i].key; + + if (key->len == 6 + && ngx_strncasecmp(key->data, (u_char *) "secure", 6) == 0) + { + if (flags & NGX_HTTP_PROXY_COOKIE_SECURE_ON) { + flags &= ~NGX_HTTP_PROXY_COOKIE_SECURE_ON; + + } else if (flags & NGX_HTTP_PROXY_COOKIE_SECURE_OFF) { + key->data = NULL; + } + + continue; + } + + if (key->len == 8 + && ngx_strncasecmp(key->data, (u_char *) "httponly", 8) == 0) + { + if (flags & NGX_HTTP_PROXY_COOKIE_HTTPONLY_ON) { + flags &= ~NGX_HTTP_PROXY_COOKIE_HTTPONLY_ON; + + } else if (flags & NGX_HTTP_PROXY_COOKIE_HTTPONLY_OFF) { + key->data = NULL; + } + + continue; + } + + if (key->len == 8 + && ngx_strncasecmp(key->data, (u_char *) "samesite", 8) == 0) + { + value = &attr[i].value; + + if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT) { + flags &= ~NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT; + + if (value->len != 6 + || ngx_strncasecmp(value->data, (u_char *) "strict", 6) + != 0) + { + ngx_str_set(key, "SameSite"); + ngx_str_set(value, "Strict"); + } + + } else if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX) { + flags &= ~NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX; + + if (value->len != 3 + || ngx_strncasecmp(value->data, (u_char *) "lax", 3) != 0) + { + ngx_str_set(key, "SameSite"); + ngx_str_set(value, "Lax"); + } + + } else if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_NONE) { + flags &= ~NGX_HTTP_PROXY_COOKIE_SAMESITE_NONE; + + if (value->len != 4 + || ngx_strncasecmp(value->data, (u_char *) "none", 4) != 0) + { + ngx_str_set(key, "SameSite"); + ngx_str_set(value, "None"); + } + + } else if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_OFF) { + key->data = NULL; + } + + continue; + } + } + + if (flags & NGX_HTTP_PROXY_COOKIE_SECURE_ON) { + attr = ngx_array_push(attrs); + if (attr == NULL) { + return NGX_ERROR; + } + + ngx_str_set(&attr->key, "Secure"); + ngx_str_null(&attr->value); + } + + if (flags & NGX_HTTP_PROXY_COOKIE_HTTPONLY_ON) { + attr = ngx_array_push(attrs); + if (attr == NULL) { + return NGX_ERROR; + } + + ngx_str_set(&attr->key, "HttpOnly"); + ngx_str_null(&attr->value); + } + + if (flags & (NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT + |NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX + |NGX_HTTP_PROXY_COOKIE_SAMESITE_NONE)) + { + attr = ngx_array_push(attrs); + if (attr == NULL) { + return NGX_ERROR; + } + + ngx_str_set(&attr->key, "SameSite"); + + if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT) { + ngx_str_set(&attr->value, "Strict"); + + } else if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX) { + ngx_str_set(&attr->value, "Lax"); + + } else { + ngx_str_set(&attr->value, "None"); + } + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_proxy_rewrite_complex_handler(ngx_http_request_t *r, ngx_str_t *value, + size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr) { ngx_str_t pattern, replacement; @@ -2628,8 +3151,7 @@ } if (pattern.len > len - || ngx_rstrncmp(h->value.data + prefix, pattern.data, - pattern.len) != 0) + || ngx_rstrncmp(value->data + prefix, pattern.data, pattern.len) != 0) { return NGX_DECLINED; } @@ -2638,20 +3160,20 @@ return NGX_ERROR; } - return ngx_http_proxy_rewrite(r, h, prefix, pattern.len, &replacement); + return ngx_http_proxy_rewrite(r, value, prefix, pattern.len, &replacement); } #if (NGX_PCRE) static ngx_int_t -ngx_http_proxy_rewrite_regex_handler(ngx_http_request_t *r, ngx_table_elt_t *h, +ngx_http_proxy_rewrite_regex_handler(ngx_http_request_t *r, ngx_str_t *value, size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr) { ngx_str_t pattern, replacement; pattern.len = len; - pattern.data = h->value.data + prefix; + pattern.data = value->data + prefix; if (ngx_http_regex_exec(r, pr->pattern.regex, &pattern) != NGX_OK) { return NGX_DECLINED; @@ -2661,20 +3183,15 @@ return NGX_ERROR; } - if (prefix == 0 && h->value.len == len) { - h->value = replacement; - return NGX_OK; - } - - return ngx_http_proxy_rewrite(r, h, prefix, len, &replacement); + return ngx_http_proxy_rewrite(r, value, prefix, len, &replacement); } #endif static ngx_int_t -ngx_http_proxy_rewrite_domain_handler(ngx_http_request_t *r, - ngx_table_elt_t *h, size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr) +ngx_http_proxy_rewrite_domain_handler(ngx_http_request_t *r, ngx_str_t *value, + size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr) { u_char *p; ngx_str_t pattern, replacement; @@ -2683,9 +3200,9 @@ return NGX_ERROR; } - p = h->value.data + prefix; + p = value->data + prefix; - if (p[0] == '.') { + if (len && p[0] == '.') { p++; prefix++; len--; @@ -2699,18 +3216,23 @@ return NGX_ERROR; } - return ngx_http_proxy_rewrite(r, h, prefix, len, &replacement); + return ngx_http_proxy_rewrite(r, value, prefix, len, &replacement); } static ngx_int_t -ngx_http_proxy_rewrite(ngx_http_request_t *r, ngx_table_elt_t *h, size_t prefix, +ngx_http_proxy_rewrite(ngx_http_request_t *r, ngx_str_t *value, size_t prefix, size_t len, ngx_str_t *replacement) { u_char *p, *data; size_t new_len; - new_len = replacement->len + h->value.len - len; + if (len == value->len) { + *value = *replacement; + return NGX_OK; + } + + new_len = replacement->len + value->len - len; if (replacement->len > len) { @@ -2719,23 +3241,22 @@ return NGX_ERROR; } - p = ngx_copy(data, h->value.data, prefix); + p = ngx_copy(data, value->data, prefix); p = ngx_copy(p, replacement->data, replacement->len); - ngx_memcpy(p, h->value.data + prefix + len, - h->value.len - len - prefix + 1); + ngx_memcpy(p, value->data + prefix + len, + value->len - len - prefix + 1); - h->value.data = data; + value->data = data; } else { - p = ngx_copy(h->value.data + prefix, replacement->data, - replacement->len); + p = ngx_copy(value->data + prefix, replacement->data, replacement->len); - ngx_memmove(p, h->value.data + prefix + len, - h->value.len - len - prefix + 1); + ngx_memmove(p, value->data + prefix + len, + value->len - len - prefix + 1); } - h->value.len = new_len; + value->len = new_len; return NGX_OK; } @@ -2811,7 +3332,6 @@ * conf->method = NULL; * conf->location = NULL; * conf->url = { 0, NULL }; - * conf->headers_source = NULL; * conf->headers.lengths = NULL; * conf->headers.values = NULL; * conf->headers.hash = { NULL, 0 }; @@ -2884,16 +3404,20 @@ conf->upstream.ssl_verify = NGX_CONF_UNSET; conf->ssl_verify_depth = NGX_CONF_UNSET_UINT; conf->ssl_passwords = NGX_CONF_UNSET_PTR; + conf->ssl_conf_commands = NGX_CONF_UNSET_PTR; #endif /* "proxy_cyclic_temp_file" is disabled */ conf->upstream.cyclic_temp_file = 0; + conf->headers_source = NGX_CONF_UNSET_PTR; + conf->redirect = NGX_CONF_UNSET; conf->upstream.change_buffering = 1; conf->cookie_domains = NGX_CONF_UNSET_PTR; conf->cookie_paths = NGX_CONF_UNSET_PTR; + conf->cookie_flags = NGX_CONF_UNSET_PTR; conf->http_version = NGX_CONF_UNSET_UINT; @@ -3228,6 +3752,9 @@ prev->ssl_certificate_key, ""); ngx_conf_merge_ptr_value(conf->ssl_passwords, prev->ssl_passwords, NULL); + ngx_conf_merge_ptr_value(conf->ssl_conf_commands, + prev->ssl_conf_commands, NULL); + if (conf->ssl && ngx_http_proxy_set_ssl(cf, conf) != NGX_OK) { return NGX_CONF_ERROR; } @@ -3289,6 +3816,8 @@ ngx_conf_merge_ptr_value(conf->cookie_paths, prev->cookie_paths, NULL); + ngx_conf_merge_ptr_value(conf->cookie_flags, prev->cookie_flags, NULL); + ngx_conf_merge_uint_value(conf->http_version, prev->http_version, NGX_HTTP_VERSION_10); @@ -3359,12 +3888,13 @@ } } - if (conf->headers_source == NULL) { + ngx_conf_merge_ptr_value(conf->headers_source, prev->headers_source, NULL); + + if (conf->headers_source == prev->headers_source) { conf->headers = prev->headers; #if (NGX_HTTP_CACHE) conf->headers_cache = prev->headers_cache; #endif - conf->headers_source = prev->headers_source; } rc = ngx_http_proxy_init_headers(cf, conf, &conf->headers, @@ -3703,7 +4233,7 @@ ngx_http_compile_complex_value_t ccv; if (plcf->redirect == 0) { - return NGX_CONF_OK; + return "is duplicate"; } plcf->redirect = 1; @@ -3712,16 +4242,12 @@ if (cf->args->nelts == 2) { if (ngx_strcmp(value[1].data, "off") == 0) { - plcf->redirect = 0; - plcf->redirects = NULL; - return NGX_CONF_OK; - } - if (ngx_strcmp(value[1].data, "false") == 0) { - ngx_conf_log_error(NGX_LOG_ERR, cf, 0, - "invalid parameter \"false\", use \"off\" instead"); + if (plcf->redirects) { + return "is duplicate"; + } + plcf->redirect = 0; - plcf->redirects = NULL; return NGX_CONF_OK; } @@ -3745,7 +4271,9 @@ return NGX_CONF_ERROR; } - if (ngx_strcmp(value[1].data, "default") == 0) { + if (cf->args->nelts == 2 + && ngx_strcmp(value[1].data, "default") == 0) + { if (plcf->proxy_lengths) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"proxy_redirect default\" cannot be used " @@ -3848,7 +4376,7 @@ ngx_http_compile_complex_value_t ccv; if (plcf->cookie_domains == NULL) { - return NGX_CONF_OK; + return "is duplicate"; } value = cf->args->elts; @@ -3856,6 +4384,11 @@ if (cf->args->nelts == 2) { if (ngx_strcmp(value[1].data, "off") == 0) { + + if (plcf->cookie_domains != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + plcf->cookie_domains = NULL; return NGX_CONF_OK; } @@ -3935,7 +4468,7 @@ ngx_http_compile_complex_value_t ccv; if (plcf->cookie_paths == NULL) { - return NGX_CONF_OK; + return "is duplicate"; } value = cf->args->elts; @@ -3943,6 +4476,11 @@ if (cf->args->nelts == 2) { if (ngx_strcmp(value[1].data, "off") == 0) { + + if (plcf->cookie_paths != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + plcf->cookie_paths = NULL; return NGX_CONF_OK; } @@ -4012,6 +4550,126 @@ } +static char * +ngx_http_proxy_cookie_flags(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_proxy_loc_conf_t *plcf = conf; + + ngx_str_t *value; + ngx_uint_t i; + ngx_http_complex_value_t *cv; + ngx_http_proxy_cookie_flags_t *pcf; + ngx_http_compile_complex_value_t ccv; +#if (NGX_PCRE) + ngx_regex_compile_t rc; + u_char errstr[NGX_MAX_CONF_ERRSTR]; +#endif + + if (plcf->cookie_flags == NULL) { + return "is duplicate"; + } + + value = cf->args->elts; + + if (cf->args->nelts == 2) { + + if (ngx_strcmp(value[1].data, "off") == 0) { + + if (plcf->cookie_flags != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + plcf->cookie_flags = NULL; + return NGX_CONF_OK; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[1]); + return NGX_CONF_ERROR; + } + + if (plcf->cookie_flags == NGX_CONF_UNSET_PTR) { + plcf->cookie_flags = ngx_array_create(cf->pool, 1, + sizeof(ngx_http_proxy_cookie_flags_t)); + if (plcf->cookie_flags == NULL) { + return NGX_CONF_ERROR; + } + } + + pcf = ngx_array_push(plcf->cookie_flags); + if (pcf == NULL) { + return NGX_CONF_ERROR; + } + + pcf->regex = 0; + + if (value[1].data[0] == '~') { + value[1].len--; + value[1].data++; + +#if (NGX_PCRE) + ngx_memzero(&rc, sizeof(ngx_regex_compile_t)); + + rc.pattern = value[1]; + rc.err.len = NGX_MAX_CONF_ERRSTR; + rc.err.data = errstr; + rc.options = NGX_REGEX_CASELESS; + + pcf->cookie.regex = ngx_http_regex_compile(cf, &rc); + if (pcf->cookie.regex == NULL) { + return NGX_CONF_ERROR; + } + + pcf->regex = 1; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "using regex \"%V\" requires PCRE library", + &value[1]); + return NGX_CONF_ERROR; +#endif + + } else { + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &pcf->cookie.complex; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + + if (ngx_array_init(&pcf->flags_values, cf->pool, cf->args->nelts - 2, + sizeof(ngx_http_complex_value_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + for (i = 2; i < cf->args->nelts; i++) { + + cv = ngx_array_push(&pcf->flags_values); + if (cv == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[i]; + ccv.complex_value = cv; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + + return NGX_CONF_OK; +} + + static ngx_int_t ngx_http_proxy_rewrite_regex(ngx_conf_t *cf, ngx_http_proxy_rewrite_t *pr, ngx_str_t *regex, ngx_uint_t caseless) @@ -4250,6 +4908,17 @@ #if (NGX_HTTP_SSL) +static char * +ngx_http_proxy_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data) +{ +#ifndef SSL_CONF_FLAG_FILE + return "is not supported on this platform"; +#else + return NGX_CONF_OK; +#endif +} + + static ngx_int_t ngx_http_proxy_set_ssl(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *plcf) { @@ -4325,6 +4994,12 @@ != NGX_OK) { return NGX_ERROR; + } + + if (ngx_ssl_conf_commands(cf, plcf->upstream.ssl, plcf->ssl_conf_commands) + != NGX_OK) + { + return NGX_ERROR; } return NGX_OK; diff -Nru nginx-1.18.0/src/http/modules/ngx_http_scgi_module.c nginx-1.20.1/src/http/modules/ngx_http_scgi_module.c --- nginx-1.18.0/src/http/modules/ngx_http_scgi_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/modules/ngx_http_scgi_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -49,6 +49,7 @@ static ngx_int_t ngx_http_scgi_reinit_request(ngx_http_request_t *r); static ngx_int_t ngx_http_scgi_process_status_line(ngx_http_request_t *r); static ngx_int_t ngx_http_scgi_process_header(ngx_http_request_t *r); +static ngx_int_t ngx_http_scgi_input_filter_init(void *data); static void ngx_http_scgi_abort_request(ngx_http_request_t *r); static void ngx_http_scgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc); @@ -534,6 +535,10 @@ u->pipe->input_filter = ngx_event_pipe_copy_input_filter; u->pipe->input_ctx = r; + u->input_filter_init = ngx_http_scgi_input_filter_init; + u->input_filter = ngx_http_upstream_non_buffered_filter; + u->input_filter_ctx = r; + if (!scf->upstream.request_buffering && scf->upstream.pass_request_body && !r->headers_in.chunked) @@ -1145,6 +1150,37 @@ } +static ngx_int_t +ngx_http_scgi_input_filter_init(void *data) +{ + ngx_http_request_t *r = data; + ngx_http_upstream_t *u; + + u = r->upstream; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http scgi filter init s:%ui l:%O", + u->headers_in.status_n, u->headers_in.content_length_n); + + if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT + || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED) + { + u->pipe->length = 0; + u->length = 0; + + } else if (r->method == NGX_HTTP_HEAD) { + u->pipe->length = -1; + u->length = -1; + + } else { + u->pipe->length = u->headers_in.content_length_n; + u->length = u->headers_in.content_length_n; + } + + return NGX_OK; +} + + static void ngx_http_scgi_abort_request(ngx_http_request_t *r) { diff -Nru nginx-1.18.0/src/http/modules/ngx_http_slice_filter_module.c nginx-1.20.1/src/http/modules/ngx_http_slice_filter_module.c --- nginx-1.18.0/src/http/modules/ngx_http_slice_filter_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/modules/ngx_http_slice_filter_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -180,6 +180,11 @@ r->headers_out.content_range->hash = 0; r->headers_out.content_range = NULL; + if (r->headers_out.accept_ranges) { + r->headers_out.accept_ranges->hash = 0; + r->headers_out.accept_ranges = NULL; + } + r->allow_ranges = 1; r->subrequest_ranges = 1; r->single_range = 1; diff -Nru nginx-1.18.0/src/http/modules/ngx_http_ssl_module.c nginx-1.20.1/src/http/modules/ngx_http_ssl_module.c --- nginx-1.18.0/src/http/modules/ngx_http_ssl_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/modules/ngx_http_ssl_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -50,6 +50,11 @@ void *conf); static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_ssl_ocsp_cache(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +static char *ngx_http_ssl_conf_command_check(ngx_conf_t *cf, void *post, + void *data); static ngx_int_t ngx_http_ssl_init(ngx_conf_t *cf); @@ -74,11 +79,23 @@ }; +static ngx_conf_enum_t ngx_http_ssl_ocsp[] = { + { ngx_string("off"), 0 }, + { ngx_string("on"), 1 }, + { ngx_string("leaf"), 2 }, + { ngx_null_string, 0 } +}; + + static ngx_conf_deprecated_t ngx_http_ssl_deprecated = { ngx_conf_deprecated, "ssl", "listen ... ssl" }; +static ngx_conf_post_t ngx_http_ssl_conf_command_post = + { ngx_http_ssl_conf_command_check }; + + static ngx_command_t ngx_http_ssl_commands[] = { { ngx_string("ssl"), @@ -214,6 +231,27 @@ offsetof(ngx_http_ssl_srv_conf_t, crl), NULL }, + { ngx_string("ssl_ocsp"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_enum_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, ocsp), + &ngx_http_ssl_ocsp }, + + { ngx_string("ssl_ocsp_responder"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, ocsp_responder), + NULL }, + + { ngx_string("ssl_ocsp_cache"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_http_ssl_ocsp_cache, + NGX_HTTP_SRV_CONF_OFFSET, + 0, + NULL }, + { ngx_string("ssl_stapling"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, @@ -249,6 +287,20 @@ offsetof(ngx_http_ssl_srv_conf_t, early_data), NULL }, + { ngx_string("ssl_conf_command"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE2, + ngx_conf_set_keyval_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, conf_commands), + &ngx_http_ssl_conf_command_post }, + + { ngx_string("ssl_reject_handshake"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, reject_handshake), + NULL }, + ngx_null_command }; @@ -561,6 +613,7 @@ * sscf->crl = { 0, NULL }; * sscf->ciphers = { 0, NULL }; * sscf->shm_zone = NULL; + * sscf->ocsp_responder = { 0, NULL }; * sscf->stapling_file = { 0, NULL }; * sscf->stapling_responder = { 0, NULL }; */ @@ -568,16 +621,20 @@ sscf->enable = NGX_CONF_UNSET; sscf->prefer_server_ciphers = NGX_CONF_UNSET; sscf->early_data = NGX_CONF_UNSET; + sscf->reject_handshake = NGX_CONF_UNSET; sscf->buffer_size = NGX_CONF_UNSET_SIZE; sscf->verify = NGX_CONF_UNSET_UINT; sscf->verify_depth = NGX_CONF_UNSET_UINT; sscf->certificates = NGX_CONF_UNSET_PTR; sscf->certificate_keys = NGX_CONF_UNSET_PTR; sscf->passwords = NGX_CONF_UNSET_PTR; + sscf->conf_commands = NGX_CONF_UNSET_PTR; sscf->builtin_session_cache = NGX_CONF_UNSET; sscf->session_timeout = NGX_CONF_UNSET; sscf->session_tickets = NGX_CONF_UNSET; sscf->session_ticket_keys = NGX_CONF_UNSET_PTR; + sscf->ocsp = NGX_CONF_UNSET_UINT; + sscf->ocsp_cache_zone = NGX_CONF_UNSET_PTR; sscf->stapling = NGX_CONF_UNSET; sscf->stapling_verify = NGX_CONF_UNSET; @@ -611,6 +668,7 @@ prev->prefer_server_ciphers, 0); ngx_conf_merge_value(conf->early_data, prev->early_data, 0); + ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0); ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 @@ -641,6 +699,13 @@ ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS); + ngx_conf_merge_ptr_value(conf->conf_commands, prev->conf_commands, NULL); + + ngx_conf_merge_uint_value(conf->ocsp, prev->ocsp, 0); + ngx_conf_merge_str_value(conf->ocsp_responder, prev->ocsp_responder, ""); + ngx_conf_merge_ptr_value(conf->ocsp_cache_zone, + prev->ocsp_cache_zone, NULL); + ngx_conf_merge_value(conf->stapling, prev->stapling, 0); ngx_conf_merge_value(conf->stapling_verify, prev->stapling_verify, 0); ngx_conf_merge_str_value(conf->stapling_file, prev->stapling_file, ""); @@ -651,38 +716,35 @@ if (conf->enable) { - if (conf->certificates == NULL) { - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "no \"ssl_certificate\" is defined for " - "the \"ssl\" directive in %s:%ui", - conf->file, conf->line); - return NGX_CONF_ERROR; - } + if (conf->certificates) { + if (conf->certificate_keys == NULL) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"ssl_certificate_key\" is defined for " + "the \"ssl\" directive in %s:%ui", + conf->file, conf->line); + return NGX_CONF_ERROR; + } - if (conf->certificate_keys == NULL) { - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "no \"ssl_certificate_key\" is defined for " - "the \"ssl\" directive in %s:%ui", - conf->file, conf->line); - return NGX_CONF_ERROR; - } + if (conf->certificate_keys->nelts < conf->certificates->nelts) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"ssl_certificate_key\" is defined " + "for certificate \"%V\" and " + "the \"ssl\" directive in %s:%ui", + ((ngx_str_t *) conf->certificates->elts) + + conf->certificates->nelts - 1, + conf->file, conf->line); + return NGX_CONF_ERROR; + } - if (conf->certificate_keys->nelts < conf->certificates->nelts) { + } else if (!conf->reject_handshake) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "no \"ssl_certificate_key\" is defined " - "for certificate \"%V\" and " + "no \"ssl_certificate\" is defined for " "the \"ssl\" directive in %s:%ui", - ((ngx_str_t *) conf->certificates->elts) - + conf->certificates->nelts - 1, conf->file, conf->line); return NGX_CONF_ERROR; } - } else { - - if (conf->certificates == NULL) { - return NGX_CONF_OK; - } + } else if (conf->certificates) { if (conf->certificate_keys == NULL || conf->certificate_keys->nelts < conf->certificates->nelts) @@ -694,6 +756,9 @@ + conf->certificates->nelts - 1); return NGX_CONF_ERROR; } + + } else if (!conf->reject_handshake) { + return NGX_CONF_OK; } if (ngx_ssl_create(&conf->ssl, conf->protocols, conf) != NGX_OK) { @@ -752,7 +817,7 @@ return NGX_CONF_ERROR; #endif - } else { + } else if (conf->certificates) { /* configure certificates */ @@ -802,6 +867,23 @@ return NGX_CONF_ERROR; } + if (conf->ocsp) { + + if (conf->verify == 3) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "\"ssl_ocsp\" is incompatible with " + "\"ssl_verify_client optional_no_ca\""); + return NGX_CONF_ERROR; + } + + if (ngx_ssl_ocsp(cf, &conf->ssl, &conf->ocsp_responder, conf->ocsp, + conf->ocsp_cache_zone) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + } + if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) { return NGX_CONF_ERROR; } @@ -857,6 +939,10 @@ return NGX_CONF_ERROR; } + if (ngx_ssl_conf_commands(cf, &conf->ssl, conf->conf_commands) != NGX_OK) { + return NGX_CONF_ERROR; + } + return NGX_CONF_OK; } @@ -870,6 +956,10 @@ ngx_http_complex_value_t *cv; ngx_http_compile_complex_value_t ccv; + if (conf->certificates == NULL) { + return NGX_OK; + } + cert = conf->certificates->elts; key = conf->certificate_keys->elts; nelts = conf->certificates->nelts; @@ -1100,6 +1190,96 @@ } +static char * +ngx_http_ssl_ocsp_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_ssl_srv_conf_t *sscf = conf; + + size_t len; + ngx_int_t n; + ngx_str_t *value, name, size; + ngx_uint_t j; + + if (sscf->ocsp_cache_zone != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + value = cf->args->elts; + + if (ngx_strcmp(value[1].data, "off") == 0) { + sscf->ocsp_cache_zone = NULL; + return NGX_CONF_OK; + } + + if (value[1].len <= sizeof("shared:") - 1 + || ngx_strncmp(value[1].data, "shared:", sizeof("shared:") - 1) != 0) + { + goto invalid; + } + + len = 0; + + for (j = sizeof("shared:") - 1; j < value[1].len; j++) { + if (value[1].data[j] == ':') { + break; + } + + len++; + } + + if (len == 0) { + goto invalid; + } + + name.len = len; + name.data = value[1].data + sizeof("shared:") - 1; + + size.len = value[1].len - j - 1; + size.data = name.data + len + 1; + + n = ngx_parse_size(&size); + + if (n == NGX_ERROR) { + goto invalid; + } + + if (n < (ngx_int_t) (8 * ngx_pagesize)) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "OCSP cache \"%V\" is too small", &value[1]); + + return NGX_CONF_ERROR; + } + + sscf->ocsp_cache_zone = ngx_shared_memory_add(cf, &name, n, + &ngx_http_ssl_module_ctx); + if (sscf->ocsp_cache_zone == NULL) { + return NGX_CONF_ERROR; + } + + sscf->ocsp_cache_zone->init = ngx_ssl_ocsp_cache_init; + + return NGX_CONF_OK; + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid OCSP cache \"%V\"", &value[1]); + + return NGX_CONF_ERROR; +} + + +static char * +ngx_http_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data) +{ +#ifndef SSL_CONF_FLAG_FILE + return "is not supported on this platform"; +#else + return NGX_CONF_OK; +#endif +} + + static ngx_int_t ngx_http_ssl_init(ngx_conf_t *cf) { @@ -1118,17 +1298,28 @@ sscf = cscfp[s]->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; - if (sscf->ssl.ctx == NULL || !sscf->stapling) { + if (sscf->ssl.ctx == NULL) { continue; } clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index]; - if (ngx_ssl_stapling_resolver(cf, &sscf->ssl, clcf->resolver, + if (sscf->stapling) { + if (ngx_ssl_stapling_resolver(cf, &sscf->ssl, clcf->resolver, + clcf->resolver_timeout) + != NGX_OK) + { + return NGX_ERROR; + } + } + + if (sscf->ocsp) { + if (ngx_ssl_ocsp_resolver(cf, &sscf->ssl, clcf->resolver, clcf->resolver_timeout) - != NGX_OK) - { - return NGX_ERROR; + != NGX_OK) + { + return NGX_ERROR; + } } } @@ -1149,7 +1340,33 @@ cscf = addr[a].default_server; sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; - if (sscf->certificates == NULL) { + if (sscf->certificates) { + continue; + } + + if (!sscf->reject_handshake) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"ssl_certificate\" is defined for " + "the \"listen ... ssl\" directive in %s:%ui", + cscf->file_name, cscf->line); + return NGX_ERROR; + } + + /* + * if no certificates are defined in the default server, + * check all non-default server blocks + */ + + cscfp = addr[a].servers.elts; + for (s = 0; s < addr[a].servers.nelts; s++) { + + cscf = cscfp[s]; + sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; + + if (sscf->certificates || sscf->reject_handshake) { + continue; + } + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "no \"ssl_certificate\" is defined for " "the \"listen ... ssl\" directive in %s:%ui", diff -Nru nginx-1.18.0/src/http/modules/ngx_http_ssl_module.h nginx-1.20.1/src/http/modules/ngx_http_ssl_module.h --- nginx-1.18.0/src/http/modules/ngx_http_ssl_module.h 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/modules/ngx_http_ssl_module.h 2021-05-25 12:35:38.000000000 +0000 @@ -21,6 +21,7 @@ ngx_flag_t prefer_server_ciphers; ngx_flag_t early_data; + ngx_flag_t reject_handshake; ngx_uint_t protocols; @@ -48,12 +49,17 @@ ngx_str_t ciphers; ngx_array_t *passwords; + ngx_array_t *conf_commands; ngx_shm_zone_t *shm_zone; ngx_flag_t session_tickets; ngx_array_t *session_ticket_keys; + ngx_uint_t ocsp; + ngx_str_t ocsp_responder; + ngx_shm_zone_t *ocsp_cache_zone; + ngx_flag_t stapling; ngx_flag_t stapling_verify; ngx_str_t stapling_file; diff -Nru nginx-1.18.0/src/http/modules/ngx_http_stub_status_module.c nginx-1.20.1/src/http/modules/ngx_http_stub_status_module.c --- nginx-1.18.0/src/http/modules/ngx_http_stub_status_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/modules/ngx_http_stub_status_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -103,16 +103,6 @@ ngx_str_set(&r->headers_out.content_type, "text/plain"); r->headers_out.content_type_lowcase = NULL; - if (r->method == NGX_HTTP_HEAD) { - r->headers_out.status = NGX_HTTP_OK; - - rc = ngx_http_send_header(r); - - if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { - return rc; - } - } - size = sizeof("Active connections: \n") + NGX_ATOMIC_T_LEN + sizeof("server accepts handled requests\n") - 1 + 6 + 3 * NGX_ATOMIC_T_LEN diff -Nru nginx-1.18.0/src/http/modules/ngx_http_upstream_keepalive_module.c nginx-1.20.1/src/http/modules/ngx_http_upstream_keepalive_module.c --- nginx-1.18.0/src/http/modules/ngx_http_upstream_keepalive_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/modules/ngx_http_upstream_keepalive_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -13,6 +13,7 @@ typedef struct { ngx_uint_t max_cached; ngx_uint_t requests; + ngx_msec_t time; ngx_msec_t timeout; ngx_queue_t cache; @@ -86,6 +87,13 @@ 0, NULL }, + { ngx_string("keepalive_time"), + NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_upstream_keepalive_srv_conf_t, time), + NULL }, + { ngx_string("keepalive_timeout"), NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1, ngx_conf_set_msec_slot, @@ -149,8 +157,9 @@ kcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_keepalive_module); + ngx_conf_init_msec_value(kcf->time, 3600000); ngx_conf_init_msec_value(kcf->timeout, 60000); - ngx_conf_init_uint_value(kcf->requests, 100); + ngx_conf_init_uint_value(kcf->requests, 1000); if (kcf->original_init_upstream(cf, us) != NGX_OK) { return NGX_ERROR; @@ -326,6 +335,10 @@ goto invalid; } + if (ngx_current_msec - c->start_time > kp->conf->time) { + goto invalid; + } + if (!u->keepalive) { goto invalid; } @@ -513,6 +526,7 @@ * conf->max_cached = 0; */ + conf->time = NGX_CONF_UNSET_MSEC; conf->timeout = NGX_CONF_UNSET_MSEC; conf->requests = NGX_CONF_UNSET_UINT; diff -Nru nginx-1.18.0/src/http/modules/ngx_http_userid_filter_module.c nginx-1.20.1/src/http/modules/ngx_http_userid_filter_module.c --- nginx-1.18.0/src/http/modules/ngx_http_userid_filter_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/modules/ngx_http_userid_filter_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -15,12 +15,21 @@ #define NGX_HTTP_USERID_V1 2 #define NGX_HTTP_USERID_ON 3 +#define NGX_HTTP_USERID_COOKIE_OFF 0x0002 +#define NGX_HTTP_USERID_COOKIE_SECURE 0x0004 +#define NGX_HTTP_USERID_COOKIE_HTTPONLY 0x0008 +#define NGX_HTTP_USERID_COOKIE_SAMESITE 0x0010 +#define NGX_HTTP_USERID_COOKIE_SAMESITE_STRICT 0x0020 +#define NGX_HTTP_USERID_COOKIE_SAMESITE_LAX 0x0040 +#define NGX_HTTP_USERID_COOKIE_SAMESITE_NONE 0x0080 + /* 31 Dec 2037 23:55:55 GMT */ #define NGX_HTTP_USERID_MAX_EXPIRES 2145916555 typedef struct { ngx_uint_t enable; + ngx_uint_t flags; ngx_int_t service; @@ -88,6 +97,20 @@ }; +static ngx_conf_bitmask_t ngx_http_userid_flags[] = { + { ngx_string("off"), NGX_HTTP_USERID_COOKIE_OFF }, + { ngx_string("secure"), NGX_HTTP_USERID_COOKIE_SECURE }, + { ngx_string("httponly"), NGX_HTTP_USERID_COOKIE_HTTPONLY }, + { ngx_string("samesite=strict"), + NGX_HTTP_USERID_COOKIE_SAMESITE|NGX_HTTP_USERID_COOKIE_SAMESITE_STRICT }, + { ngx_string("samesite=lax"), + NGX_HTTP_USERID_COOKIE_SAMESITE|NGX_HTTP_USERID_COOKIE_SAMESITE_LAX }, + { ngx_string("samesite=none"), + NGX_HTTP_USERID_COOKIE_SAMESITE|NGX_HTTP_USERID_COOKIE_SAMESITE_NONE }, + { ngx_null_string, 0 } +}; + + static ngx_conf_post_handler_pt ngx_http_userid_domain_p = ngx_http_userid_domain; static ngx_conf_post_handler_pt ngx_http_userid_path_p = ngx_http_userid_path; @@ -138,6 +161,13 @@ 0, NULL }, + { ngx_string("userid_flags"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123, + ngx_conf_set_bitmask_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_userid_conf_t, flags), + &ngx_http_userid_flags }, + { ngx_string("userid_p3p"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, @@ -383,6 +413,26 @@ len += conf->domain.len; } + if (conf->flags & NGX_HTTP_USERID_COOKIE_SECURE) { + len += sizeof("; secure") - 1; + } + + if (conf->flags & NGX_HTTP_USERID_COOKIE_HTTPONLY) { + len += sizeof("; httponly") - 1; + } + + if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_STRICT) { + len += sizeof("; samesite=strict") - 1; + } + + if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_LAX) { + len += sizeof("; samesite=lax") - 1; + } + + if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_NONE) { + len += sizeof("; samesite=none") - 1; + } + cookie = ngx_pnalloc(r->pool, len); if (cookie == NULL) { return NGX_ERROR; @@ -422,6 +472,26 @@ p = ngx_copy(p, conf->path.data, conf->path.len); + if (conf->flags & NGX_HTTP_USERID_COOKIE_SECURE) { + p = ngx_cpymem(p, "; secure", sizeof("; secure") - 1); + } + + if (conf->flags & NGX_HTTP_USERID_COOKIE_HTTPONLY) { + p = ngx_cpymem(p, "; httponly", sizeof("; httponly") - 1); + } + + if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_STRICT) { + p = ngx_cpymem(p, "; samesite=strict", sizeof("; samesite=strict") - 1); + } + + if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_LAX) { + p = ngx_cpymem(p, "; samesite=lax", sizeof("; samesite=lax") - 1); + } + + if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_NONE) { + p = ngx_cpymem(p, "; samesite=none", sizeof("; samesite=none") - 1); + } + set_cookie = ngx_list_push(&r->headers_out.headers); if (set_cookie == NULL) { return NGX_ERROR; @@ -658,6 +728,7 @@ /* * set by ngx_pcalloc(): * + * conf->flags = 0; * conf->name = { 0, NULL }; * conf->domain = { 0, NULL }; * conf->path = { 0, NULL }; @@ -682,6 +753,9 @@ ngx_conf_merge_uint_value(conf->enable, prev->enable, NGX_HTTP_USERID_OFF); + ngx_conf_merge_bitmask_value(conf->flags, prev->flags, + (NGX_CONF_BITMASK_SET|NGX_HTTP_USERID_COOKIE_OFF)); + ngx_conf_merge_str_value(conf->name, prev->name, "uid"); ngx_conf_merge_str_value(conf->domain, prev->domain, ""); ngx_conf_merge_str_value(conf->path, prev->path, "; path=/"); diff -Nru nginx-1.18.0/src/http/modules/ngx_http_uwsgi_module.c nginx-1.20.1/src/http/modules/ngx_http_uwsgi_module.c --- nginx-1.18.0/src/http/modules/ngx_http_uwsgi_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/modules/ngx_http_uwsgi_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -57,6 +57,7 @@ ngx_str_t ssl_certificate; ngx_str_t ssl_certificate_key; ngx_array_t *ssl_passwords; + ngx_array_t *ssl_conf_commands; #endif } ngx_http_uwsgi_loc_conf_t; @@ -67,6 +68,7 @@ static ngx_int_t ngx_http_uwsgi_reinit_request(ngx_http_request_t *r); static ngx_int_t ngx_http_uwsgi_process_status_line(ngx_http_request_t *r); static ngx_int_t ngx_http_uwsgi_process_header(ngx_http_request_t *r); +static ngx_int_t ngx_http_uwsgi_input_filter_init(void *data); static void ngx_http_uwsgi_abort_request(ngx_http_request_t *r); static void ngx_http_uwsgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc); @@ -95,6 +97,8 @@ #if (NGX_HTTP_SSL) static char *ngx_http_uwsgi_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_uwsgi_ssl_conf_command_check(ngx_conf_t *cf, void *post, + void *data); static ngx_int_t ngx_http_uwsgi_set_ssl(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *uwcf); #endif @@ -133,6 +137,9 @@ { ngx_null_string, 0 } }; +static ngx_conf_post_t ngx_http_uwsgi_ssl_conf_command_post = + { ngx_http_uwsgi_ssl_conf_command_check }; + #endif @@ -560,6 +567,13 @@ 0, NULL }, + { ngx_string("uwsgi_ssl_conf_command"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, + ngx_conf_set_keyval_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_uwsgi_loc_conf_t, ssl_conf_commands), + &ngx_http_uwsgi_ssl_conf_command_post }, + #endif ngx_null_command @@ -703,6 +717,10 @@ u->pipe->input_filter = ngx_event_pipe_copy_input_filter; u->pipe->input_ctx = r; + u->input_filter_init = ngx_http_uwsgi_input_filter_init; + u->input_filter = ngx_http_upstream_non_buffered_filter; + u->input_filter_ctx = r; + if (!uwcf->upstream.request_buffering && uwcf->upstream.pass_request_body && !r->headers_in.chunked) @@ -1141,6 +1159,7 @@ r->upstream->request_bufs = cl; } + b->flush = 1; cl->next = NULL; return NGX_OK; @@ -1355,6 +1374,37 @@ } +static ngx_int_t +ngx_http_uwsgi_input_filter_init(void *data) +{ + ngx_http_request_t *r = data; + ngx_http_upstream_t *u; + + u = r->upstream; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http uwsgi filter init s:%ui l:%O", + u->headers_in.status_n, u->headers_in.content_length_n); + + if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT + || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED) + { + u->pipe->length = 0; + u->length = 0; + + } else if (r->method == NGX_HTTP_HEAD) { + u->pipe->length = -1; + u->length = -1; + + } else { + u->pipe->length = u->headers_in.content_length_n; + u->length = u->headers_in.content_length_n; + } + + return NGX_OK; +} + + static void ngx_http_uwsgi_abort_request(ngx_http_request_t *r) { @@ -1463,6 +1513,7 @@ conf->upstream.ssl_verify = NGX_CONF_UNSET; conf->ssl_verify_depth = NGX_CONF_UNSET_UINT; conf->ssl_passwords = NGX_CONF_UNSET_PTR; + conf->ssl_conf_commands = NGX_CONF_UNSET_PTR; #endif /* "uwsgi_cyclic_temp_file" is disabled */ @@ -1793,6 +1844,9 @@ prev->ssl_certificate_key, ""); ngx_conf_merge_ptr_value(conf->ssl_passwords, prev->ssl_passwords, NULL); + ngx_conf_merge_ptr_value(conf->ssl_conf_commands, + prev->ssl_conf_commands, NULL); + if (conf->ssl && ngx_http_uwsgi_set_ssl(cf, conf) != NGX_OK) { return NGX_CONF_ERROR; } @@ -2339,6 +2393,17 @@ } +static char * +ngx_http_uwsgi_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data) +{ +#ifndef SSL_CONF_FLAG_FILE + return "is not supported on this platform"; +#else + return NGX_CONF_OK; +#endif +} + + static ngx_int_t ngx_http_uwsgi_set_ssl(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *uwcf) { @@ -2414,6 +2479,12 @@ != NGX_OK) { return NGX_ERROR; + } + + if (ngx_ssl_conf_commands(cf, uwcf->upstream.ssl, uwcf->ssl_conf_commands) + != NGX_OK) + { + return NGX_ERROR; } return NGX_OK; diff -Nru nginx-1.18.0/src/http/modules/ngx_http_xslt_filter_module.c nginx-1.20.1/src/http/modules/ngx_http_xslt_filter_module.c --- nginx-1.18.0/src/http/modules/ngx_http_xslt_filter_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/modules/ngx_http_xslt_filter_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -233,6 +233,7 @@ ngx_http_set_ctx(r, ctx, ngx_http_xslt_filter_module); r->main_filter_need_in_memory = 1; + r->allow_ranges = 0; return NGX_OK; } diff -Nru nginx-1.18.0/src/http/ngx_http.c nginx-1.20.1/src/http/ngx_http.c --- nginx-1.18.0/src/http/ngx_http.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/ngx_http.c 2021-05-25 12:35:38.000000000 +0000 @@ -1469,14 +1469,14 @@ NGX_HASH_WILDCARD_KEY); if (rc == NGX_ERROR) { - return NGX_ERROR; + goto failed; } if (rc == NGX_DECLINED) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "invalid server name or wildcard \"%V\" on %V", &name[n].name, &addr->opt.addr_text); - return NGX_ERROR; + goto failed; } if (rc == NGX_BUSY) { @@ -1714,7 +1714,6 @@ cscf = addr->default_server; ls->pool_size = cscf->connection_pool_size; - ls->post_accept_timeout = cscf->client_header_timeout; clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index]; diff -Nru nginx-1.18.0/src/http/ngx_http_cache.h nginx-1.20.1/src/http/ngx_http_cache.h --- nginx-1.18.0/src/http/ngx_http_cache.h 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/ngx_http_cache.h 2021-05-25 12:35:38.000000000 +0000 @@ -80,6 +80,7 @@ ngx_str_t vary; u_char variant[NGX_HTTP_CACHE_KEY_LEN]; + size_t buffer_size; size_t header_start; size_t body_start; off_t length; @@ -116,6 +117,7 @@ unsigned purged:1; unsigned reading:1; unsigned secondary:1; + unsigned update_variant:1; unsigned background:1; unsigned stale_updating:1; @@ -160,6 +162,7 @@ ngx_path_t *path; + off_t min_free; off_t max_size; size_t bsize; diff -Nru nginx-1.18.0/src/http/ngx_http_core_module.c nginx-1.20.1/src/http/ngx_http_core_module.c --- nginx-1.18.0/src/http/ngx_http_core_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/ngx_http_core_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -495,6 +495,13 @@ offsetof(ngx_http_core_loc_conf_t, limit_rate_after), NULL }, + { ngx_string("keepalive_time"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, keepalive_time), + NULL }, + { ngx_string("keepalive_timeout"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, ngx_http_core_keepalive, @@ -1190,8 +1197,13 @@ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "delaying unauthorized request"); - if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; + if (r->connection->read->ready) { + ngx_post_event(r->connection->read, &ngx_posted_events); + + } else { + if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } } r->read_event_handler = ngx_http_test_reading; @@ -1330,6 +1342,11 @@ } else if (r->connection->requests >= clcf->keepalive_requests) { r->keepalive = 0; + } else if (ngx_current_msec - r->connection->start_time + > clcf->keepalive_time) + { + r->keepalive = 0; + } else if (r->headers_in.msie6 && r->method == NGX_HTTP_POST && (clcf->keepalive_disable @@ -1782,7 +1799,7 @@ } } - if (r->method == NGX_HTTP_HEAD || (r != r->main && val.len == 0)) { + if (r != r->main && val.len == 0) { return ngx_http_send_header(r); } @@ -3495,6 +3512,7 @@ clcf->send_timeout = NGX_CONF_UNSET_MSEC; clcf->send_lowat = NGX_CONF_UNSET_SIZE; clcf->postpone_output = NGX_CONF_UNSET_SIZE; + clcf->keepalive_time = NGX_CONF_UNSET_MSEC; clcf->keepalive_timeout = NGX_CONF_UNSET_MSEC; clcf->keepalive_header = NGX_CONF_UNSET; clcf->keepalive_requests = NGX_CONF_UNSET_UINT; @@ -3733,12 +3751,14 @@ conf->limit_rate_after = prev->limit_rate_after; } + ngx_conf_merge_msec_value(conf->keepalive_time, + prev->keepalive_time, 3600000); ngx_conf_merge_msec_value(conf->keepalive_timeout, prev->keepalive_timeout, 75000); ngx_conf_merge_sec_value(conf->keepalive_header, prev->keepalive_header, 0); ngx_conf_merge_uint_value(conf->keepalive_requests, - prev->keepalive_requests, 100); + prev->keepalive_requests, 1000); ngx_conf_merge_uint_value(conf->lingering_close, prev->lingering_close, NGX_HTTP_LINGERING_ON); ngx_conf_merge_msec_value(conf->lingering_time, @@ -4078,14 +4098,6 @@ #endif } - if (ngx_strcmp(value[n].data, "spdy") == 0) { - ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "invalid parameter \"spdy\": " - "ngx_http_spdy_module was superseded " - "by ngx_http_v2_module"); - continue; - } - if (ngx_strncmp(value[n].data, "so_keepalive=", 13) == 0) { if (ngx_strcmp(&value[n].data[13], "on") == 0) { diff -Nru nginx-1.18.0/src/http/ngx_http_core_module.h nginx-1.20.1/src/http/ngx_http_core_module.h --- nginx-1.18.0/src/http/ngx_http_core_module.h 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/ngx_http_core_module.h 2021-05-25 12:35:38.000000000 +0000 @@ -359,6 +359,7 @@ ngx_msec_t client_body_timeout; /* client_body_timeout */ ngx_msec_t send_timeout; /* send_timeout */ + ngx_msec_t keepalive_time; /* keepalive_time */ ngx_msec_t keepalive_timeout; /* keepalive_timeout */ ngx_msec_t lingering_time; /* lingering_time */ ngx_msec_t lingering_timeout; /* lingering_timeout */ diff -Nru nginx-1.18.0/src/http/ngx_http_file_cache.c nginx-1.20.1/src/http/ngx_http_file_cache.c --- nginx-1.18.0/src/http/ngx_http_file_cache.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/ngx_http_file_cache.c 2021-05-25 12:35:38.000000000 +0000 @@ -294,6 +294,8 @@ cln->data = c; } + c->buffer_size = c->body_start; + rc = ngx_http_file_cache_exists(cache, c); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -852,7 +854,7 @@ if (fcn->exists || fcn->uses >= c->min_uses) { c->exists = fcn->exists; - if (fcn->body_start) { + if (fcn->body_start && !c->update_variant) { c->body_start = fcn->body_start; } @@ -1230,7 +1232,7 @@ c->secondary = 1; c->file.name.len = 0; - c->body_start = c->buf->end - c->buf->start; + c->body_start = c->buffer_size; ngx_memcpy(c->key, c->variant, NGX_HTTP_CACHE_KEY_LEN); @@ -1337,6 +1339,7 @@ ngx_shmtx_unlock(&cache->shpool->mutex); c->file.name.len = 0; + c->update_variant = 1; ngx_memcpy(c->key, c->main, NGX_HTTP_CACHE_KEY_LEN); @@ -1959,7 +1962,7 @@ { ngx_http_file_cache_t *cache = data; - off_t size; + off_t size, free; time_t wait; ngx_msec_t elapsed, next; ngx_uint_t count, watermark; @@ -1988,7 +1991,19 @@ size, count, (ngx_int_t) watermark); if (size < cache->max_size && count < watermark) { - break; + + if (!cache->min_free) { + break; + } + + free = ngx_fs_available(cache->path->name.data); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "http file cache free: %O", free); + + if (free > cache->min_free) { + break; + } } wait = ngx_http_file_cache_forced_expire(cache); @@ -2304,7 +2319,7 @@ { char *confp = conf; - off_t max_size; + off_t max_size, min_free; u_char *last, *p; time_t inactive; ssize_t size; @@ -2341,6 +2356,7 @@ name.len = 0; size = 0; max_size = NGX_MAX_OFF_T_VALUE; + min_free = 0; value = cf->args->elts; @@ -2476,6 +2492,29 @@ continue; } + if (ngx_strncmp(value[i].data, "min_free=", 9) == 0) { + +#if (NGX_WIN32 || NGX_HAVE_STATFS || NGX_HAVE_STATVFS) + + s.len = value[i].len - 9; + s.data = value[i].data + 9; + + min_free = ngx_parse_offset(&s); + if (min_free < 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid min_free value \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + +#else + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "min_free is not supported " + "on this platform, ignored"); +#endif + + continue; + } + if (ngx_strncmp(value[i].data, "loader_files=", 13) == 0) { loader_files = ngx_atoi(value[i].data + 13, value[i].len - 13); @@ -2607,6 +2646,7 @@ cache->inactive = inactive; cache->max_size = max_size; + cache->min_free = min_free; caches = (ngx_array_t *) (confp + cmd->offset); diff -Nru nginx-1.18.0/src/http/ngx_http_parse.c nginx-1.20.1/src/http/ngx_http_parse.c --- nginx-1.18.0/src/http/ngx_http_parse.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/ngx_http_parse.c 2021-05-25 12:35:38.000000000 +0000 @@ -380,6 +380,12 @@ r->uri_start = p; state = sw_after_slash_in_uri; break; + case '?': + r->uri_start = p; + r->args_start = p + 1; + r->empty_path_in_uri = 1; + state = sw_uri; + break; case ' ': /* * use single "/" from request line to preserve pointers, @@ -446,6 +452,13 @@ r->uri_start = p; state = sw_after_slash_in_uri; break; + case '?': + r->port_end = p; + r->uri_start = p; + r->args_start = p + 1; + r->empty_path_in_uri = 1; + state = sw_uri; + break; case ' ': r->port_end = p; /* @@ -1287,6 +1300,10 @@ r->uri_ext = NULL; r->args_start = NULL; + if (r->empty_path_in_uri) { + *u++ = '/'; + } + ch = *p++; while (p <= r->uri_end) { diff -Nru nginx-1.18.0/src/http/ngx_http_request_body.c nginx-1.20.1/src/http/ngx_http_request_body.c --- nginx-1.18.0/src/http/ngx_http_request_body.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/ngx_http_request_body.c 2021-05-25 12:35:38.000000000 +0000 @@ -12,6 +12,8 @@ static void ngx_http_read_client_request_body_handler(ngx_http_request_t *r); static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r); +static ngx_int_t ngx_http_copy_pipelined_header(ngx_http_request_t *r, + ngx_buf_t *buf); static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r); static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r); static ngx_int_t ngx_http_discard_request_body_filter(ngx_http_request_t *r, @@ -135,8 +137,9 @@ } else { /* set rb->rest */ - if (ngx_http_request_body_filter(r, NULL) != NGX_OK) { - rc = NGX_HTTP_INTERNAL_SERVER_ERROR; + rc = ngx_http_request_body_filter(r, NULL); + + if (rc != NGX_OK) { goto done; } } @@ -282,28 +285,12 @@ for ( ;; ) { if (rb->buf->last == rb->buf->end) { - if (rb->buf->pos != rb->buf->last) { - - /* pass buffer to request body filter chain */ - - out.buf = rb->buf; - out.next = NULL; - - rc = ngx_http_request_body_filter(r, &out); - - if (rc != NGX_OK) { - return rc; - } - - } else { + /* update chains */ - /* update chains */ + rc = ngx_http_request_body_filter(r, NULL); - rc = ngx_http_request_body_filter(r, NULL); - - if (rc != NGX_OK) { - return rc; - } + if (rc != NGX_OK) { + return rc; } if (rb->busy != NULL) { @@ -319,6 +306,9 @@ return NGX_AGAIN; } + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "busy buffers after request body flush"); + return NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -355,17 +345,15 @@ rb->buf->last += n; r->request_length += n; - if (n == rest) { - /* pass buffer to request body filter chain */ + /* pass buffer to request body filter chain */ - out.buf = rb->buf; - out.next = NULL; + out.buf = rb->buf; + out.next = NULL; - rc = ngx_http_request_body_filter(r, &out); + rc = ngx_http_request_body_filter(r, &out); - if (rc != NGX_OK) { - return rc; - } + if (rc != NGX_OK) { + return rc; } if (rb->rest == 0) { @@ -386,21 +374,6 @@ if (!c->read->ready) { - if (r->request_body_no_buffering - && rb->buf->pos != rb->buf->last) - { - /* pass buffer to request body filter chain */ - - out.buf = rb->buf; - out.next = NULL; - - rc = ngx_http_request_body_filter(r, &out); - - if (rc != NGX_OK) { - return rc; - } - } - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ngx_add_timer(c->read, clcf->client_body_timeout); @@ -412,6 +385,10 @@ } } + if (ngx_http_copy_pipelined_header(r, rb->buf) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + if (c->read->timer_set) { ngx_del_timer(c->read); } @@ -426,6 +403,88 @@ static ngx_int_t +ngx_http_copy_pipelined_header(ngx_http_request_t *r, ngx_buf_t *buf) +{ + size_t n; + ngx_buf_t *b; + ngx_chain_t *cl; + ngx_http_connection_t *hc; + ngx_http_core_srv_conf_t *cscf; + + b = r->header_in; + n = buf->last - buf->pos; + + if (buf == b || n == 0) { + return NGX_OK; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http body pipelined header: %uz", n); + + /* + * if there is a pipelined request in the client body buffer, + * copy it to the r->header_in buffer if there is enough room, + * or allocate a large client header buffer + */ + + if (n > (size_t) (b->end - b->last)) { + + hc = r->http_connection; + + if (hc->free) { + cl = hc->free; + hc->free = cl->next; + + b = cl->buf; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http large header free: %p %uz", + b->pos, b->end - b->last); + + } else { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + b = ngx_create_temp_buf(r->connection->pool, + cscf->large_client_header_buffers.size); + if (b == NULL) { + return NGX_ERROR; + } + + cl = ngx_alloc_chain_link(r->connection->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http large header alloc: %p %uz", + b->pos, b->end - b->last); + } + + cl->next = hc->busy; + hc->busy = cl; + hc->nbusy++; + + r->header_in = b; + + if (n > (size_t) (b->end - b->last)) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "too large pipelined header after reading body"); + return NGX_ERROR; + } + } + + ngx_memcpy(b->last, buf->pos, n); + + b->last += n; + r->request_length -= n; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r) { ssize_t n; @@ -619,6 +678,7 @@ if (rc == NGX_OK) { r->discard_body = 0; r->lingering_close = 0; + r->lingering_time = 0; ngx_http_finalize_request(r, NGX_DONE); return; } @@ -670,8 +730,7 @@ for ( ;; ) { if (r->headers_in.content_length_n == 0) { - r->read_event_handler = ngx_http_block_reading; - return NGX_OK; + break; } if (!r->connection->read->ready) { @@ -705,15 +764,24 @@ return rc; } } + + if (ngx_http_copy_pipelined_header(r, &b) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + r->read_event_handler = ngx_http_block_reading; + + return NGX_OK; } static ngx_int_t ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b) { - size_t size; - ngx_int_t rc; - ngx_http_request_body_t *rb; + size_t size; + ngx_int_t rc; + ngx_http_request_body_t *rb; + ngx_http_core_srv_conf_t *cscf; if (r->headers_in.chunked) { @@ -768,7 +836,10 @@ /* set amount of data we want to see next time */ - r->headers_in.content_length_n = rb->chunked->length; + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + r->headers_in.content_length_n = ngx_max(rb->chunked->length, + (off_t) cscf->large_client_header_buffers.size); break; } @@ -936,6 +1007,7 @@ ngx_chain_t *cl, *out, *tl, **ll; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; + ngx_http_core_srv_conf_t *cscf; rb = r->request_body; @@ -949,8 +1021,10 @@ return NGX_HTTP_INTERNAL_SERVER_ERROR; } + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + r->headers_in.content_length_n = 0; - rb->rest = 3; + rb->rest = cscf->large_client_header_buffers.size; } out = NULL; @@ -958,6 +1032,8 @@ for (cl = in; cl; cl = cl->next) { + b = NULL; + for ( ;; ) { ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, @@ -992,6 +1068,29 @@ return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; } + if (b + && rb->chunked->size <= 128 + && cl->buf->last - cl->buf->pos >= rb->chunked->size) + { + r->headers_in.content_length_n += rb->chunked->size; + + if (rb->chunked->size < 8) { + + while (rb->chunked->size) { + *b->last++ = *cl->buf->pos++; + rb->chunked->size--; + } + + } else { + ngx_memmove(b->last, cl->buf->pos, rb->chunked->size); + b->last += rb->chunked->size; + cl->buf->pos += rb->chunked->size; + rb->chunked->size = 0; + } + + continue; + } + tl = ngx_chain_get_free_buf(r->pool, &rb->free); if (tl == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; @@ -1057,7 +1156,10 @@ /* set rb->rest, amount of data we want to see next time */ - rb->rest = rb->chunked->length; + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + rb->rest = ngx_max(rb->chunked->length, + (off_t) cscf->large_client_header_buffers.size); break; } diff -Nru nginx-1.18.0/src/http/ngx_http_request.c nginx-1.20.1/src/http/ngx_http_request.c --- nginx-1.18.0/src/http/ngx_http_request.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/ngx_http_request.c 2021-05-25 12:35:38.000000000 +0000 @@ -49,7 +49,7 @@ static void ngx_http_set_keepalive(ngx_http_request_t *r); static void ngx_http_keepalive_handler(ngx_event_t *ev); -static void ngx_http_set_lingering_close(ngx_http_request_t *r); +static void ngx_http_set_lingering_close(ngx_connection_t *c); static void ngx_http_lingering_close_handler(ngx_event_t *ev); static ngx_int_t ngx_http_post_action(ngx_http_request_t *r); static void ngx_http_close_request(ngx_http_request_t *r, ngx_int_t error); @@ -206,16 +206,17 @@ void ngx_http_init_connection(ngx_connection_t *c) { - ngx_uint_t i; - ngx_event_t *rev; - struct sockaddr_in *sin; - ngx_http_port_t *port; - ngx_http_in_addr_t *addr; - ngx_http_log_ctx_t *ctx; - ngx_http_connection_t *hc; + ngx_uint_t i; + ngx_event_t *rev; + struct sockaddr_in *sin; + ngx_http_port_t *port; + ngx_http_in_addr_t *addr; + ngx_http_log_ctx_t *ctx; + ngx_http_connection_t *hc; + ngx_http_core_srv_conf_t *cscf; #if (NGX_HAVE_INET6) - struct sockaddr_in6 *sin6; - ngx_http_in6_addr_t *addr6; + struct sockaddr_in6 *sin6; + ngx_http_in6_addr_t *addr6; #endif hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t)); @@ -361,7 +362,9 @@ return; } - ngx_add_timer(rev, c->listening->post_accept_timeout); + 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) { @@ -431,7 +434,7 @@ if (n == NGX_AGAIN) { if (!rev->timer_set) { - ngx_add_timer(rev, c->listening->post_accept_timeout); + ngx_add_timer(rev, cscf->client_header_timeout); ngx_reusable_connection(c, 1); } @@ -649,6 +652,7 @@ ngx_http_connection_t *hc; ngx_http_ssl_srv_conf_t *sscf; ngx_http_core_loc_conf_t *clcf; + ngx_http_core_srv_conf_t *cscf; c = rev->data; hc = c->data; @@ -680,7 +684,9 @@ rev->ready = 0; if (!rev->timer_set) { - ngx_add_timer(rev, c->listening->post_accept_timeout); + 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); } @@ -755,7 +761,9 @@ if (rc == NGX_AGAIN) { if (!rev->timer_set) { - ngx_add_timer(rev, c->listening->post_accept_timeout); + cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, + ngx_http_core_module); + ngx_add_timer(rev, cscf->client_header_timeout); } c->ssl->handler = ngx_http_ssl_handshake_handler; @@ -871,10 +879,14 @@ return SSL_TLSEXT_ERR_ALERT_FATAL; } + hc = c->data; + servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name); if (servername == NULL) { - return SSL_TLSEXT_ERR_OK; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "SSL server name: null"); + goto done; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, @@ -883,7 +895,7 @@ host.len = ngx_strlen(servername); if (host.len == 0) { - return SSL_TLSEXT_ERR_OK; + goto done; } host.data = (u_char *) servername; @@ -891,32 +903,27 @@ rc = ngx_http_validate_host(&host, c->pool, 1); if (rc == NGX_ERROR) { - *ad = SSL_AD_INTERNAL_ERROR; - return SSL_TLSEXT_ERR_ALERT_FATAL; + goto error; } if (rc == NGX_DECLINED) { - return SSL_TLSEXT_ERR_OK; + goto done; } - hc = c->data; - rc = ngx_http_find_virtual_server(c, hc->addr_conf->virtual_names, &host, NULL, &cscf); if (rc == NGX_ERROR) { - *ad = SSL_AD_INTERNAL_ERROR; - return SSL_TLSEXT_ERR_ALERT_FATAL; + goto error; } if (rc == NGX_DECLINED) { - return SSL_TLSEXT_ERR_OK; + goto done; } hc->ssl_servername = ngx_palloc(c->pool, sizeof(ngx_str_t)); if (hc->ssl_servername == NULL) { - *ad = SSL_AD_INTERNAL_ERROR; - return SSL_TLSEXT_ERR_ALERT_FATAL; + goto error; } *hc->ssl_servername = host; @@ -932,7 +939,9 @@ c->ssl->buffer_size = sscf->buffer_size; if (sscf->ssl.ctx) { - SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx); + if (SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx) == NULL) { + goto error; + } /* * SSL_set_SSL_CTX() only changes certs as of 1.0.0d @@ -957,7 +966,22 @@ #endif } +done: + + sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module); + + if (sscf->reject_handshake) { + c->ssl->handshake_rejected = 1; + *ad = SSL_AD_UNRECOGNIZED_NAME; + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + return SSL_TLSEXT_ERR_OK; + +error: + + *ad = SSL_AD_INTERNAL_ERROR; + return SSL_TLSEXT_ERR_ALERT_FATAL; } #endif @@ -1208,9 +1232,13 @@ r->uri.len = r->uri_end - r->uri_start; } - if (r->complex_uri || r->quoted_uri) { + if (r->complex_uri || r->quoted_uri || r->empty_path_in_uri) { - r->uri.data = ngx_pnalloc(r->pool, r->uri.len + 1); + if (r->empty_path_in_uri) { + r->uri.len++; + } + + r->uri.data = ngx_pnalloc(r->pool, r->uri.len); if (r->uri.data == NULL) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_ERROR; @@ -1234,7 +1262,7 @@ r->unparsed_uri.len = r->uri_end - r->uri_start; r->unparsed_uri.data = r->uri_start; - r->valid_unparsed_uri = r->space_in_uri ? 0 : 1; + r->valid_unparsed_uri = (r->space_in_uri || r->empty_path_in_uri) ? 0 : 1; if (r->uri_ext) { if (r->args_start) { @@ -1647,6 +1675,12 @@ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http large header copy: %uz", r->header_in->pos - old); + if (r->header_in->pos - old > b->end - b->start) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "too large header to copy"); + return NGX_ERROR; + } + new = b->start; ngx_memcpy(new, old, r->header_in->pos - old); @@ -1993,6 +2027,7 @@ if (r->http_connection->ssl) { long rc; X509 *cert; + const char *s; ngx_http_ssl_srv_conf_t *sscf; if (c->ssl == NULL) { @@ -2037,6 +2072,17 @@ X509_free(cert); } + + if (ngx_ssl_ocsp_get_status(c, &s) != NGX_OK) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client SSL certificate verify error: %s", s); + + ngx_ssl_remove_cached_session(c->ssl->session_ctx, + (SSL_get0_session(c->ssl->connection))); + + ngx_http_finalize_request(r, NGX_HTTPS_CERT_ERROR); + return; + } } } @@ -2597,11 +2643,6 @@ ngx_del_timer(c->write); } - if (c->read->eof) { - ngx_http_close_request(r, 0); - return; - } - ngx_http_finalize_connection(r); } @@ -2700,6 +2741,11 @@ r = r->main; + if (r->connection->read->eof) { + ngx_http_close_request(r, 0); + return; + } + if (r->reading_body) { r->keepalive = 0; r->lingering_close = 1; @@ -2720,7 +2766,7 @@ || r->header_in->pos < r->header_in->last || r->connection->read->ready))) { - ngx_http_set_lingering_close(r); + ngx_http_set_lingering_close(r->connection); return; } @@ -2974,6 +3020,12 @@ rev->error = 1; } +#if (NGX_HTTP_SSL) + if (c->ssl) { + c->ssl->no_send_shutdown = 1; + } +#endif + ngx_log_error(NGX_LOG_INFO, c->log, err, "client prematurely closed connection"); @@ -2999,13 +3051,6 @@ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "set http keepalive handler"); - if (r->discard_body) { - r->write_event_handler = ngx_http_request_empty_handler; - r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000); - ngx_add_timer(rev, clcf->lingering_timeout); - return; - } - c->log->action = "closing request"; hc = r->http_connection; @@ -3335,22 +3380,41 @@ static void -ngx_http_set_lingering_close(ngx_http_request_t *r) +ngx_http_set_lingering_close(ngx_connection_t *c) { ngx_event_t *rev, *wev; - ngx_connection_t *c; + ngx_http_request_t *r; ngx_http_core_loc_conf_t *clcf; - c = r->connection; + r = c->data; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + if (r->lingering_time == 0) { + r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000); + } + +#if (NGX_HTTP_SSL) + if (c->ssl) { + ngx_int_t rc; + + rc = ngx_ssl_shutdown(c); + + if (rc == NGX_ERROR) { + ngx_http_close_request(r, 0); + return; + } + + if (rc == NGX_AGAIN) { + c->ssl->handler = ngx_http_set_lingering_close; + return; + } + } +#endif + rev = c->read; rev->handler = ngx_http_lingering_close_handler; - r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000); - ngx_add_timer(rev, clcf->lingering_timeout); - if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_request(r, 0); return; @@ -3373,6 +3437,11 @@ return; } + c->close = 0; + ngx_reusable_connection(c, 1); + + ngx_add_timer(rev, clcf->lingering_timeout); + if (rev->ready) { ngx_http_lingering_close_handler(rev); } @@ -3395,7 +3464,7 @@ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http lingering close handler"); - if (rev->timedout) { + if (rev->timedout || c->close) { ngx_http_close_request(r, 0); return; } diff -Nru nginx-1.18.0/src/http/ngx_http_request.h nginx-1.20.1/src/http/ngx_http_request.h --- nginx-1.18.0/src/http/ngx_http_request.h 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/ngx_http_request.h 2021-05-25 12:35:38.000000000 +0000 @@ -470,6 +470,9 @@ /* URI with " " */ unsigned space_in_uri:1; + /* URI with empty path */ + unsigned empty_path_in_uri:1; + unsigned invalid_header:1; unsigned add_uri_to_alias:1; diff -Nru nginx-1.18.0/src/http/ngx_http_special_response.c nginx-1.20.1/src/http/ngx_http_special_response.c --- nginx-1.18.0/src/http/ngx_http_special_response.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/ngx_http_special_response.c 2021-05-25 12:35:38.000000000 +0000 @@ -575,6 +575,10 @@ r->headers_out.headers.part.next = NULL; r->headers_out.headers.last = &r->headers_out.headers.part; + r->headers_out.trailers.part.nelts = 0; + r->headers_out.trailers.part.next = NULL; + r->headers_out.trailers.last = &r->headers_out.trailers.part; + r->headers_out.content_length_n = -1; r->headers_out.last_modified_time = -1; } diff -Nru nginx-1.18.0/src/http/ngx_http_upstream.c nginx-1.20.1/src/http/ngx_http_upstream.c --- nginx-1.18.0/src/http/ngx_http_upstream.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/ngx_http_upstream.c 2021-05-25 12:35:38.000000000 +0000 @@ -77,9 +77,6 @@ static void ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r, ngx_uint_t do_write); -static ngx_int_t ngx_http_upstream_non_buffered_filter_init(void *data); -static ngx_int_t ngx_http_upstream_non_buffered_filter(void *data, - ssize_t bytes); #if (NGX_THREADS) static ngx_int_t ngx_http_upstream_thread_handler(ngx_thread_task_t *task, ngx_file_t *file); @@ -610,6 +607,17 @@ u->store = u->conf->store; if (!u->store && !r->post_action && !u->conf->ignore_client_abort) { + + if (r->connection->read->ready) { + ngx_post_event(r->connection->read, &ngx_posted_events); + + } else { + if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + } + r->read_event_handler = ngx_http_upstream_rd_check_broken_connection; r->write_event_handler = ngx_http_upstream_wr_check_broken_connection; } @@ -1919,6 +1927,7 @@ u->keepalive = 0; u->upgrade = 0; + u->error = 0; ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t)); u->headers_in.content_length_n = -1; @@ -2475,7 +2484,7 @@ #if (NGX_HTTP_CACHE) if (u->cache_status == NGX_HTTP_CACHE_EXPIRED - && ((u->conf->cache_use_stale & un->mask) || r->cache->stale_error)) + && (u->conf->cache_use_stale & un->mask)) { ngx_int_t rc; @@ -2502,6 +2511,8 @@ } #endif + + break; } #if (NGX_HTTP_CACHE) @@ -3011,9 +3022,7 @@ return; } - if (u->peer.connection->read->ready || u->length == 0) { - ngx_http_upstream_process_non_buffered_upstream(r, u); - } + ngx_http_upstream_process_non_buffered_upstream(r, u); } return; @@ -3625,7 +3634,7 @@ return; } - if (upstream->read->error) { + if (upstream->read->error || u->error) { ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY); return; @@ -3703,14 +3712,14 @@ } -static ngx_int_t +ngx_int_t ngx_http_upstream_non_buffered_filter_init(void *data) { return NGX_OK; } -static ngx_int_t +ngx_int_t ngx_http_upstream_non_buffered_filter(void *data, ssize_t bytes) { ngx_http_request_t *r = data; @@ -3721,6 +3730,13 @@ u = r->upstream; + if (u->length == 0) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "upstream sent more data than specified in " + "\"Content-Length\" header"); + return NGX_OK; + } + for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) { ll = &cl->next; } @@ -3746,6 +3762,18 @@ return NGX_OK; } + if (bytes > u->length) { + + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "upstream sent more data than specified in " + "\"Content-Length\" header"); + + cl->buf->last = cl->buf->pos + u->length; + u->length = 0; + + return NGX_OK; + } + u->length -= bytes; return NGX_OK; diff -Nru nginx-1.18.0/src/http/ngx_http_upstream.h nginx-1.20.1/src/http/ngx_http_upstream.h --- nginx-1.18.0/src/http/ngx_http_upstream.h 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/ngx_http_upstream.h 2021-05-25 12:35:38.000000000 +0000 @@ -391,6 +391,7 @@ unsigned buffering:1; unsigned keepalive:1; unsigned upgrade:1; + unsigned error:1; unsigned request_sent:1; unsigned request_body_sent:1; @@ -414,6 +415,8 @@ ngx_int_t ngx_http_upstream_create(ngx_http_request_t *r); void ngx_http_upstream_init(ngx_http_request_t *r); +ngx_int_t ngx_http_upstream_non_buffered_filter_init(void *data); +ngx_int_t ngx_http_upstream_non_buffered_filter(void *data, ssize_t bytes); ngx_http_upstream_srv_conf_t *ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags); char *ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, diff -Nru nginx-1.18.0/src/http/ngx_http_upstream_round_robin.c nginx-1.20.1/src/http/ngx_http_upstream_round_robin.c --- nginx-1.18.0/src/http/ngx_http_upstream_round_robin.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/ngx_http_upstream_round_robin.c 2021-05-25 12:35:38.000000000 +0000 @@ -10,8 +10,8 @@ #include -#define ngx_http_upstream_tries(p) ((p)->number \ - + ((p)->next ? (p)->next->number : 0)) +#define ngx_http_upstream_tries(p) ((p)->tries \ + + ((p)->next ? (p)->next->tries : 0)) static ngx_http_upstream_rr_peer_t *ngx_http_upstream_get_peer( @@ -32,7 +32,7 @@ ngx_http_upstream_srv_conf_t *us) { ngx_url_t u; - ngx_uint_t i, j, n, w; + ngx_uint_t i, j, n, w, t; ngx_http_upstream_server_t *server; ngx_http_upstream_rr_peer_t *peer, **peerp; ngx_http_upstream_rr_peers_t *peers, *backup; @@ -44,6 +44,7 @@ n = 0; w = 0; + t = 0; for (i = 0; i < us->servers->nelts; i++) { if (server[i].backup) { @@ -52,6 +53,10 @@ n += server[i].naddrs; w += server[i].naddrs * server[i].weight; + + if (!server[i].down) { + t += server[i].naddrs; + } } if (n == 0) { @@ -75,6 +80,7 @@ peers->number = n; peers->weighted = (w != n); peers->total_weight = w; + peers->tries = t; peers->name = &us->host; n = 0; @@ -110,6 +116,7 @@ n = 0; w = 0; + t = 0; for (i = 0; i < us->servers->nelts; i++) { if (!server[i].backup) { @@ -118,6 +125,10 @@ n += server[i].naddrs; w += server[i].naddrs * server[i].weight; + + if (!server[i].down) { + t += server[i].naddrs; + } } if (n == 0) { @@ -139,6 +150,7 @@ backup->number = n; backup->weighted = (w != n); backup->total_weight = w; + backup->tries = t; backup->name = &us->host; n = 0; @@ -214,6 +226,7 @@ peers->number = n; peers->weighted = 0; peers->total_weight = n; + peers->tries = n; peers->name = &us->host; peerp = &peers->peer; @@ -332,6 +345,7 @@ peers->single = (ur->naddrs == 1); peers->number = ur->naddrs; + peers->tries = ur->naddrs; peers->name = &ur->host; if (ur->sockaddr) { diff -Nru nginx-1.18.0/src/http/ngx_http_upstream_round_robin.h nginx-1.20.1/src/http/ngx_http_upstream_round_robin.h --- nginx-1.18.0/src/http/ngx_http_upstream_round_robin.h 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/ngx_http_upstream_round_robin.h 2021-05-25 12:35:38.000000000 +0000 @@ -68,6 +68,7 @@ #endif ngx_uint_t total_weight; + ngx_uint_t tries; unsigned single:1; unsigned weighted:1; diff -Nru nginx-1.18.0/src/http/ngx_http_variables.c nginx-1.20.1/src/http/ngx_http_variables.c --- nginx-1.18.0/src/http/ngx_http_variables.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/ngx_http_variables.c 2021-05-25 12:35:38.000000000 +0000 @@ -129,6 +129,8 @@ ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_connection_requests(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_connection_time(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_nginx_version(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); @@ -342,6 +344,9 @@ { ngx_string("connection_requests"), NULL, ngx_http_variable_connection_requests, 0, 0, 0 }, + { ngx_string("connection_time"), NULL, ngx_http_variable_connection_time, + 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + { ngx_string("nginx_version"), NULL, ngx_http_variable_nginx_version, 0, 0, 0 }, @@ -1075,7 +1080,7 @@ len = name->len - (sizeof("arg_") - 1); arg = name->data + sizeof("arg_") - 1; - if (ngx_http_arg(r, arg, len, &value) != NGX_OK) { + if (len == 0 || ngx_http_arg(r, arg, len, &value) != NGX_OK) { v->not_found = 1; return NGX_OK; } @@ -2246,6 +2251,31 @@ v->valid = 1; v->no_cacheable = 0; v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_connection_time(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + ngx_msec_int_t ms; + + p = ngx_pnalloc(r->pool, NGX_TIME_T_LEN + 4); + if (p == NULL) { + return NGX_ERROR; + } + + ms = ngx_current_msec - r->connection->start_time; + ms = ngx_max(ms, 0); + + v->len = ngx_sprintf(p, "%T.%03M", (time_t) ms / 1000, ms % 1000) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; v->data = p; return NGX_OK; diff -Nru nginx-1.18.0/src/http/v2/ngx_http_v2.c nginx-1.20.1/src/http/v2/ngx_http_v2.c --- nginx-1.18.0/src/http/v2/ngx_http_v2.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/v2/ngx_http_v2.c 2021-05-25 12:35:38.000000000 +0000 @@ -60,6 +60,8 @@ static void ngx_http_v2_read_handler(ngx_event_t *rev); static void ngx_http_v2_write_handler(ngx_event_t *wev); static void ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c); +static void ngx_http_v2_lingering_close(ngx_connection_t *c); +static void ngx_http_v2_lingering_close_handler(ngx_event_t *rev); static u_char *ngx_http_v2_state_proxy_protocol(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end); @@ -236,6 +238,7 @@ ngx_http_v2_srv_conf_t *h2scf; ngx_http_v2_main_conf_t *h2mcf; ngx_http_v2_connection_t *h2c; + ngx_http_core_srv_conf_t *cscf; c = rev->data; hc = c->data; @@ -274,7 +277,7 @@ h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module); h2c->concurrent_pushes = h2scf->concurrent_pushes; - h2c->priority_limit = h2scf->concurrent_streams; + h2c->priority_limit = ngx_max(h2scf->concurrent_streams, 100); h2c->pool = ngx_create_pool(h2scf->pool_size, h2c->connection->log); if (h2c->pool == NULL) { @@ -323,7 +326,14 @@ rev->handler = ngx_http_v2_read_handler; c->write->handler = ngx_http_v2_write_handler; + if (!rev->timer_set) { + cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, + ngx_http_core_module); + ngx_add_timer(rev, cscf->client_header_timeout); + } + c->idle = 1; + ngx_reusable_connection(c, 0); ngx_http_v2_read_handler(rev); } @@ -360,6 +370,11 @@ return; } + if (!h2c->processing && !h2c->pushing) { + ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_NO_ERROR); + return; + } + if (!h2c->goaway) { h2c->goaway = 1; @@ -447,14 +462,6 @@ h2c->blocked = 0; - if (h2c->processing || h2c->pushing) { - if (rev->timer_set) { - ngx_del_timer(rev); - } - - return; - } - ngx_http_v2_handle_connection(h2c); } @@ -473,6 +480,7 @@ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http2 write event timed out"); c->error = 1; + c->timedout = 1; ngx_http_v2_finalize_connection(h2c, 0); return; } @@ -626,9 +634,9 @@ static void ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c) { - ngx_int_t rc; - ngx_connection_t *c; - ngx_http_v2_srv_conf_t *h2scf; + ngx_int_t rc; + ngx_connection_t *c; + ngx_http_core_loc_conf_t *clcf; if (h2c->last_out || h2c->processing || h2c->pushing) { return; @@ -661,14 +669,20 @@ } if (h2c->goaway) { - ngx_http_close_connection(c); + ngx_http_v2_lingering_close(c); return; } - h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, - ngx_http_v2_module); + clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx, + ngx_http_core_module); + + if (!c->read->timer_set) { + ngx_add_timer(c->read, clcf->keepalive_timeout); + } + + ngx_reusable_connection(c, 1); + if (h2c->state.incomplete) { - ngx_add_timer(c->read, h2scf->recv_timeout); return; } @@ -686,7 +700,6 @@ #endif c->destroyed = 1; - ngx_reusable_connection(c, 1); c->write->handler = ngx_http_empty_handler; c->read->handler = ngx_http_v2_idle_handler; @@ -694,8 +707,142 @@ if (c->write->timer_set) { ngx_del_timer(c->write); } +} + + +static void +ngx_http_v2_lingering_close(ngx_connection_t *c) +{ + ngx_event_t *rev, *wev; + ngx_http_v2_connection_t *h2c; + ngx_http_core_loc_conf_t *clcf; + + h2c = c->data; + + clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx, + ngx_http_core_module); + + if (clcf->lingering_close == NGX_HTTP_LINGERING_OFF) { + ngx_http_close_connection(c); + return; + } + + if (h2c->lingering_time == 0) { + h2c->lingering_time = ngx_time() + + (time_t) (clcf->lingering_time / 1000); + } - ngx_add_timer(c->read, h2scf->idle_timeout); +#if (NGX_HTTP_SSL) + if (c->ssl) { + ngx_int_t rc; + + rc = ngx_ssl_shutdown(c); + + if (rc == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } + + if (rc == NGX_AGAIN) { + c->ssl->handler = ngx_http_v2_lingering_close; + return; + } + } +#endif + + rev = c->read; + rev->handler = ngx_http_v2_lingering_close_handler; + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_connection(c); + return; + } + + wev = c->write; + wev->handler = ngx_http_empty_handler; + + if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) { + if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) { + ngx_http_close_connection(c); + return; + } + } + + if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) { + ngx_connection_error(c, ngx_socket_errno, + ngx_shutdown_socket_n " failed"); + ngx_http_close_connection(c); + return; + } + + c->close = 0; + ngx_reusable_connection(c, 1); + + ngx_add_timer(rev, clcf->lingering_timeout); + + if (rev->ready) { + ngx_http_v2_lingering_close_handler(rev); + } +} + + +static void +ngx_http_v2_lingering_close_handler(ngx_event_t *rev) +{ + ssize_t n; + ngx_msec_t timer; + ngx_connection_t *c; + ngx_http_core_loc_conf_t *clcf; + ngx_http_v2_connection_t *h2c; + u_char buffer[NGX_HTTP_LINGERING_BUFFER_SIZE]; + + c = rev->data; + h2c = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http2 lingering close handler"); + + if (rev->timedout || c->close) { + ngx_http_close_connection(c); + return; + } + + timer = (ngx_msec_t) h2c->lingering_time - (ngx_msec_t) ngx_time(); + if ((ngx_msec_int_t) timer <= 0) { + ngx_http_close_connection(c); + return; + } + + do { + n = c->recv(c, buffer, NGX_HTTP_LINGERING_BUFFER_SIZE); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "lingering read: %z", n); + + if (n == NGX_AGAIN) { + break; + } + + if (n == NGX_ERROR || n == 0) { + ngx_http_close_connection(c); + return; + } + + } while (rev->ready); + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_connection(c); + return; + } + + clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx, + ngx_http_core_module); + timer *= 1000; + + if (timer > clcf->lingering_timeout) { + timer = clcf->lingering_timeout; + } + + ngx_add_timer(rev, timer); } @@ -731,9 +878,8 @@ } if (ngx_memcmp(pos, preface, sizeof(preface) - 1) != 0) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "invalid http2 connection preface \"%*s\"", - sizeof(preface) - 1, pos); + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "invalid connection preface"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); } @@ -754,9 +900,8 @@ } if (ngx_memcmp(pos, preface, sizeof(preface) - 1) != 0) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "invalid http2 connection preface \"%*s\"", - sizeof(preface) - 1, pos); + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "invalid connection preface"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); } @@ -845,6 +990,13 @@ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, "http2 DATA frame"); + if (h2c->state.sid == 0) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent DATA frame with incorrect identifier"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + } + if (size > h2c->recv_window) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, "client violated connection flow control: " @@ -943,6 +1095,7 @@ size_t size; ngx_buf_t *buf; ngx_int_t rc; + ngx_connection_t *fc; ngx_http_request_t *r; ngx_http_v2_stream_t *stream; ngx_http_v2_srv_conf_t *h2scf; @@ -961,6 +1114,7 @@ } r = stream->request; + fc = r->connection; if (r->reading_body && !r->request_body_no_buffering) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, @@ -969,6 +1123,13 @@ return ngx_http_v2_state_skip_padded(h2c, pos, end); } + if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "skipping http2 DATA frame"); + + return ngx_http_v2_state_skip_padded(h2c, pos, end); + } + size = end - pos; if (size >= h2c->state.length) { @@ -986,6 +1147,8 @@ ngx_http_finalize_request(r, rc); } + ngx_http_run_posted_requests(fc); + } else if (size) { buf = stream->preread; @@ -1031,12 +1194,15 @@ ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) { - size_t size; - ngx_uint_t padded, priority, depend, dependency, excl, weight; - ngx_uint_t status; - ngx_http_v2_node_t *node; - ngx_http_v2_stream_t *stream; - ngx_http_v2_srv_conf_t *h2scf; + size_t size; + ngx_uint_t padded, priority, depend, dependency, excl, + weight; + ngx_uint_t status; + ngx_http_v2_node_t *node; + ngx_http_v2_stream_t *stream; + ngx_http_v2_srv_conf_t *h2scf; + ngx_http_core_srv_conf_t *cscf; + ngx_http_core_loc_conf_t *clcf; padded = h2c->state.flags & NGX_HTTP_V2_PADDED_FLAG; priority = h2c->state.flags & NGX_HTTP_V2_PRIORITY_FLAG; @@ -1137,11 +1303,15 @@ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); } + cscf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, + ngx_http_core_module); + + h2c->state.header_limit = cscf->large_client_header_buffers.size + * cscf->large_client_header_buffers.num; + h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, ngx_http_v2_module); - h2c->state.header_limit = h2scf->max_header_size; - if (h2c->processing >= h2scf->concurrent_streams) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, "concurrent streams exceeded %ui", h2c->processing); @@ -1195,7 +1365,14 @@ ngx_http_v2_set_dependency(h2c, node, depend, excl); } - if (h2c->connection->requests >= h2scf->max_requests) { + clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx, + ngx_http_core_module); + + if (clcf->keepalive_timeout == 0 + || h2c->connection->requests >= clcf->keepalive_requests + || ngx_current_msec - h2c->connection->start_time + > clcf->keepalive_time) + { h2c->goaway = 1; if (ngx_http_v2_send_goaway(h2c, NGX_HTTP_V2_NO_ERROR) == NGX_ERROR) { @@ -1320,10 +1497,10 @@ ngx_http_v2_state_field_len(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) { - size_t alloc; - ngx_int_t len; - ngx_uint_t huff; - ngx_http_v2_srv_conf_t *h2scf; + size_t alloc; + ngx_int_t len; + ngx_uint_t huff; + ngx_http_core_srv_conf_t *cscf; if (!(h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) && h2c->state.length < NGX_HTTP_V2_INT_OCTETS) @@ -1370,12 +1547,12 @@ "http2 %s string, len:%i", huff ? "encoded" : "raw", len); - h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, - ngx_http_v2_module); + cscf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, + ngx_http_core_module); - if ((size_t) len > h2scf->max_field_size) { + if ((size_t) len > cscf->large_client_header_buffers.size) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client exceeded http2_max_field_size limit"); + "client sent too large header field"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_ENHANCE_YOUR_CALM); } @@ -1590,7 +1767,7 @@ if (len > h2c->state.header_limit) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client exceeded http2_max_header_size limit"); + "client sent too large header"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_ENHANCE_YOUR_CALM); } @@ -1987,6 +2164,16 @@ ngx_http_v2_state_settings(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 SETTINGS frame"); + + if (h2c->state.sid) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent SETTINGS frame with incorrect identifier"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + } + if (h2c->state.flags == NGX_HTTP_V2_ACK_FLAG) { if (h2c->state.length != 0) { @@ -2010,9 +2197,6 @@ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); } - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2 SETTINGS frame"); - return ngx_http_v2_state_settings_params(h2c, pos, end); } @@ -2161,6 +2345,13 @@ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, "http2 PING frame"); + if (h2c->state.sid) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent PING frame with incorrect identifier"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + } + if (h2c->state.flags & NGX_HTTP_V2_ACK_FLAG) { return ngx_http_v2_state_skip(h2c, pos, end); } @@ -2202,6 +2393,13 @@ return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_goaway); } + if (h2c->state.sid) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent GOAWAY frame with incorrect identifier"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + } + #if (NGX_DEBUG) h2c->state.length -= NGX_HTTP_V2_GOAWAY_SIZE; @@ -3103,6 +3301,10 @@ h2c->priority_limit += h2scf->concurrent_streams; + if (h2c->connection->read->timer_set) { + ngx_del_timer(h2c->connection->read); + } + return stream; } @@ -4469,6 +4671,7 @@ ngx_connection_t *c; ngx_http_v2_srv_conf_t *h2scf; ngx_http_v2_connection_t *h2c; + ngx_http_core_loc_conf_t *clcf; c = rev->data; h2c = c->data; @@ -4500,10 +4703,10 @@ #endif - h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, - ngx_http_v2_module); + clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx, + ngx_http_core_module); - if (h2c->idle++ > 10 * h2scf->max_requests) { + if (h2c->idle++ > 10 * clcf->keepalive_requests) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, "http2 flood detected"); ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_NO_ERROR); @@ -4513,6 +4716,9 @@ c->destroyed = 0; ngx_reusable_connection(c, 0); + h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, + ngx_http_v2_module); + h2c->pool = ngx_create_pool(h2scf->pool_size, h2c->connection->log); if (h2c->pool == NULL) { ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_INTERNAL_ERROR); @@ -4543,16 +4749,15 @@ h2c->blocked = 1; if (!c->error && !h2c->goaway) { + h2c->goaway = 1; + if (ngx_http_v2_send_goaway(h2c, status) != NGX_ERROR) { (void) ngx_http_v2_send_output_queue(h2c); } } - c->error = 1; - if (!h2c->processing && !h2c->pushing) { - ngx_http_close_connection(c); - return; + goto done; } c->read->handler = ngx_http_empty_handler; @@ -4600,10 +4805,18 @@ h2c->blocked = 0; if (h2c->processing || h2c->pushing) { + c->error = 1; + return; + } + +done: + + if (c->error) { + ngx_http_close_connection(c); return; } - ngx_http_close_connection(c); + ngx_http_v2_lingering_close(c); } diff -Nru nginx-1.18.0/src/http/v2/ngx_http_v2.h nginx-1.20.1/src/http/v2/ngx_http_v2.h --- nginx-1.18.0/src/http/v2/ngx_http_v2.h 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/v2/ngx_http_v2.h 2021-05-25 12:35:38.000000000 +0000 @@ -157,6 +157,8 @@ ngx_uint_t last_sid; ngx_uint_t last_push; + time_t lingering_time; + unsigned closed_nodes:8; unsigned settings_ack:1; unsigned table_update:1; diff -Nru nginx-1.18.0/src/http/v2/ngx_http_v2_module.c nginx-1.20.1/src/http/v2/ngx_http_v2_module.c --- nginx-1.18.0/src/http/v2/ngx_http_v2_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/v2/ngx_http_v2_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -36,10 +36,31 @@ static char *ngx_http_v2_streams_index_mask(ngx_conf_t *cf, void *post, void *data); static char *ngx_http_v2_chunk_size(ngx_conf_t *cf, void *post, void *data); -static char *ngx_http_v2_spdy_deprecated(ngx_conf_t *cf, ngx_command_t *cmd, +static char *ngx_http_v2_obsolete(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static ngx_conf_deprecated_t ngx_http_v2_recv_timeout_deprecated = { + ngx_conf_deprecated, "http2_recv_timeout", "client_header_timeout" +}; + +static ngx_conf_deprecated_t ngx_http_v2_idle_timeout_deprecated = { + ngx_conf_deprecated, "http2_idle_timeout", "keepalive_timeout" +}; + +static ngx_conf_deprecated_t ngx_http_v2_max_requests_deprecated = { + ngx_conf_deprecated, "http2_max_requests", "keepalive_requests" +}; + +static ngx_conf_deprecated_t ngx_http_v2_max_field_size_deprecated = { + ngx_conf_deprecated, "http2_max_field_size", "large_client_header_buffers" +}; + +static ngx_conf_deprecated_t ngx_http_v2_max_header_size_deprecated = { + ngx_conf_deprecated, "http2_max_header_size", "large_client_header_buffers" +}; + + static ngx_conf_post_t ngx_http_v2_recv_buffer_size_post = { ngx_http_v2_recv_buffer_size }; static ngx_conf_post_t ngx_http_v2_pool_size_post = @@ -84,24 +105,24 @@ { ngx_string("http2_max_requests"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_num_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_v2_srv_conf_t, max_requests), - NULL }, + ngx_http_v2_obsolete, + 0, + 0, + &ngx_http_v2_max_requests_deprecated }, { ngx_string("http2_max_field_size"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_size_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_v2_srv_conf_t, max_field_size), - NULL }, + ngx_http_v2_obsolete, + 0, + 0, + &ngx_http_v2_max_field_size_deprecated }, { ngx_string("http2_max_header_size"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_size_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_v2_srv_conf_t, max_header_size), - NULL }, + ngx_http_v2_obsolete, + 0, + 0, + &ngx_http_v2_max_header_size_deprecated }, { ngx_string("http2_body_preread_size"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, @@ -119,17 +140,17 @@ { ngx_string("http2_recv_timeout"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_msec_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_v2_srv_conf_t, recv_timeout), - NULL }, + ngx_http_v2_obsolete, + 0, + 0, + &ngx_http_v2_recv_timeout_deprecated }, { ngx_string("http2_idle_timeout"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_msec_slot, - NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_v2_srv_conf_t, idle_timeout), - NULL }, + ngx_http_v2_obsolete, + 0, + 0, + &ngx_http_v2_idle_timeout_deprecated }, { ngx_string("http2_chunk_size"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, @@ -152,62 +173,6 @@ 0, NULL }, - { ngx_string("spdy_recv_buffer_size"), - NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, - ngx_http_v2_spdy_deprecated, - NGX_HTTP_MAIN_CONF_OFFSET, - 0, - NULL }, - - { ngx_string("spdy_pool_size"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_http_v2_spdy_deprecated, - NGX_HTTP_SRV_CONF_OFFSET, - 0, - NULL }, - - { ngx_string("spdy_max_concurrent_streams"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_http_v2_spdy_deprecated, - NGX_HTTP_SRV_CONF_OFFSET, - 0, - NULL }, - - { ngx_string("spdy_streams_index_size"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_http_v2_spdy_deprecated, - NGX_HTTP_SRV_CONF_OFFSET, - 0, - NULL }, - - { ngx_string("spdy_recv_timeout"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_http_v2_spdy_deprecated, - NGX_HTTP_SRV_CONF_OFFSET, - 0, - NULL }, - - { ngx_string("spdy_keepalive_timeout"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_http_v2_spdy_deprecated, - NGX_HTTP_SRV_CONF_OFFSET, - 0, - NULL }, - - { ngx_string("spdy_headers_comp"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_http_v2_spdy_deprecated, - NGX_HTTP_SRV_CONF_OFFSET, - 0, - NULL }, - - { ngx_string("spdy_chunk_size"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_http_v2_spdy_deprecated, - NGX_HTTP_LOC_CONF_OFFSET, - 0, - NULL }, - ngx_null_command }; @@ -353,18 +318,11 @@ h2scf->concurrent_streams = NGX_CONF_UNSET_UINT; h2scf->concurrent_pushes = NGX_CONF_UNSET_UINT; - h2scf->max_requests = NGX_CONF_UNSET_UINT; - - h2scf->max_field_size = NGX_CONF_UNSET_SIZE; - h2scf->max_header_size = NGX_CONF_UNSET_SIZE; h2scf->preread_size = NGX_CONF_UNSET_SIZE; h2scf->streams_index_mask = NGX_CONF_UNSET_UINT; - h2scf->recv_timeout = NGX_CONF_UNSET_MSEC; - h2scf->idle_timeout = NGX_CONF_UNSET_MSEC; - return h2scf; } @@ -381,23 +339,12 @@ prev->concurrent_streams, 128); ngx_conf_merge_uint_value(conf->concurrent_pushes, prev->concurrent_pushes, 10); - ngx_conf_merge_uint_value(conf->max_requests, prev->max_requests, 1000); - - ngx_conf_merge_size_value(conf->max_field_size, prev->max_field_size, - 4096); - ngx_conf_merge_size_value(conf->max_header_size, prev->max_header_size, - 16384); ngx_conf_merge_size_value(conf->preread_size, prev->preread_size, 65536); ngx_conf_merge_uint_value(conf->streams_index_mask, prev->streams_index_mask, 32 - 1); - ngx_conf_merge_msec_value(conf->recv_timeout, - prev->recv_timeout, 30000); - ngx_conf_merge_msec_value(conf->idle_timeout, - prev->idle_timeout, 180000); - return NGX_CONF_OK; } @@ -600,11 +547,14 @@ static char * -ngx_http_v2_spdy_deprecated(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +ngx_http_v2_obsolete(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { + ngx_conf_deprecated_t *d = cmd->post; + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "invalid directive \"%V\": ngx_http_spdy_module " - "was superseded by ngx_http_v2_module", &cmd->name); + "the \"%s\" directive is obsolete, " + "use the \"%s\" directive instead", + d->old_name, d->new_name); return NGX_CONF_OK; } diff -Nru nginx-1.18.0/src/http/v2/ngx_http_v2_module.h nginx-1.20.1/src/http/v2/ngx_http_v2_module.h --- nginx-1.18.0/src/http/v2/ngx_http_v2_module.h 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/http/v2/ngx_http_v2_module.h 2021-05-25 12:35:38.000000000 +0000 @@ -24,13 +24,8 @@ size_t pool_size; ngx_uint_t concurrent_streams; ngx_uint_t concurrent_pushes; - ngx_uint_t max_requests; - size_t max_field_size; - size_t max_header_size; size_t preread_size; ngx_uint_t streams_index_mask; - ngx_msec_t recv_timeout; - ngx_msec_t idle_timeout; } ngx_http_v2_srv_conf_t; diff -Nru nginx-1.18.0/src/mail/ngx_mail_auth_http_module.c nginx-1.20.1/src/mail/ngx_mail_auth_http_module.c --- nginx-1.18.0/src/mail/ngx_mail_auth_http_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/mail/ngx_mail_auth_http_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -1135,10 +1135,10 @@ size_t len; ngx_buf_t *b; ngx_str_t login, passwd; + ngx_connection_t *c; #if (NGX_MAIL_SSL) ngx_str_t verify, subject, issuer, serial, fingerprint, raw_cert, cert; - ngx_connection_t *c; ngx_mail_ssl_conf_t *sslcf; #endif ngx_mail_core_srv_conf_t *cscf; @@ -1151,9 +1151,10 @@ return NULL; } + c = s->connection; + #if (NGX_MAIL_SSL) - c = s->connection; sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); if (c->ssl && sslcf->verify) { @@ -1224,22 +1225,49 @@ + sizeof("Client-IP: ") - 1 + s->connection->addr_text.len + sizeof(CRLF) - 1 + sizeof("Client-Host: ") - 1 + s->host.len + sizeof(CRLF) - 1 - + sizeof("Auth-SMTP-Helo: ") - 1 + s->smtp_helo.len + sizeof(CRLF) - 1 - + sizeof("Auth-SMTP-From: ") - 1 + s->smtp_from.len + sizeof(CRLF) - 1 - + sizeof("Auth-SMTP-To: ") - 1 + s->smtp_to.len + sizeof(CRLF) - 1 -#if (NGX_MAIL_SSL) - + sizeof("Auth-SSL: on" CRLF) - 1 - + sizeof("Auth-SSL-Verify: ") - 1 + verify.len + sizeof(CRLF) - 1 - + sizeof("Auth-SSL-Subject: ") - 1 + subject.len + sizeof(CRLF) - 1 - + sizeof("Auth-SSL-Issuer: ") - 1 + issuer.len + sizeof(CRLF) - 1 - + sizeof("Auth-SSL-Serial: ") - 1 + serial.len + sizeof(CRLF) - 1 - + sizeof("Auth-SSL-Fingerprint: ") - 1 + fingerprint.len - + sizeof(CRLF) - 1 - + sizeof("Auth-SSL-Cert: ") - 1 + cert.len + sizeof(CRLF) - 1 -#endif + ahcf->header.len + sizeof(CRLF) - 1; + if (c->proxy_protocol) { + len += sizeof("Proxy-Protocol-Addr: ") - 1 + + c->proxy_protocol->src_addr.len + sizeof(CRLF) - 1 + + sizeof("Proxy-Protocol-Port: ") - 1 + + sizeof("65535") - 1 + sizeof(CRLF) - 1 + + sizeof("Proxy-Protocol-Server-Addr: ") - 1 + + c->proxy_protocol->dst_addr.len + sizeof(CRLF) - 1 + + sizeof("Proxy-Protocol-Server-Port: ") - 1 + + sizeof("65535") - 1 + sizeof(CRLF) - 1; + } + + if (s->auth_method == NGX_MAIL_AUTH_NONE) { + len += sizeof("Auth-SMTP-Helo: ") - 1 + s->smtp_helo.len + + sizeof(CRLF) - 1 + + sizeof("Auth-SMTP-From: ") - 1 + s->smtp_from.len + + sizeof(CRLF) - 1 + + sizeof("Auth-SMTP-To: ") - 1 + s->smtp_to.len + + sizeof(CRLF) - 1; + } + +#if (NGX_MAIL_SSL) + + if (c->ssl) { + len += sizeof("Auth-SSL: on" CRLF) - 1 + + sizeof("Auth-SSL-Verify: ") - 1 + verify.len + + sizeof(CRLF) - 1 + + sizeof("Auth-SSL-Subject: ") - 1 + subject.len + + sizeof(CRLF) - 1 + + sizeof("Auth-SSL-Issuer: ") - 1 + issuer.len + + sizeof(CRLF) - 1 + + sizeof("Auth-SSL-Serial: ") - 1 + serial.len + + sizeof(CRLF) - 1 + + sizeof("Auth-SSL-Fingerprint: ") - 1 + fingerprint.len + + sizeof(CRLF) - 1 + + sizeof("Auth-SSL-Cert: ") - 1 + cert.len + + sizeof(CRLF) - 1; + } + +#endif + b = ngx_create_temp_buf(pool, len); if (b == NULL) { return NULL; @@ -1298,6 +1326,26 @@ *b->last++ = CR; *b->last++ = LF; } + if (c->proxy_protocol) { + b->last = ngx_cpymem(b->last, "Proxy-Protocol-Addr: ", + sizeof("Proxy-Protocol-Addr: ") - 1); + b->last = ngx_copy(b->last, c->proxy_protocol->src_addr.data, + c->proxy_protocol->src_addr.len); + *b->last++ = CR; *b->last++ = LF; + + b->last = ngx_sprintf(b->last, "Proxy-Protocol-Port: %d" CRLF, + c->proxy_protocol->src_port); + + b->last = ngx_cpymem(b->last, "Proxy-Protocol-Server-Addr: ", + sizeof("Proxy-Protocol-Server-Addr: ") - 1); + b->last = ngx_copy(b->last, c->proxy_protocol->dst_addr.data, + c->proxy_protocol->dst_addr.len); + *b->last++ = CR; *b->last++ = LF; + + b->last = ngx_sprintf(b->last, "Proxy-Protocol-Server-Port: %d" CRLF, + c->proxy_protocol->dst_port); + } + if (s->auth_method == NGX_MAIL_AUTH_NONE) { /* HELO, MAIL FROM, and RCPT TO can't contain CRLF, no need to escape */ diff -Nru nginx-1.18.0/src/mail/ngx_mail.c nginx-1.20.1/src/mail/ngx_mail.c --- nginx-1.18.0/src/mail/ngx_mail.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/mail/ngx_mail.c 2021-05-25 12:35:38.000000000 +0000 @@ -405,6 +405,7 @@ #if (NGX_MAIL_SSL) addrs[i].conf.ssl = addr[i].opt.ssl; #endif + addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; addrs[i].conf.addr_text = addr[i].opt.addr_text; } @@ -439,6 +440,7 @@ #if (NGX_MAIL_SSL) addrs6[i].conf.ssl = addr[i].opt.ssl; #endif + addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; addrs6[i].conf.addr_text = addr[i].opt.addr_text; } diff -Nru nginx-1.18.0/src/mail/ngx_mail_core_module.c nginx-1.20.1/src/mail/ngx_mail_core_module.c --- nginx-1.18.0/src/mail/ngx_mail_core_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/mail/ngx_mail_core_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -548,6 +548,11 @@ #endif } + if (ngx_strcmp(value[i].data, "proxy_protocol") == 0) { + ls->proxy_protocol = 1; + continue; + } + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "the invalid \"%V\" parameter", &value[i]); return NGX_CONF_ERROR; diff -Nru nginx-1.18.0/src/mail/ngx_mail.h nginx-1.20.1/src/mail/ngx_mail.h --- nginx-1.18.0/src/mail/ngx_mail.h 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/mail/ngx_mail.h 2021-05-25 12:35:38.000000000 +0000 @@ -41,6 +41,7 @@ unsigned ipv6only:1; #endif unsigned so_keepalive:2; + unsigned proxy_protocol:1; #if (NGX_HAVE_KEEPALIVE_TUNABLE) int tcp_keepidle; int tcp_keepintvl; @@ -55,7 +56,8 @@ typedef struct { ngx_mail_conf_ctx_t *ctx; ngx_str_t addr_text; - ngx_uint_t ssl; /* unsigned ssl:1; */ + unsigned ssl:1; + unsigned proxy_protocol:1; } ngx_mail_addr_conf_t; typedef struct { @@ -162,10 +164,12 @@ ngx_smtp_auth_external, ngx_smtp_helo, ngx_smtp_helo_xclient, + ngx_smtp_helo_auth, ngx_smtp_helo_from, ngx_smtp_xclient, ngx_smtp_xclient_from, ngx_smtp_xclient_helo, + ngx_smtp_xclient_auth, ngx_smtp_from, ngx_smtp_to } ngx_smtp_state_e; @@ -174,6 +178,7 @@ typedef struct { ngx_peer_connection_t upstream; ngx_buf_t *buffer; + ngx_uint_t proxy_protocol; /* unsigned proxy_protocol:1; */ } ngx_mail_proxy_ctx_t; @@ -195,6 +200,7 @@ ngx_uint_t mail_state; + unsigned ssl:1; unsigned protocol:3; unsigned blocked:1; unsigned quit:1; @@ -403,6 +409,7 @@ /* STUB */ void ngx_mail_proxy_init(ngx_mail_session_t *s, ngx_addr_t *peer); void ngx_mail_auth_http_init(ngx_mail_session_t *s); +ngx_int_t ngx_mail_realip_handler(ngx_mail_session_t *s); /**/ diff -Nru nginx-1.18.0/src/mail/ngx_mail_handler.c nginx-1.20.1/src/mail/ngx_mail_handler.c --- nginx-1.18.0/src/mail/ngx_mail_handler.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/mail/ngx_mail_handler.c 2021-05-25 12:35:38.000000000 +0000 @@ -11,6 +11,8 @@ #include +static void ngx_mail_proxy_protocol_handler(ngx_event_t *rev); +static void ngx_mail_init_session_handler(ngx_event_t *rev); static void ngx_mail_init_session(ngx_connection_t *c); #if (NGX_MAIL_SSL) @@ -26,6 +28,7 @@ { size_t len; ngx_uint_t i; + ngx_event_t *rev; ngx_mail_port_t *port; struct sockaddr *sa; struct sockaddr_in *sin; @@ -129,6 +132,10 @@ s->main_conf = addr_conf->ctx->main_conf; s->srv_conf = addr_conf->ctx->srv_conf; +#if (NGX_MAIL_SSL) + s->ssl = addr_conf->ssl; +#endif + s->addr_text = &addr_conf->addr_text; c->data = s; @@ -159,13 +166,126 @@ c->log_error = NGX_ERROR_INFO; + rev = c->read; + rev->handler = ngx_mail_init_session_handler; + + if (addr_conf->proxy_protocol) { + c->log->action = "reading PROXY protocol"; + + rev->handler = ngx_mail_proxy_protocol_handler; + + if (!rev->ready) { + ngx_add_timer(rev, cscf->timeout); + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_mail_close_connection(c); + } + + return; + } + } + + if (ngx_use_accept_mutex) { + ngx_post_event(rev, &ngx_posted_events); + return; + } + + rev->handler(rev); +} + + +static void +ngx_mail_proxy_protocol_handler(ngx_event_t *rev) +{ + u_char *p, buf[NGX_PROXY_PROTOCOL_MAX_HEADER]; + size_t size; + ssize_t n; + ngx_err_t err; + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_mail_core_srv_conf_t *cscf; + + c = rev->data; + s = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail PROXY protocol handler"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_mail_close_connection(c); + return; + } + + n = recv(c->fd, (char *) buf, sizeof(buf), MSG_PEEK); + + err = ngx_socket_errno; + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "recv(): %z", n); + + if (n == -1) { + if (err == NGX_EAGAIN) { + rev->ready = 0; + + if (!rev->timer_set) { + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + ngx_add_timer(rev, cscf->timeout); + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_mail_close_connection(c); + } + + return; + } + + ngx_connection_error(c, err, "recv() failed"); + + ngx_mail_close_connection(c); + return; + } + + p = ngx_proxy_protocol_read(c, buf, buf + n); + + if (p == NULL) { + ngx_mail_close_connection(c); + return; + } + + size = p - buf; + + if (c->recv(c, buf, size) != (ssize_t) size) { + ngx_mail_close_connection(c); + return; + } + + if (ngx_mail_realip_handler(s) != NGX_OK) { + ngx_mail_close_connection(c); + return; + } + + ngx_mail_init_session_handler(rev); +} + + +static void +ngx_mail_init_session_handler(ngx_event_t *rev) +{ + ngx_connection_t *c; + + c = rev->data; + #if (NGX_MAIL_SSL) { + ngx_mail_session_t *s; ngx_mail_ssl_conf_t *sslcf; + s = c->data; + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); - if (sslcf->enable || addr_conf->ssl) { + if (sslcf->enable || s->ssl) { c->log->action = "SSL handshaking"; ngx_mail_ssl_init_connection(&sslcf->ssl, c); @@ -215,9 +335,10 @@ s = c->data; - cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - - ngx_add_timer(c->read, cscf->timeout); + if (!c->read->timer_set) { + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + ngx_add_timer(c->read, cscf->timeout); + } c->ssl->handler = ngx_mail_ssl_handshake_handler; @@ -338,6 +459,8 @@ s = c->data; + c->log->action = "sending client greeting line"; + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); s->protocol = cscf->protocol->type; @@ -722,11 +845,6 @@ } if (n == NGX_AGAIN) { - if (ngx_handle_read_event(c->read, 0) != NGX_OK) { - ngx_mail_session_internal_server_error(s); - return NGX_ERROR; - } - if (s->buffer->pos == s->buffer->last) { return NGX_AGAIN; } diff -Nru nginx-1.18.0/src/mail/ngx_mail_imap_handler.c nginx-1.20.1/src/mail/ngx_mail_imap_handler.c --- nginx-1.18.0/src/mail/ngx_mail_imap_handler.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/mail/ngx_mail_imap_handler.c 2021-05-25 12:35:38.000000000 +0000 @@ -123,6 +123,12 @@ if (s->out.len) { ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap send handler busy"); s->blocked = 1; + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_close_connection(c); + return; + } + return; } @@ -130,7 +136,16 @@ rc = ngx_mail_read_command(s, c); - if (rc == NGX_AGAIN || rc == NGX_ERROR) { + if (rc == NGX_AGAIN) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_session_internal_server_error(s); + return; + } + + return; + } + + if (rc == NGX_ERROR) { return; } @@ -293,6 +308,11 @@ } } + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_session_internal_server_error(s); + return; + } + ngx_mail_send(c->write); } diff -Nru nginx-1.18.0/src/mail/ngx_mail_pop3_handler.c nginx-1.20.1/src/mail/ngx_mail_pop3_handler.c --- nginx-1.18.0/src/mail/ngx_mail_pop3_handler.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/mail/ngx_mail_pop3_handler.c 2021-05-25 12:35:38.000000000 +0000 @@ -138,6 +138,12 @@ if (s->out.len) { ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 send handler busy"); s->blocked = 1; + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_close_connection(c); + return; + } + return; } @@ -145,7 +151,16 @@ rc = ngx_mail_read_command(s, c); - if (rc == NGX_AGAIN || rc == NGX_ERROR) { + if (rc == NGX_AGAIN) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_session_internal_server_error(s); + return; + } + + return; + } + + if (rc == NGX_ERROR) { return; } @@ -275,6 +290,11 @@ s->arg_start = s->buffer->start; } + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_session_internal_server_error(s); + return; + } + ngx_mail_send(c->write); } } diff -Nru nginx-1.18.0/src/mail/ngx_mail_proxy_module.c nginx-1.20.1/src/mail/ngx_mail_proxy_module.c --- nginx-1.18.0/src/mail/ngx_mail_proxy_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/mail/ngx_mail_proxy_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -16,6 +16,8 @@ ngx_flag_t enable; ngx_flag_t pass_error_message; ngx_flag_t xclient; + ngx_flag_t smtp_auth; + ngx_flag_t proxy_protocol; size_t buffer_size; ngx_msec_t timeout; } ngx_mail_proxy_conf_t; @@ -25,7 +27,8 @@ static void ngx_mail_proxy_pop3_handler(ngx_event_t *rev); static void ngx_mail_proxy_imap_handler(ngx_event_t *rev); static void ngx_mail_proxy_smtp_handler(ngx_event_t *rev); -static void ngx_mail_proxy_dummy_handler(ngx_event_t *ev); +static void ngx_mail_proxy_write_handler(ngx_event_t *wev); +static ngx_int_t ngx_mail_proxy_send_proxy_protocol(ngx_mail_session_t *s); static ngx_int_t ngx_mail_proxy_read_response(ngx_mail_session_t *s, ngx_uint_t state); static void ngx_mail_proxy_handler(ngx_event_t *ev); @@ -74,6 +77,20 @@ offsetof(ngx_mail_proxy_conf_t, xclient), NULL }, + { ngx_string("proxy_smtp_auth"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_proxy_conf_t, smtp_auth), + NULL }, + + { ngx_string("proxy_protocol"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_proxy_conf_t, proxy_protocol), + NULL }, + ngx_null_command }; @@ -148,7 +165,7 @@ p->upstream.connection->pool = s->connection->pool; s->connection->read->handler = ngx_mail_proxy_block_read; - p->upstream.connection->write->handler = ngx_mail_proxy_dummy_handler; + p->upstream.connection->write->handler = ngx_mail_proxy_write_handler; pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); @@ -159,6 +176,8 @@ return; } + s->proxy->proxy_protocol = pcf->proxy_protocol; + s->out.len = 0; switch (s->protocol) { @@ -178,6 +197,12 @@ s->mail_state = ngx_smtp_start; break; } + + if (rc == NGX_AGAIN) { + return; + } + + ngx_mail_proxy_write_handler(p->upstream.connection->write); } @@ -222,9 +247,25 @@ return; } + if (s->proxy->proxy_protocol) { + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "mail proxy pop3 busy"); + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + return; + } + rc = ngx_mail_proxy_read_response(s, 0); if (rc == NGX_AGAIN) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_proxy_internal_server_error(s); + return; + } + return; } @@ -306,6 +347,11 @@ return; } + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_proxy_internal_server_error(s); + return; + } + s->proxy->buffer->pos = s->proxy->buffer->start; s->proxy->buffer->last = s->proxy->buffer->start; } @@ -335,9 +381,25 @@ return; } + if (s->proxy->proxy_protocol) { + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "mail proxy imap busy"); + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + return; + } + rc = ngx_mail_proxy_read_response(s, s->mail_state); if (rc == NGX_AGAIN) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_proxy_internal_server_error(s); + return; + } + return; } @@ -440,6 +502,11 @@ return; } + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_proxy_internal_server_error(s); + return; + } + s->proxy->buffer->pos = s->proxy->buffer->start; s->proxy->buffer->last = s->proxy->buffer->start; } @@ -450,7 +517,7 @@ { u_char *p; ngx_int_t rc; - ngx_str_t line; + ngx_str_t line, auth, encoded; ngx_buf_t *b; ngx_connection_t *c; ngx_mail_session_t *s; @@ -471,9 +538,25 @@ return; } + if (s->proxy->proxy_protocol) { + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "mail proxy smtp busy"); + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + return; + } + rc = ngx_mail_proxy_read_response(s, s->mail_state); if (rc == NGX_AGAIN) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_proxy_internal_server_error(s); + return; + } + return; } @@ -513,6 +596,9 @@ } else if (s->auth_method == NGX_MAIL_AUTH_NONE) { s->mail_state = ngx_smtp_helo_from; + } else if (pcf->smtp_auth) { + s->mail_state = ngx_smtp_helo_auth; + } else { s->mail_state = ngx_smtp_helo; } @@ -552,7 +638,9 @@ p = ngx_copy(p, s->connection->addr_text.data, s->connection->addr_text.len); - if (s->login.len) { + pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); + + if (s->login.len && !pcf->smtp_auth) { p = ngx_cpymem(p, " LOGIN=", sizeof(" LOGIN=") - 1); p = ngx_copy(p, s->login.data, s->login.len); } @@ -570,6 +658,9 @@ } else if (s->auth_method == NGX_MAIL_AUTH_NONE) { s->mail_state = ngx_smtp_xclient_from; + } else if (pcf->smtp_auth) { + s->mail_state = ngx_smtp_xclient_auth; + } else { s->mail_state = ngx_smtp_xclient; } @@ -595,8 +686,62 @@ &s->smtp_helo) - line.data; - s->mail_state = (s->auth_method == NGX_MAIL_AUTH_NONE) ? - ngx_smtp_helo_from : ngx_smtp_helo; + pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); + + if (s->auth_method == NGX_MAIL_AUTH_NONE) { + s->mail_state = ngx_smtp_helo_from; + + } else if (pcf->smtp_auth) { + s->mail_state = ngx_smtp_helo_auth; + + } else { + s->mail_state = ngx_smtp_helo; + } + + break; + + case ngx_smtp_helo_auth: + case ngx_smtp_xclient_auth: + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, + "mail proxy send auth"); + + s->connection->log->action = "sending AUTH to upstream"; + + if (s->passwd.data == NULL) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "no password available"); + ngx_mail_proxy_internal_server_error(s); + return; + } + + auth.len = 1 + s->login.len + 1 + s->passwd.len; + auth.data = ngx_pnalloc(c->pool, auth.len); + if (auth.data == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + auth.len = ngx_sprintf(auth.data, "%Z%V%Z%V", &s->login, &s->passwd) + - auth.data; + + line.len = sizeof("AUTH PLAIN " CRLF) - 1 + + ngx_base64_encoded_length(auth.len); + + line.data = ngx_pnalloc(c->pool, line.len); + if (line.data == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + encoded.data = ngx_cpymem(line.data, "AUTH PLAIN ", + sizeof("AUTH PLAIN ") - 1); + + ngx_encode_base64(&encoded, &auth); + + p = encoded.data + encoded.len; + *p++ = CR; *p = LF; + + s->mail_state = ngx_smtp_auth_plain; break; @@ -643,6 +788,7 @@ case ngx_smtp_helo: case ngx_smtp_xclient: + case ngx_smtp_auth_plain: case ngx_smtp_to: b = s->proxy->buffer; @@ -692,29 +838,107 @@ return; } + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_proxy_internal_server_error(s); + return; + } + s->proxy->buffer->pos = s->proxy->buffer->start; s->proxy->buffer->last = s->proxy->buffer->start; } static void -ngx_mail_proxy_dummy_handler(ngx_event_t *wev) +ngx_mail_proxy_write_handler(ngx_event_t *wev) { ngx_connection_t *c; ngx_mail_session_t *s; - ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0, "mail proxy dummy handler"); + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0, "mail proxy write handler"); + + c = wev->data; + s = c->data; + + if (s->proxy->proxy_protocol) { + if (ngx_mail_proxy_send_proxy_protocol(s) != NGX_OK) { + return; + } + + s->proxy->proxy_protocol = 0; + } if (ngx_handle_write_event(wev, 0) != NGX_OK) { - c = wev->data; - s = c->data; + ngx_mail_proxy_internal_server_error(s); + } - ngx_mail_proxy_close_session(s); + if (c->read->ready) { + ngx_post_event(c->read, &ngx_posted_events); } } static ngx_int_t +ngx_mail_proxy_send_proxy_protocol(ngx_mail_session_t *s) +{ + u_char *p; + ssize_t n, size; + ngx_connection_t *c; + u_char buf[NGX_PROXY_PROTOCOL_MAX_HEADER]; + + s->connection->log->action = "sending PROXY protocol header to upstream"; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0, + "mail proxy send PROXY protocol header"); + + p = ngx_proxy_protocol_write(s->connection, buf, + buf + NGX_PROXY_PROTOCOL_MAX_HEADER); + if (p == NULL) { + ngx_mail_proxy_internal_server_error(s); + return NGX_ERROR; + } + + c = s->proxy->upstream.connection; + + size = p - buf; + + n = c->send(c, buf, size); + + if (n == NGX_AGAIN) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { + ngx_mail_proxy_internal_server_error(s); + return NGX_ERROR; + } + + return NGX_AGAIN; + } + + if (n == NGX_ERROR) { + ngx_mail_proxy_internal_server_error(s); + return NGX_ERROR; + } + + if (n != size) { + + /* + * PROXY protocol specification: + * The sender must always ensure that the header + * is sent at once, so that the transport layer + * maintains atomicity along the path to the receiver. + */ + + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "could not send PROXY protocol header at once"); + + ngx_mail_proxy_internal_server_error(s); + + return NGX_ERROR; + } + + return NGX_OK; +} + + +static ngx_int_t ngx_mail_proxy_read_response(ngx_mail_session_t *s, ngx_uint_t state) { u_char *p, *m; @@ -824,6 +1048,7 @@ case ngx_smtp_helo: case ngx_smtp_helo_xclient: case ngx_smtp_helo_from: + case ngx_smtp_helo_auth: case ngx_smtp_from: if (p[0] == '2' && p[1] == '5' && p[2] == '0') { return NGX_OK; @@ -833,11 +1058,18 @@ case ngx_smtp_xclient: case ngx_smtp_xclient_from: case ngx_smtp_xclient_helo: + case ngx_smtp_xclient_auth: if (p[0] == '2' && (p[1] == '2' || p[1] == '5') && p[2] == '0') { return NGX_OK; } break; + case ngx_smtp_auth_plain: + if (p[0] == '2' && p[1] == '3' && p[2] == '5') { + return NGX_OK; + } + break; + case ngx_smtp_to: return NGX_OK; } @@ -1102,6 +1334,8 @@ pcf->enable = NGX_CONF_UNSET; pcf->pass_error_message = NGX_CONF_UNSET; pcf->xclient = NGX_CONF_UNSET; + pcf->smtp_auth = NGX_CONF_UNSET; + pcf->proxy_protocol = NGX_CONF_UNSET; pcf->buffer_size = NGX_CONF_UNSET_SIZE; pcf->timeout = NGX_CONF_UNSET_MSEC; @@ -1118,6 +1352,8 @@ ngx_conf_merge_value(conf->enable, prev->enable, 0); ngx_conf_merge_value(conf->pass_error_message, prev->pass_error_message, 0); ngx_conf_merge_value(conf->xclient, prev->xclient, 1); + ngx_conf_merge_value(conf->smtp_auth, prev->smtp_auth, 0); + ngx_conf_merge_value(conf->proxy_protocol, prev->proxy_protocol, 0); ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, (size_t) ngx_pagesize); ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 24 * 60 * 60000); diff -Nru nginx-1.18.0/src/mail/ngx_mail_realip_module.c nginx-1.20.1/src/mail/ngx_mail_realip_module.c --- nginx-1.18.0/src/mail/ngx_mail_realip_module.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/src/mail/ngx_mail_realip_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -0,0 +1,269 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + ngx_array_t *from; /* array of ngx_cidr_t */ +} ngx_mail_realip_srv_conf_t; + + +static ngx_int_t ngx_mail_realip_set_addr(ngx_mail_session_t *s, + ngx_addr_t *addr); +static char *ngx_mail_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static void *ngx_mail_realip_create_srv_conf(ngx_conf_t *cf); +static char *ngx_mail_realip_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); + + +static ngx_command_t ngx_mail_realip_commands[] = { + + { ngx_string("set_real_ip_from"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_mail_realip_from, + NGX_MAIL_SRV_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_mail_module_t ngx_mail_realip_module_ctx = { + NULL, /* protocol */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_mail_realip_create_srv_conf, /* create server configuration */ + ngx_mail_realip_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_mail_realip_module = { + NGX_MODULE_V1, + &ngx_mail_realip_module_ctx, /* module context */ + ngx_mail_realip_commands, /* module directives */ + NGX_MAIL_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +ngx_int_t +ngx_mail_realip_handler(ngx_mail_session_t *s) +{ + ngx_addr_t addr; + ngx_connection_t *c; + ngx_mail_realip_srv_conf_t *rscf; + + rscf = ngx_mail_get_module_srv_conf(s, ngx_mail_realip_module); + + if (rscf->from == NULL) { + return NGX_OK; + } + + c = s->connection; + + if (c->proxy_protocol == NULL) { + return NGX_OK; + } + + if (ngx_cidr_match(c->sockaddr, rscf->from) != NGX_OK) { + return NGX_OK; + } + + if (ngx_parse_addr(c->pool, &addr, c->proxy_protocol->src_addr.data, + c->proxy_protocol->src_addr.len) + != NGX_OK) + { + return NGX_OK; + } + + ngx_inet_set_port(addr.sockaddr, c->proxy_protocol->src_port); + + return ngx_mail_realip_set_addr(s, &addr); +} + + +static ngx_int_t +ngx_mail_realip_set_addr(ngx_mail_session_t *s, ngx_addr_t *addr) +{ + size_t len; + u_char *p; + u_char text[NGX_SOCKADDR_STRLEN]; + ngx_connection_t *c; + + c = s->connection; + + len = ngx_sock_ntop(addr->sockaddr, addr->socklen, text, + NGX_SOCKADDR_STRLEN, 0); + if (len == 0) { + return NGX_ERROR; + } + + p = ngx_pnalloc(c->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(p, text, len); + + c->sockaddr = addr->sockaddr; + c->socklen = addr->socklen; + c->addr_text.len = len; + c->addr_text.data = p; + + return NGX_OK; +} + + +static char * +ngx_mail_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_mail_realip_srv_conf_t *rscf = conf; + + ngx_int_t rc; + ngx_str_t *value; + ngx_url_t u; + ngx_cidr_t c, *cidr; + ngx_uint_t i; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + value = cf->args->elts; + + if (rscf->from == NULL) { + rscf->from = ngx_array_create(cf->pool, 2, + sizeof(ngx_cidr_t)); + if (rscf->from == NULL) { + return NGX_CONF_ERROR; + } + } + +#if (NGX_HAVE_UNIX_DOMAIN) + + if (ngx_strcmp(value[1].data, "unix:") == 0) { + cidr = ngx_array_push(rscf->from); + if (cidr == NULL) { + return NGX_CONF_ERROR; + } + + cidr->family = AF_UNIX; + return NGX_CONF_OK; + } + +#endif + + rc = ngx_ptocidr(&value[1], &c); + + if (rc != NGX_ERROR) { + if (rc == NGX_DONE) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "low address bits of %V are meaningless", + &value[1]); + } + + cidr = ngx_array_push(rscf->from); + if (cidr == NULL) { + return NGX_CONF_ERROR; + } + + *cidr = c; + + return NGX_CONF_OK; + } + + ngx_memzero(&u, sizeof(ngx_url_t)); + u.host = value[1]; + + if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) { + if (u.err) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "%s in set_real_ip_from \"%V\"", + u.err, &u.host); + } + + return NGX_CONF_ERROR; + } + + cidr = ngx_array_push_n(rscf->from, u.naddrs); + if (cidr == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(cidr, u.naddrs * sizeof(ngx_cidr_t)); + + for (i = 0; i < u.naddrs; i++) { + cidr[i].family = u.addrs[i].sockaddr->sa_family; + + switch (cidr[i].family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) u.addrs[i].sockaddr; + cidr[i].u.in6.addr = sin6->sin6_addr; + ngx_memset(cidr[i].u.in6.mask.s6_addr, 0xff, 16); + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) u.addrs[i].sockaddr; + cidr[i].u.in.addr = sin->sin_addr.s_addr; + cidr[i].u.in.mask = 0xffffffff; + break; + } + } + + return NGX_CONF_OK; +} + + +static void * +ngx_mail_realip_create_srv_conf(ngx_conf_t *cf) +{ + ngx_mail_realip_srv_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_realip_srv_conf_t)); + if (conf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * conf->from = NULL; + */ + + return conf; +} + + +static char * +ngx_mail_realip_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_mail_realip_srv_conf_t *prev = parent; + ngx_mail_realip_srv_conf_t *conf = child; + + if (conf->from == NULL) { + conf->from = prev->from; + } + + return NGX_CONF_OK; +} diff -Nru nginx-1.18.0/src/mail/ngx_mail_smtp_handler.c nginx-1.20.1/src/mail/ngx_mail_smtp_handler.c --- nginx-1.18.0/src/mail/ngx_mail_smtp_handler.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/mail/ngx_mail_smtp_handler.c 2021-05-25 12:35:38.000000000 +0000 @@ -449,6 +449,12 @@ if (s->out.len) { ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp send handler busy"); s->blocked = 1; + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_close_connection(c); + return; + } + return; } @@ -456,7 +462,16 @@ rc = ngx_mail_read_command(s, c); - if (rc == NGX_AGAIN || rc == NGX_ERROR) { + if (rc == NGX_AGAIN) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_session_internal_server_error(s); + return; + } + + return; + } + + if (rc == NGX_ERROR) { return; } @@ -568,6 +583,11 @@ s->arg_start = s->buffer->pos; } + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_session_internal_server_error(s); + return; + } + ngx_mail_send(c->write); } } diff -Nru nginx-1.18.0/src/mail/ngx_mail_ssl_module.c nginx-1.20.1/src/mail/ngx_mail_ssl_module.c --- nginx-1.18.0/src/mail/ngx_mail_ssl_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/mail/ngx_mail_ssl_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -26,6 +26,9 @@ static char *ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_mail_ssl_conf_command_check(ngx_conf_t *cf, void *post, + void *data); + static ngx_conf_enum_t ngx_mail_starttls_state[] = { { ngx_string("off"), NGX_MAIL_STARTTLS_OFF }, @@ -61,6 +64,10 @@ }; +static ngx_conf_post_t ngx_mail_ssl_conf_command_post = + { ngx_mail_ssl_conf_command_check }; + + static ngx_command_t ngx_mail_ssl_commands[] = { { ngx_string("ssl"), @@ -196,6 +203,13 @@ offsetof(ngx_mail_ssl_conf_t, crl), NULL }, + { ngx_string("ssl_conf_command"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE2, + ngx_conf_set_keyval_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, conf_commands), + &ngx_mail_ssl_conf_command_post }, + ngx_null_command }; @@ -259,6 +273,7 @@ scf->certificates = NGX_CONF_UNSET_PTR; scf->certificate_keys = NGX_CONF_UNSET_PTR; scf->passwords = NGX_CONF_UNSET_PTR; + scf->conf_commands = NGX_CONF_UNSET_PTR; scf->prefer_server_ciphers = NGX_CONF_UNSET; scf->verify = NGX_CONF_UNSET_UINT; scf->verify_depth = NGX_CONF_UNSET_UINT; @@ -316,6 +331,8 @@ ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS); + ngx_conf_merge_ptr_value(conf->conf_commands, prev->conf_commands, NULL); + conf->ssl.log = cf->log; @@ -461,6 +478,10 @@ return NGX_CONF_ERROR; } + if (ngx_ssl_conf_commands(cf, &conf->ssl, conf->conf_commands) != NGX_OK) { + return NGX_CONF_ERROR; + } + return NGX_CONF_OK; } @@ -654,3 +675,14 @@ return NGX_CONF_ERROR; } + + +static char * +ngx_mail_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data) +{ +#ifndef SSL_CONF_FLAG_FILE + return "is not supported on this platform"; +#else + return NGX_CONF_OK; +#endif +} diff -Nru nginx-1.18.0/src/mail/ngx_mail_ssl_module.h nginx-1.20.1/src/mail/ngx_mail_ssl_module.h --- nginx-1.18.0/src/mail/ngx_mail_ssl_module.h 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/mail/ngx_mail_ssl_module.h 2021-05-25 12:35:38.000000000 +0000 @@ -48,6 +48,7 @@ ngx_str_t ciphers; ngx_array_t *passwords; + ngx_array_t *conf_commands; ngx_shm_zone_t *shm_zone; diff -Nru nginx-1.18.0/src/misc/ngx_cpp_test_module.cpp nginx-1.20.1/src/misc/ngx_cpp_test_module.cpp --- nginx-1.18.0/src/misc/ngx_cpp_test_module.cpp 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/misc/ngx_cpp_test_module.cpp 2021-05-25 12:35:38.000000000 +0000 @@ -14,6 +14,8 @@ #include #include #include + + #include } // nginx header files should go before other, because they define 64-bit off_t diff -Nru nginx-1.18.0/src/os/unix/ngx_errno.c nginx-1.20.1/src/os/unix/ngx_errno.c --- nginx-1.18.0/src/os/unix/ngx_errno.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/os/unix/ngx_errno.c 2021-05-25 12:35:38.000000000 +0000 @@ -9,6 +9,49 @@ #include +static ngx_str_t ngx_unknown_error = ngx_string("Unknown error"); + + +#if (NGX_HAVE_STRERRORDESC_NP) + +/* + * The strerrordesc_np() function, introduced in glibc 2.32, is + * async-signal-safe. This makes it possible to use it directly, + * without copying error messages. + */ + + +u_char * +ngx_strerror(ngx_err_t err, u_char *errstr, size_t size) +{ + size_t len; + const char *msg; + + msg = strerrordesc_np(err); + + if (msg == NULL) { + msg = (char *) ngx_unknown_error.data; + len = ngx_unknown_error.len; + + } else { + len = ngx_strlen(msg); + } + + size = ngx_min(size, len); + + return ngx_cpymem(errstr, msg, size); +} + + +ngx_int_t +ngx_strerror_init(void) +{ + return NGX_OK; +} + + +#else + /* * The strerror() messages are copied because: * @@ -26,7 +69,8 @@ static ngx_str_t *ngx_sys_errlist; -static ngx_str_t ngx_unknown_error = ngx_string("Unknown error"); +static ngx_err_t ngx_first_error; +static ngx_err_t ngx_last_error; u_char * @@ -34,8 +78,13 @@ { ngx_str_t *msg; - msg = ((ngx_uint_t) err < NGX_SYS_NERR) ? &ngx_sys_errlist[err]: - &ngx_unknown_error; + if (err >= ngx_first_error && err < ngx_last_error) { + msg = &ngx_sys_errlist[err - ngx_first_error]; + + } else { + msg = &ngx_unknown_error; + } + size = ngx_min(size, msg->len); return ngx_cpymem(errstr, msg->data, size); @@ -50,20 +99,92 @@ size_t len; ngx_err_t err; +#if (NGX_SYS_NERR) + ngx_first_error = 0; + ngx_last_error = NGX_SYS_NERR; + +#elif (EPERM > 1000 && EPERM < 0x7fffffff - 1000) + + /* + * If number of errors is not known, and EPERM error code has large + * but reasonable value, guess possible error codes based on the error + * messages returned by strerror(), starting from EPERM. Notably, + * this covers GNU/Hurd, where errors start at 0x40000001. + */ + + for (err = EPERM; err > EPERM - 1000; err--) { + ngx_set_errno(0); + msg = strerror(err); + + if (errno == EINVAL + || msg == NULL + || strncmp(msg, "Unknown error", 13) == 0) + { + continue; + } + + ngx_first_error = err; + } + + for (err = EPERM; err < EPERM + 1000; err++) { + ngx_set_errno(0); + msg = strerror(err); + + if (errno == EINVAL + || msg == NULL + || strncmp(msg, "Unknown error", 13) == 0) + { + continue; + } + + ngx_last_error = err + 1; + } + +#else + + /* + * If number of errors is not known, guess it based on the error + * messages returned by strerror(). + */ + + ngx_first_error = 0; + + for (err = 0; err < 1000; err++) { + ngx_set_errno(0); + msg = strerror(err); + + if (errno == EINVAL + || msg == NULL + || strncmp(msg, "Unknown error", 13) == 0) + { + continue; + } + + ngx_last_error = err + 1; + } + +#endif + /* * ngx_strerror() is not ready to work at this stage, therefore, * malloc() is used and possible errors are logged using strerror(). */ - len = NGX_SYS_NERR * sizeof(ngx_str_t); + len = (ngx_last_error - ngx_first_error) * sizeof(ngx_str_t); ngx_sys_errlist = malloc(len); if (ngx_sys_errlist == NULL) { goto failed; } - for (err = 0; err < NGX_SYS_NERR; err++) { + for (err = ngx_first_error; err < ngx_last_error; err++) { msg = strerror(err); + + if (msg == NULL) { + ngx_sys_errlist[err - ngx_first_error] = ngx_unknown_error; + continue; + } + len = ngx_strlen(msg); p = malloc(len); @@ -72,8 +193,8 @@ } ngx_memcpy(p, msg, len); - ngx_sys_errlist[err].len = len; - ngx_sys_errlist[err].data = p; + ngx_sys_errlist[err - ngx_first_error].len = len; + ngx_sys_errlist[err - ngx_first_error].data = p; } return NGX_OK; @@ -85,3 +206,5 @@ return NGX_ERROR; } + +#endif diff -Nru nginx-1.18.0/src/os/unix/ngx_files.c nginx-1.20.1/src/os/unix/ngx_files.c --- nginx-1.18.0/src/os/unix/ngx_files.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/os/unix/ngx_files.c 2021-05-25 12:35:38.000000000 +0000 @@ -875,9 +875,28 @@ return 512; } +#if (NGX_LINUX) + if ((size_t) fs.f_bsize > ngx_pagesize) { + return 512; + } +#endif + return (size_t) fs.f_bsize; } + +off_t +ngx_fs_available(u_char *name) +{ + struct statfs fs; + + if (statfs((char *) name, &fs) == -1) { + return NGX_MAX_OFF_T_VALUE; + } + + return (off_t) fs.f_bavail * fs.f_bsize; +} + #elif (NGX_HAVE_STATVFS) size_t @@ -893,9 +912,28 @@ return 512; } +#if (NGX_LINUX) + if ((size_t) fs.f_frsize > ngx_pagesize) { + return 512; + } +#endif + return (size_t) fs.f_frsize; } + +off_t +ngx_fs_available(u_char *name) +{ + struct statvfs fs; + + if (statvfs((char *) name, &fs) == -1) { + return NGX_MAX_OFF_T_VALUE; + } + + return (off_t) fs.f_bavail * fs.f_frsize; +} + #else size_t @@ -904,4 +942,11 @@ return 512; } + +off_t +ngx_fs_available(u_char *name) +{ + return NGX_MAX_OFF_T_VALUE; +} + #endif diff -Nru nginx-1.18.0/src/os/unix/ngx_files.h nginx-1.20.1/src/os/unix/ngx_files.h --- nginx-1.18.0/src/os/unix/ngx_files.h 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/os/unix/ngx_files.h 2021-05-25 12:35:38.000000000 +0000 @@ -185,7 +185,10 @@ #define ngx_is_exec(sb) (((sb)->st_mode & S_IXUSR) == S_IXUSR) #define ngx_file_access(sb) ((sb)->st_mode & 0777) #define ngx_file_size(sb) (sb)->st_size -#define ngx_file_fs_size(sb) ngx_max((sb)->st_size, (sb)->st_blocks * 512) +#define ngx_file_fs_size(sb) \ + (((sb)->st_blocks * 512 > (sb)->st_size \ + && (sb)->st_blocks * 512 < (sb)->st_size + 8 * (sb)->st_blksize) \ + ? (sb)->st_blocks * 512 : (sb)->st_size) #define ngx_file_mtime(sb) (sb)->st_mtime #define ngx_file_uniq(sb) (sb)->st_ino @@ -346,6 +349,7 @@ #endif size_t ngx_fs_bsize(u_char *name); +off_t ngx_fs_available(u_char *name); #if (NGX_HAVE_OPENAT) diff -Nru nginx-1.18.0/src/os/unix/ngx_process_cycle.c nginx-1.20.1/src/os/unix/ngx_process_cycle.c --- nginx-1.18.0/src/os/unix/ngx_process_cycle.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/os/unix/ngx_process_cycle.c 2021-05-25 12:35:38.000000000 +0000 @@ -15,7 +15,7 @@ ngx_int_t type); static void ngx_start_cache_manager_processes(ngx_cycle_t *cycle, ngx_uint_t respawn); -static void ngx_pass_open_channel(ngx_cycle_t *cycle, ngx_channel_t *ch); +static void ngx_pass_open_channel(ngx_cycle_t *cycle); static void ngx_signal_worker_processes(ngx_cycle_t *cycle, int signo); static ngx_uint_t ngx_reap_children(ngx_cycle_t *cycle); static void ngx_master_process_exit(ngx_cycle_t *cycle); @@ -77,12 +77,11 @@ u_char *p; size_t size; ngx_int_t i; - ngx_uint_t n, sigio; + ngx_uint_t sigio; sigset_t set; struct itimerval itv; ngx_uint_t live; ngx_msec_t delay; - ngx_listening_t *ls; ngx_core_conf_t *ccf; sigemptyset(&set); @@ -204,16 +203,7 @@ if (ngx_quit) { ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); - - ls = cycle->listening.elts; - for (n = 0; n < cycle->listening.nelts; n++) { - if (ngx_close_socket(ls[n].fd) == -1) { - ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno, - ngx_close_socket_n " %V failed", - &ls[n].addr_text); - } - } - cycle->listening.nelts = 0; + ngx_close_listening_sockets(cycle); continue; } @@ -345,25 +335,16 @@ static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type) { - ngx_int_t i; - ngx_channel_t ch; + ngx_int_t i; ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start worker processes"); - ngx_memzero(&ch, sizeof(ngx_channel_t)); - - ch.command = NGX_CMD_OPEN_CHANNEL; - for (i = 0; i < n; i++) { ngx_spawn_process(cycle, ngx_worker_process_cycle, (void *) (intptr_t) i, "worker process", type); - ch.pid = ngx_processes[ngx_process_slot].pid; - ch.slot = ngx_process_slot; - ch.fd = ngx_processes[ngx_process_slot].channel[0]; - - ngx_pass_open_channel(cycle, &ch); + ngx_pass_open_channel(cycle); } } @@ -371,9 +352,8 @@ static void ngx_start_cache_manager_processes(ngx_cycle_t *cycle, ngx_uint_t respawn) { - ngx_uint_t i, manager, loader; - ngx_path_t **path; - ngx_channel_t ch; + ngx_uint_t i, manager, loader; + ngx_path_t **path; manager = 0; loader = 0; @@ -398,14 +378,7 @@ &ngx_cache_manager_ctx, "cache manager process", respawn ? NGX_PROCESS_JUST_RESPAWN : NGX_PROCESS_RESPAWN); - ngx_memzero(&ch, sizeof(ngx_channel_t)); - - ch.command = NGX_CMD_OPEN_CHANNEL; - ch.pid = ngx_processes[ngx_process_slot].pid; - ch.slot = ngx_process_slot; - ch.fd = ngx_processes[ngx_process_slot].channel[0]; - - ngx_pass_open_channel(cycle, &ch); + ngx_pass_open_channel(cycle); if (loader == 0) { return; @@ -415,19 +388,20 @@ &ngx_cache_loader_ctx, "cache loader process", respawn ? NGX_PROCESS_JUST_SPAWN : NGX_PROCESS_NORESPAWN); - ch.command = NGX_CMD_OPEN_CHANNEL; - ch.pid = ngx_processes[ngx_process_slot].pid; - ch.slot = ngx_process_slot; - ch.fd = ngx_processes[ngx_process_slot].channel[0]; - - ngx_pass_open_channel(cycle, &ch); + ngx_pass_open_channel(cycle); } static void -ngx_pass_open_channel(ngx_cycle_t *cycle, ngx_channel_t *ch) +ngx_pass_open_channel(ngx_cycle_t *cycle) { - ngx_int_t i; + ngx_int_t i; + ngx_channel_t ch; + + ch.command = NGX_CMD_OPEN_CHANNEL; + ch.pid = ngx_processes[ngx_process_slot].pid; + ch.slot = ngx_process_slot; + ch.fd = ngx_processes[ngx_process_slot].channel[0]; for (i = 0; i < ngx_last_process; i++) { @@ -440,14 +414,14 @@ ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0, "pass channel s:%i pid:%P fd:%d to s:%i pid:%P fd:%d", - ch->slot, ch->pid, ch->fd, + ch.slot, ch.pid, ch.fd, i, ngx_processes[i].pid, ngx_processes[i].channel[0]); /* TODO: NGX_AGAIN */ ngx_write_channel(ngx_processes[i].channel[0], - ch, sizeof(ngx_channel_t), cycle->log); + &ch, sizeof(ngx_channel_t), cycle->log); } } @@ -631,12 +605,7 @@ } - ch.command = NGX_CMD_OPEN_CHANNEL; - ch.pid = ngx_processes[ngx_process_slot].pid; - ch.slot = ngx_process_slot; - ch.fd = ngx_processes[ngx_process_slot].channel[0]; - - ngx_pass_open_channel(cycle, &ch); + ngx_pass_open_channel(cycle); live = 1; diff -Nru nginx-1.18.0/src/os/unix/ngx_udp_sendmsg_chain.c nginx-1.20.1/src/os/unix/ngx_udp_sendmsg_chain.c --- nginx-1.18.0/src/os/unix/ngx_udp_sendmsg_chain.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/os/unix/ngx_udp_sendmsg_chain.c 2021-05-25 12:35:38.000000000 +0000 @@ -189,6 +189,13 @@ return cl; } + /* zero-sized datagram; pretend to have at least 1 iov */ + if (n == 0) { + iov = &vec->iovs[n++]; + iov->iov_base = NULL; + iov->iov_len = 0; + } + vec->count = n; vec->size = total; diff -Nru nginx-1.18.0/src/stream/ngx_stream_proxy_module.c nginx-1.20.1/src/stream/ngx_stream_proxy_module.c --- nginx-1.18.0/src/stream/ngx_stream_proxy_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/stream/ngx_stream_proxy_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -49,6 +49,7 @@ ngx_str_t ssl_certificate; ngx_str_t ssl_certificate_key; ngx_array_t *ssl_passwords; + ngx_array_t *ssl_conf_commands; ngx_ssl_t *ssl; #endif @@ -94,6 +95,8 @@ static ngx_int_t ngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s); static char *ngx_stream_proxy_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_stream_proxy_ssl_conf_command_check(ngx_conf_t *cf, void *post, + void *data); static void ngx_stream_proxy_ssl_init_connection(ngx_stream_session_t *s); static void ngx_stream_proxy_ssl_handshake(ngx_connection_t *pc); static void ngx_stream_proxy_ssl_save_session(ngx_connection_t *c); @@ -112,6 +115,9 @@ { ngx_null_string, 0 } }; +static ngx_conf_post_t ngx_stream_proxy_ssl_conf_command_post = + { ngx_stream_proxy_ssl_conf_command_check }; + #endif @@ -331,6 +337,13 @@ 0, NULL }, + { ngx_string("proxy_ssl_conf_command"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2, + ngx_conf_set_keyval_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, ssl_conf_commands), + &ngx_stream_proxy_ssl_conf_command_post }, + #endif ngx_null_command @@ -839,7 +852,7 @@ u->upstream_buf.last = p; } - if (c->buffer && c->buffer->pos < c->buffer->last) { + if (c->buffer && c->buffer->pos <= c->buffer->last) { ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, "stream proxy add preread buffer: %uz", c->buffer->last - c->buffer->pos); @@ -853,6 +866,7 @@ *cl->buf = *c->buffer; cl->buf->tag = (ngx_buf_tag_t) &ngx_stream_proxy_module; + cl->buf->temporary = (cl->buf->pos == cl->buf->last) ? 0 : 1; cl->buf->flush = 1; cl->next = u->upstream_out; @@ -1007,6 +1021,17 @@ } +static char * +ngx_stream_proxy_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data) +{ +#ifndef SSL_CONF_FLAG_FILE + return "is not supported on this platform"; +#else + return NGX_CONF_OK; +#endif +} + + static void ngx_stream_proxy_ssl_init_connection(ngx_stream_session_t *s) { @@ -1984,6 +2009,7 @@ conf->ssl_verify = NGX_CONF_UNSET; conf->ssl_verify_depth = NGX_CONF_UNSET_UINT; conf->ssl_passwords = NGX_CONF_UNSET_PTR; + conf->ssl_conf_commands = NGX_CONF_UNSET_PTR; #endif return conf; @@ -2071,6 +2097,9 @@ ngx_conf_merge_ptr_value(conf->ssl_passwords, prev->ssl_passwords, NULL); + ngx_conf_merge_ptr_value(conf->ssl_conf_commands, + prev->ssl_conf_commands, NULL); + if (conf->ssl_enable && ngx_stream_proxy_set_ssl(cf, conf) != NGX_OK) { return NGX_CONF_ERROR; } @@ -2153,6 +2182,12 @@ != NGX_OK) { return NGX_ERROR; + } + + if (ngx_ssl_conf_commands(cf, pscf->ssl, pscf->ssl_conf_commands) + != NGX_OK) + { + return NGX_ERROR; } return NGX_OK; diff -Nru nginx-1.18.0/src/stream/ngx_stream_set_module.c nginx-1.20.1/src/stream/ngx_stream_set_module.c --- nginx-1.18.0/src/stream/ngx_stream_set_module.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-1.20.1/src/stream/ngx_stream_set_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -0,0 +1,226 @@ + +/* + * Copyright (C) Pavel Pautov + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + ngx_int_t index; + ngx_stream_set_variable_pt set_handler; + uintptr_t data; + ngx_stream_complex_value_t value; +} ngx_stream_set_cmd_t; + + +typedef struct { + ngx_array_t commands; +} ngx_stream_set_srv_conf_t; + + +static ngx_int_t ngx_stream_set_handler(ngx_stream_session_t *s); +static ngx_int_t ngx_stream_set_var(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_set_init(ngx_conf_t *cf); +static void *ngx_stream_set_create_srv_conf(ngx_conf_t *cf); +static char *ngx_stream_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + + +static ngx_command_t ngx_stream_set_commands[] = { + + { ngx_string("set"), + NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2, + ngx_stream_set, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_set_module_ctx = { + NULL, /* preconfiguration */ + ngx_stream_set_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_stream_set_create_srv_conf, /* create server configuration */ + NULL /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_set_module = { + NGX_MODULE_V1, + &ngx_stream_set_module_ctx, /* module context */ + ngx_stream_set_commands, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_int_t +ngx_stream_set_handler(ngx_stream_session_t *s) +{ + ngx_str_t str; + ngx_uint_t i; + ngx_stream_set_cmd_t *cmds; + ngx_stream_set_srv_conf_t *scf; + ngx_stream_variable_value_t vv; + + scf = ngx_stream_get_module_srv_conf(s, ngx_stream_set_module); + cmds = scf->commands.elts; + vv = ngx_stream_variable_null_value; + + for (i = 0; i < scf->commands.nelts; i++) { + if (ngx_stream_complex_value(s, &cmds[i].value, &str) != NGX_OK) { + return NGX_ERROR; + } + + if (cmds[i].set_handler != NULL) { + vv.len = str.len; + vv.data = str.data; + cmds[i].set_handler(s, &vv, cmds[i].data); + + } else { + s->variables[cmds[i].index].len = str.len; + s->variables[cmds[i].index].valid = 1; + s->variables[cmds[i].index].no_cacheable = 0; + s->variables[cmds[i].index].not_found = 0; + s->variables[cmds[i].index].data = str.data; + } + } + + return NGX_DECLINED; +} + + +static ngx_int_t +ngx_stream_set_var(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, + uintptr_t data) +{ + *v = ngx_stream_variable_null_value; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_set_init(ngx_conf_t *cf) +{ + ngx_stream_handler_pt *h; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + + h = ngx_array_push(&cmcf->phases[NGX_STREAM_PREACCESS_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_stream_set_handler; + + return NGX_OK; +} + + +static void * +ngx_stream_set_create_srv_conf(ngx_conf_t *cf) +{ + ngx_stream_set_srv_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_set_srv_conf_t)); + if (conf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * conf->commands = { NULL }; + */ + + return conf; +} + + +static char * +ngx_stream_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_set_srv_conf_t *scf = conf; + + ngx_str_t *args; + ngx_int_t index; + ngx_stream_set_cmd_t *set_cmd; + ngx_stream_variable_t *v; + ngx_stream_compile_complex_value_t ccv; + + args = cf->args->elts; + + if (args[1].data[0] != '$') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"%V\"", &args[1]); + return NGX_CONF_ERROR; + } + + args[1].len--; + args[1].data++; + + v = ngx_stream_add_variable(cf, &args[1], + NGX_STREAM_VAR_CHANGEABLE|NGX_STREAM_VAR_WEAK); + if (v == NULL) { + return NGX_CONF_ERROR; + } + + index = ngx_stream_get_variable_index(cf, &args[1]); + if (index == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + if (v->get_handler == NULL) { + v->get_handler = ngx_stream_set_var; + } + + if (scf->commands.elts == NULL) { + if (ngx_array_init(&scf->commands, cf->pool, 1, + sizeof(ngx_stream_set_cmd_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + } + + set_cmd = ngx_array_push(&scf->commands); + if (set_cmd == NULL) { + return NGX_CONF_ERROR; + } + + set_cmd->index = index; + set_cmd->set_handler = v->set_handler; + set_cmd->data = v->data; + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &args[2]; + ccv.complex_value = &set_cmd->value; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} diff -Nru nginx-1.18.0/src/stream/ngx_stream_ssl_module.c nginx-1.20.1/src/stream/ngx_stream_ssl_module.c --- nginx-1.18.0/src/stream/ngx_stream_ssl_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/stream/ngx_stream_ssl_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -45,6 +45,10 @@ void *conf); static char *ngx_stream_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + +static char *ngx_stream_ssl_conf_command_check(ngx_conf_t *cf, void *post, + void *data); + static ngx_int_t ngx_stream_ssl_init(ngx_conf_t *cf); @@ -68,6 +72,10 @@ }; +static ngx_conf_post_t ngx_stream_ssl_conf_command_post = + { ngx_stream_ssl_conf_command_check }; + + static ngx_command_t ngx_stream_ssl_commands[] = { { ngx_string("ssl_handshake_timeout"), @@ -196,6 +204,13 @@ offsetof(ngx_stream_ssl_conf_t, crl), NULL }, + { ngx_string("ssl_conf_command"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2, + ngx_conf_set_keyval_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_conf_t, conf_commands), + &ngx_stream_ssl_conf_command_post }, + ngx_null_command }; @@ -595,6 +610,7 @@ scf->certificates = NGX_CONF_UNSET_PTR; scf->certificate_keys = NGX_CONF_UNSET_PTR; scf->passwords = NGX_CONF_UNSET_PTR; + scf->conf_commands = NGX_CONF_UNSET_PTR; scf->prefer_server_ciphers = NGX_CONF_UNSET; scf->verify = NGX_CONF_UNSET_UINT; scf->verify_depth = NGX_CONF_UNSET_UINT; @@ -650,6 +666,8 @@ ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS); + ngx_conf_merge_ptr_value(conf->conf_commands, prev->conf_commands, NULL); + conf->ssl.log = cf->log; @@ -811,6 +829,10 @@ return NGX_CONF_ERROR; } + if (ngx_ssl_conf_commands(cf, &conf->ssl, conf->conf_commands) != NGX_OK) { + return NGX_CONF_ERROR; + } + return NGX_CONF_OK; } @@ -1034,6 +1056,17 @@ } +static char * +ngx_stream_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data) +{ +#ifndef SSL_CONF_FLAG_FILE + return "is not supported on this platform"; +#else + return NGX_CONF_OK; +#endif +} + + static ngx_int_t ngx_stream_ssl_init(ngx_conf_t *cf) { diff -Nru nginx-1.18.0/src/stream/ngx_stream_ssl_module.h nginx-1.20.1/src/stream/ngx_stream_ssl_module.h --- nginx-1.18.0/src/stream/ngx_stream_ssl_module.h 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/stream/ngx_stream_ssl_module.h 2021-05-25 12:35:38.000000000 +0000 @@ -46,6 +46,7 @@ ngx_str_t ciphers; ngx_array_t *passwords; + ngx_array_t *conf_commands; ngx_shm_zone_t *shm_zone; diff -Nru nginx-1.18.0/src/stream/ngx_stream_upstream_round_robin.c nginx-1.20.1/src/stream/ngx_stream_upstream_round_robin.c --- nginx-1.18.0/src/stream/ngx_stream_upstream_round_robin.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/stream/ngx_stream_upstream_round_robin.c 2021-05-25 12:35:38.000000000 +0000 @@ -10,8 +10,8 @@ #include -#define ngx_stream_upstream_tries(p) ((p)->number \ - + ((p)->next ? (p)->next->number : 0)) +#define ngx_stream_upstream_tries(p) ((p)->tries \ + + ((p)->next ? (p)->next->tries : 0)) static ngx_stream_upstream_rr_peer_t *ngx_stream_upstream_get_peer( @@ -38,7 +38,7 @@ ngx_stream_upstream_srv_conf_t *us) { ngx_url_t u; - ngx_uint_t i, j, n, w; + ngx_uint_t i, j, n, w, t; ngx_stream_upstream_server_t *server; ngx_stream_upstream_rr_peer_t *peer, **peerp; ngx_stream_upstream_rr_peers_t *peers, *backup; @@ -50,6 +50,7 @@ n = 0; w = 0; + t = 0; for (i = 0; i < us->servers->nelts; i++) { if (server[i].backup) { @@ -58,6 +59,10 @@ n += server[i].naddrs; w += server[i].naddrs * server[i].weight; + + if (!server[i].down) { + t += server[i].naddrs; + } } if (n == 0) { @@ -81,6 +86,7 @@ peers->number = n; peers->weighted = (w != n); peers->total_weight = w; + peers->tries = t; peers->name = &us->host; n = 0; @@ -116,6 +122,7 @@ n = 0; w = 0; + t = 0; for (i = 0; i < us->servers->nelts; i++) { if (!server[i].backup) { @@ -124,6 +131,10 @@ n += server[i].naddrs; w += server[i].naddrs * server[i].weight; + + if (!server[i].down) { + t += server[i].naddrs; + } } if (n == 0) { @@ -145,6 +156,7 @@ backup->number = n; backup->weighted = (w != n); backup->total_weight = w; + backup->tries = t; backup->name = &us->host; n = 0; @@ -220,6 +232,7 @@ peers->number = n; peers->weighted = 0; peers->total_weight = n; + peers->tries = n; peers->name = &us->host; peerp = &peers->peer; @@ -342,6 +355,7 @@ peers->single = (ur->naddrs == 1); peers->number = ur->naddrs; + peers->tries = ur->naddrs; peers->name = &ur->host; if (ur->sockaddr) { diff -Nru nginx-1.18.0/src/stream/ngx_stream_upstream_round_robin.h nginx-1.20.1/src/stream/ngx_stream_upstream_round_robin.h --- nginx-1.18.0/src/stream/ngx_stream_upstream_round_robin.h 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/stream/ngx_stream_upstream_round_robin.h 2021-05-25 12:35:38.000000000 +0000 @@ -66,6 +66,7 @@ #endif ngx_uint_t total_weight; + ngx_uint_t tries; unsigned single:1; unsigned weighted:1; diff -Nru nginx-1.18.0/src/stream/ngx_stream_write_filter_module.c nginx-1.20.1/src/stream/ngx_stream_write_filter_module.c --- nginx-1.18.0/src/stream/ngx_stream_write_filter_module.c 2020-04-21 14:09:01.000000000 +0000 +++ nginx-1.20.1/src/stream/ngx_stream_write_filter_module.c 2021-05-25 12:35:38.000000000 +0000 @@ -234,7 +234,8 @@ if (size == 0 && !(c->buffered & NGX_LOWLEVEL_BUFFERED) - && !(last && c->need_last_buf)) + && !(last && c->need_last_buf) + && !(c->type == SOCK_DGRAM && flush)) { if (last || flush || sync) { for (cl = *out; cl; /* void */) {