From 8e280162c688d6b00fc47690a1c7e015e8ddbb35 Mon Sep 17 00:00:00 2001 From: Christos Trochalakis Date: Fri, 29 Jul 2016 16:46:55 +0300 Subject: [PATCH 001/475] Imported Upstream version 1.11.3 --- CHANGES | 100 +- CHANGES.ru | 101 +- auto/cc/acc | 1 - auto/cc/clang | 6 - auto/cc/conf | 2 +- auto/cc/gcc | 19 +- auto/cc/icc | 4 - auto/cc/sunc | 9 +- auto/endianness | 2 +- auto/feature | 2 +- auto/include | 5 +- auto/lib/conf | 29 - auto/lib/make | 8 - auto/lib/md5/conf | 103 -- auto/lib/md5/make | 96 - auto/lib/md5/makefile.bcc | 22 - auto/lib/md5/makefile.msvc | 22 - auto/lib/md5/makefile.owc | 11 - auto/lib/sha1/conf | 79 - auto/lib/sha1/make | 96 - auto/lib/sha1/makefile.bcc | 22 - auto/lib/sha1/makefile.msvc | 22 - auto/lib/sha1/makefile.owc | 11 - auto/lib/test | 40 - auto/module | 18 +- auto/modules | 75 +- auto/options | 68 +- auto/os/darwin | 4 +- auto/os/linux | 17 + auto/os/solaris | 2 +- auto/sources | 1 + auto/summary | 14 - auto/types/sizeof | 2 +- auto/types/typedef | 2 +- auto/types/uintptr_t | 4 +- auto/unix | 79 +- src/core/nginx.h | 4 +- src/core/ngx_conf_file.h | 1 - src/core/ngx_connection.c | 13 +- src/core/ngx_connection.h | 8 +- src/core/ngx_crypt.c | 13 - src/core/ngx_file.c | 19 +- src/core/ngx_file.h | 4 +- src/core/ngx_inet.c | 196 +- src/core/ngx_inet.h | 36 +- src/core/ngx_md5.c | 6 - src/core/ngx_md5.h | 32 - src/core/ngx_module.h | 9 - src/core/ngx_proxy_protocol.c | 52 +- src/core/ngx_resolver.c | 54 +- src/core/ngx_sha1.c | 294 +++ src/core/ngx_sha1.h | 19 +- src/core/ngx_string.c | 4 +- src/event/modules/ngx_epoll_module.c | 93 +- src/event/ngx_event.c | 35 +- src/event/ngx_event.h | 20 +- src/event/ngx_event_accept.c | 19 +- src/event/ngx_event_connect.c | 153 ++ src/event/ngx_event_connect.h | 3 + src/event/ngx_event_openssl.c | 240 ++- src/event/ngx_event_openssl.h | 5 + src/event/ngx_event_openssl_stapling.c | 150 +- src/http/modules/ngx_http_dav_module.c | 6 + src/http/modules/ngx_http_fastcgi_module.c | 2 +- src/http/modules/ngx_http_log_module.c | 4 +- src/http/modules/ngx_http_map_module.c | 126 +- src/http/modules/ngx_http_memcached_module.c | 2 +- src/http/modules/ngx_http_proxy_module.c | 10 +- src/http/modules/ngx_http_realip_module.c | 66 +- src/http/modules/ngx_http_scgi_module.c | 2 +- src/http/modules/ngx_http_ssl_module.c | 66 +- src/http/modules/ngx_http_ssl_module.h | 5 +- src/http/modules/ngx_http_sub_filter_module.c | 124 +- .../ngx_http_upstream_keepalive_module.c | 2 +- .../modules/ngx_http_userid_filter_module.c | 2 +- src/http/modules/ngx_http_uwsgi_module.c | 10 +- src/http/ngx_http.c | 73 +- src/http/ngx_http_core_module.c | 18 +- src/http/ngx_http_core_module.h | 13 +- src/http/ngx_http_file_cache.c | 11 +- src/http/ngx_http_header_filter_module.c | 37 +- src/http/ngx_http_parse.c | 38 +- src/http/ngx_http_request.c | 14 +- src/http/ngx_http_request.h | 2 + src/http/ngx_http_script.c | 14 +- src/http/ngx_http_special_response.c | 15 +- src/http/ngx_http_upstream.c | 115 +- src/http/ngx_http_upstream.h | 3 + src/http/ngx_http_upstream_round_robin.c | 11 +- src/http/ngx_http_variables.c | 132 +- src/http/v2/ngx_http_v2.c | 220 ++- src/http/v2/ngx_http_v2.h | 7 +- src/http/v2/ngx_http_v2_filter_module.c | 55 +- src/http/v2/ngx_http_v2_module.c | 31 + src/http/v2/ngx_http_v2_module.h | 1 + src/mail/ngx_mail.c | 42 +- src/mail/ngx_mail.h | 13 +- src/mail/ngx_mail_auth_http_module.c | 29 +- src/mail/ngx_mail_core_module.c | 58 +- src/mail/ngx_mail_ssl_module.c | 64 +- src/mail/ngx_mail_ssl_module.h | 5 +- src/os/unix/ngx_readv_chain.c | 32 + src/os/unix/ngx_recv.c | 33 + src/os/unix/ngx_thread_mutex.c | 2 +- src/stream/ngx_stream.c | 65 +- src/stream/ngx_stream.h | 148 +- src/stream/ngx_stream_access_module.c | 1 + src/stream/ngx_stream_core_module.c | 197 ++- src/stream/ngx_stream_geo_module.c | 1572 +++++++++++++++++ src/stream/ngx_stream_geoip_module.c | 814 +++++++++ src/stream/ngx_stream_handler.c | 9 + src/stream/ngx_stream_limit_conn_module.c | 142 +- src/stream/ngx_stream_map_module.c | 574 ++++++ src/stream/ngx_stream_proxy_module.c | 546 +++++- src/stream/ngx_stream_return_module.c | 207 +++ src/stream/ngx_stream_script.c | 854 +++++++++ src/stream/ngx_stream_script.h | 123 ++ src/stream/ngx_stream_split_clients_module.c | 244 +++ src/stream/ngx_stream_ssl_module.c | 163 +- src/stream/ngx_stream_ssl_module.h | 5 +- src/stream/ngx_stream_upstream.c | 3 +- src/stream/ngx_stream_upstream.h | 16 + src/stream/ngx_stream_upstream_hash_module.c | 26 +- .../ngx_stream_upstream_least_conn_module.c | 3 +- src/stream/ngx_stream_upstream_round_robin.c | 135 ++ src/stream/ngx_stream_upstream_round_robin.h | 2 + src/stream/ngx_stream_upstream_zone_module.c | 3 +- src/stream/ngx_stream_variables.c | 971 ++++++++++ src/stream/ngx_stream_variables.h | 109 ++ 129 files changed, 8999 insertions(+), 2028 deletions(-) delete mode 100644 auto/lib/md5/conf delete mode 100644 auto/lib/md5/make delete mode 100644 auto/lib/md5/makefile.bcc delete mode 100644 auto/lib/md5/makefile.msvc delete mode 100644 auto/lib/md5/makefile.owc delete mode 100644 auto/lib/sha1/conf delete mode 100644 auto/lib/sha1/make delete mode 100644 auto/lib/sha1/makefile.bcc delete mode 100644 auto/lib/sha1/makefile.msvc delete mode 100644 auto/lib/sha1/makefile.owc delete mode 100644 auto/lib/test create mode 100644 src/core/ngx_sha1.c create mode 100644 src/stream/ngx_stream_geo_module.c create mode 100644 src/stream/ngx_stream_geoip_module.c create mode 100644 src/stream/ngx_stream_map_module.c create mode 100644 src/stream/ngx_stream_return_module.c create mode 100644 src/stream/ngx_stream_script.c create mode 100644 src/stream/ngx_stream_script.h create mode 100644 src/stream/ngx_stream_split_clients_module.c create mode 100644 src/stream/ngx_stream_variables.c create mode 100644 src/stream/ngx_stream_variables.h diff --git a/CHANGES b/CHANGES index 4396472..a795ef2 100644 --- a/CHANGES +++ b/CHANGES @@ -1,14 +1,108 @@ -Changes with nginx 1.10.1 31 May 2016 +Changes with nginx 1.11.3 26 Jul 2016 + + *) Change: now the "accept_mutex" directive is turned off by default. + + *) Feature: now nginx uses EPOLLEXCLUSIVE on Linux. + + *) Feature: the ngx_stream_geo_module. + + *) Feature: the ngx_stream_geoip_module. + + *) Feature: the ngx_stream_split_clients_module. + + *) Feature: variables support in the "proxy_pass" and "proxy_ssl_name" + directives in the stream module. + + *) Bugfix: socket leak when using HTTP/2. + + *) Bugfix: in configure tests. + Thanks to Piotr Sikora. + + +Changes with nginx 1.11.2 05 Jul 2016 + + *) Change: now nginx always uses internal MD5 and SHA1 implementations; + the --with-md5 and --with-sha1 configure options were canceled. + + *) Feature: variables support in the stream module. + + *) Feature: the ngx_stream_map_module. + + *) Feature: the ngx_stream_return_module. + + *) Feature: a port can be specified in the "proxy_bind", "fastcgi_bind", + "memcached_bind", "scgi_bind", and "uwsgi_bind" directives. + + *) Feature: now nginx uses the IP_BIND_ADDRESS_NO_PORT socket option + when available. + + *) Bugfix: a segmentation fault might occur in a worker process when + using HTTP/2 and the "proxy_request_buffering" directive. + + *) Bugfix: the "Content-Length" request header line was always added to + requests passed to backends, including requests without body, when + using HTTP/2. + + *) Bugfix: "http request count is zero" alerts might appear in logs when + using HTTP/2. + + *) Bugfix: unnecessary buffering might occur when using the "sub_filter" + directive; the issue had appeared in 1.9.4. + + +Changes with nginx 1.11.1 31 May 2016 *) Security: a segmentation fault might occur in a worker process while writing a specially crafted request body to a temporary file (CVE-2016-4450); the bug had appeared in 1.3.9. -Changes with nginx 1.10.0 26 Apr 2016 +Changes with nginx 1.11.0 24 May 2016 - *) 1.10.x stable branch. + *) Feature: the "transparent" parameter of the "proxy_bind", + "fastcgi_bind", "memcached_bind", "scgi_bind", and "uwsgi_bind" + directives. + + *) Feature: the $request_id variable. + + *) Feature: the "map" directive supports combinations of multiple + variables as resulting values. + + *) Feature: now nginx checks if EPOLLRDHUP events are supported by + kernel, and optimizes connection handling accordingly if the "epoll" + method is used. + + *) Feature: the "ssl_certificate" and "ssl_certificate_key" directives + can be specified multiple times to load certificates of different + types (for example, RSA and ECDSA). + + *) Feature: the "ssl_ecdh_curve" directive now allows specifying a list + of curves when using OpenSSL 1.0.2 or newer; by default a list built + into OpenSSL is used. + + *) Change: to use DHE ciphers it is now required to specify parameters + using the "ssl_dhparam" directive. + + *) Feature: the $proxy_protocol_port variable. + + *) Feature: the $realip_remote_port variable in the + ngx_http_realip_module. + + *) Feature: the ngx_http_realip_module is now able to set the client + port in addition to the address. + + *) Change: the "421 Misdirected Request" response now used when + rejecting requests to a virtual server different from one negotiated + during an SSL handshake; this improves interoperability with some + HTTP/2 clients when using client certificates. + + *) Change: HTTP/2 clients can now start sending request body + immediately; the "http2_body_preread_size" directive controls size of + the buffer used before nginx will start reading client request body. + + *) Bugfix: cached error responses were not updated when using the + "proxy_cache_bypass" directive. Changes with nginx 1.9.15 19 Apr 2016 diff --git a/CHANGES.ru b/CHANGES.ru index c96cf93..cd424e9 100644 --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,14 +1,109 @@ -Изменения в nginx 1.10.1 31.05.2016 +Изменения в nginx 1.11.3 26.07.2016 + + *) Изменение: теперь accept_mutex по умолчанию выключен. + + *) Добавление: теперь nginx использует EPOLLEXCLUSIVE на Linux. + + *) Добавление: модуль ngx_stream_geo_module. + + *) Добавление: модуль ngx_stream_geoip_module. + + *) Добавление: модуль ngx_stream_split_clients_module. + + *) Добавление: директивы proxy_pass и proxy_ssl_name в модуле stream + поддерживают переменные. + + *) Исправление: утечки сокетов при использовании HTTP/2. + + *) Исправление: в configure. + Спасибо Piotr Sikora. + + +Изменения в nginx 1.11.2 05.07.2016 + + *) Изменение: теперь nginx всегда использует внутренние реализации MD5 и + SHA1; параметры configure --with-md5 и --with-sha1 упразднены. + + *) Добавление: поддержка переменных в модуле stream. + + *) Добавление: модуль ngx_stream_map_module. + + *) Добавление: модуль ngx_stream_return_module. + + *) Добавление: в директивах proxy_bind, fastcgi_bind, memcached_bind, + scgi_bind и uwsgi_bind теперь можно указывать порт. + + *) Добавление: теперь nginx использует опцию сокета + IP_BIND_ADDRESS_NO_PORT, если она доступна. + + *) Исправление: при использовании HTTP/2 и директивы + proxy_request_buffering в рабочем процессе мог произойти segmentation + fault. + + *) Исправление: при использовании HTTP/2 к запросам, передаваемым на + бэкенд, всегда добавлялась строка заголовка "Content-Length", даже + если у запроса не было тела. + + *) Исправление: при использовании HTTP/2 в логах могли появляться + сообщения "http request count is zero". + + *) Исправление: при использовании директивы sub_filter могло + буферизироваться больше данных, чем это необходимо; проблема + появилась в 1.9.4. + + +Изменения в nginx 1.11.1 31.05.2016 *) Безопасность: при записи тела специально созданного запроса во временный файл в рабочем процессе мог происходить segmentation fault (CVE-2016-4450); ошибка появилась в 1.3.9. -Изменения в nginx 1.10.0 26.04.2016 +Изменения в nginx 1.11.0 24.05.2016 - *) Стабильная ветка 1.10.x. + *) Добавление: параметр transparent директив proxy_bind, fastcgi_bind, + memcached_bind, scgi_bind и uwsgi_bind. + + *) Добавление: переменная $request_id. + + *) Добавление: директива map поддерживает комбинации нескольких + переменных в качестве результирующих значений. + + *) Добавление: теперь при использовании метода epoll nginx проверяет, + поддерживает ли ядро события EPOLLRDHUP, и соответственно + оптимизирует обработку соединений. + + *) Добавление: директивы ssl_certificate и ssl_certificate_key теперь + можно указывать несколько раз для загрузки сертификатов разных типов + (например, RSA и ECDSA). + + *) Добавление: при использовании OpenSSL 1.0.2 и новее с помощью + директивы ssl_ecdh_curve теперь можно задать список кривых; по + умолчанию используется встроенный в OpenSSL список кривых. + + *) Изменение: для использования DHE-шифров теперь надо явно задавать + файл параметров с помощью директивы ssl_dhparam. + + *) Добавление: переменная $proxy_protocol_port. + + *) Добавление: переменная $realip_remote_port в модуле + ngx_http_realip_module. + + *) Добавление: модуль ngx_http_realip_module теперь позволяет + устанавливать не только адрес, но и порт клиента. + + *) Изменение: при попытке запросить виртуальный сервер, отличающийся от + согласованного в процессе SSL handshake, теперь возвращается ответ + "421 Misdirected Request"; это улучшает совместимость с некоторыми + HTTP/2-клиентами в случае использования клиентских сертификатов. + + *) Изменение: HTTP/2-клиенты теперь могут сразу присылать тело запроса; + директива http2_body_preread_size позволяет указать размер буфера, + который будет использоваться до того, как nginx начнёт читать тело. + + *) Исправление: при использовании директивы proxy_cache_bypass не + обновлялись закэшированные ошибочные ответы. Изменения в nginx 1.9.15 19.04.2016 diff --git a/auto/cc/acc b/auto/cc/acc index 6baee67..64fa671 100644 --- a/auto/cc/acc +++ b/auto/cc/acc @@ -12,4 +12,3 @@ CC_TEST_FLAGS="-Ae" PCRE_OPT="$PCRE_OPT -Ae" ZLIB_OPT="$ZLIB_OPT -Ae" -MD5_OPT="$MD5_OPT -Ae" diff --git a/auto/cc/clang b/auto/cc/clang index 25707b4..19bdaaa 100644 --- a/auto/cc/clang +++ b/auto/cc/clang @@ -66,12 +66,6 @@ else PCRE_OPT="$PCRE_OPT -pipe" fi -if [ ".$MD5_OPT" = "." ]; then - MD5_OPT="-O2 -pipe $CPU_OPT" -else - MD5_OPT="$MD5_OPT -pipe" -fi - if [ ".$ZLIB_OPT" = "." ]; then ZLIB_OPT="-O2 -pipe $CPU_OPT" else diff --git a/auto/cc/conf b/auto/cc/conf index f2c25ed..b3b9f92 100644 --- a/auto/cc/conf +++ b/auto/cc/conf @@ -231,7 +231,7 @@ if [ "$NGX_PLATFORM" != win32 ]; then ngx_feature_incs= ngx_feature_path= ngx_feature_libs= - ngx_feature_test="__builtin_bswap64(0)" + ngx_feature_test="if (__builtin_bswap64(0)) return 1" . auto/feature diff --git a/auto/cc/gcc b/auto/cc/gcc index c9101fe..a5c5c18 100644 --- a/auto/cc/gcc +++ b/auto/cc/gcc @@ -128,12 +128,6 @@ else PCRE_OPT="$PCRE_OPT $PIPE" fi -if [ ".$MD5_OPT" = "." ]; then - MD5_OPT="-O2 -fomit-frame-pointer $PIPE $CPU_OPT" -else - MD5_OPT="$MD5_OPT $PIPE" -fi - if [ ".$ZLIB_OPT" = "." ]; then ZLIB_OPT="-O2 -fomit-frame-pointer $PIPE $CPU_OPT" else @@ -151,9 +145,13 @@ CFLAGS="$CFLAGS -Wall -Wpointer-arith" #CFLAGS="$CFLAGS -Winline" #CFLAGS="$CFLAGS -Wmissing-prototypes" - case "$NGX_GCC_VER" in - [3-5].*) + 2.*) + # we have a lot of the unused function arguments + CFLAGS="$CFLAGS -Wno-unused" + ;; + + *) # we have a lot of the unused function arguments CFLAGS="$CFLAGS -Wno-unused-parameter" # 4.2.1 shows the warning in wrong places @@ -164,11 +162,6 @@ case "$NGX_GCC_VER" in CFLAGS="$CFLAGS -Wno-deprecated-declarations" fi ;; - - *) - # we have a lot of the unused function arguments - CFLAGS="$CFLAGS -Wno-unused" - ;; esac diff --git a/auto/cc/icc b/auto/cc/icc index 1c0df1a..c47f6e4 100644 --- a/auto/cc/icc +++ b/auto/cc/icc @@ -43,10 +43,6 @@ if [ ".$PCRE_OPT" = "." ]; then PCRE_OPT="-O $CPU_OPT" fi -if [ ".$MD5_OPT" = "." ]; then - MD5_OPT="-O $CPU_OPT" -fi - if [ ".$ZLIB_OPT" = "." ]; then ZLIB_OPT="-O $CPU_OPT" fi diff --git a/auto/cc/sunc b/auto/cc/sunc index 8360c49..806ccc4 100644 --- a/auto/cc/sunc +++ b/auto/cc/sunc @@ -20,7 +20,10 @@ have=NGX_COMPILER value="\"Sun C $NGX_SUNC_VER\"" . auto/define cat << END > $NGX_AUTOTEST.c -int main() { printf("%d", __SUNPRO_C); } +int main(void) { + printf("%d", __SUNPRO_C); + return 0; +} END @@ -145,10 +148,6 @@ if [ ".$PCRE_OPT" = "." ]; then PCRE_OPT="$ngx_fast $IPO $CPU_OPT" fi -if [ ".$MD5_OPT" = "." ]; then - MD5_OPT="$ngx_fast $IPO $CPU_OPT" -fi - if [ ".$ZLIB_OPT" = "." ]; then ZLIB_OPT="$ngx_fast $IPO $CPU_OPT" fi diff --git a/auto/endianness b/auto/endianness index 70b0a10..1b552b6 100644 --- a/auto/endianness +++ b/auto/endianness @@ -15,7 +15,7 @@ END cat << END > $NGX_AUTOTEST.c -int main() { +int main(void) { int i = 0x11223344; char *p; diff --git a/auto/feature b/auto/feature index 1145f28..3561f59 100644 --- a/auto/feature +++ b/auto/feature @@ -31,7 +31,7 @@ cat << END > $NGX_AUTOTEST.c $NGX_INCLUDE_UNISTD_H $ngx_feature_incs -int main() { +int main(void) { $ngx_feature_test; return 0; } diff --git a/auto/include b/auto/include index e34dabd..c1bd364 100644 --- a/auto/include +++ b/auto/include @@ -20,7 +20,7 @@ cat << END > $NGX_AUTOTEST.c $NGX_INCLUDE_SYS_PARAM_H #include <$ngx_include> -int main() { +int main(void) { return 0; } @@ -45,9 +45,6 @@ if [ -x $NGX_AUTOTEST ]; then eval "NGX_INCLUDE_$ngx_name='#include <$ngx_include>'" - #STUB - eval "NGX_$ngx_name='#include <$ngx_include>'" - else echo " not found" diff --git a/auto/lib/conf b/auto/lib/conf index a6242e7..0b8545a 100644 --- a/auto/lib/conf +++ b/auto/lib/conf @@ -25,35 +25,6 @@ if [ $USE_OPENSSL = YES ]; then . auto/lib/openssl/conf fi -if [ $USE_MD5 = YES ]; then - - if [ $USE_OPENSSL = YES ]; then - have=NGX_HAVE_OPENSSL_MD5_H . auto/have - have=NGX_OPENSSL_MD5 . auto/have - have=NGX_HAVE_MD5 . auto/have - MD5=YES - MD5_LIB=OpenSSL - - else - . auto/lib/md5/conf - fi - -fi - -if [ $USE_SHA1 = YES ]; then - - if [ $USE_OPENSSL = YES ]; then - have=NGX_HAVE_OPENSSL_SHA1_H . auto/have - have=NGX_HAVE_SHA1 . auto/have - SHA1=YES - SHA1_LIB=OpenSSL - - else - . auto/lib/sha1/conf - fi - -fi - if [ $USE_ZLIB = YES ]; then . auto/lib/zlib/conf fi diff --git a/auto/lib/make b/auto/lib/make index 6298b94..b64e329 100644 --- a/auto/lib/make +++ b/auto/lib/make @@ -7,14 +7,6 @@ if [ $PCRE != NONE -a $PCRE != NO -a $PCRE != YES ]; then . auto/lib/pcre/make fi -if [ $MD5 != NONE -a $MD5 != NO -a $MD5 != YES ]; then - . auto/lib/md5/make -fi - -if [ $SHA1 != NONE -a $SHA1 != NO -a $SHA1 != YES ]; then - . auto/lib/sha1/make -fi - if [ $OPENSSL != NONE -a $OPENSSL != NO -a $OPENSSL != YES ]; then . auto/lib/openssl/make fi diff --git a/auto/lib/md5/conf b/auto/lib/md5/conf deleted file mode 100644 index 49c0ddf..0000000 --- a/auto/lib/md5/conf +++ /dev/null @@ -1,103 +0,0 @@ - -# Copyright (C) Igor Sysoev -# Copyright (C) Nginx, Inc. - - -if [ $MD5 != NONE ]; then - - if grep MD5_Init $MD5/md5.h 2>&1 >/dev/null; then - # OpenSSL md5 - OPENSSL_MD5=YES - have=NGX_HAVE_OPENSSL_MD5 . auto/have - have=NGX_OPENSSL_MD5 . auto/have - else - # rsaref md5 - OPENSSL_MD5=NO - fi - - have=NGX_HAVE_MD5 . auto/have - CORE_INCS="$CORE_INCS $MD5" - - case "$NGX_CC_NAME" in - - msvc | owc | bcc) - LINK_DEPS="$LINK_DEPS $MD5/md5.lib" - CORE_LIBS="$CORE_LIBS $MD5/md5.lib" - ;; - - icc) - LINK_DEPS="$LINK_DEPS $MD5/libmd5.a" - - # to allow -ipo optimization we link with the *.o but not library - CORE_LIBS="$CORE_LIBS $MD5/md5_dgst.o" - - if [ $MD5_ASM = YES ]; then - CORE_LIBS="$CORE_LIBS $MD5/asm/mx86-elf.o" - fi - ;; - - *) - LINK_DEPS="$LINK_DEPS $MD5/libmd5.a" - CORE_LIBS="$CORE_LIBS $MD5/libmd5.a" - #CORE_LIBS="$CORE_LIBS -L $MD5 -lmd5" - ;; - - esac - -else - - if [ "$NGX_PLATFORM" != win32 ]; then - - MD5=NO - - # FreeBSD, Solaris 10 - - ngx_feature="md5 in system md library" - ngx_feature_name=NGX_HAVE_MD5 - ngx_feature_run=no - ngx_feature_incs="#include " - ngx_feature_path= - ngx_feature_libs="-lmd" - ngx_feature_test="MD5_CTX md5; MD5Init(&md5)" - . auto/feature - - ngx_md5_lib="system md" - - if [ $ngx_found = no ]; then - - # Solaris 8/9 - - ngx_feature="md5 in system md5 library" - ngx_feature_libs="-lmd5" - . auto/feature - - ngx_md5_lib="system md5" - fi - - if [ $ngx_found = no ]; then - - # OpenSSL crypto library - - ngx_feature="md5 in system OpenSSL crypto library" - ngx_feature_name="NGX_OPENSSL_MD5" - ngx_feature_incs="#include " - ngx_feature_libs="-lcrypto" - ngx_feature_test="MD5_CTX md5; MD5_Init(&md5)" - . auto/feature - - ngx_md5_lib="system crypto" - - if [ $ngx_found = yes ]; then - have=NGX_HAVE_OPENSSL_MD5_H . auto/have - have=NGX_HAVE_MD5 . auto/have - fi - fi - - if [ $ngx_found = yes ]; then - CORE_LIBS="$CORE_LIBS $ngx_feature_libs" - MD5=YES - MD5_LIB=$ngx_md5_lib - fi - fi - -fi diff --git a/auto/lib/md5/make b/auto/lib/md5/make deleted file mode 100644 index 7000b20..0000000 --- a/auto/lib/md5/make +++ /dev/null @@ -1,96 +0,0 @@ - -# Copyright (C) Igor Sysoev -# Copyright (C) Nginx, Inc. - - -case "$NGX_CC_NAME" in - - msvc) - ngx_makefile=makefile.msvc - ngx_opt="CPU_OPT=\"$CPU_OPT\" LIBC=$LIBC MD5_ASM=$MD5_ASM" - ngx_md5="MD5=\"$MD5\"" - ;; - - owc) - ngx_makefile=makefile.owc - ngx_opt="CPU_OPT=\"$CPU_OPT\"" - ngx_md5=`echo MD5=\"$MD5\" | sed -e "s/\//$ngx_regex_dirsep/g"` - ;; - - bcc) - ngx_makefile=makefile.bcc - ngx_opt="-DCPU_OPT=\"$CPU_OPT\" -DMD5_ASM=$MD5_ASM" - ngx_md5=`echo \-DMD5=\"$MD5\" | sed -e "s/\//$ngx_regex_dirsep/g"` - ;; - -esac - - -done=NO - - -case "$NGX_PLATFORM" in - - win32) - cat << END >> $NGX_MAKEFILE - -`echo "$MD5/md5.lib: $NGX_MAKEFILE" | sed -e "s/\//$ngx_regex_dirsep/g"` - \$(MAKE) -f auto/lib/md5/$ngx_makefile $ngx_opt $ngx_md5 - -END - - done=YES - ;; - - SunOS:*:i86pc) - if [ $MD5_ASM = YES ]; then - - cat << END >> $NGX_MAKEFILE - -$MD5/libmd5.a: $NGX_MAKEFILE - cd $MD5 \\ - && \$(MAKE) CFLAGS="$MD5_OPT -DSOL -DMD5_ASM -DL_ENDIAN" \\ - CC="\$(CC)" CPP="\$(CPP)" \\ - MD5_ASM_OBJ=asm/mx86-sol.o clean libmd5.a - -END - - done=YES - fi - ;; - - # FreeBSD: i386 - # Linux: i686 - - *:i386 | *:i686) - if [ $MD5_ASM = YES ]; then - - cat << END >> $NGX_MAKEFILE - -$MD5/libmd5.a: $NGX_MAKEFILE - cd $MD5 \\ - && \$(MAKE) CFLAGS="$MD5_OPT -DELF -DMD5_ASM -DL_ENDIAN" \\ - CC="\$(CC)" CPP="\$(CPP)" \\ - MD5_ASM_OBJ=asm/mx86-elf.o clean libmd5.a - -END - - done=YES - fi - ;; - -esac - - -if [ $done = NO ]; then - - cat << END >> $NGX_MAKEFILE - -$MD5/libmd5.a: $NGX_MAKEFILE - cd $MD5 \\ - && \$(MAKE) CFLAGS="$MD5_OPT" \\ - CC="\$(CC)" MD5_ASM_OBJ= clean libmd5.a - -END - -fi diff --git a/auto/lib/md5/makefile.bcc b/auto/lib/md5/makefile.bcc deleted file mode 100644 index eb6fb62..0000000 --- a/auto/lib/md5/makefile.bcc +++ /dev/null @@ -1,22 +0,0 @@ - -# Copyright (C) Igor Sysoev -# Copyright (C) Nginx, Inc. - - -CFLAGS = -q -O2 -tWM $(CPU_OPT) -DL_ENDIAN - -!if "$(MD5_ASM)" == "YES" - -md5.lib: - cd $(MD5) - bcc32 -c $(CFLAGS) -DMD5_ASM md5_dgst.c - tlib md5.lib +md5_dgst.obj +"asm\m-win32.obj" - -!else - -md5.lib: - cd $(MD5) - bcc32 -c $(CFLAGS) md5_dgst.c - tlib md5.lib +md5_dgst.obj - -!endif diff --git a/auto/lib/md5/makefile.msvc b/auto/lib/md5/makefile.msvc deleted file mode 100644 index 90d62fa..0000000 --- a/auto/lib/md5/makefile.msvc +++ /dev/null @@ -1,22 +0,0 @@ - -# Copyright (C) Igor Sysoev -# Copyright (C) Nginx, Inc. - - -CFLAGS = -nologo -O2 -Ob1 -Oi -Gs $(LIBC) $(CPU_OPT) -D L_ENDIAN - -!IF "$(MD5_ASM)" == "YES" - -md5.lib: - cd $(MD5) - cl -c $(CFLAGS) -D MD5_ASM md5_dgst.c - link -lib -out:md5.lib md5_dgst.obj asm/m-win32.obj - -!ELSE - -md5.lib: - cd $(MD5) - cl -c $(CFLAGS) md5_dgst.c - link -lib -out:md5.lib md5_dgst.obj - -!ENDIF diff --git a/auto/lib/md5/makefile.owc b/auto/lib/md5/makefile.owc deleted file mode 100644 index 78c1e61..0000000 --- a/auto/lib/md5/makefile.owc +++ /dev/null @@ -1,11 +0,0 @@ - -# Copyright (C) Igor Sysoev -# Copyright (C) Nginx, Inc. - - -CFLAGS = -zq -bt=nt -bm -ot -op -oi -oe -s $(CPU_OPT) - -md5.lib: - cd $(MD5) - wcl386 -c $(CFLAGS) -dL_ENDIAN md5_dgst.c - wlib -n md5.lib md5_dgst.obj diff --git a/auto/lib/sha1/conf b/auto/lib/sha1/conf deleted file mode 100644 index 78f9efd..0000000 --- a/auto/lib/sha1/conf +++ /dev/null @@ -1,79 +0,0 @@ - -# Copyright (C) Igor Sysoev -# Copyright (C) Nginx, Inc. - - -if [ $SHA1 != NONE ]; then - - have=NGX_HAVE_SHA1 . auto/have - CORE_INCS="$CORE_INCS $SHA1" - - case "$NGX_CC_NAME" in - - msvc | owc | bcc) - LINK_DEPS="$LINK_DEPS $SHA1/sha1.lib" - CORE_LIBS="$CORE_LIBS $SHA1/sha1.lib" - ;; - - icc) - LINK_DEPS="$LINK_DEPS $SHA1/libsha.a" - - # to allow -ipo optimization we link with the *.o but not library - CORE_LIBS="$CORE_LIBS $SHA1/sha1_dgst.o" - - if [ $SHA1_ASM = YES ]; then - CORE_LIBS="$CORE_LIBS $SHA1/asm/sx86-elf.o" - fi - ;; - - *) - LINK_DEPS="$LINK_DEPS $SHA1/libsha.a" - CORE_LIBS="$CORE_LIBS $SHA1/libsha.a" - #CORE_LIBS="$CORE_LIBS -L $SHA1 -lsha" - ;; - - esac - -else - - if [ "$NGX_PLATFORM" != win32 ]; then - - SHA1=NO - - # FreeBSD - - ngx_feature="sha1 in system md library" - ngx_feature_name=NGX_HAVE_SHA1 - ngx_feature_run=no - ngx_feature_incs="#include " - ngx_feature_path= - ngx_feature_libs="-lmd" - ngx_feature_test="SHA_CTX sha1; SHA1_Init(&sha1)" - . auto/feature - - ngx_sha1_lib="system md" - - if [ $ngx_found = no ]; then - - # OpenSSL crypto library - - ngx_feature="sha1 in system OpenSSL crypto library" - ngx_feature_incs="#include " - ngx_feature_libs="-lcrypto" - . auto/feature - - ngx_sha1_lib="system crypto" - - if [ $ngx_found = yes ]; then - have=NGX_HAVE_OPENSSL_SHA1_H . auto/have - fi - fi - - if [ $ngx_found = yes ]; then - CORE_LIBS="$CORE_LIBS $ngx_feature_libs" - SHA1=YES - SHA1_LIB=$ngx_sha1_lib - fi - fi - -fi diff --git a/auto/lib/sha1/make b/auto/lib/sha1/make deleted file mode 100644 index fc3b340..0000000 --- a/auto/lib/sha1/make +++ /dev/null @@ -1,96 +0,0 @@ - -# Copyright (C) Igor Sysoev -# Copyright (C) Nginx, Inc. - - -case "$NGX_CC_NAME" in - - msvc) - ngx_makefile=makefile.msvc - ngx_opt="CPU_OPT=\"$CPU_OPT\" LIBC=$LIBC SHA1_ASM=$SHA1_ASM" - ngx_sha1="SHA1=\"$SHA1\"" - ;; - - owc) - ngx_makefile=makefile.owc - ngx_opt="CPU_OPT=\"$CPU_OPT\"" - ngx_sha1=`echo SHA1=\"$SHA1\" | sed -e "s/\//$ngx_regex_dirsep/g"` - ;; - - bcc) - ngx_makefile=makefile.bcc - ngx_opt="-DCPU_OPT=\"$CPU_OPT\" -DSHA1_ASM=$SHA1_ASM" - ngx_sha1=`echo \-DSHA1=\"$SHA1\" | sed -e "s/\//$ngx_regex_dirsep/g"` - ;; - -esac - - -done=NO - - -case "$NGX_PLATFORM" in - - win32) - cat << END >> $NGX_MAKEFILE - -`echo "$SHA1/sha1.lib: $NGX_MAKEFILE" | sed -e "s/\//$ngx_regex_dirsep/g"` - \$(MAKE) -f auto/lib/sha1/$ngx_makefile $ngx_opt $ngx_sha1 - -END - - done=YES - ;; - - SunOS:*:i86pc) - if [ $SHA1_ASM = YES ]; then - - cat << END >> $NGX_MAKEFILE - -$SHA1/libsha.a: $NGX_MAKEFILE - cd $SHA1 \\ - && \$(MAKE) CFLAGS="$SHA1_OPT -DSOL -DSHA1_ASM -DL_ENDIAN" \\ - CC="\$(CC)" CPP="\$(CPP)" \\ - SHA_ASM_OBJ=asm/sx86-sol.o clean libsha.a - -END - - done=YES - fi - ;; - - # FreeBSD: i386 - # Linux: i686 - - *:i386 | *:i686) - if [ $SHA1_ASM = YES ]; then - - cat << END >> $NGX_MAKEFILE - -$SHA1/libsha.a: $NGX_MAKEFILE - cd $SHA1 \\ - && \$(MAKE) CFLAGS="$SHA1_OPT -DELF -DSHA1_ASM -DL_ENDIAN" \\ - CC="\$(CC)" CPP="\$(CPP)" \\ - SHA_ASM_OBJ=asm/sx86-elf.o clean libsha.a - -END - - done=YES - fi - ;; - -esac - - -if [ $done = NO ]; then - - cat << END >> $NGX_MAKEFILE - -$SHA1/libsha.a: $NGX_MAKEFILE - cd $SHA1 \\ - && \$(MAKE) CFLAGS="$SHA1_OPT" \\ - CC="\$(CC)" SHA_ASM_OBJ= clean libsha.a - -END - -fi diff --git a/auto/lib/sha1/makefile.bcc b/auto/lib/sha1/makefile.bcc deleted file mode 100644 index b0685fa..0000000 --- a/auto/lib/sha1/makefile.bcc +++ /dev/null @@ -1,22 +0,0 @@ - -# Copyright (C) Igor Sysoev -# Copyright (C) Nginx, Inc. - - -CFLAGS = -q -O2 -tWM $(CPU_OPT) -DL_ENDIAN - -!if "$(SHA1_ASM)" == "YES" - -sha1.lib: - cd $(SHA1) - bcc32 -c $(CFLAGS) -DSHA1_ASM sha1dgst.c - tlib sha1.lib +sha1dgst.obj +"asm\s-win32.obj" - -!else - -sha1.lib: - cd $(SHA1) - bcc32 -c $(CFLAGS) sha1dgst.c - tlib sha1.lib +sha1dgst.obj - -!endif diff --git a/auto/lib/sha1/makefile.msvc b/auto/lib/sha1/makefile.msvc deleted file mode 100644 index 3cbd21b..0000000 --- a/auto/lib/sha1/makefile.msvc +++ /dev/null @@ -1,22 +0,0 @@ - -# Copyright (C) Igor Sysoev -# Copyright (C) Nginx, Inc. - - -CFLAGS = -nologo -O2 -Ob1 -Oi -Gs $(LIBC) $(CPU_OPT) -D L_ENDIAN - -!IF "$(SHA1_ASM)" == "YES" - -sha1.lib: - cd $(SHA1) - cl -c $(CFLAGS) -D SHA1_ASM sha1dgst.c - link -lib -out:sha1.lib sha1dgst.obj asm/s-win32.obj - -!ELSE - -sha1.lib: - cd $(SHA1) - cl -c $(CFLAGS) sha1dgst.c - link -lib -out:sha1.lib sha1dgst.obj - -!ENDIF diff --git a/auto/lib/sha1/makefile.owc b/auto/lib/sha1/makefile.owc deleted file mode 100644 index fc095cc..0000000 --- a/auto/lib/sha1/makefile.owc +++ /dev/null @@ -1,11 +0,0 @@ - -# Copyright (C) Igor Sysoev -# Copyright (C) Nginx, Inc. - - -CFLAGS = -zq -bt=nt -bm -ot -op -oi -oe -s $(CPU_OPT) - -sha1.lib: - cd $(SHA1) - wcl386 -c $(CFLAGS) -dL_ENDIAN sha1dgst.c - wlib -n sha1.lib sha1dgst.obj diff --git a/auto/lib/test b/auto/lib/test deleted file mode 100644 index ba943a2..0000000 --- a/auto/lib/test +++ /dev/null @@ -1,40 +0,0 @@ - -# Copyright (C) Igor Sysoev -# Copyright (C) Nginx, Inc. - - -echo $ngx_n "checking for $ngx_lib ...$ngx_c" - -cat << END >> $NGX_AUTOCONF_ERR - ----------------------------------------- -checking for $ngx_lib - -END - -ngx_found=no - -cat << END > $NGX_AUTOTEST.c - -$ngx_lib_incs - -int main() { - $ngx_lib_test; - return 0; -} - - -eval "$CC $cc_test_flags $ngx_lib_cflags \ - -o $NGX_AUTOTEST $NGX_AUTOTEST.c $ngx_libs \ - >> $NGX_ERR 2>&1" - -if [ -x $NGX_AUTOTEST ]; then - echo " found" - - ngx_found=yes - -else - echo " not found" -fi - -rm -rf $NGX_AUTOTEST* diff --git a/auto/module b/auto/module index 16a816f..3b00a07 100644 --- a/auto/module +++ b/auto/module @@ -48,10 +48,14 @@ if [ "$ngx_module_link" = DYNAMIC ]; then fi ;; - PCRE | OPENSSL | MD5 | SHA1 | ZLIB) + PCRE | OPENSSL | ZLIB) eval USE_${lib}=YES ;; + MD5 | SHA1) + # obsolete + ;; + *) libs="$libs $lib" ;; @@ -79,10 +83,14 @@ elif [ "$ngx_module_link" = YES ]; then do case $lib in - PCRE | OPENSSL | MD5 | SHA1 | ZLIB | LIBXSLT | LIBGD | PERL | GEOIP) + PCRE | OPENSSL | ZLIB | LIBXSLT | LIBGD | PERL | GEOIP) eval USE_${lib}=YES ;; + MD5 | SHA1) + # obsolete + ;; + *) CORE_LIBS="$CORE_LIBS $lib" ;; @@ -109,10 +117,14 @@ elif [ "$ngx_module_link" = ADDON ]; then do case $lib in - PCRE | OPENSSL | MD5 | SHA1 | ZLIB | LIBXSLT | LIBGD | PERL | GEOIP) + PCRE | OPENSSL | ZLIB | LIBXSLT | LIBGD | PERL | GEOIP) eval USE_${lib}=YES ;; + MD5 | SHA1) + # obsolete + ;; + *) CORE_LIBS="$CORE_LIBS $lib" ;; diff --git a/auto/modules b/auto/modules index 22ff6d9..614037c 100644 --- a/auto/modules +++ b/auto/modules @@ -43,6 +43,7 @@ fi if [ $NGX_TEST_BUILD_EPOLL = YES ]; then have=NGX_HAVE_EPOLL . auto/have have=NGX_HAVE_EPOLLRDHUP . auto/have + have=NGX_HAVE_EPOLLEXCLUSIVE . auto/have have=NGX_HAVE_EVENTFD . auto/have have=NGX_TEST_BUILD_EPOLL . auto/have EVENT_MODULES="$EVENT_MODULES $EPOLL_MODULE" @@ -101,7 +102,6 @@ fi if [ $HTTP_CACHE = YES ]; then - USE_MD5=YES have=NGX_HTTP_CACHE . auto/have HTTP_SRCS="$HTTP_SRCS $HTTP_FILE_CACHE_SRCS" fi @@ -522,8 +522,6 @@ if [ $HTTP_AUTH_REQUEST = YES ]; then fi if [ $HTTP_AUTH_BASIC = YES ]; then - USE_MD5=YES - USE_SHA1=YES have=NGX_CRYPT . auto/have ngx_module_name=ngx_http_auth_basic_module @@ -682,7 +680,6 @@ fi if [ $HTTP_PROXY = YES ]; then have=NGX_HTTP_X_FORWARDED_FOR . auto/have - #USE_MD5=YES ngx_module_name=ngx_http_proxy_module ngx_module_incs= @@ -772,8 +769,6 @@ if [ $HTTP_BROWSER = YES ]; then fi if [ $HTTP_SECURE_LINK = YES ]; then - USE_MD5=YES - ngx_module_name=ngx_http_secure_link_module ngx_module_incs= ngx_module_deps= @@ -971,8 +966,6 @@ if [ $STREAM != NO ]; then STREAM_INCS= ngx_module_type=STREAM - ngx_module_libs= - ngx_module_link=YES ngx_module_order= @@ -982,9 +975,13 @@ if [ $STREAM != NO ]; then ngx_stream_upstream_module" ngx_module_incs="src/stream" ngx_module_deps="src/stream/ngx_stream.h \ + src/stream/ngx_stream_variables.h \ + src/stream/ngx_stream_script.h \ src/stream/ngx_stream_upstream.h \ src/stream/ngx_stream_upstream_round_robin.h" ngx_module_srcs="src/stream/ngx_stream.c \ + src/stream/ngx_stream_variables.c \ + src/stream/ngx_stream_script.c \ src/stream/ngx_stream_handler.c \ src/stream/ngx_stream_core_module.c \ src/stream/ngx_stream_proxy_module.c \ @@ -1002,6 +999,8 @@ if [ $STREAM != NO ]; then ngx_module_name=ngx_stream_ssl_module ngx_module_deps=src/stream/ngx_stream_ssl_module.h ngx_module_srcs=src/stream/ngx_stream_ssl_module.c + ngx_module_libs= + ngx_module_link=$STREAM_SSL . auto/module fi @@ -1010,6 +1009,8 @@ if [ $STREAM != NO ]; then ngx_module_name=ngx_stream_limit_conn_module ngx_module_deps= ngx_module_srcs=src/stream/ngx_stream_limit_conn_module.c + ngx_module_libs= + ngx_module_link=$STREAM_LIMIT_CONN . auto/module fi @@ -1018,6 +1019,58 @@ if [ $STREAM != NO ]; then ngx_module_name=ngx_stream_access_module ngx_module_deps= ngx_module_srcs=src/stream/ngx_stream_access_module.c + ngx_module_libs= + ngx_module_link=$STREAM_ACCESS + + . auto/module + fi + + if [ $STREAM_GEO = YES ]; then + ngx_module_name=ngx_stream_geo_module + ngx_module_deps= + ngx_module_srcs=src/stream/ngx_stream_geo_module.c + ngx_module_libs= + ngx_module_link=$STREAM_GEO + + . auto/module + fi + + if [ $STREAM_GEOIP != NO ]; then + ngx_module_name=ngx_stream_geoip_module + ngx_module_deps= + ngx_module_srcs=src/stream/ngx_stream_geoip_module.c + ngx_module_libs=GEOIP + ngx_module_link=$STREAM_GEOIP + + . auto/module + fi + + if [ $STREAM_MAP = YES ]; then + ngx_module_name=ngx_stream_map_module + ngx_module_deps= + ngx_module_srcs=src/stream/ngx_stream_map_module.c + ngx_module_libs= + ngx_module_link=$STREAM_MAP + + . auto/module + fi + + if [ $STREAM_SPLIT_CLIENTS = YES ]; then + ngx_module_name=ngx_stream_split_clients_module + ngx_module_deps= + ngx_module_srcs=src/stream/ngx_stream_split_clients_module.c + ngx_module_libs= + ngx_module_link=$STREAM_SPLIT_CLIENTS + + . auto/module + fi + + if [ $STREAM_RETURN = YES ]; then + ngx_module_name=ngx_stream_return_module + ngx_module_deps= + ngx_module_srcs=src/stream/ngx_stream_return_module.c + ngx_module_libs= + ngx_module_link=$STREAM_RETURN . auto/module fi @@ -1026,6 +1079,8 @@ if [ $STREAM != NO ]; then ngx_module_name=ngx_stream_upstream_hash_module ngx_module_deps= ngx_module_srcs=src/stream/ngx_stream_upstream_hash_module.c + ngx_module_libs= + ngx_module_link=$STREAM_UPSTREAM_HASH . auto/module fi @@ -1034,6 +1089,8 @@ if [ $STREAM != NO ]; then ngx_module_name=ngx_stream_upstream_least_conn_module ngx_module_deps= ngx_module_srcs=src/stream/ngx_stream_upstream_least_conn_module.c + ngx_module_libs= + ngx_module_link=$STREAM_UPSTREAM_LEAST_CONN . auto/module fi @@ -1044,6 +1101,8 @@ if [ $STREAM != NO ]; then ngx_module_name=ngx_stream_upstream_zone_module ngx_module_deps= ngx_module_srcs=src/stream/ngx_stream_upstream_zone_module.c + ngx_module_libs= + ngx_module_link=$STREAM_UPSTREAM_ZONE . auto/module fi diff --git a/auto/options b/auto/options index ac8beb1..a8fce30 100644 --- a/auto/options +++ b/auto/options @@ -117,6 +117,11 @@ STREAM=NO STREAM_SSL=NO STREAM_LIMIT_CONN=YES STREAM_ACCESS=YES +STREAM_GEO=YES +STREAM_GEOIP=NO +STREAM_MAP=YES +STREAM_SPLIT_CLIENTS=YES +STREAM_RETURN=YES STREAM_UPSTREAM_HASH=YES STREAM_UPSTREAM_LEAST_CONN=YES STREAM_UPSTREAM_ZONE=YES @@ -136,16 +141,6 @@ PCRE_JIT=NO USE_OPENSSL=NO OPENSSL=NONE -USE_MD5=NO -MD5=NONE -MD5_OPT= -MD5_ASM=NO - -USE_SHA1=NO -SHA1=NONE -SHA1_OPT= -SHA1_ASM=NO - USE_ZLIB=NO ZLIB=NONE ZLIB_OPT= @@ -301,9 +296,17 @@ use the \"--with-mail_ssl_module\" option instead" --with-stream) STREAM=YES ;; --with-stream=dynamic) STREAM=DYNAMIC ;; --with-stream_ssl_module) STREAM_SSL=YES ;; + --with-stream_geoip_module) STREAM_GEOIP=YES ;; + --with-stream_geoip_module=dynamic) + STREAM_GEOIP=DYNAMIC ;; --without-stream_limit_conn_module) STREAM_LIMIT_CONN=NO ;; --without-stream_access_module) STREAM_ACCESS=NO ;; + --without-stream_geo_module) STREAM_GEO=NO ;; + --without-stream_map_module) STREAM_MAP=NO ;; + --without-stream_split_clients_module) + STREAM_SPLIT_CLIENTS=NO ;; + --without-stream_return_module) STREAM_RETURN=NO ;; --without-stream_upstream_hash_module) STREAM_UPSTREAM_HASH=NO ;; --without-stream_upstream_least_conn_module) @@ -333,13 +336,31 @@ use the \"--with-mail_ssl_module\" option instead" --with-openssl=*) OPENSSL="$value" ;; --with-openssl-opt=*) OPENSSL_OPT="$value" ;; - --with-md5=*) MD5="$value" ;; - --with-md5-opt=*) MD5_OPT="$value" ;; - --with-md5-asm) MD5_ASM=YES ;; + --with-md5=*) + NGX_POST_CONF_MSG="$NGX_POST_CONF_MSG +$0: warning: the \"--with-md5\" option is deprecated" + ;; + --with-md5-opt=*) + NGX_POST_CONF_MSG="$NGX_POST_CONF_MSG +$0: warning: the \"--with-md5-opt\" option is deprecated" + ;; + --with-md5-asm) + NGX_POST_CONF_MSG="$NGX_POST_CONF_MSG +$0: warning: the \"--with-md5-asm\" option is deprecated" + ;; - --with-sha1=*) SHA1="$value" ;; - --with-sha1-opt=*) SHA1_OPT="$value" ;; - --with-sha1-asm) SHA1_ASM=YES ;; + --with-sha1=*) + NGX_POST_CONF_MSG="$NGX_POST_CONF_MSG +$0: warning: the \"--with-sha1\" option is deprecated" + ;; + --with-sha1-opt=*) + NGX_POST_CONF_MSG="$NGX_POST_CONF_MSG +$0: warning: the \"--with-sha1-opt\" option is deprecated" + ;; + --with-sha1-asm) + NGX_POST_CONF_MSG="$NGX_POST_CONF_MSG +$0: warning: the \"--with-sha1-asm\" option is deprecated" + ;; --with-zlib=*) ZLIB="$value" ;; --with-zlib-opt=*) ZLIB_OPT="$value" ;; @@ -482,8 +503,15 @@ cat << END --with-stream enable TCP/UDP proxy module --with-stream=dynamic enable dynamic TCP/UDP proxy module --with-stream_ssl_module enable ngx_stream_ssl_module + --with-stream_geoip_module enable ngx_stream_geoip_module + --with-stream_geoip_module=dynamic enable dynamic ngx_stream_geoip_module --without-stream_limit_conn_module disable ngx_stream_limit_conn_module --without-stream_access_module disable ngx_stream_access_module + --without-stream_geo_module disable ngx_stream_geo_module + --without-stream_map_module disable ngx_stream_map_module + --without-stream_split_clients_module + disable ngx_stream_split_clients_module + --without-stream_return_module disable ngx_stream_return_module --without-stream_upstream_hash_module disable ngx_stream_upstream_hash_module --without-stream_upstream_least_conn_module @@ -511,14 +539,6 @@ cat << END --with-pcre-opt=OPTIONS set additional build options for PCRE --with-pcre-jit build PCRE with JIT compilation support - --with-md5=DIR set path to md5 library sources - --with-md5-opt=OPTIONS set additional build options for md5 - --with-md5-asm use md5 assembler sources - - --with-sha1=DIR set path to sha1 library sources - --with-sha1-opt=OPTIONS set additional build options for sha1 - --with-sha1-asm use sha1 assembler sources - --with-zlib=DIR set path to zlib library sources --with-zlib-opt=OPTIONS set additional build options for zlib --with-zlib-asm=CPU use zlib assembler sources optimized diff --git a/auto/os/darwin b/auto/os/darwin index 9b31b1f..b4b3ad3 100644 --- a/auto/os/darwin +++ b/auto/os/darwin @@ -113,6 +113,6 @@ ngx_feature_run=no ngx_feature_incs="#include " ngx_feature_path= ngx_feature_libs= -ngx_feature_test="int32_t lock, n; - n = OSAtomicCompareAndSwap32Barrier(0, 1, &lock)" +ngx_feature_test="int32_t lock = 0; + if (!OSAtomicCompareAndSwap32Barrier(0, 1, &lock)) return 1" . auto/feature diff --git a/auto/os/linux b/auto/os/linux index c932267..fae8842 100644 --- a/auto/os/linux +++ b/auto/os/linux @@ -44,6 +44,7 @@ ngx_feature_test="int efd = 0; struct epoll_event ee; ee.events = EPOLLIN|EPOLLOUT|EPOLLET; ee.data.ptr = NULL; + (void) ee; efd = epoll_create(100); if (efd == -1) return 1;" . auto/feature @@ -69,6 +70,22 @@ if [ $ngx_found = yes ]; then ee.data.ptr = NULL; epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ee)" . auto/feature + + + # EPOLLEXCLUSIVE appeared in Linux 4.5, glibc 2.24 + + ngx_feature="EPOLLEXCLUSIVE" + ngx_feature_name="NGX_HAVE_EPOLLEXCLUSIVE" + ngx_feature_run=no + ngx_feature_incs="#include " + ngx_feature_path= + ngx_feature_libs= + ngx_feature_test="int efd = 0, fd = 0; + struct epoll_event ee; + ee.events = EPOLLIN|EPOLLEXCLUSIVE; + ee.data.ptr = NULL; + epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ee)" + . auto/feature fi diff --git a/auto/os/solaris b/auto/os/solaris index d39df0b..1dcfe84 100644 --- a/auto/os/solaris +++ b/auto/os/solaris @@ -52,7 +52,7 @@ ngx_feature_run=no ngx_feature_incs="#include " ngx_feature_path= ngx_feature_libs= -ngx_feature_test="int n = port_create()" +ngx_feature_test="(void) port_create()" . auto/feature if [ $ngx_found = yes ]; then diff --git a/auto/sources b/auto/sources index 27849e6..216e900 100644 --- a/auto/sources +++ b/auto/sources @@ -61,6 +61,7 @@ CORE_SRCS="src/core/nginx.c \ src/core/ngx_crc32.c \ src/core/ngx_murmurhash.c \ src/core/ngx_md5.c \ + src/core/ngx_sha1.c \ src/core/ngx_rbtree.c \ src/core/ngx_radix_tree.c \ src/core/ngx_slab.c \ diff --git a/auto/summary b/auto/summary index dc8fe4f..9aa776e 100644 --- a/auto/summary +++ b/auto/summary @@ -28,20 +28,6 @@ case $OPENSSL in *) echo " + using OpenSSL library: $OPENSSL" ;; esac -case $MD5 in - YES) echo " + md5: using $MD5_LIB library" ;; - NONE) echo " + md5 library is not used" ;; - NO) echo " + using builtin md5 code" ;; - *) echo " + using md5 library: $MD5" ;; -esac - -case $SHA1 in - YES) echo " + sha1: using $SHA1_LIB library" ;; - NONE) echo " + sha1 library is not used" ;; - NO) echo " + sha1 library is not found" ;; - *) echo " + using sha1 library: $SHA1" ;; -esac - case $ZLIB in YES) echo " + using system zlib library" ;; NONE) echo " + zlib library is not used" ;; diff --git a/auto/types/sizeof b/auto/types/sizeof index b5b71bb..480d8cf 100644 --- a/auto/types/sizeof +++ b/auto/types/sizeof @@ -25,7 +25,7 @@ $NGX_INCLUDE_UNISTD_H $NGX_INCLUDE_INTTYPES_H $NGX_INCLUDE_AUTO_CONFIG_H -int main() { +int main(void) { printf("%d", (int) sizeof($ngx_type)); return 0; } diff --git a/auto/types/typedef b/auto/types/typedef index b55237e..d54c289 100644 --- a/auto/types/typedef +++ b/auto/types/typedef @@ -27,7 +27,7 @@ do #include $NGX_INCLUDE_INTTYPES_H -int main() { +int main(void) { $ngx_try i = 0; return (int) i; } diff --git a/auto/types/uintptr_t b/auto/types/uintptr_t index 2b7212e..a33d6d0 100644 --- a/auto/types/uintptr_t +++ b/auto/types/uintptr_t @@ -17,9 +17,9 @@ found=no cat << END > $NGX_AUTOTEST.c #include -$NGX_INTTYPES_H +$NGX_INCLUDE_INTTYPES_H -int main() { +int main(void) { uintptr_t i = 0; return (int) i; } diff --git a/auto/unix b/auto/unix index 8c0e813..dbc0f0e 100755 --- a/auto/unix +++ b/auto/unix @@ -75,7 +75,7 @@ if test -z "$NGX_KQUEUE_CHECKED"; then ngx_feature_incs="#include " ngx_feature_path= ngx_feature_libs= - ngx_feature_test="int kq; kq = kqueue()" + ngx_feature_test="(void) kqueue()" . auto/feature if [ $ngx_found = yes ]; then @@ -92,7 +92,8 @@ if test -z "$NGX_KQUEUE_CHECKED"; then ngx_feature_path= ngx_feature_libs= ngx_feature_test="struct kevent kev; - kev.fflags = NOTE_LOWAT;" + kev.fflags = NOTE_LOWAT; + (void) kev" . auto/feature @@ -260,11 +261,11 @@ ngx_feature_run=no ngx_feature_incs="#include " ngx_feature_path= ngx_feature_libs= -ngx_feature_test="dlopen(NULL, RTLD_NOW | RTLD_GLOBAL); dlsym(NULL, NULL)" +ngx_feature_test="dlopen(NULL, RTLD_NOW | RTLD_GLOBAL); dlsym(NULL, \"\")" . auto/feature -if [ $ngx_found != yes ]; then +if [ $ngx_found = no ]; then ngx_feature="dlopen() in libdl" ngx_feature_libs="-ldl" @@ -287,7 +288,7 @@ ngx_feature_test="sched_yield()" . auto/feature -if [ $ngx_found != yes ]; then +if [ $ngx_found = no ]; then ngx_feature="sched_yield() in librt" ngx_feature_libs="-lrt" @@ -329,6 +330,57 @@ ngx_feature_test="setsockopt(0, SOL_SOCKET, SO_ACCEPTFILTER, NULL, 0)" . auto/feature +# NetBSD bind to any address for transparent proxying + +ngx_feature="SO_BINDANY" +ngx_feature_name="NGX_HAVE_TRANSPARENT_PROXY" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="setsockopt(0, SOL_SOCKET, SO_BINDANY, NULL, 0)" +. auto/feature + + +# Linux IP_BIND_ADDRESS_NO_PORT + +ngx_feature="IP_BIND_ADDRESS_NO_PORT" +ngx_feature_name="NGX_HAVE_IP_BIND_ADDRESS_NO_PORT" +ngx_feature_run=no +ngx_feature_incs="#include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="setsockopt(0, IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT, NULL, 0)" +. auto/feature + + +# Linux transparent proxying + +ngx_feature="IP_TRANSPARENT" +ngx_feature_name="NGX_HAVE_TRANSPARENT_PROXY" +ngx_feature_run=no +ngx_feature_incs="#include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="setsockopt(0, IPPROTO_IP, IP_TRANSPARENT, NULL, 0)" +. auto/feature + + +# FreeBSD bind to any address for transparent proxying + +ngx_feature="IP_BINDANY" +ngx_feature_name="NGX_HAVE_TRANSPARENT_PROXY" +ngx_feature_run=no +ngx_feature_incs="#include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="setsockopt(0, IPPROTO_IP, IP_BINDANY, NULL, 0)" +. auto/feature + + # BSD way to get IPv4 datagram destination address ngx_feature="IP_RECVDSTADDR" @@ -441,9 +493,9 @@ if [ $NGX_FILE_AIO = YES ]; then ngx_feature_incs="#include " ngx_feature_path= ngx_feature_libs= - ngx_feature_test="int n; struct aiocb iocb; + ngx_feature_test="struct aiocb iocb; iocb.aio_sigevent.sigev_notify = SIGEV_KEVENT; - n = aio_read(&iocb)" + (void) aio_read(&iocb)" . auto/feature if [ $ngx_found = yes ]; then @@ -463,6 +515,7 @@ if [ $NGX_FILE_AIO = YES ]; then iocb.aio_lio_opcode = IOCB_CMD_PREAD; iocb.aio_flags = IOCB_FLAG_RESFD; iocb.aio_resfd = -1; + (void) iocb; (void) eventfd(0, 0)" . auto/feature @@ -478,11 +531,12 @@ if [ $NGX_FILE_AIO = YES ]; then ngx_feature="Linux AIO support (SYS_eventfd)" ngx_feature_incs="#include #include " - ngx_feature_test="int n = SYS_eventfd; - struct iocb iocb; + ngx_feature_test="struct iocb iocb; iocb.aio_lio_opcode = IOCB_CMD_PREAD; iocb.aio_flags = IOCB_FLAG_RESFD; - iocb.aio_resfd = -1;" + iocb.aio_resfd = -1; + (void) iocb; + (void) SYS_eventfd" . auto/feature if [ $ngx_found = yes ]; then @@ -520,7 +574,7 @@ else ngx_feature="eventfd() (SYS_eventfd)" ngx_feature_incs="#include " - ngx_feature_test="int n = SYS_eventfd" + ngx_feature_test="(void) SYS_eventfd" . auto/feature fi fi @@ -593,7 +647,8 @@ if [ $NGX_IPV6 = YES ]; then ngx_feature_path= ngx_feature_libs= ngx_feature_test="struct sockaddr_in6 sin6; - sin6.sin6_family = AF_INET6;" + sin6.sin6_family = AF_INET6; + (void) sin6" . auto/feature fi diff --git a/src/core/nginx.h b/src/core/nginx.h index e2b8005..e303e66 100644 --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1010001 -#define NGINX_VERSION "1.10.1" +#define nginx_version 1011003 +#define NGINX_VERSION "1.11.3" #define NGINX_VER "nginx/" NGINX_VERSION #ifdef NGX_BUILD diff --git a/src/core/ngx_conf_file.h b/src/core/ngx_conf_file.h index 9ccee36..213611f 100644 --- a/src/core/ngx_conf_file.h +++ b/src/core/ngx_conf_file.h @@ -45,7 +45,6 @@ #define NGX_CONF_ANY 0x00000400 #define NGX_CONF_1MORE 0x00000800 #define NGX_CONF_2MORE 0x00001000 -#define NGX_CONF_MULTI 0x00000000 /* compatibility */ #define NGX_DIRECT_CONF 0x00010000 diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c index 5a53bac..16ba630 100644 --- a/src/core/ngx_connection.c +++ b/src/core/ngx_connection.c @@ -17,7 +17,8 @@ static void ngx_drain_connections(void); ngx_listening_t * -ngx_create_listening(ngx_conf_t *cf, void *sockaddr, socklen_t socklen) +ngx_create_listening(ngx_conf_t *cf, struct sockaddr *sockaddr, + socklen_t socklen) { size_t len; ngx_listening_t *ls; @@ -150,12 +151,12 @@ ngx_set_inherited_sockets(ngx_cycle_t *cycle) ls = cycle->listening.elts; for (i = 0; i < cycle->listening.nelts; i++) { - ls[i].sockaddr = ngx_palloc(cycle->pool, NGX_SOCKADDRLEN); + ls[i].sockaddr = ngx_palloc(cycle->pool, sizeof(ngx_sockaddr_t)); if (ls[i].sockaddr == NULL) { return NGX_ERROR; } - ls[i].socklen = NGX_SOCKADDRLEN; + ls[i].socklen = sizeof(ngx_sockaddr_t); if (getsockname(ls[i].fd, ls[i].sockaddr, &ls[i].socklen) == -1) { ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno, "getsockname() of the inherited " @@ -1277,7 +1278,7 @@ ngx_connection_local_sockaddr(ngx_connection_t *c, ngx_str_t *s, { socklen_t len; ngx_uint_t addr; - u_char sa[NGX_SOCKADDRLEN]; + ngx_sockaddr_t sa; struct sockaddr_in *sin; #if (NGX_HAVE_INET6) ngx_uint_t i; @@ -1315,9 +1316,9 @@ ngx_connection_local_sockaddr(ngx_connection_t *c, ngx_str_t *s, if (addr == 0) { - len = NGX_SOCKADDRLEN; + len = sizeof(ngx_sockaddr_t); - if (getsockname(c->fd, (struct sockaddr *) &sa, &len) == -1) { + if (getsockname(c->fd, &sa.sockaddr, &len) == -1) { ngx_connection_error(c, ngx_socket_errno, "getsockname() failed"); return NGX_ERROR; } diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h index b0d162a..e484c81 100644 --- a/src/core/ngx_connection.h +++ b/src/core/ngx_connection.h @@ -149,6 +149,7 @@ struct ngx_connection_s { ngx_str_t addr_text; ngx_str_t proxy_protocol_addr; + in_port_t proxy_protocol_port; #if (NGX_SSL) ngx_ssl_connection_t *ssl; @@ -169,7 +170,6 @@ struct ngx_connection_s { unsigned log_error:3; /* ngx_connection_log_error_e */ - unsigned unexpected_eof:1; unsigned timedout:1; unsigned error:1; unsigned destroyed:1; @@ -186,10 +186,6 @@ struct ngx_connection_s { unsigned need_last_buf:1; -#if (NGX_HAVE_IOCP) - unsigned accept_context_updated:1; -#endif - #if (NGX_HAVE_AIO_SENDFILE) unsigned busy_count:2; #endif @@ -211,7 +207,7 @@ struct ngx_connection_s { } -ngx_listening_t *ngx_create_listening(ngx_conf_t *cf, void *sockaddr, +ngx_listening_t *ngx_create_listening(ngx_conf_t *cf, struct sockaddr *sockaddr, socklen_t socklen); ngx_int_t ngx_clone_listening(ngx_conf_t *cf, ngx_listening_t *ls); ngx_int_t ngx_set_inherited_sockets(ngx_cycle_t *cycle); diff --git a/src/core/ngx_crypt.c b/src/core/ngx_crypt.c index 9db74f4..868dc5d 100644 --- a/src/core/ngx_crypt.c +++ b/src/core/ngx_crypt.c @@ -8,9 +8,7 @@ #include #include #include -#if (NGX_HAVE_SHA1) #include -#endif #if (NGX_CRYPT) @@ -19,16 +17,11 @@ static ngx_int_t ngx_crypt_apr1(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted); static ngx_int_t ngx_crypt_plain(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted); - -#if (NGX_HAVE_SHA1) - static ngx_int_t ngx_crypt_ssha(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted); static ngx_int_t ngx_crypt_sha(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted); -#endif - static u_char *ngx_crypt_to64(u_char *p, uint32_t v, size_t n); @@ -42,13 +35,11 @@ ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted) } else if (ngx_strncmp(salt, "{PLAIN}", sizeof("{PLAIN}") - 1) == 0) { return ngx_crypt_plain(pool, key, salt, encrypted); -#if (NGX_HAVE_SHA1) } else if (ngx_strncmp(salt, "{SSHA}", sizeof("{SSHA}") - 1) == 0) { return ngx_crypt_ssha(pool, key, salt, encrypted); } else if (ngx_strncmp(salt, "{SHA}", sizeof("{SHA}") - 1) == 0) { return ngx_crypt_sha(pool, key, salt, encrypted); -#endif } /* fallback to libc crypt() */ @@ -193,8 +184,6 @@ ngx_crypt_plain(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted) } -#if (NGX_HAVE_SHA1) - static ngx_int_t ngx_crypt_ssha(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted) { @@ -278,6 +267,4 @@ ngx_crypt_sha(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted) return NGX_OK; } -#endif /* NGX_HAVE_SHA1 */ - #endif /* NGX_CRYPT */ diff --git a/src/core/ngx_file.c b/src/core/ngx_file.c index fc2dfd3..c1137cc 100644 --- a/src/core/ngx_file.c +++ b/src/core/ngx_file.c @@ -225,7 +225,7 @@ ngx_create_hashed_filename(ngx_path_t *path, u_char *file, size_t len) file[path->name.len + path->len] = '/'; - for (n = 0; n < 3; n++) { + for (n = 0; n < NGX_MAX_PATH_LEVEL; n++) { level = path->level[n]; if (level == 0) { @@ -249,7 +249,7 @@ ngx_create_path(ngx_file_t *file, ngx_path_t *path) pos = path->name.len; - for (i = 0; i < 3; i++) { + for (i = 0; i < NGX_MAX_PATH_LEVEL; i++) { if (path->level[i] == 0) { break; } @@ -399,6 +399,8 @@ char * ngx_conf_merge_path_value(ngx_conf_t *cf, ngx_path_t **path, ngx_path_t *prev, ngx_path_init_t *init) { + ngx_uint_t i; + if (*path) { return NGX_CONF_OK; } @@ -419,13 +421,10 @@ ngx_conf_merge_path_value(ngx_conf_t *cf, ngx_path_t **path, ngx_path_t *prev, return NGX_CONF_ERROR; } - (*path)->level[0] = init->level[0]; - (*path)->level[1] = init->level[1]; - (*path)->level[2] = init->level[2]; - - (*path)->len = init->level[0] + (init->level[0] ? 1 : 0) - + init->level[1] + (init->level[1] ? 1 : 0) - + init->level[2] + (init->level[2] ? 1 : 0); + for (i = 0; i < NGX_MAX_PATH_LEVEL; i++) { + (*path)->level[i] = init->level[i]; + (*path)->len += init->level[i] + (init->level[i] ? 1 : 0); + } if (ngx_add_path(cf, path) != NGX_OK) { return NGX_CONF_ERROR; @@ -518,7 +517,7 @@ ngx_add_path(ngx_conf_t *cf, ngx_path_t **slot) return NGX_ERROR; } - for (n = 0; n < 3; n++) { + for (n = 0; n < NGX_MAX_PATH_LEVEL; n++) { if (p[i]->level[n] != path->level[n]) { if (path->conf_file == NULL) { if (p[i]->conf_file == NULL) { diff --git a/src/core/ngx_file.h b/src/core/ngx_file.h index 5f8228b..a723c3d 100644 --- a/src/core/ngx_file.h +++ b/src/core/ngx_file.h @@ -49,7 +49,7 @@ typedef void (*ngx_path_loader_pt) (void *data); typedef struct { ngx_str_t name; size_t len; - size_t level[3]; + size_t level[NGX_MAX_PATH_LEVEL]; ngx_path_manager_pt manager; ngx_path_loader_pt loader; @@ -62,7 +62,7 @@ typedef struct { typedef struct { ngx_str_t name; - size_t level[3]; + size_t level[NGX_MAX_PATH_LEVEL]; } ngx_path_init_t; diff --git a/src/core/ngx_inet.c b/src/core/ngx_inet.c index 33b303d..c4aaf3a 100644 --- a/src/core/ngx_inet.c +++ b/src/core/ngx_inet.c @@ -525,6 +525,68 @@ ngx_parse_addr(ngx_pool_t *pool, ngx_addr_t *addr, u_char *text, size_t len) } +ngx_int_t +ngx_parse_addr_port(ngx_pool_t *pool, ngx_addr_t *addr, u_char *text, + size_t len) +{ + u_char *p, *last; + size_t plen; + ngx_int_t rc, port; + + rc = ngx_parse_addr(pool, addr, text, len); + + if (rc != NGX_DECLINED) { + return rc; + } + + last = text + len; + +#if (NGX_HAVE_INET6) + if (len && text[0] == '[') { + + p = ngx_strlchr(text, last, ']'); + + if (p == NULL || p == last - 1 || *++p != ':') { + return NGX_DECLINED; + } + + text++; + len -= 2; + + } else +#endif + + { + p = ngx_strlchr(text, last, ':'); + + if (p == NULL) { + return NGX_DECLINED; + } + } + + p++; + plen = last - p; + + port = ngx_atoi(p, plen); + + if (port < 1 || port > 65535) { + return NGX_DECLINED; + } + + len -= plen + 1; + + rc = ngx_parse_addr(pool, addr, text, len); + + if (rc != NGX_OK) { + return rc; + } + + ngx_inet_set_port(addr->sockaddr, (in_port_t) port); + + return NGX_OK; +} + + ngx_int_t ngx_parse_url(ngx_pool_t *pool, ngx_url_t *u) { @@ -763,7 +825,7 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u) return NGX_ERROR; } - ngx_memcpy(sin, u->sockaddr, sizeof(struct sockaddr_in)); + ngx_memcpy(sin, &u->sockaddr, sizeof(struct sockaddr_in)); u->addrs[0].sockaddr = (struct sockaddr *) sin; u->addrs[0].socklen = sizeof(struct sockaddr_in); @@ -790,7 +852,7 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u) u->family = u->addrs[0].sockaddr->sa_family; u->socklen = u->addrs[0].socklen; - ngx_memcpy(u->sockaddr, u->addrs[0].sockaddr, u->addrs[0].socklen); + ngx_memcpy(&u->sockaddr, u->addrs[0].sockaddr, u->addrs[0].socklen); switch (u->family) { @@ -843,47 +905,49 @@ ngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u) return NGX_ERROR; } - if (last - p) { + port = p + 1; - port = p + 1; + uri = ngx_strlchr(port, last, '/'); - uri = ngx_strlchr(port, last, '/'); - - if (uri) { - if (u->listen || !u->uri_part) { - u->err = "invalid host"; - return NGX_ERROR; - } - - u->uri.len = last - uri; - u->uri.data = uri; - - last = uri; + if (uri) { + if (u->listen || !u->uri_part) { + u->err = "invalid host"; + return NGX_ERROR; } - if (*port == ':') { - port++; + u->uri.len = last - uri; + u->uri.data = uri; - len = last - port; + last = uri; + } - n = ngx_atoi(port, len); - - if (n < 1 || n > 65535) { - u->err = "invalid port"; - return NGX_ERROR; - } - - u->port = (in_port_t) n; - sin6->sin6_port = htons((in_port_t) n); - - u->port_text.len = len; - u->port_text.data = port; - - } else { - u->no_port = 1; - u->port = u->default_port; - sin6->sin6_port = htons(u->default_port); + if (port < last) { + if (*port != ':') { + u->err = "invalid host"; + return NGX_ERROR; } + + port++; + + len = last - port; + + n = ngx_atoi(port, len); + + if (n < 1 || n > 65535) { + u->err = "invalid port"; + return NGX_ERROR; + } + + u->port = (in_port_t) n; + sin6->sin6_port = htons((in_port_t) n); + + u->port_text.len = len; + u->port_text.data = port; + + } else { + u->no_port = 1; + u->port = u->default_port; + sin6->sin6_port = htons(u->default_port); } len = p - host; @@ -918,7 +982,7 @@ ngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u) return NGX_ERROR; } - ngx_memcpy(sin6, u->sockaddr, sizeof(struct sockaddr_in6)); + ngx_memcpy(sin6, &u->sockaddr, sizeof(struct sockaddr_in6)); u->addrs[0].sockaddr = (struct sockaddr *) sin6; u->addrs[0].socklen = sizeof(struct sockaddr_in6); @@ -1275,3 +1339,61 @@ ngx_cmp_sockaddr(struct sockaddr *sa1, socklen_t slen1, return NGX_OK; } + + +in_port_t +ngx_inet_get_port(struct sockaddr *sa) +{ + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + switch (sa->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) sa; + return ntohs(sin6->sin6_port); +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + return 0; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) sa; + return ntohs(sin->sin_port); + } +} + + +void +ngx_inet_set_port(struct sockaddr *sa, in_port_t port) +{ + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + switch (sa->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) sa; + sin6->sin6_port = htons(port); + break; +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) sa; + sin->sin_port = htons(port); + break; + } +} diff --git a/src/core/ngx_inet.h b/src/core/ngx_inet.h index 0555750..97dc354 100644 --- a/src/core/ngx_inet.h +++ b/src/core/ngx_inet.h @@ -13,14 +13,6 @@ #include -/* - * TODO: autoconfigure NGX_SOCKADDRLEN and NGX_SOCKADDR_STRLEN as - * sizeof(struct sockaddr_storage) - * sizeof(struct sockaddr_un) - * sizeof(struct sockaddr_in6) - * sizeof(struct sockaddr_in) - */ - #define NGX_INET_ADDRSTRLEN (sizeof("255.255.255.255") - 1) #define NGX_INET6_ADDRSTRLEN \ (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") - 1) @@ -29,15 +21,26 @@ #if (NGX_HAVE_UNIX_DOMAIN) #define NGX_SOCKADDR_STRLEN (sizeof("unix:") - 1 + NGX_UNIX_ADDRSTRLEN) -#else +#elif (NGX_HAVE_INET6) #define NGX_SOCKADDR_STRLEN (NGX_INET6_ADDRSTRLEN + sizeof("[]:65535") - 1) +#else +#define NGX_SOCKADDR_STRLEN (NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1) #endif -#if (NGX_HAVE_UNIX_DOMAIN) -#define NGX_SOCKADDRLEN sizeof(struct sockaddr_un) -#else -#define NGX_SOCKADDRLEN 512 +/* compatibility */ +#define NGX_SOCKADDRLEN sizeof(ngx_sockaddr_t) + + +typedef union { + struct sockaddr sockaddr; + struct sockaddr_in sockaddr_in; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 sockaddr_in6; #endif +#if (NGX_HAVE_UNIX_DOMAIN) + struct sockaddr_un sockaddr_un; +#endif +} ngx_sockaddr_t; typedef struct { @@ -87,13 +90,12 @@ typedef struct { unsigned listen:1; unsigned uri_part:1; unsigned no_resolve:1; - unsigned one_addr:1; /* compatibility */ unsigned no_port:1; unsigned wildcard:1; socklen_t socklen; - u_char sockaddr[NGX_SOCKADDRLEN]; + ngx_sockaddr_t sockaddr; ngx_addr_t *addrs; ngx_uint_t naddrs; @@ -113,10 +115,14 @@ size_t ngx_inet_ntop(int family, void *addr, u_char *text, size_t len); ngx_int_t ngx_ptocidr(ngx_str_t *text, ngx_cidr_t *cidr); ngx_int_t ngx_parse_addr(ngx_pool_t *pool, ngx_addr_t *addr, u_char *text, size_t len); +ngx_int_t ngx_parse_addr_port(ngx_pool_t *pool, ngx_addr_t *addr, + u_char *text, size_t len); ngx_int_t ngx_parse_url(ngx_pool_t *pool, ngx_url_t *u); ngx_int_t ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u); ngx_int_t ngx_cmp_sockaddr(struct sockaddr *sa1, socklen_t slen1, struct sockaddr *sa2, socklen_t slen2, ngx_uint_t cmp_port); +in_port_t ngx_inet_get_port(struct sockaddr *sa); +void ngx_inet_set_port(struct sockaddr *sa, in_port_t port); #endif /* _NGX_INET_H_INCLUDED_ */ diff --git a/src/core/ngx_md5.c b/src/core/ngx_md5.c index 440c75b..c25d002 100644 --- a/src/core/ngx_md5.c +++ b/src/core/ngx_md5.c @@ -3,8 +3,6 @@ * An internal implementation, based on Alexander Peslyak's * public domain implementation: * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 - * It is not expected to be optimal and is used only - * if no MD5 implementation was found in system. */ @@ -13,8 +11,6 @@ #include -#if !(NGX_HAVE_MD5) - static const u_char *ngx_md5_body(ngx_md5_t *ctx, const u_char *data, size_t size); @@ -285,5 +281,3 @@ ngx_md5_body(ngx_md5_t *ctx, const u_char *data, size_t size) return p; } - -#endif diff --git a/src/core/ngx_md5.h b/src/core/ngx_md5.h index 18d09d6..713b614 100644 --- a/src/core/ngx_md5.h +++ b/src/core/ngx_md5.h @@ -13,36 +13,6 @@ #include -#if (NGX_HAVE_MD5) - -#if (NGX_HAVE_OPENSSL_MD5_H) -#include -#else -#include -#endif - - -typedef MD5_CTX ngx_md5_t; - - -#if (NGX_OPENSSL_MD5) - -#define ngx_md5_init MD5_Init -#define ngx_md5_update MD5_Update -#define ngx_md5_final MD5_Final - -#else - -#define ngx_md5_init MD5Init -#define ngx_md5_update MD5Update -#define ngx_md5_final MD5Final - -#endif - - -#else /* !NGX_HAVE_MD5 */ - - typedef struct { uint64_t bytes; uint32_t a, b, c, d; @@ -55,6 +25,4 @@ void ngx_md5_update(ngx_md5_t *ctx, const void *data, size_t size); void ngx_md5_final(u_char result[16], ngx_md5_t *ctx); -#endif - #endif /* _NGX_MD5_H_INCLUDED_ */ diff --git a/src/core/ngx_module.h b/src/core/ngx_module.h index e911cb4..a1a0d6c 100644 --- a/src/core/ngx_module.h +++ b/src/core/ngx_module.h @@ -119,17 +119,8 @@ #define NGX_MODULE_SIGNATURE_16 "0" #endif -#if (NGX_HAVE_MD5) -#define NGX_MODULE_SIGNATURE_17 "1" -#else #define NGX_MODULE_SIGNATURE_17 "0" -#endif - -#if (NGX_HAVE_SHA1) -#define NGX_MODULE_SIGNATURE_18 "1" -#else #define NGX_MODULE_SIGNATURE_18 "0" -#endif #if (NGX_HAVE_OPENAT) #define NGX_MODULE_SIGNATURE_19 "1" diff --git a/src/core/ngx_proxy_protocol.c b/src/core/ngx_proxy_protocol.c index f347e7f..523ec35 100644 --- a/src/core/ngx_proxy_protocol.c +++ b/src/core/ngx_proxy_protocol.c @@ -12,8 +12,9 @@ u_char * ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, u_char *last) { - size_t len; - u_char ch, *p, *addr; + size_t len; + u_char ch, *p, *addr, *port; + ngx_int_t n; p = buf; len = last - buf; @@ -71,8 +72,40 @@ ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, u_char *last) ngx_memcpy(c->proxy_protocol_addr.data, addr, len); c->proxy_protocol_addr.len = len; - ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, - "PROXY protocol address: \"%V\"", &c->proxy_protocol_addr); + for ( ;; ) { + if (p == last) { + goto invalid; + } + + if (*p++ == ' ') { + break; + } + } + + port = p; + + for ( ;; ) { + if (p == last) { + goto invalid; + } + + if (*p++ == ' ') { + break; + } + } + + len = p - port - 1; + + n = ngx_atoi(port, len); + + if (n < 0 || n > 65535) { + goto invalid; + } + + c->proxy_protocol_port = (in_port_t) n; + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol address: %V %i", &c->proxy_protocol_addr, n); skip: @@ -108,19 +141,11 @@ ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, u_char *last) case AF_INET: buf = ngx_cpymem(buf, "PROXY TCP4 ", sizeof("PROXY TCP4 ") - 1); - - port = ntohs(((struct sockaddr_in *) c->sockaddr)->sin_port); - lport = ntohs(((struct sockaddr_in *) c->local_sockaddr)->sin_port); - break; #if (NGX_HAVE_INET6) case AF_INET6: buf = ngx_cpymem(buf, "PROXY TCP6 ", sizeof("PROXY TCP6 ") - 1); - - port = ntohs(((struct sockaddr_in6 *) c->sockaddr)->sin6_port); - lport = ntohs(((struct sockaddr_in6 *) c->local_sockaddr)->sin6_port); - break; #endif @@ -136,5 +161,8 @@ ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, u_char *last) buf += ngx_sock_ntop(c->local_sockaddr, c->local_socklen, buf, last - buf, 0); + port = ngx_inet_get_port(c->sockaddr); + lport = ngx_inet_get_port(c->local_sockaddr); + return ngx_slprintf(buf, last, " %ui %ui" CRLF, port, lport); } diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c index e00fe22..53dae6b 100644 --- a/src/core/ngx_resolver.c +++ b/src/core/ngx_resolver.c @@ -2992,16 +2992,12 @@ failed: static void ngx_resolver_srv_names_handler(ngx_resolver_ctx_t *cctx) { - ngx_uint_t i; - u_char (*sockaddr)[NGX_SOCKADDRLEN]; - ngx_addr_t *addrs; - ngx_resolver_t *r; - struct sockaddr_in *sin; - ngx_resolver_ctx_t *ctx; - ngx_resolver_srv_name_t *srv; -#if (NGX_HAVE_INET6) - struct sockaddr_in6 *sin6; -#endif + ngx_uint_t i; + ngx_addr_t *addrs; + ngx_resolver_t *r; + ngx_sockaddr_t *sockaddr; + ngx_resolver_ctx_t *ctx; + ngx_resolver_srv_name_t *srv; r = cctx->resolver; ctx = cctx->data; @@ -3026,7 +3022,7 @@ ngx_resolver_srv_names_handler(ngx_resolver_ctx_t *cctx) return; } - sockaddr = ngx_resolver_alloc(r, cctx->naddrs * NGX_SOCKADDRLEN); + sockaddr = ngx_resolver_alloc(r, cctx->naddrs * sizeof(ngx_sockaddr_t)); if (sockaddr == NULL) { ngx_resolver_free(r, addrs); ngx_resolve_name_done(cctx); @@ -3039,23 +3035,13 @@ ngx_resolver_srv_names_handler(ngx_resolver_ctx_t *cctx) } for (i = 0; i < cctx->naddrs; i++) { - addrs[i].sockaddr = (struct sockaddr *) sockaddr[i]; + addrs[i].sockaddr = &sockaddr[i].sockaddr; addrs[i].socklen = cctx->addrs[i].socklen; - ngx_memcpy(sockaddr[i], cctx->addrs[i].sockaddr, + ngx_memcpy(&sockaddr[i], cctx->addrs[i].sockaddr, addrs[i].socklen); - switch (addrs[i].sockaddr->sa_family) { -#if (NGX_HAVE_INET6) - case AF_INET6: - sin6 = (struct sockaddr_in6 *) addrs[i].sockaddr; - sin6->sin6_port = htons(srv->port); - break; -#endif - default: /* AF_INET */ - sin = (struct sockaddr_in *) addrs[i].sockaddr; - sin->sin_port = htons(srv->port); - } + ngx_inet_set_port(addrs[i].sockaddr, srv->port); } srv->addrs = addrs; @@ -4161,14 +4147,14 @@ static ngx_resolver_addr_t * ngx_resolver_export(ngx_resolver_t *r, ngx_resolver_node_t *rn, ngx_uint_t rotate) { - ngx_uint_t d, i, j, n; - u_char (*sockaddr)[NGX_SOCKADDRLEN]; - in_addr_t *addr; - struct sockaddr_in *sin; - ngx_resolver_addr_t *dst; + ngx_uint_t d, i, j, n; + in_addr_t *addr; + ngx_sockaddr_t *sockaddr; + struct sockaddr_in *sin; + ngx_resolver_addr_t *dst; #if (NGX_HAVE_INET6) - struct in6_addr *addr6; - struct sockaddr_in6 *sin6; + struct in6_addr *addr6; + struct sockaddr_in6 *sin6; #endif n = rn->naddrs; @@ -4181,7 +4167,7 @@ ngx_resolver_export(ngx_resolver_t *r, ngx_resolver_node_t *rn, return NULL; } - sockaddr = ngx_resolver_calloc(r, n * NGX_SOCKADDRLEN); + sockaddr = ngx_resolver_calloc(r, n * sizeof(ngx_sockaddr_t)); if (sockaddr == NULL) { ngx_resolver_free(r, dst); return NULL; @@ -4196,7 +4182,7 @@ ngx_resolver_export(ngx_resolver_t *r, ngx_resolver_node_t *rn, addr = (rn->naddrs == 1) ? &rn->u.addr : rn->u.addrs; do { - sin = (struct sockaddr_in *) sockaddr[d]; + sin = &sockaddr[d].sockaddr_in; sin->sin_family = AF_INET; sin->sin_addr.s_addr = addr[j++]; dst[d].sockaddr = (struct sockaddr *) sin; @@ -4219,7 +4205,7 @@ ngx_resolver_export(ngx_resolver_t *r, ngx_resolver_node_t *rn, addr6 = (rn->naddrs6 == 1) ? &rn->u6.addr6 : rn->u6.addrs6; do { - sin6 = (struct sockaddr_in6 *) sockaddr[d]; + sin6 = &sockaddr[d].sockaddr_in6; sin6->sin6_family = AF_INET6; ngx_memcpy(sin6->sin6_addr.s6_addr, addr6[j++].s6_addr, 16); dst[d].sockaddr = (struct sockaddr *) sin6; diff --git a/src/core/ngx_sha1.c b/src/core/ngx_sha1.c new file mode 100644 index 0000000..f00dc52 --- /dev/null +++ b/src/core/ngx_sha1.c @@ -0,0 +1,294 @@ + +/* + * Copyright (C) Maxim Dounin + * Copyright (C) Nginx, Inc. + * + * An internal SHA1 implementation. + */ + + +#include +#include +#include + + +static const u_char *ngx_sha1_body(ngx_sha1_t *ctx, const u_char *data, + size_t size); + + +void +ngx_sha1_init(ngx_sha1_t *ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + ctx->e = 0xc3d2e1f0; + + ctx->bytes = 0; +} + + +void +ngx_sha1_update(ngx_sha1_t *ctx, const void *data, size_t size) +{ + size_t used, free; + + used = (size_t) (ctx->bytes & 0x3f); + ctx->bytes += size; + + if (used) { + free = 64 - used; + + if (size < free) { + ngx_memcpy(&ctx->buffer[used], data, size); + return; + } + + ngx_memcpy(&ctx->buffer[used], data, free); + data = (u_char *) data + free; + size -= free; + (void) ngx_sha1_body(ctx, ctx->buffer, 64); + } + + if (size >= 64) { + data = ngx_sha1_body(ctx, data, size & ~(size_t) 0x3f); + size &= 0x3f; + } + + ngx_memcpy(ctx->buffer, data, size); +} + + +void +ngx_sha1_final(u_char result[20], ngx_sha1_t *ctx) +{ + size_t used, free; + + used = (size_t) (ctx->bytes & 0x3f); + + ctx->buffer[used++] = 0x80; + + free = 64 - used; + + if (free < 8) { + ngx_memzero(&ctx->buffer[used], free); + (void) ngx_sha1_body(ctx, ctx->buffer, 64); + used = 0; + free = 64; + } + + ngx_memzero(&ctx->buffer[used], free - 8); + + ctx->bytes <<= 3; + ctx->buffer[56] = (u_char) (ctx->bytes >> 56); + ctx->buffer[57] = (u_char) (ctx->bytes >> 48); + ctx->buffer[58] = (u_char) (ctx->bytes >> 40); + ctx->buffer[59] = (u_char) (ctx->bytes >> 32); + ctx->buffer[60] = (u_char) (ctx->bytes >> 24); + ctx->buffer[61] = (u_char) (ctx->bytes >> 16); + ctx->buffer[62] = (u_char) (ctx->bytes >> 8); + ctx->buffer[63] = (u_char) ctx->bytes; + + (void) ngx_sha1_body(ctx, ctx->buffer, 64); + + result[0] = (u_char) (ctx->a >> 24); + result[1] = (u_char) (ctx->a >> 16); + result[2] = (u_char) (ctx->a >> 8); + result[3] = (u_char) ctx->a; + result[4] = (u_char) (ctx->b >> 24); + result[5] = (u_char) (ctx->b >> 16); + result[6] = (u_char) (ctx->b >> 8); + result[7] = (u_char) ctx->b; + result[8] = (u_char) (ctx->c >> 24); + result[9] = (u_char) (ctx->c >> 16); + result[10] = (u_char) (ctx->c >> 8); + result[11] = (u_char) ctx->c; + result[12] = (u_char) (ctx->d >> 24); + result[13] = (u_char) (ctx->d >> 16); + result[14] = (u_char) (ctx->d >> 8); + result[15] = (u_char) ctx->d; + result[16] = (u_char) (ctx->e >> 24); + result[17] = (u_char) (ctx->e >> 16); + result[18] = (u_char) (ctx->e >> 8); + result[19] = (u_char) ctx->e; + + ngx_memzero(ctx, sizeof(*ctx)); +} + + +/* + * Helper functions. + */ + +#define ROTATE(bits, word) (((word) << (bits)) | ((word) >> (32 - (bits)))) + +#define F1(b, c, d) (((b) & (c)) | ((~(b)) & (d))) +#define F2(b, c, d) ((b) ^ (c) ^ (d)) +#define F3(b, c, d) (((b) & (c)) | ((b) & (d)) | ((c) & (d))) + +#define STEP(f, a, b, c, d, e, w, t) \ + temp = ROTATE(5, (a)) + f((b), (c), (d)) + (e) + (w) + (t); \ + (e) = (d); \ + (d) = (c); \ + (c) = ROTATE(30, (b)); \ + (b) = (a); \ + (a) = temp; + + +/* + * GET() reads 4 input bytes in big-endian byte order and returns + * them as uint32_t. + */ + +#define GET(n) \ + ((uint32_t) p[n * 4 + 3] | \ + ((uint32_t) p[n * 4 + 2] << 8) | \ + ((uint32_t) p[n * 4 + 1] << 16) | \ + ((uint32_t) p[n * 4] << 24)) + + +/* + * This processes one or more 64-byte data blocks, but does not update + * the bit counters. There are no alignment requirements. + */ + +static const u_char * +ngx_sha1_body(ngx_sha1_t *ctx, const u_char *data, size_t size) +{ + uint32_t a, b, c, d, e, temp; + uint32_t saved_a, saved_b, saved_c, saved_d, saved_e; + uint32_t words[80]; + ngx_uint_t i; + const u_char *p; + + p = data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + e = ctx->e; + + do { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + saved_e = e; + + /* Load data block into the words array */ + + for (i = 0; i < 16; i++) { + words[i] = GET(i); + } + + for (i = 16; i < 80; i++) { + words[i] = ROTATE(1, words[i - 3] ^ words[i - 8] ^ words[i - 14] + ^ words[i - 16]); + } + + /* Transformations */ + + STEP(F1, a, b, c, d, e, words[0], 0x5a827999); + STEP(F1, a, b, c, d, e, words[1], 0x5a827999); + STEP(F1, a, b, c, d, e, words[2], 0x5a827999); + STEP(F1, a, b, c, d, e, words[3], 0x5a827999); + STEP(F1, a, b, c, d, e, words[4], 0x5a827999); + STEP(F1, a, b, c, d, e, words[5], 0x5a827999); + STEP(F1, a, b, c, d, e, words[6], 0x5a827999); + STEP(F1, a, b, c, d, e, words[7], 0x5a827999); + STEP(F1, a, b, c, d, e, words[8], 0x5a827999); + STEP(F1, a, b, c, d, e, words[9], 0x5a827999); + STEP(F1, a, b, c, d, e, words[10], 0x5a827999); + STEP(F1, a, b, c, d, e, words[11], 0x5a827999); + STEP(F1, a, b, c, d, e, words[12], 0x5a827999); + STEP(F1, a, b, c, d, e, words[13], 0x5a827999); + STEP(F1, a, b, c, d, e, words[14], 0x5a827999); + STEP(F1, a, b, c, d, e, words[15], 0x5a827999); + STEP(F1, a, b, c, d, e, words[16], 0x5a827999); + STEP(F1, a, b, c, d, e, words[17], 0x5a827999); + STEP(F1, a, b, c, d, e, words[18], 0x5a827999); + STEP(F1, a, b, c, d, e, words[19], 0x5a827999); + + STEP(F2, a, b, c, d, e, words[20], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[21], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[22], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[23], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[24], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[25], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[26], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[27], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[28], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[29], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[30], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[31], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[32], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[33], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[34], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[35], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[36], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[37], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[38], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[39], 0x6ed9eba1); + + STEP(F3, a, b, c, d, e, words[40], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[41], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[42], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[43], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[44], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[45], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[46], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[47], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[48], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[49], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[50], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[51], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[52], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[53], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[54], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[55], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[56], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[57], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[58], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[59], 0x8f1bbcdc); + + STEP(F2, a, b, c, d, e, words[60], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[61], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[62], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[63], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[64], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[65], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[66], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[67], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[68], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[69], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[70], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[71], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[72], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[73], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[74], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[75], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[76], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[77], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[78], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[79], 0xca62c1d6); + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + e += saved_e; + + p += 64; + + } while (size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + ctx->e = e; + + return p; +} diff --git a/src/core/ngx_sha1.h b/src/core/ngx_sha1.h index 81c909e..4a98f71 100644 --- a/src/core/ngx_sha1.h +++ b/src/core/ngx_sha1.h @@ -13,19 +13,16 @@ #include -#if (NGX_HAVE_OPENSSL_SHA1_H) -#include -#else -#include -#endif +typedef struct { + uint64_t bytes; + uint32_t a, b, c, d, e, f; + u_char buffer[64]; +} ngx_sha1_t; -typedef SHA_CTX ngx_sha1_t; - - -#define ngx_sha1_init SHA1_Init -#define ngx_sha1_update SHA1_Update -#define ngx_sha1_final SHA1_Final +void ngx_sha1_init(ngx_sha1_t *ctx); +void ngx_sha1_update(ngx_sha1_t *ctx, const void *data, size_t size); +void ngx_sha1_final(u_char result[20], ngx_sha1_t *ctx); #endif /* _NGX_SHA1_H_INCLUDED_ */ diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c index cf665a4..7a73ef5 100644 --- a/src/core/ngx_string.c +++ b/src/core/ngx_string.c @@ -1563,7 +1563,7 @@ ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type) n = 0; while (size) { - if (escape[*src >> 5] & (1 << (*src & 0x1f))) { + if (escape[*src >> 5] & (1U << (*src & 0x1f))) { n++; } src++; @@ -1574,7 +1574,7 @@ ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type) } while (size) { - if (escape[*src >> 5] & (1 << (*src & 0x1f))) { + if (escape[*src >> 5] & (1U << (*src & 0x1f))) { *dst++ = '%'; *dst++ = hex[*src >> 4]; *dst++ = hex[*src & 0xf]; diff --git a/src/event/modules/ngx_epoll_module.c b/src/event/modules/ngx_epoll_module.c index 166c461..c267fd6 100644 --- a/src/event/modules/ngx_epoll_module.c +++ b/src/event/modules/ngx_epoll_module.c @@ -17,18 +17,19 @@ #define EPOLLIN 0x001 #define EPOLLPRI 0x002 #define EPOLLOUT 0x004 +#define EPOLLERR 0x008 +#define EPOLLHUP 0x010 #define EPOLLRDNORM 0x040 #define EPOLLRDBAND 0x080 #define EPOLLWRNORM 0x100 #define EPOLLWRBAND 0x200 #define EPOLLMSG 0x400 -#define EPOLLERR 0x008 -#define EPOLLHUP 0x010 #define EPOLLRDHUP 0x2000 -#define EPOLLET 0x80000000 +#define EPOLLEXCLUSIVE 0x10000000 #define EPOLLONESHOT 0x40000000 +#define EPOLLET 0x80000000 #define EPOLL_CTL_ADD 1 #define EPOLL_CTL_DEL 2 @@ -105,6 +106,9 @@ static ngx_int_t ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer); static ngx_int_t ngx_epoll_notify_init(ngx_log_t *log); static void ngx_epoll_notify_handler(ngx_event_t *ev); #endif +#if (NGX_HAVE_EPOLLRDHUP) +static void ngx_epoll_test_rdhup(ngx_cycle_t *cycle); +#endif static void ngx_epoll_done(ngx_cycle_t *cycle); static ngx_int_t ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); @@ -146,6 +150,10 @@ static ngx_connection_t ngx_eventfd_conn; #endif +#if (NGX_HAVE_EPOLLRDHUP) +ngx_uint_t ngx_use_epoll_rdhup; +#endif + static ngx_str_t epoll_name = ngx_string("epoll"); static ngx_command_t ngx_epoll_commands[] = { @@ -334,9 +342,11 @@ ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer) #endif #if (NGX_HAVE_FILE_AIO) - ngx_epoll_aio_init(cycle, epcf); +#endif +#if (NGX_HAVE_EPOLLRDHUP) + ngx_epoll_test_rdhup(cycle); #endif } @@ -449,6 +459,73 @@ ngx_epoll_notify_handler(ngx_event_t *ev) #endif +#if (NGX_HAVE_EPOLLRDHUP) + +static void +ngx_epoll_test_rdhup(ngx_cycle_t *cycle) +{ + int s[2], events; + struct epoll_event ee; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "socketpair() failed"); + return; + } + + ee.events = EPOLLET|EPOLLIN|EPOLLRDHUP; + + if (epoll_ctl(ep, EPOLL_CTL_ADD, s[0], &ee) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "epoll_ctl() failed"); + goto failed; + } + + if (close(s[1]) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "close() failed"); + s[1] = -1; + goto failed; + } + + s[1] = -1; + + events = epoll_wait(ep, &ee, 1, 5000); + + if (events == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "epoll_wait() failed"); + goto failed; + } + + if (events) { + ngx_use_epoll_rdhup = ee.events & EPOLLRDHUP; + + } else { + ngx_log_error(NGX_LOG_ALERT, cycle->log, NGX_ETIMEDOUT, + "epoll_wait() timed out"); + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "testing the EPOLLRDHUP flag: %s", + ngx_use_epoll_rdhup ? "success" : "fail"); + +failed: + + if (s[1] != -1 && close(s[1]) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "close() failed"); + } + + if (close(s[0]) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "close() failed"); + } +} + +#endif + + static void ngx_epoll_done(ngx_cycle_t *cycle) { @@ -534,6 +611,12 @@ ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) op = EPOLL_CTL_ADD; } +#if (NGX_HAVE_EPOLLEXCLUSIVE && NGX_HAVE_EPOLLRDHUP) + if (flags & NGX_EXCLUSIVE_EVENT) { + events &= ~EPOLLRDHUP; + } +#endif + ee.events = events | (uint32_t) flags; ee.data.ptr = (void *) ((uintptr_t) c | ev->instance); @@ -808,6 +891,8 @@ ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) if (revents & EPOLLRDHUP) { rev->pending_eof = 1; } + + rev->available = 1; #endif rev->ready = 1; diff --git a/src/event/ngx_event.c b/src/event/ngx_event.c index c8ae5b2..9d6c4c9 100644 --- a/src/event/ngx_event.c +++ b/src/event/ngx_event.c @@ -822,15 +822,38 @@ ngx_event_process_init(ngx_cycle_t *cycle) rev->handler = (c->type == SOCK_STREAM) ? ngx_event_accept : ngx_event_recvmsg; - if (ngx_use_accept_mutex #if (NGX_HAVE_REUSEPORT) - && !ls[i].reuseport -#endif - ) - { + + if (ls[i].reuseport) { + if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { + return NGX_ERROR; + } + continue; } +#endif + + if (ngx_use_accept_mutex) { + continue; + } + +#if (NGX_HAVE_EPOLLEXCLUSIVE) + + if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) + && ccf->worker_processes > 1) + { + if (ngx_add_event(rev, NGX_READ_EVENT, NGX_EXCLUSIVE_EVENT) + == NGX_ERROR) + { + return NGX_ERROR; + } + + continue; + } + +#endif + if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { return NGX_ERROR; } @@ -1261,7 +1284,7 @@ ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf) ngx_conf_init_ptr_value(ecf->name, event_module->name->data); ngx_conf_init_value(ecf->multi_accept, 0); - ngx_conf_init_value(ecf->accept_mutex, 1); + ngx_conf_init_value(ecf->accept_mutex, 0); ngx_conf_init_msec_value(ecf->accept_mutex_delay, 500); return NGX_CONF_OK; diff --git a/src/event/ngx_event.h b/src/event/ngx_event.h index ed0682c..27139ee 100644 --- a/src/event/ngx_event.h +++ b/src/event/ngx_event.h @@ -76,11 +76,6 @@ struct ngx_event_s { unsigned cancelable:1; -#if (NGX_WIN32) - /* setsockopt(SO_UPDATE_ACCEPT_CONTEXT) was successful */ - unsigned accept_context_updated:1; -#endif - #if (NGX_HAVE_KQUEUE) unsigned kq_vnode:1; @@ -96,6 +91,10 @@ struct ngx_event_s { * write: available space in buffer when event is ready * or lowat when event is set with NGX_LOWAT_EVENT flag * + * epoll with EPOLLRDHUP: + * accept: 1 if accept many, 0 otherwise + * read: 1 if there can be data to read, 0 otherwise + * * iocp: TODO * * otherwise: @@ -196,6 +195,9 @@ typedef struct { extern ngx_event_actions_t ngx_event_actions; +#if (NGX_HAVE_EPOLLRDHUP) +extern ngx_uint_t ngx_use_epoll_rdhup; +#endif /* @@ -365,6 +367,9 @@ extern ngx_event_actions_t ngx_event_actions; #define NGX_ONESHOT_EVENT EPOLLONESHOT #endif +#if (NGX_HAVE_EPOLLEXCLUSIVE) +#define NGX_EXCLUSIVE_EVENT EPOLLEXCLUSIVE +#endif #elif (NGX_HAVE_POLL) @@ -393,6 +398,11 @@ extern ngx_event_actions_t ngx_event_actions; #endif +#if (NGX_TEST_BUILD_EPOLL) +#define NGX_EXCLUSIVE_EVENT 0 +#endif + + #ifndef NGX_CLEAR_EVENT #define NGX_CLEAR_EVENT 0 /* dummy declaration */ #endif diff --git a/src/event/ngx_event_accept.c b/src/event/ngx_event_accept.c index 1c87a34..4445adc 100644 --- a/src/event/ngx_event_accept.c +++ b/src/event/ngx_event_accept.c @@ -28,10 +28,10 @@ ngx_event_accept(ngx_event_t *ev) ngx_uint_t level; ngx_socket_t s; ngx_event_t *rev, *wev; + ngx_sockaddr_t sa; ngx_listening_t *ls; ngx_connection_t *c, *lc; ngx_event_conf_t *ecf; - u_char sa[NGX_SOCKADDRLEN]; #if (NGX_HAVE_ACCEPT4) static ngx_uint_t use_accept4 = 1; #endif @@ -58,17 +58,16 @@ ngx_event_accept(ngx_event_t *ev) "accept on %V, ready: %d", &ls->addr_text, ev->available); do { - socklen = NGX_SOCKADDRLEN; + socklen = sizeof(ngx_sockaddr_t); #if (NGX_HAVE_ACCEPT4) if (use_accept4) { - s = accept4(lc->fd, (struct sockaddr *) sa, &socklen, - SOCK_NONBLOCK); + s = accept4(lc->fd, &sa.sockaddr, &socklen, SOCK_NONBLOCK); } else { - s = accept(lc->fd, (struct sockaddr *) sa, &socklen); + s = accept(lc->fd, &sa.sockaddr, &socklen); } #else - s = accept(lc->fd, (struct sockaddr *) sa, &socklen); + s = accept(lc->fd, &sa.sockaddr, &socklen); #endif if (s == (ngx_socket_t) -1) { @@ -171,7 +170,7 @@ ngx_event_accept(ngx_event_t *ev) return; } - ngx_memcpy(c->sockaddr, sa, socklen); + ngx_memcpy(c->sockaddr, &sa, socklen); log = ngx_palloc(c->pool, sizeof(ngx_log_t)); if (log == NULL) { @@ -217,8 +216,6 @@ ngx_event_accept(ngx_event_t *ev) c->local_sockaddr = ls->sockaddr; c->local_socklen = ls->socklen; - c->unexpected_eof = 1; - #if (NGX_HAVE_UNIX_DOMAIN) if (c->sockaddr->sa_family == AF_UNIX) { c->tcp_nopush = NGX_TCP_NOPUSH_DISABLED; @@ -330,10 +327,10 @@ ngx_event_recvmsg(ngx_event_t *ev) ngx_event_t *rev, *wev; struct iovec iov[1]; struct msghdr msg; + ngx_sockaddr_t sa; ngx_listening_t *ls; ngx_event_conf_t *ecf; ngx_connection_t *c, *lc; - u_char sa[NGX_SOCKADDRLEN]; static u_char buffer[65535]; #if (NGX_HAVE_MSGHDR_MSG_CONTROL) @@ -378,7 +375,7 @@ ngx_event_recvmsg(ngx_event_t *ev) iov[0].iov_len = sizeof(buffer); msg.msg_name = &sa; - msg.msg_namelen = sizeof(sa); + msg.msg_namelen = sizeof(ngx_sockaddr_t); msg.msg_iov = iov; msg.msg_iovlen = 1; diff --git a/src/event/ngx_event_connect.c b/src/event/ngx_event_connect.c index 8aca862..30cb59a 100644 --- a/src/event/ngx_event_connect.c +++ b/src/event/ngx_event_connect.c @@ -11,10 +11,19 @@ #include +#if (NGX_HAVE_TRANSPARENT_PROXY) +static ngx_int_t ngx_event_connect_set_transparent(ngx_peer_connection_t *pc, + ngx_socket_t s); +#endif + + ngx_int_t ngx_event_connect_peer(ngx_peer_connection_t *pc) { int rc, type; +#if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT || NGX_LINUX) + in_port_t port; +#endif ngx_int_t event; ngx_err_t err; ngx_uint_t level; @@ -72,6 +81,62 @@ ngx_event_connect_peer(ngx_peer_connection_t *pc) } if (pc->local) { + +#if (NGX_HAVE_TRANSPARENT_PROXY) + if (pc->transparent) { + if (ngx_event_connect_set_transparent(pc, s) != NGX_OK) { + goto failed; + } + } +#endif + +#if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT || NGX_LINUX) + port = ngx_inet_get_port(pc->sockaddr); +#endif + +#if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT) + + if (pc->sockaddr->sa_family != AF_UNIX && port == 0) { + static int bind_address_no_port = 1; + + if (bind_address_no_port) { + if (setsockopt(s, IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT, + (const void *) &bind_address_no_port, + sizeof(int)) == -1) + { + err = ngx_socket_errno; + + if (err != NGX_EOPNOTSUPP && err != NGX_ENOPROTOOPT) { + ngx_log_error(NGX_LOG_ALERT, pc->log, err, + "setsockopt(IP_BIND_ADDRESS_NO_PORT) " + "failed, ignored"); + + } else { + bind_address_no_port = 0; + } + } + } + } + +#endif + +#if (NGX_LINUX) + + if (pc->type == SOCK_DGRAM && port != 0) { + int reuse_addr = 1; + + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, + (const void *) &reuse_addr, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, + "setsockopt(SO_REUSEADDR) failed"); + goto failed; + } + } + +#endif + if (bind(s, pc->local->sockaddr, pc->local->socklen) == -1) { ngx_log_error(NGX_LOG_CRIT, pc->log, ngx_socket_errno, "bind(%V) failed", &pc->local->name); @@ -249,6 +314,94 @@ failed: } +#if (NGX_HAVE_TRANSPARENT_PROXY) + +static ngx_int_t +ngx_event_connect_set_transparent(ngx_peer_connection_t *pc, ngx_socket_t s) +{ + int value; + + value = 1; + +#if defined(SO_BINDANY) + + if (setsockopt(s, SOL_SOCKET, SO_BINDANY, + (const void *) &value, sizeof(int)) == -1) + { + ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, + "setsockopt(SO_BINDANY) failed"); + return NGX_ERROR; + } + +#else + + switch (pc->local->sockaddr->sa_family) { + + case AF_INET: + +#if defined(IP_TRANSPARENT) + + if (setsockopt(s, IPPROTO_IP, IP_TRANSPARENT, + (const void *) &value, sizeof(int)) == -1) + { + ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, + "setsockopt(IP_TRANSPARENT) failed"); + return NGX_ERROR; + } + +#elif defined(IP_BINDANY) + + if (setsockopt(s, IPPROTO_IP, IP_BINDANY, + (const void *) &value, sizeof(int)) == -1) + { + ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, + "setsockopt(IP_BINDANY) failed"); + return NGX_ERROR; + } + +#endif + + break; + +#if (NGX_HAVE_INET6) + + case AF_INET6: + +#if defined(IPV6_TRANSPARENT) + + if (setsockopt(s, IPPROTO_IPV6, IPV6_TRANSPARENT, + (const void *) &value, sizeof(int)) == -1) + { + ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, + "setsockopt(IPV6_TRANSPARENT) failed"); + return NGX_ERROR; + } + +#elif defined(IPV6_BINDANY) + + if (setsockopt(s, IPPROTO_IPV6, IPV6_BINDANY, + (const void *) &value, sizeof(int)) == -1) + { + ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, + "setsockopt(IPV6_BINDANY) failed"); + return NGX_ERROR; + } + +#endif + break; + +#endif /* NGX_HAVE_INET6 */ + + } + +#endif /* SO_BINDANY */ + + return NGX_OK; +} + +#endif + + ngx_int_t ngx_event_get_peer(ngx_peer_connection_t *pc, void *data) { diff --git a/src/event/ngx_event_connect.h b/src/event/ngx_event_connect.h index 1bacf82..10b72a1 100644 --- a/src/event/ngx_event_connect.h +++ b/src/event/ngx_event_connect.h @@ -61,6 +61,9 @@ struct ngx_peer_connection_s { ngx_log_t *log; unsigned cached:1; +#if (NGX_HAVE_TRANSPARENT_PROXY) + unsigned transparent:1; +#endif /* ngx_connection_log_error_e */ unsigned log_error:2; diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index de10296..bb9a900 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -105,6 +105,7 @@ int ngx_ssl_server_conf_index; int ngx_ssl_session_cache_index; int ngx_ssl_session_ticket_keys_index; int ngx_ssl_certificate_index; +int ngx_ssl_next_certificate_index; int ngx_ssl_stapling_index; @@ -187,11 +188,17 @@ ngx_ssl_init(ngx_log_t *log) return NGX_ERROR; } - ngx_ssl_stapling_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, - NULL); + ngx_ssl_next_certificate_index = X509_get_ex_new_index(0, NULL, NULL, NULL, + NULL); + if (ngx_ssl_next_certificate_index == -1) { + ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed"); + return NGX_ERROR; + } + + ngx_ssl_stapling_index = X509_get_ex_new_index(0, NULL, NULL, NULL, NULL); + if (ngx_ssl_stapling_index == -1) { - ngx_ssl_error(NGX_LOG_ALERT, log, 0, - "SSL_CTX_get_ex_new_index() failed"); + ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed"); return NGX_ERROR; } @@ -215,6 +222,12 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data) return NGX_ERROR; } + if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, NULL) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_set_ex_data() failed"); + return NGX_ERROR; + } + ssl->buffer_size = NGX_SSL_BUFSIZE; /* client side options */ @@ -308,6 +321,29 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data) } +ngx_int_t +ngx_ssl_certificates(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *certs, + ngx_array_t *keys, ngx_array_t *passwords) +{ + ngx_str_t *cert, *key; + ngx_uint_t i; + + cert = certs->elts; + key = keys->elts; + + for (i = 0; i < certs->nelts; i++) { + + if (ngx_ssl_certificate(cf, ssl, &cert[i], &key[i], passwords) + != NGX_OK) + { + return NGX_ERROR; + } + } + + return NGX_OK; +} + + ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords) @@ -351,6 +387,16 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, return NGX_ERROR; } + if (X509_set_ex_data(x509, ngx_ssl_next_certificate_index, + SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index)) + == 0) + { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed"); + X509_free(x509); + BIO_free(bio); + return NGX_ERROR; + } + if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, x509) == 0) { @@ -361,8 +407,6 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, return NGX_ERROR; } - X509_free(x509); - /* read rest of the chain */ for ( ;; ) { @@ -387,6 +431,24 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, return NGX_ERROR; } +#ifdef SSL_CTRL_CHAIN_CERT + + /* + * SSL_CTX_add0_chain_cert() is needed to add chain to + * a particular certificate when multiple certificates are used; + * only available in OpenSSL 1.0.2+ + */ + + if (SSL_CTX_add0_chain_cert(ssl->ctx, x509) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_add0_chain_cert(\"%s\") failed", + cert->data); + X509_free(x509); + BIO_free(bio); + return NGX_ERROR; + } + +#else if (SSL_CTX_add_extra_chain_cert(ssl->ctx, x509) == 0) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "SSL_CTX_add_extra_chain_cert(\"%s\") failed", @@ -395,6 +457,7 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, BIO_free(bio); return NGX_ERROR; } +#endif } BIO_free(bio); @@ -528,6 +591,30 @@ ngx_ssl_password_callback(char *buf, int size, int rwflag, void *userdata) } +ngx_int_t +ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers, + ngx_uint_t prefer_server_ciphers) +{ + if (SSL_CTX_set_cipher_list(ssl->ctx, (char *) ciphers->data) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_set_cipher_list(\"%V\") failed", + ciphers); + return NGX_ERROR; + } + + if (prefer_server_ciphers) { + SSL_CTX_set_options(ssl->ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); + } + +#if (OPENSSL_VERSION_NUMBER < 0x10100001L && !defined LIBRESSL_VERSION_NUMBER) + /* a temporary 512-bit RSA key is required for export versions of MSIE */ + SSL_CTX_set_tmp_rsa_callback(ssl->ctx, ngx_ssl_rsa512_key_callback); +#endif + + return NGX_OK; +} + + ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_int_t depth) @@ -918,52 +1005,7 @@ ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file) DH *dh; BIO *bio; - /* - * -----BEGIN DH PARAMETERS----- - * MIGHAoGBALu8LcrYRnSQfEP89YDpz9vZWKP1aLQtSwju1OsPs1BMbAMCducQgAxc - * y7qokiYUxb7spWWl/fHSh6K8BJvmd4Bg6RqSp1fjBI9osHb302zI8pul34HcLKcl - * 7OZicMyaUDXYzs7vnqAnSmOrHlj6/UmI0PZdFGdX2gcd8EXP4WubAgEC - * -----END DH PARAMETERS----- - */ - - static unsigned char dh1024_p[] = { - 0xBB, 0xBC, 0x2D, 0xCA, 0xD8, 0x46, 0x74, 0x90, 0x7C, 0x43, 0xFC, 0xF5, - 0x80, 0xE9, 0xCF, 0xDB, 0xD9, 0x58, 0xA3, 0xF5, 0x68, 0xB4, 0x2D, 0x4B, - 0x08, 0xEE, 0xD4, 0xEB, 0x0F, 0xB3, 0x50, 0x4C, 0x6C, 0x03, 0x02, 0x76, - 0xE7, 0x10, 0x80, 0x0C, 0x5C, 0xCB, 0xBA, 0xA8, 0x92, 0x26, 0x14, 0xC5, - 0xBE, 0xEC, 0xA5, 0x65, 0xA5, 0xFD, 0xF1, 0xD2, 0x87, 0xA2, 0xBC, 0x04, - 0x9B, 0xE6, 0x77, 0x80, 0x60, 0xE9, 0x1A, 0x92, 0xA7, 0x57, 0xE3, 0x04, - 0x8F, 0x68, 0xB0, 0x76, 0xF7, 0xD3, 0x6C, 0xC8, 0xF2, 0x9B, 0xA5, 0xDF, - 0x81, 0xDC, 0x2C, 0xA7, 0x25, 0xEC, 0xE6, 0x62, 0x70, 0xCC, 0x9A, 0x50, - 0x35, 0xD8, 0xCE, 0xCE, 0xEF, 0x9E, 0xA0, 0x27, 0x4A, 0x63, 0xAB, 0x1E, - 0x58, 0xFA, 0xFD, 0x49, 0x88, 0xD0, 0xF6, 0x5D, 0x14, 0x67, 0x57, 0xDA, - 0x07, 0x1D, 0xF0, 0x45, 0xCF, 0xE1, 0x6B, 0x9B - }; - - static unsigned char dh1024_g[] = { 0x02 }; - - if (file->len == 0) { - - dh = DH_new(); - if (dh == NULL) { - ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "DH_new() failed"); - return NGX_ERROR; - } - - dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL); - dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL); - - if (dh->p == NULL || dh->g == NULL) { - ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "BN_bin2bn() failed"); - DH_free(dh); - return NGX_ERROR; - } - - SSL_CTX_set_tmp_dh(ssl->ctx, dh); - - DH_free(dh); - return NGX_OK; } @@ -1000,27 +1042,69 @@ ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name) { #if OPENSSL_VERSION_NUMBER >= 0x0090800fL #ifndef OPENSSL_NO_ECDH - int nid; - EC_KEY *ecdh; /* * Elliptic-Curve Diffie-Hellman parameters are either "named curves" * from RFC 4492 section 5.1.1, or explicitly described curves over - * binary fields. OpenSSL only supports the "named curves", which provide + * binary fields. OpenSSL only supports the "named curves", which provide * maximum interoperability. */ - nid = OBJ_sn2nid((const char *) name->data); +#ifdef SSL_CTRL_SET_CURVES_LIST + + /* + * OpenSSL 1.0.2+ allows configuring a curve list instead of a single + * curve previously supported. By default an internal list is used, + * with prime256v1 being preferred by server in OpenSSL 1.0.2b+ + * and X25519 in OpenSSL 1.1.0+. + * + * By default a curve preferred by the client will be used for + * key exchange. The SSL_OP_CIPHER_SERVER_PREFERENCE option can + * be used to prefer server curves instead, similar to what it + * does for ciphers. + */ + + SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_ECDH_USE); + +#if SSL_CTRL_SET_ECDH_AUTO + /* not needed in OpenSSL 1.1.0+ */ + SSL_CTX_set_ecdh_auto(ssl->ctx, 1); +#endif + + if (ngx_strcmp(name->data, "auto") == 0) { + return NGX_OK; + } + + if (SSL_CTX_set1_curves_list(ssl->ctx, (char *) name->data) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_set1_curves_list(\"%s\") failed", name->data); + return NGX_ERROR; + } + +#else + + int nid; + char *curve; + EC_KEY *ecdh; + + if (ngx_strcmp(name->data, "auto") == 0) { + curve = "prime256v1"; + + } else { + curve = (char *) name->data; + } + + nid = OBJ_sn2nid(curve); if (nid == 0) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, - "Unknown curve name \"%s\"", name->data); + "OBJ_sn2nid(\"%s\") failed: unknown curve", curve); return NGX_ERROR; } ecdh = EC_KEY_new_by_curve_name(nid); if (ecdh == NULL) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, - "Unable to create curve \"%s\"", name->data); + "EC_KEY_new_by_curve_name(\"%s\") failed", curve); return NGX_ERROR; } @@ -1030,6 +1114,7 @@ ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name) EC_KEY_free(ecdh); #endif +#endif #endif return NGX_OK; @@ -2102,7 +2187,7 @@ ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx, * session reuse (see SSL_SESS_CACHE_OFF above), then * Outlook Express fails to upload a sent email to * the Sent Items folder on the IMAP server via a separate IMAP - * connection in the background. Therefore we have a special + * connection in the background. Therefore we have a special * mode (SSL_SESS_CACHE_SERVER|SSL_SESS_CACHE_NO_INTERNAL_STORE) * where the server pretends that it supports session reuse, * but it does not actually store any session. @@ -2164,7 +2249,7 @@ ngx_ssl_session_id_context(ngx_ssl_t *ssl, ngx_str_t *sess_ctx) /* * Session ID context is set based on the string provided, - * the server certificate, and the client CA list. + * the server certificates, and the client CA list. */ md = EVP_MD_CTX_create(); @@ -2184,18 +2269,21 @@ ngx_ssl_session_id_context(ngx_ssl_t *ssl, ngx_str_t *sess_ctx) goto failed; } - cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index); + for (cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index); + cert; + cert = X509_get_ex_data(cert, ngx_ssl_next_certificate_index)) + { + if (X509_digest(cert, EVP_sha1(), buf, &len) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "X509_digest() failed"); + goto failed; + } - if (X509_digest(cert, EVP_sha1(), buf, &len) == 0) { - ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, - "X509_digest() failed"); - goto failed; - } - - if (EVP_DigestUpdate(md, buf, len) == 0) { - ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, - "EVP_DigestUpdate() failed"); - goto failed; + if (EVP_DigestUpdate(md, buf, len) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "EVP_DigestUpdate() failed"); + goto failed; + } } list = SSL_CTX_get_client_CA_list(ssl->ctx); @@ -2951,6 +3039,16 @@ ngx_ssl_cleanup_ctx(void *data) { ngx_ssl_t *ssl = data; + X509 *cert, *next; + + cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index); + + while (cert) { + next = X509_get_ex_data(cert, ngx_ssl_next_certificate_index); + X509_free(cert); + cert = next; + } + SSL_CTX_free(ssl->ctx); } @@ -3526,7 +3624,7 @@ ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) value = cf->args->elts; - engine = ENGINE_by_id((const char *) value[1].data); + engine = ENGINE_by_id((char *) value[1].data); if (engine == NULL) { ngx_ssl_error(NGX_LOG_WARN, cf->log, 0, diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index 09654db..3367d10 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -140,8 +140,12 @@ typedef struct { ngx_int_t ngx_ssl_init(ngx_log_t *log); ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data); +ngx_int_t ngx_ssl_certificates(ngx_conf_t *cf, ngx_ssl_t *ssl, + ngx_array_t *certs, ngx_array_t *keys, ngx_array_t *passwords); ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords); +ngx_int_t ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers, + ngx_uint_t prefer_server_ciphers); ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_int_t depth); ngx_int_t ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, @@ -227,6 +231,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_certificate_index; +extern int ngx_ssl_next_certificate_index; extern int ngx_ssl_stapling_index; diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c index 5322b1b..cce8e9e 100644 --- a/src/event/ngx_event_openssl_stapling.c +++ b/src/event/ngx_event_openssl_stapling.c @@ -83,11 +83,14 @@ struct ngx_ssl_ocsp_ctx_s { }; +static ngx_int_t ngx_ssl_stapling_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, + X509 *cert, ngx_str_t *file, ngx_str_t *responder, ngx_uint_t verify); static ngx_int_t ngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl, - ngx_str_t *file); -static ngx_int_t ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl); + ngx_ssl_stapling_t *staple, ngx_str_t *file); +static ngx_int_t ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl, + ngx_ssl_stapling_t *staple); static ngx_int_t ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl, - ngx_str_t *responder); + ngx_ssl_stapling_t *staple, ngx_str_t *responder); static int ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data); @@ -121,9 +124,32 @@ ngx_int_t ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file, ngx_str_t *responder, ngx_uint_t verify) { - ngx_int_t rc; - ngx_pool_cleanup_t *cln; - ngx_ssl_stapling_t *staple; + X509 *cert; + + for (cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index); + cert; + cert = X509_get_ex_data(cert, ngx_ssl_next_certificate_index)) + { + if (ngx_ssl_stapling_certificate(cf, ssl, cert, file, responder, verify) + != NGX_OK) + { + return NGX_ERROR; + } + } + + SSL_CTX_set_tlsext_status_cb(ssl->ctx, ngx_ssl_certificate_status_callback); + + return NGX_OK; +} + + +static ngx_int_t +ngx_ssl_stapling_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, X509 *cert, + ngx_str_t *file, ngx_str_t *responder, ngx_uint_t verify) +{ + ngx_int_t rc; + ngx_pool_cleanup_t *cln; + ngx_ssl_stapling_t *staple; staple = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_stapling_t)); if (staple == NULL) { @@ -138,29 +164,27 @@ ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file, cln->handler = ngx_ssl_stapling_cleanup; cln->data = staple; - if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_stapling_index, staple) - == 0) - { - ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, - "SSL_CTX_set_ex_data() failed"); + if (X509_set_ex_data(cert, ngx_ssl_stapling_index, staple) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed"); return NGX_ERROR; } staple->ssl_ctx = ssl->ctx; staple->timeout = 60000; staple->verify = verify; + staple->cert = cert; if (file->len) { /* use OCSP response from the file */ - if (ngx_ssl_stapling_file(cf, ssl, file) != NGX_OK) { + if (ngx_ssl_stapling_file(cf, ssl, staple, file) != NGX_OK) { return NGX_ERROR; } - goto done; + return NGX_OK; } - rc = ngx_ssl_stapling_issuer(cf, ssl); + rc = ngx_ssl_stapling_issuer(cf, ssl, staple); if (rc == NGX_DECLINED) { return NGX_OK; @@ -170,7 +194,7 @@ ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file, return NGX_ERROR; } - rc = ngx_ssl_stapling_responder(cf, ssl, responder); + rc = ngx_ssl_stapling_responder(cf, ssl, staple, responder); if (rc == NGX_DECLINED) { return NGX_OK; @@ -180,25 +204,18 @@ ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file, return NGX_ERROR; } -done: - - SSL_CTX_set_tlsext_status_cb(ssl->ctx, ngx_ssl_certificate_status_callback); - SSL_CTX_set_tlsext_status_arg(ssl->ctx, staple); - return NGX_OK; } static ngx_int_t -ngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file) +ngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl, + ngx_ssl_stapling_t *staple, ngx_str_t *file) { - BIO *bio; - int len; - u_char *p, *buf; - OCSP_RESPONSE *response; - ngx_ssl_stapling_t *staple; - - staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index); + BIO *bio; + int len; + u_char *p, *buf; + OCSP_RESPONSE *response; if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) { return NGX_ERROR; @@ -259,19 +276,24 @@ failed: static ngx_int_t -ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl) +ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl, + ngx_ssl_stapling_t *staple) { - int i, n, rc; - X509 *cert, *issuer; - X509_STORE *store; - X509_STORE_CTX *store_ctx; - STACK_OF(X509) *chain; - ngx_ssl_stapling_t *staple; + int i, n, rc; + X509 *cert, *issuer; + X509_STORE *store; + X509_STORE_CTX *store_ctx; + STACK_OF(X509) *chain; - staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index); - cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index); + cert = staple->cert; -#if OPENSSL_VERSION_NUMBER >= 0x10001000L +#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; @@ -294,7 +316,6 @@ ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl) ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0, "SSL get issuer: found %p in extra certs", issuer); - staple->cert = cert; staple->issuer = issuer; return NGX_OK; @@ -343,7 +364,6 @@ ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl) ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0, "SSL get issuer: found %p in cert store", issuer); - staple->cert = cert; staple->issuer = issuer; return NGX_OK; @@ -351,15 +371,13 @@ ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl) static ngx_int_t -ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder) +ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl, + ngx_ssl_stapling_t *staple, ngx_str_t *responder) { ngx_url_t u; char *s; - ngx_ssl_stapling_t *staple; STACK_OF(OPENSSL_STRING) *aia; - staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index); - if (responder->len == 0) { /* extract OCSP responder URL from certificate */ @@ -443,12 +461,17 @@ ngx_int_t ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_resolver_t *resolver, ngx_msec_t resolver_timeout) { + X509 *cert; ngx_ssl_stapling_t *staple; - staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index); - - staple->resolver = resolver; - staple->resolver_timeout = resolver_timeout; + for (cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index); + cert; + cert = X509_get_ex_data(cert, ngx_ssl_next_certificate_index)) + { + staple = X509_get_ex_data(cert, ngx_ssl_stapling_index); + staple->resolver = resolver; + staple->resolver_timeout = resolver_timeout; + } return NGX_OK; } @@ -458,6 +481,7 @@ static int ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data) { int rc; + X509 *cert; u_char *p; ngx_connection_t *c; ngx_ssl_stapling_t *staple; @@ -467,9 +491,15 @@ ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data) ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL certificate status callback"); - staple = data; rc = SSL_TLSEXT_ERR_NOACK; + cert = SSL_get_certificate(ssl_conn); + staple = X509_get_ex_data(cert, ngx_ssl_stapling_index); + + if (staple == NULL) { + return rc; + } + if (staple->staple.len && staple->valid >= ngx_time()) { @@ -597,7 +627,13 @@ ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx) goto error; } -#if OPENSSL_VERSION_NUMBER >= 0x10001000L +#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; @@ -884,7 +920,6 @@ ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve) u_char *p; size_t len; - in_port_t port; socklen_t socklen; ngx_uint_t i; struct sockaddr *sockaddr; @@ -926,8 +961,6 @@ ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve) goto failed; } - port = htons(ctx->port); - for (i = 0; i < resolve->naddrs; i++) { socklen = resolve->addrs[i].socklen; @@ -938,16 +971,7 @@ ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve) } ngx_memcpy(sockaddr, resolve->addrs[i].sockaddr, socklen); - - switch (sockaddr->sa_family) { -#if (NGX_HAVE_INET6) - case AF_INET6: - ((struct sockaddr_in6 *) sockaddr)->sin6_port = port; - break; -#endif - default: /* AF_INET */ - ((struct sockaddr_in *) sockaddr)->sin_port = port; - } + ngx_inet_set_port(sockaddr, ctx->port); ctx->addrs[i].sockaddr = sockaddr; ctx->addrs[i].socklen = socklen; diff --git a/src/http/modules/ngx_http_dav_module.c b/src/http/modules/ngx_http_dav_module.c index 3600265..012a0fb 100644 --- a/src/http/modules/ngx_http_dav_module.c +++ b/src/http/modules/ngx_http_dav_module.c @@ -161,6 +161,12 @@ ngx_http_dav_handler(ngx_http_request_t *r) return NGX_HTTP_CONFLICT; } + if (r->headers_in.content_range) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "PUT with range is unsupported"); + return NGX_HTTP_NOT_IMPLEMENTED; + } + r->request_body_in_file_only = 1; r->request_body_in_persistent_file = 1; r->request_body_in_clean_file = 1; diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c index 2d288ce..62502b0 100644 --- a/src/http/modules/ngx_http_fastcgi_module.c +++ b/src/http/modules/ngx_http_fastcgi_module.c @@ -279,7 +279,7 @@ static ngx_command_t ngx_http_fastcgi_commands[] = { NULL }, { ngx_string("fastcgi_bind"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, ngx_http_upstream_bind_set_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_fastcgi_loc_conf_t, upstream.local), diff --git a/src/http/modules/ngx_http_log_module.c b/src/http/modules/ngx_http_log_module.c index df9424f..c42fb08 100644 --- a/src/http/modules/ngx_http_log_module.c +++ b/src/http/modules/ngx_http_log_module.c @@ -1000,7 +1000,7 @@ ngx_http_log_escape(u_char *dst, u_char *src, size_t size) n = 0; while (size) { - if (escape[*src >> 5] & (1 << (*src & 0x1f))) { + if (escape[*src >> 5] & (1U << (*src & 0x1f))) { n++; } src++; @@ -1011,7 +1011,7 @@ ngx_http_log_escape(u_char *dst, u_char *src, size_t size) } while (size) { - if (escape[*src >> 5] & (1 << (*src & 0x1f))) { + if (escape[*src >> 5] & (1U << (*src & 0x1f))) { *dst++ = '\\'; *dst++ = 'x'; *dst++ = hex[*src >> 4]; diff --git a/src/http/modules/ngx_http_map_module.c b/src/http/modules/ngx_http_map_module.c index 091ff09..732aa5d 100644 --- a/src/http/modules/ngx_http_map_module.c +++ b/src/http/modules/ngx_http_map_module.c @@ -20,7 +20,6 @@ typedef struct { ngx_hash_keys_arrays_t keys; ngx_array_t *values_hash; - ngx_array_t var_values; #if (NGX_PCRE) ngx_array_t regexes; #endif @@ -110,7 +109,8 @@ ngx_http_map_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, { ngx_http_map_ctx_t *map = (ngx_http_map_ctx_t *) data; - ngx_str_t val; + ngx_str_t val, str; + ngx_http_complex_value_t *cv; ngx_http_variable_value_t *value; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -131,14 +131,21 @@ ngx_http_map_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, } if (!value->valid) { - value = ngx_http_get_flushed_variable(r, (uintptr_t) value->data); + cv = (ngx_http_complex_value_t *) value->data; - if (value == NULL || value->not_found) { - value = &ngx_http_variable_null_value; + if (ngx_http_complex_value(r, cv, &str) != NGX_OK) { + return NGX_ERROR; } - } - *v = *value; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->len = str.len; + v->data = str.data; + + } else { + *v = *value; + } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http map: \"%V\" \"%v\"", &val, v); @@ -246,14 +253,6 @@ ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_ERROR; } - if (ngx_array_init(&ctx.var_values, cf->pool, 2, - sizeof(ngx_http_variable_value_t)) - != NGX_OK) - { - ngx_destroy_pool(pool); - return NGX_CONF_ERROR; - } - #if (NGX_PCRE) if (ngx_array_init(&ctx.regexes, cf->pool, 2, sizeof(ngx_http_map_regex_t)) != NGX_OK) @@ -375,11 +374,15 @@ ngx_http_map_cmp_dns_wildcards(const void *one, const void *two) static char * ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) { - ngx_int_t rv, index; - ngx_str_t *value, name; - ngx_uint_t i, key; - ngx_http_map_conf_ctx_t *ctx; - ngx_http_variable_value_t *var, **vp; + u_char *data; + size_t len; + ngx_int_t rv; + ngx_str_t *value, v; + ngx_uint_t i, key; + ngx_http_map_conf_ctx_t *ctx; + ngx_http_complex_value_t cv, *cvp; + ngx_http_variable_value_t *var, **vp; + ngx_http_compile_complex_value_t ccv; ctx = cf->ctx; @@ -401,39 +404,6 @@ ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) return ngx_conf_include(cf, dummy, conf); } - if (value[1].data[0] == '$') { - name = value[1]; - name.len--; - name.data++; - - index = ngx_http_get_variable_index(ctx->cf, &name); - if (index == NGX_ERROR) { - return NGX_CONF_ERROR; - } - - var = ctx->var_values.elts; - - for (i = 0; i < ctx->var_values.nelts; i++) { - if (index == (intptr_t) var[i].data) { - var = &var[i]; - goto found; - } - } - - var = ngx_array_push(&ctx->var_values); - if (var == NULL) { - return NGX_CONF_ERROR; - } - - var->valid = 0; - var->no_cacheable = 0; - var->not_found = 0; - var->len = 0; - var->data = (u_char *) (intptr_t) index; - - goto found; - } - key = 0; for (i = 0; i < value[1].len; i++) { @@ -446,11 +416,22 @@ ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) if (vp) { for (i = 0; i < ctx->values_hash[key].nelts; i++) { - if (value[1].len != (size_t) vp[i]->len) { + + if (vp[i]->valid) { + data = vp[i]->data; + len = vp[i]->len; + + } else { + cvp = (ngx_http_complex_value_t *) vp[i]->data; + data = cvp->value.data; + len = cvp->value.len; + } + + if (value[1].len != len) { continue; } - if (ngx_strncmp(value[1].data, vp[i]->data, value[1].len) == 0) { + if (ngx_strncmp(value[1].data, data, len) == 0) { var = vp[i]; goto found; } @@ -470,13 +451,40 @@ ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) return NGX_CONF_ERROR; } - var->len = value[1].len; - var->data = ngx_pstrdup(ctx->keys.pool, &value[1]); - if (var->data == NULL) { + v.len = value[1].len; + v.data = ngx_pstrdup(ctx->keys.pool, &value[1]); + if (v.data == NULL) { return NGX_CONF_ERROR; } - var->valid = 1; + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = ctx->cf; + ccv.value = &v; + ccv.complex_value = &cv; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (cv.lengths != NULL) { + cvp = ngx_palloc(ctx->keys.pool, sizeof(ngx_http_complex_value_t)); + if (cvp == NULL) { + return NGX_CONF_ERROR; + } + + *cvp = cv; + + var->len = 0; + var->data = (u_char *) cvp; + var->valid = 0; + + } else { + var->len = v.len; + var->data = v.data; + var->valid = 1; + } + var->no_cacheable = 0; var->not_found = 0; diff --git a/src/http/modules/ngx_http_memcached_module.c b/src/http/modules/ngx_http_memcached_module.c index d31996a..69f28fa 100644 --- a/src/http/modules/ngx_http_memcached_module.c +++ b/src/http/modules/ngx_http_memcached_module.c @@ -61,7 +61,7 @@ static ngx_command_t ngx_http_memcached_commands[] = { NULL }, { ngx_string("memcached_bind"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, ngx_http_upstream_bind_set_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_memcached_loc_conf_t, upstream.local), diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c index c24ef17..4f49a52 100644 --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -316,7 +316,7 @@ static ngx_command_t ngx_http_proxy_commands[] = { NULL }, { ngx_string("proxy_bind"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, ngx_http_upstream_bind_set_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_proxy_loc_conf_t, upstream.local), @@ -4323,13 +4323,9 @@ ngx_http_proxy_set_ssl(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *plcf) } } - if (SSL_CTX_set_cipher_list(plcf->upstream.ssl->ctx, - (const char *) plcf->ssl_ciphers.data) - == 0) + if (ngx_ssl_ciphers(cf, plcf->upstream.ssl, &plcf->ssl_ciphers, 0) + != NGX_OK) { - ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, - "SSL_CTX_set_cipher_list(\"%V\") failed", - &plcf->ssl_ciphers); return NGX_ERROR; } diff --git a/src/http/modules/ngx_http_realip_module.c b/src/http/modules/ngx_http_realip_module.c index b7befe6..490a53d 100644 --- a/src/http/modules/ngx_http_realip_module.c +++ b/src/http/modules/ngx_http_realip_module.c @@ -45,10 +45,14 @@ static char *ngx_http_realip_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); static ngx_int_t ngx_http_realip_add_variables(ngx_conf_t *cf); static ngx_int_t ngx_http_realip_init(ngx_conf_t *cf); +static ngx_http_realip_ctx_t *ngx_http_realip_get_module_ctx( + ngx_http_request_t *r); static ngx_int_t ngx_http_realip_remote_addr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_realip_remote_port_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_command_t ngx_http_realip_commands[] = { @@ -115,6 +119,9 @@ static ngx_http_variable_t ngx_http_realip_vars[] = { { ngx_string("realip_remote_addr"), NULL, ngx_http_realip_remote_addr_variable, 0, 0, 0 }, + { ngx_string("realip_remote_port"), NULL, + ngx_http_realip_remote_port_variable, 0, 0, 0 }, + { ngx_null_string, NULL, NULL, 0, 0, 0 } }; @@ -230,6 +237,10 @@ found: rlcf->recursive) != NGX_DECLINED) { + if (rlcf->type == NGX_HTTP_REALIP_PROXY) { + ngx_inet_set_port(addr.sockaddr, c->proxy_protocol_port); + } + return ngx_http_realip_set_addr(r, &addr); } @@ -358,6 +369,10 @@ ngx_http_realip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_str_t *value; + if (rlcf->type != NGX_CONF_UNSET_UINT) { + return "is duplicate"; + } + value = cf->args->elts; if (ngx_strcmp(value[1].data, "X-Real-IP") == 0) { @@ -475,11 +490,9 @@ ngx_http_realip_init(ngx_conf_t *cf) } -static ngx_int_t -ngx_http_realip_remote_addr_variable(ngx_http_request_t *r, - ngx_http_variable_value_t *v, uintptr_t data) +static ngx_http_realip_ctx_t * +ngx_http_realip_get_module_ctx(ngx_http_request_t *r) { - ngx_str_t *addr_text; ngx_pool_cleanup_t *cln; ngx_http_realip_ctx_t *ctx; @@ -500,6 +513,19 @@ ngx_http_realip_remote_addr_variable(ngx_http_request_t *r, } } + return ctx; +} + + +static ngx_int_t +ngx_http_realip_remote_addr_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_str_t *addr_text; + ngx_http_realip_ctx_t *ctx; + + ctx = ngx_http_realip_get_module_ctx(r); + addr_text = ctx ? &ctx->addr_text : &r->connection->addr_text; v->len = addr_text->len; @@ -510,3 +536,35 @@ ngx_http_realip_remote_addr_variable(ngx_http_request_t *r, return NGX_OK; } + + +static ngx_int_t +ngx_http_realip_remote_port_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_uint_t port; + struct sockaddr *sa; + ngx_http_realip_ctx_t *ctx; + + ctx = ngx_http_realip_get_module_ctx(r); + + sa = ctx ? ctx->sockaddr : r->connection->sockaddr; + + v->len = 0; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1); + if (v->data == NULL) { + return NGX_ERROR; + } + + port = ngx_inet_get_port(sa); + + if (port > 0 && port < 65536) { + v->len = ngx_sprintf(v->data, "%ui", port) - v->data; + } + + return NGX_OK; +} diff --git a/src/http/modules/ngx_http_scgi_module.c b/src/http/modules/ngx_http_scgi_module.c index f09617e..36656ec 100644 --- a/src/http/modules/ngx_http_scgi_module.c +++ b/src/http/modules/ngx_http_scgi_module.c @@ -136,7 +136,7 @@ static ngx_command_t ngx_http_scgi_commands[] = { NULL }, { ngx_string("scgi_bind"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, ngx_http_upstream_bind_set_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_scgi_loc_conf_t, upstream.local), diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index 6a4108c..d685ae9 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -15,7 +15,7 @@ typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c, #define NGX_DEFAULT_CIPHERS "HIGH:!aNULL:!MD5" -#define NGX_DEFAULT_ECDH_CURVE "prime256v1" +#define NGX_DEFAULT_ECDH_CURVE "auto" #define NGX_HTTP_NPN_ADVERTISE "\x08http/1.1" @@ -81,16 +81,16 @@ static ngx_command_t ngx_http_ssl_commands[] = { { ngx_string("ssl_certificate"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_str_slot, + ngx_conf_set_str_array_slot, NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_ssl_srv_conf_t, certificate), + offsetof(ngx_http_ssl_srv_conf_t, certificates), NULL }, { ngx_string("ssl_certificate_key"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_str_slot, + ngx_conf_set_str_array_slot, NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_ssl_srv_conf_t, certificate_key), + offsetof(ngx_http_ssl_srv_conf_t, certificate_keys), NULL }, { ngx_string("ssl_password_file"), @@ -508,8 +508,6 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf) * set by ngx_pcalloc(): * * sscf->protocols = 0; - * sscf->certificate = { 0, NULL }; - * sscf->certificate_key = { 0, NULL }; * sscf->dhparam = { 0, NULL }; * sscf->ecdh_curve = { 0, NULL }; * sscf->client_certificate = { 0, NULL }; @@ -526,6 +524,8 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf) 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->builtin_session_cache = NGX_CONF_UNSET; sscf->session_timeout = NGX_CONF_UNSET; @@ -573,8 +573,9 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_uint_value(conf->verify, prev->verify, 0); ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1); - ngx_conf_merge_str_value(conf->certificate, prev->certificate, ""); - ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, ""); + ngx_conf_merge_ptr_value(conf->certificates, prev->certificates, NULL); + ngx_conf_merge_ptr_value(conf->certificate_keys, prev->certificate_keys, + NULL); ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL); @@ -601,7 +602,7 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) if (conf->enable) { - if (conf->certificate.len == 0) { + 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", @@ -609,7 +610,7 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_ERROR; } - if (conf->certificate_key.len == 0) { + 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", @@ -617,16 +618,31 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) 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; + } + } else { - if (conf->certificate.len == 0) { + if (conf->certificates == NULL) { return NGX_CONF_OK; } - if (conf->certificate_key.len == 0) { + if (conf->certificate_keys == NULL + || 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\"", &conf->certificate); + "for certificate \"%V\"", + ((ngx_str_t *) conf->certificates->elts) + + conf->certificates->nelts - 1); return NGX_CONF_ERROR; } } @@ -666,20 +682,17 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) cln->handler = ngx_ssl_cleanup_ctx; cln->data = &conf->ssl; - if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate, - &conf->certificate_key, conf->passwords) + if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates, + conf->certificate_keys, conf->passwords) != NGX_OK) { return NGX_CONF_ERROR; } - if (SSL_CTX_set_cipher_list(conf->ssl.ctx, - (const char *) conf->ciphers.data) - == 0) + if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers, + conf->prefer_server_ciphers) + != NGX_OK) { - ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, - "SSL_CTX_set_cipher_list(\"%V\") failed", - &conf->ciphers); return NGX_CONF_ERROR; } @@ -714,15 +727,6 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_ERROR; } - if (conf->prefer_server_ciphers) { - SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); - } - -#if (OPENSSL_VERSION_NUMBER < 0x10100001L && !defined LIBRESSL_VERSION_NUMBER) - /* a temporary 512-bit RSA key is required for export versions of MSIE */ - SSL_CTX_set_tmp_rsa_callback(conf->ssl.ctx, ngx_ssl_rsa512_key_callback); -#endif - if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) { return NGX_CONF_ERROR; } diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h index 8e69e9e..57f5941 100644 --- a/src/http/modules/ngx_http_ssl_module.h +++ b/src/http/modules/ngx_http_ssl_module.h @@ -32,8 +32,9 @@ typedef struct { time_t session_timeout; - ngx_str_t certificate; - ngx_str_t certificate_key; + ngx_array_t *certificates; + ngx_array_t *certificate_keys; + ngx_str_t dhparam; ngx_str_t ecdh_curve; ngx_str_t client_certificate; diff --git a/src/http/modules/ngx_http_sub_filter_module.c b/src/http/modules/ngx_http_sub_filter_module.c index bb1c50b..e8d1d80 100644 --- a/src/http/modules/ngx_http_sub_filter_module.c +++ b/src/http/modules/ngx_http_sub_filter_module.c @@ -83,7 +83,9 @@ static ngx_uint_t ngx_http_sub_cmp_index; static ngx_int_t ngx_http_sub_output(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx); static ngx_int_t ngx_http_sub_parse(ngx_http_request_t *r, - ngx_http_sub_ctx_t *ctx); + ngx_http_sub_ctx_t *ctx, ngx_uint_t flush); +static ngx_int_t ngx_http_sub_match(ngx_http_sub_ctx_t *ctx, ngx_int_t start, + ngx_str_t *m); static char * ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -285,6 +287,7 @@ ngx_http_sub_body_filter(ngx_http_request_t *r, ngx_chain_t *in) ngx_int_t rc; ngx_buf_t *b; ngx_str_t *sub; + ngx_uint_t flush, last; ngx_chain_t *cl; ngx_http_sub_ctx_t *ctx; ngx_http_sub_match_t *match; @@ -326,6 +329,9 @@ ngx_http_sub_body_filter(ngx_http_request_t *r, ngx_chain_t *in) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http sub filter \"%V\"", &r->uri); + flush = 0; + last = 0; + while (ctx->in || ctx->buf) { if (ctx->buf == NULL) { @@ -334,11 +340,19 @@ ngx_http_sub_body_filter(ngx_http_request_t *r, ngx_chain_t *in) ctx->pos = ctx->buf->pos; } + if (ctx->buf->flush || ctx->buf->recycled) { + flush = 1; + } + + if (ctx->in == NULL) { + last = flush; + } + b = NULL; while (ctx->pos < ctx->buf->last) { - rc = ngx_http_sub_parse(r, ctx); + rc = ngx_http_sub_parse(r, ctx, last); ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "parse: %i, looked: \"%V\" %p-%p", @@ -590,9 +604,10 @@ ngx_http_sub_output(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx) static ngx_int_t -ngx_http_sub_parse(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx) +ngx_http_sub_parse(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx, + ngx_uint_t flush) { - u_char *p, *last, *pat, *pat_end, c; + u_char *p, c; ngx_str_t *m; ngx_int_t offset, start, next, end, len, rc; ngx_uint_t shift, i, j; @@ -602,6 +617,7 @@ ngx_http_sub_parse(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx) slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module); tables = ctx->tables; + match = ctx->matches->elts; offset = ctx->offset; end = ctx->buf->last - ctx->pos; @@ -628,7 +644,6 @@ ngx_http_sub_parse(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx) /* a potential match */ start = offset - (ngx_int_t) tables->min_match_len + 1; - match = ctx->matches->elts; i = ngx_max(tables->index[c], ctx->index); j = tables->index[c + 1]; @@ -641,41 +656,15 @@ ngx_http_sub_parse(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx) m = &match[i].match; - pat = m->data; - pat_end = m->data + m->len; + rc = ngx_http_sub_match(ctx, start, m); - if (start >= 0) { - p = ctx->pos + start; - - } else { - last = ctx->looked.data + ctx->looked.len; - p = last + start; - - while (p < last && pat < pat_end) { - if (ngx_tolower(*p) != *pat) { - goto next; - } - - p++; - pat++; - } - - p = ctx->pos; - } - - while (p < ctx->buf->last && pat < pat_end) { - if (ngx_tolower(*p) != *pat) { - goto next; - } - - p++; - pat++; + if (rc == NGX_DECLINED) { + goto next; } ctx->index = i; - if (pat != pat_end) { - /* partial match */ + if (rc == NGX_AGAIN) { goto again; } @@ -695,6 +684,26 @@ ngx_http_sub_parse(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx) ctx->index = 0; } + if (flush) { + for ( ;; ) { + start = offset - (ngx_int_t) tables->min_match_len + 1; + + if (start >= end) { + break; + } + + for (i = 0; i < ctx->matches->nelts; i++) { + m = &match[i].match; + + if (ngx_http_sub_match(ctx, start, m) == NGX_AGAIN) { + goto again; + } + } + + offset++; + } + } + again: ctx->offset = offset; @@ -731,6 +740,51 @@ done: } +static ngx_int_t +ngx_http_sub_match(ngx_http_sub_ctx_t *ctx, ngx_int_t start, ngx_str_t *m) +{ + u_char *p, *last, *pat, *pat_end; + + pat = m->data; + pat_end = m->data + m->len; + + if (start >= 0) { + p = ctx->pos + start; + + } else { + last = ctx->looked.data + ctx->looked.len; + p = last + start; + + while (p < last && pat < pat_end) { + if (ngx_tolower(*p) != *pat) { + return NGX_DECLINED; + } + + p++; + pat++; + } + + p = ctx->pos; + } + + while (p < ctx->buf->last && pat < pat_end) { + if (ngx_tolower(*p) != *pat) { + return NGX_DECLINED; + } + + p++; + pat++; + } + + if (pat != pat_end) { + /* partial match */ + return NGX_AGAIN; + } + + return NGX_OK; +} + + static char * ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { diff --git a/src/http/modules/ngx_http_upstream_keepalive_module.c b/src/http/modules/ngx_http_upstream_keepalive_module.c index 85bfcdb..0048e6b 100644 --- a/src/http/modules/ngx_http_upstream_keepalive_module.c +++ b/src/http/modules/ngx_http_upstream_keepalive_module.c @@ -29,7 +29,7 @@ typedef struct { ngx_connection_t *connection; socklen_t socklen; - u_char sockaddr[NGX_SOCKADDRLEN]; + ngx_sockaddr_t sockaddr; } ngx_http_upstream_keepalive_cache_t; diff --git a/src/http/modules/ngx_http_userid_filter_module.c b/src/http/modules/ngx_http_userid_filter_module.c index 1487c09..0dbacba 100644 --- a/src/http/modules/ngx_http_userid_filter_module.c +++ b/src/http/modules/ngx_http_userid_filter_module.c @@ -836,7 +836,7 @@ ngx_http_userid_init_worker(ngx_cycle_t *cycle) ngx_gettimeofday(&tp); /* use the most significant usec part that fits to 16 bits */ - start_value = ((tp.tv_usec / 20) << 16) | ngx_pid; + start_value = (((uint32_t) tp.tv_usec / 20) << 16) | ngx_pid; return NGX_OK; } diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c index fef2c46..7f916e8 100644 --- a/src/http/modules/ngx_http_uwsgi_module.c +++ b/src/http/modules/ngx_http_uwsgi_module.c @@ -196,7 +196,7 @@ static ngx_command_t ngx_http_uwsgi_commands[] = { NULL }, { ngx_string("uwsgi_bind"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, ngx_http_upstream_bind_set_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_uwsgi_loc_conf_t, upstream.local), @@ -2325,13 +2325,9 @@ ngx_http_uwsgi_set_ssl(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *uwcf) } } - if (SSL_CTX_set_cipher_list(uwcf->upstream.ssl->ctx, - (const char *) uwcf->ssl_ciphers.data) - == 0) + if (ngx_ssl_ciphers(cf, uwcf->upstream.ssl, &uwcf->ssl_ciphers, 0) + != NGX_OK) { - ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, - "SSL_CTX_set_cipher_list(\"%V\") failed", - &uwcf->ssl_ciphers); return NGX_ERROR; } diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c index 0ceb613..7a46b3e 100644 --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -1144,12 +1144,8 @@ ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, in_port_t p; ngx_uint_t i; struct sockaddr *sa; - struct sockaddr_in *sin; ngx_http_conf_port_t *port; ngx_http_core_main_conf_t *cmcf; -#if (NGX_HAVE_INET6) - struct sockaddr_in6 *sin6; -#endif cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); @@ -1161,28 +1157,8 @@ ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, } } - sa = &lsopt->u.sockaddr; - - switch (sa->sa_family) { - -#if (NGX_HAVE_INET6) - case AF_INET6: - sin6 = &lsopt->u.sockaddr_in6; - p = sin6->sin6_port; - break; -#endif - -#if (NGX_HAVE_UNIX_DOMAIN) - case AF_UNIX: - p = 0; - break; -#endif - - default: /* AF_INET */ - sin = &lsopt->u.sockaddr_in; - p = sin->sin_port; - break; - } + sa = &lsopt->sockaddr.sockaddr; + p = ngx_inet_get_port(sa); port = cmcf->ports->elts; for (i = 0; i < cmcf->ports->nelts; i++) { @@ -1215,14 +1191,8 @@ static ngx_int_t ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, ngx_http_conf_port_t *port, ngx_http_listen_opt_t *lsopt) { - u_char *p; - size_t len, off; ngx_uint_t i, default_server, proxy_protocol; - struct sockaddr *sa; ngx_http_conf_addr_t *addr; -#if (NGX_HAVE_UNIX_DOMAIN) - struct sockaddr_un *saun; -#endif #if (NGX_HTTP_SSL) ngx_uint_t ssl; #endif @@ -1235,37 +1205,15 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, * may fill some fields in inherited sockaddr struct's */ - sa = &lsopt->u.sockaddr; - - switch (sa->sa_family) { - -#if (NGX_HAVE_INET6) - case AF_INET6: - off = offsetof(struct sockaddr_in6, sin6_addr); - len = 16; - break; -#endif - -#if (NGX_HAVE_UNIX_DOMAIN) - case AF_UNIX: - off = offsetof(struct sockaddr_un, sun_path); - len = sizeof(saun->sun_path); - break; -#endif - - default: /* AF_INET */ - off = offsetof(struct sockaddr_in, sin_addr); - len = 4; - break; - } - - p = lsopt->u.sockaddr_data + off; - addr = port->addrs.elts; for (i = 0; i < port->addrs.nelts; i++) { - if (ngx_memcmp(p, addr[i].opt.u.sockaddr_data + off, len) != 0) { + if (ngx_cmp_sockaddr(&lsopt->sockaddr.sockaddr, lsopt->socklen, + &addr[i].opt.sockaddr.sockaddr, + addr[i].opt.socklen, 0) + != NGX_OK) + { continue; } @@ -1756,7 +1704,8 @@ ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr) ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; - ls = ngx_create_listening(cf, &addr->opt.u.sockaddr, addr->opt.socklen); + ls = ngx_create_listening(cf, &addr->opt.sockaddr.sockaddr, + addr->opt.socklen); if (ls == NULL) { return NULL; } @@ -1846,7 +1795,7 @@ ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport, for (i = 0; i < hport->naddrs; i++) { - sin = &addr[i].opt.u.sockaddr_in; + sin = &addr[i].opt.sockaddr.sockaddr_in; addrs[i].addr = sin->sin_addr.s_addr; addrs[i].conf.default_server = addr[i].default_server; #if (NGX_HTTP_SSL) @@ -1911,7 +1860,7 @@ ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport, for (i = 0; i < hport->naddrs; i++) { - sin6 = &addr[i].opt.u.sockaddr_in6; + sin6 = &addr[i].opt.sockaddr.sockaddr_in6; addrs6[i].addr6 = sin6->sin6_addr; addrs6[i].conf.default_server = addr[i].default_server; #if (NGX_HTTP_SSL) diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index bd36aec..76917bb 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -793,8 +793,6 @@ ngx_http_handler(ngx_http_request_t *r) r->connection->log->action = NULL; - r->connection->unexpected_eof = 0; - if (!r->internal) { switch (r->headers_in.connection_type) { case 0: @@ -2912,7 +2910,9 @@ ngx_http_get_forwarded_addr_internal(ngx_http_request_t *r, ngx_addr_t *addr, } } - if (ngx_parse_addr(r->pool, &paddr, p, xfflen - (p - xff)) != NGX_OK) { + if (ngx_parse_addr_port(r->pool, &paddr, p, xfflen - (p - xff)) + != NGX_OK) + { return NGX_DECLINED; } @@ -3032,7 +3032,7 @@ ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) if (rv == NGX_CONF_OK && !cscf->listen) { ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t)); - sin = &lsopt.u.sockaddr_in; + sin = &lsopt.sockaddr.sockaddr_in; sin->sin_family = AF_INET; #if (NGX_WIN32) @@ -3055,8 +3055,8 @@ ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) #endif lsopt.wildcard = 1; - (void) ngx_sock_ntop(&lsopt.u.sockaddr, lsopt.socklen, lsopt.addr, - NGX_SOCKADDR_STRLEN, 1); + (void) ngx_sock_ntop(&lsopt.sockaddr.sockaddr, lsopt.socklen, + lsopt.addr, NGX_SOCKADDR_STRLEN, 1); if (ngx_http_add_listen(cf, cscf, &lsopt) != NGX_OK) { return NGX_CONF_ERROR; @@ -4000,7 +4000,7 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t)); - ngx_memcpy(&lsopt.u.sockaddr, u.sockaddr, u.socklen); + ngx_memcpy(&lsopt.sockaddr.sockaddr, &u.sockaddr, u.socklen); lsopt.socklen = u.socklen; lsopt.backlog = NGX_LISTEN_BACKLOG; @@ -4017,7 +4017,7 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) lsopt.ipv6only = 1; #endif - (void) ngx_sock_ntop(&lsopt.u.sockaddr, lsopt.socklen, lsopt.addr, + (void) ngx_sock_ntop(&lsopt.sockaddr.sockaddr, lsopt.socklen, lsopt.addr, NGX_SOCKADDR_STRLEN, 1); for (n = 2; n < cf->args->nelts; n++) { @@ -4146,7 +4146,7 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) struct sockaddr *sa; - sa = &lsopt.u.sockaddr; + sa = &lsopt.sockaddr.sockaddr; if (sa->sa_family == AF_INET6) { diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h index 6c446a0..773c215 100644 --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -58,18 +58,7 @@ typedef struct ngx_http_core_loc_conf_s ngx_http_core_loc_conf_t; typedef struct { - union { - struct sockaddr sockaddr; - struct sockaddr_in sockaddr_in; -#if (NGX_HAVE_INET6) - struct sockaddr_in6 sockaddr_in6; -#endif -#if (NGX_HAVE_UNIX_DOMAIN) - struct sockaddr_un sockaddr_un; -#endif - u_char sockaddr_data[NGX_SOCKADDRLEN]; - } u; - + ngx_sockaddr_t sockaddr; socklen_t socklen; unsigned set:1; diff --git a/src/http/ngx_http_file_cache.c b/src/http/ngx_http_file_cache.c index 37cd377..4bf0f7f 100644 --- a/src/http/ngx_http_file_cache.c +++ b/src/http/ngx_http_file_cache.c @@ -101,7 +101,7 @@ ngx_http_file_cache_init(ngx_shm_zone_t *shm_zone, void *data) return NGX_ERROR; } - for (n = 0; n < 3; n++) { + for (n = 0; n < NGX_MAX_PATH_LEVEL; n++) { if (cache->path->level[n] != ocache->path->level[n]) { ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, "cache \"%V\" had previously different levels", @@ -1403,6 +1403,7 @@ ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf) ngx_shmtx_lock(&cache->shpool->mutex); c->node->count--; + c->node->error = 0; c->node->uniq = uniq; c->node->body_start = c->body_start; @@ -2256,7 +2257,7 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) p = value[i].data + 7; last = value[i].data + value[i].len; - for (n = 0; n < 3 && p < last; n++) { + for (n = 0; n < NGX_MAX_PATH_LEVEL && p < last; n++) { if (*p > '0' && *p < '3') { @@ -2267,7 +2268,7 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) break; } - if (*p++ == ':' && n < 2 && p != last) { + if (*p++ == ':' && n < NGX_MAX_PATH_LEVEL - 1 && p < last) { continue; } @@ -2277,7 +2278,7 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) goto invalid_levels; } - if (cache->path->len < 10 + 3) { + if (cache->path->len < 10 + NGX_MAX_PATH_LEVEL) { continue; } @@ -2449,7 +2450,7 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_memcpy(p, "/temp", sizeof("/temp")); ngx_memcpy(&cache->temp_path->level, &cache->path->level, - 3 * sizeof(size_t)); + NGX_MAX_PATH_LEVEL * sizeof(size_t)); cache->temp_path->len = cache->path->len; cache->temp_path->conf_file = cf->conf_file->file.name.data; diff --git a/src/http/ngx_http_header_filter_module.c b/src/http/ngx_http_header_filter_module.c index 507dc93..f000b2e 100644 --- a/src/http/ngx_http_header_filter_module.c +++ b/src/http/ngx_http_header_filter_module.c @@ -95,17 +95,17 @@ static ngx_str_t ngx_http_status_lines[] = { ngx_string("414 Request-URI Too Large"), ngx_string("415 Unsupported Media Type"), ngx_string("416 Requested Range Not Satisfiable"), + ngx_null_string, /* "417 Expectation Failed" */ + ngx_null_string, /* "418 unused" */ + ngx_null_string, /* "419 unused" */ + ngx_null_string, /* "420 unused" */ + ngx_string("421 Misdirected Request"), - /* ngx_null_string, */ /* "417 Expectation Failed" */ - /* ngx_null_string, */ /* "418 unused" */ - /* ngx_null_string, */ /* "419 unused" */ - /* ngx_null_string, */ /* "420 unused" */ - /* ngx_null_string, */ /* "421 unused" */ /* ngx_null_string, */ /* "422 Unprocessable Entity" */ /* ngx_null_string, */ /* "423 Locked" */ /* ngx_null_string, */ /* "424 Failed Dependency" */ -#define NGX_HTTP_LAST_4XX 417 +#define NGX_HTTP_LAST_4XX 422 #define NGX_HTTP_OFF_5XX (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX) ngx_string("500 Internal Server Error"), @@ -113,10 +113,10 @@ static ngx_str_t ngx_http_status_lines[] = { ngx_string("502 Bad Gateway"), ngx_string("503 Service Temporarily Unavailable"), ngx_string("504 Gateway Time-out"), - ngx_null_string, /* "505 HTTP Version Not Supported" */ ngx_null_string, /* "506 Variant Also Negotiates" */ ngx_string("507 Insufficient Storage"), + /* ngx_null_string, */ /* "508 unused" */ /* ngx_null_string, */ /* "509 unused" */ /* ngx_null_string, */ /* "510 Not Extended" */ @@ -161,10 +161,6 @@ ngx_http_header_filter(ngx_http_request_t *r) ngx_connection_t *c; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; - struct sockaddr_in *sin; -#if (NGX_HAVE_INET6) - struct sockaddr_in6 *sin6; -#endif u_char addr[NGX_SOCKADDR_STRLEN]; if (r->header_sent) { @@ -333,24 +329,7 @@ ngx_http_header_filter(ngx_http_request_t *r) } } - switch (c->local_sockaddr->sa_family) { - -#if (NGX_HAVE_INET6) - case AF_INET6: - sin6 = (struct sockaddr_in6 *) c->local_sockaddr; - port = ntohs(sin6->sin6_port); - break; -#endif -#if (NGX_HAVE_UNIX_DOMAIN) - case AF_UNIX: - port = 0; - break; -#endif - default: /* AF_INET */ - sin = (struct sockaddr_in *) c->local_sockaddr; - port = ntohs(sin->sin_port); - break; - } + port = ngx_inet_get_port(c->local_sockaddr); len += sizeof("Location: https://") - 1 + host.len diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c index 0e0b3a2..bd6c9c9 100644 --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -481,7 +481,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) /* check "/.", "//", "%", and "\" (Win32) in URI */ case sw_after_slash_in_uri: - if (usual[ch >> 5] & (1 << (ch & 0x1f))) { + if (usual[ch >> 5] & (1U << (ch & 0x1f))) { state = sw_check_uri; break; } @@ -540,7 +540,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) /* check "/", "%" and "\" (Win32) in URI */ case sw_check_uri: - if (usual[ch >> 5] & (1 << (ch & 0x1f))) { + if (usual[ch >> 5] & (1U << (ch & 0x1f))) { break; } @@ -626,7 +626,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) /* URI */ case sw_uri: - if (usual[ch >> 5] & (1 << (ch & 0x1f))) { + if (usual[ch >> 5] & (1U << (ch & 0x1f))) { break; } @@ -737,6 +737,10 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) return NGX_HTTP_PARSE_INVALID_REQUEST; } + if (r->http_major > 99) { + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + r->http_major = r->http_major * 10 + ch - '0'; break; @@ -770,6 +774,10 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) return NGX_HTTP_PARSE_INVALID_REQUEST; } + if (r->http_minor > 99) { + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + r->http_minor = r->http_minor * 10 + ch - '0'; break; @@ -1123,7 +1131,7 @@ ngx_http_parse_uri(ngx_http_request_t *r) /* check "/.", "//", "%", and "\" (Win32) in URI */ case sw_after_slash_in_uri: - if (usual[ch >> 5] & (1 << (ch & 0x1f))) { + if (usual[ch >> 5] & (1U << (ch & 0x1f))) { state = sw_check_uri; break; } @@ -1171,7 +1179,7 @@ ngx_http_parse_uri(ngx_http_request_t *r) /* check "/", "%" and "\" (Win32) in URI */ case sw_check_uri: - if (usual[ch >> 5] & (1 << (ch & 0x1f))) { + if (usual[ch >> 5] & (1U << (ch & 0x1f))) { break; } @@ -1220,7 +1228,7 @@ ngx_http_parse_uri(ngx_http_request_t *r) /* URI */ case sw_uri: - if (usual[ch >> 5] & (1 << (ch & 0x1f))) { + if (usual[ch >> 5] & (1U << (ch & 0x1f))) { break; } @@ -1281,7 +1289,7 @@ ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes) case sw_usual: - if (usual[ch >> 5] & (1 << (ch & 0x1f))) { + if (usual[ch >> 5] & (1U << (ch & 0x1f))) { *u++ = ch; ch = *p++; break; @@ -1350,7 +1358,7 @@ ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes) case sw_slash: - if (usual[ch >> 5] & (1 << (ch & 0x1f))) { + if (usual[ch >> 5] & (1U << (ch & 0x1f))) { state = sw_usual; *u++ = ch; ch = *p++; @@ -1393,7 +1401,7 @@ ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes) case sw_dot: - if (usual[ch >> 5] & (1 << (ch & 0x1f))) { + if (usual[ch >> 5] & (1U << (ch & 0x1f))) { state = sw_usual; *u++ = ch; ch = *p++; @@ -1434,7 +1442,7 @@ ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes) case sw_dot_dot: - if (usual[ch >> 5] & (1 << (ch & 0x1f))) { + if (usual[ch >> 5] & (1U << (ch & 0x1f))) { state = sw_usual; *u++ = ch; ch = *p++; @@ -1680,6 +1688,10 @@ ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b, return NGX_ERROR; } + if (r->http_major > 99) { + return NGX_ERROR; + } + r->http_major = r->http_major * 10 + ch - '0'; break; @@ -1704,6 +1716,10 @@ ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b, return NGX_ERROR; } + if (r->http_minor > 99) { + return NGX_ERROR; + } + r->http_minor = r->http_minor * 10 + ch - '0'; break; @@ -1820,7 +1836,7 @@ ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri, continue; } - if (usual[ch >> 5] & (1 << (ch & 0x1f))) { + if (usual[ch >> 5] & (1U << (ch & 0x1f))) { continue; } diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 7d6cada..6ff7903 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -110,6 +110,10 @@ ngx_http_header_t ngx_http_headers_in[] = { offsetof(ngx_http_headers_in_t, content_length), ngx_http_process_unique_header_line }, + { ngx_string("Content-Range"), + offsetof(ngx_http_headers_in_t, content_range), + ngx_http_process_unique_header_line }, + { ngx_string("Content-Type"), offsetof(ngx_http_headers_in_t, content_type), ngx_http_process_header_line }, @@ -2064,8 +2068,8 @@ ngx_http_set_virtual_server(ngx_http_request_t *r, ngx_str_t *host) if (sscf->verify) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "client attempted to request the server name " - "different from that one was negotiated"); - ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + "different from the one that was negotiated"); + ngx_http_finalize_request(r, NGX_HTTP_MISDIRECTED_REQUEST); return NGX_ERROR; } } @@ -2752,9 +2756,13 @@ ngx_http_test_reading(ngx_http_request_t *r) #if (NGX_HAVE_EPOLLRDHUP) - if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) && rev->pending_eof) { + if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) && ngx_use_epoll_rdhup) { socklen_t len; + if (!rev->pending_eof) { + return; + } + rev->eof = 1; c->error = 1; diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index cfde7dc..499c1ef 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -95,6 +95,7 @@ #define NGX_HTTP_REQUEST_URI_TOO_LARGE 414 #define NGX_HTTP_UNSUPPORTED_MEDIA_TYPE 415 #define NGX_HTTP_RANGE_NOT_SATISFIABLE 416 +#define NGX_HTTP_MISDIRECTED_REQUEST 421 /* Our own HTTP codes */ @@ -182,6 +183,7 @@ typedef struct { ngx_table_elt_t *user_agent; ngx_table_elt_t *referer; ngx_table_elt_t *content_length; + ngx_table_elt_t *content_range; ngx_table_elt_t *content_type; ngx_table_elt_t *range; diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c index bff9525..c2b1658 100644 --- a/src/http/ngx_http_script.c +++ b/src/http/ngx_http_script.c @@ -350,11 +350,9 @@ ngx_http_script_compile(ngx_http_script_compile_t *sc) goto invalid_variable; } -#if (NGX_PCRE) - { - ngx_uint_t n; - if (sc->source->data[i] >= '1' && sc->source->data[i] <= '9') { +#if (NGX_PCRE) + ngx_uint_t n; n = sc->source->data[i] - '0'; @@ -371,9 +369,13 @@ ngx_http_script_compile(ngx_http_script_compile_t *sc) i++; continue; - } - } +#else + ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0, + "using variable \"$%c\" requires " + "PCRE library", sc->source->data[i]); + return NGX_ERROR; #endif + } if (sc->source->data[i] == '{') { bracket = 1; diff --git a/src/http/ngx_http_special_response.c b/src/http/ngx_http_special_response.c index 2771e58..64e5acd 100644 --- a/src/http/ngx_http_special_response.c +++ b/src/http/ngx_http_special_response.c @@ -210,6 +210,14 @@ static char ngx_http_error_416_page[] = ; +static char ngx_http_error_421_page[] = +"" CRLF +"421 Misdirected Request" CRLF +"" CRLF +"

421 Misdirected Request

" CRLF +; + + static char ngx_http_error_494_page[] = "" CRLF "400 Request Header Or Cookie Too Large" @@ -334,8 +342,13 @@ static ngx_str_t ngx_http_error_pages[] = { ngx_string(ngx_http_error_414_page), ngx_string(ngx_http_error_415_page), ngx_string(ngx_http_error_416_page), + ngx_null_string, /* 417 */ + ngx_null_string, /* 418 */ + ngx_null_string, /* 419 */ + ngx_null_string, /* 420 */ + ngx_string(ngx_http_error_421_page), -#define NGX_HTTP_LAST_4XX 417 +#define NGX_HTTP_LAST_4XX 422 #define NGX_HTTP_OFF_5XX (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX) ngx_string(ngx_http_error_494_page), /* 494, request header too large */ diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index 1386bdb..0f6b3ae 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -165,8 +165,8 @@ static char *ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy); static char *ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static ngx_addr_t *ngx_http_upstream_get_local(ngx_http_request_t *r, - ngx_http_upstream_local_t *local); +static ngx_int_t ngx_http_upstream_set_local(ngx_http_request_t *r, + ngx_http_upstream_t *u, ngx_http_upstream_local_t *local); static void *ngx_http_upstream_create_main_conf(ngx_conf_t *cf); static char *ngx_http_upstream_init_main_conf(ngx_conf_t *cf, void *conf); @@ -588,7 +588,10 @@ ngx_http_upstream_init_request(ngx_http_request_t *r) return; } - u->peer.local = ngx_http_upstream_get_local(r, u->conf->local); + if (ngx_http_upstream_set_local(r, u, u->conf->local) != NGX_OK) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); @@ -1219,9 +1222,13 @@ ngx_http_upstream_check_broken_connection(ngx_http_request_t *r, #if (NGX_HAVE_EPOLLRDHUP) - if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) && ev->pending_eof) { + if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) && ngx_use_epoll_rdhup) { socklen_t len; + if (!ev->pending_eof) { + return; + } + ev->eof = 1; c->error = 1; @@ -5785,7 +5792,7 @@ ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, value = cf->args->elts; - if (ngx_strcmp(value[1].data, "off") == 0) { + if (cf->args->nelts == 2 && ngx_strcmp(value[1].data, "off") == 0) { *plocal = NULL; return NGX_CONF_OK; } @@ -5815,34 +5822,52 @@ ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, *local->value = cv; - return NGX_CONF_OK; + } else { + local->addr = ngx_palloc(cf->pool, sizeof(ngx_addr_t)); + if (local->addr == NULL) { + return NGX_CONF_ERROR; + } + + rc = ngx_parse_addr_port(cf->pool, local->addr, value[1].data, + value[1].len); + + switch (rc) { + case NGX_OK: + local->addr->name = value[1]; + break; + + case NGX_DECLINED: + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid address \"%V\"", &value[1]); + /* fall through */ + + default: + return NGX_CONF_ERROR; + } } - local->addr = ngx_palloc(cf->pool, sizeof(ngx_addr_t)); - if (local->addr == NULL) { - return NGX_CONF_ERROR; + if (cf->args->nelts > 2) { + if (ngx_strcmp(value[2].data, "transparent") == 0) { +#if (NGX_HAVE_TRANSPARENT_PROXY) + local->transparent = 1; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "transparent proxying is not supported " + "on this platform, ignored"); +#endif + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } } - rc = ngx_parse_addr(cf->pool, local->addr, value[1].data, value[1].len); - - switch (rc) { - case NGX_OK: - local->addr->name = value[1]; - return NGX_CONF_OK; - - case NGX_DECLINED: - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid address \"%V\"", &value[1]); - /* fall through */ - - default: - return NGX_CONF_ERROR; - } + return NGX_CONF_OK; } -static ngx_addr_t * -ngx_http_upstream_get_local(ngx_http_request_t *r, +static ngx_int_t +ngx_http_upstream_set_local(ngx_http_request_t *r, ngx_http_upstream_t *u, ngx_http_upstream_local_t *local) { ngx_int_t rc; @@ -5850,41 +5875,47 @@ ngx_http_upstream_get_local(ngx_http_request_t *r, ngx_addr_t *addr; if (local == NULL) { - return NULL; + u->peer.local = NULL; + return NGX_OK; } +#if (NGX_HAVE_TRANSPARENT_PROXY) + u->peer.transparent = local->transparent; +#endif + if (local->value == NULL) { - return local->addr; + u->peer.local = local->addr; + return NGX_OK; } if (ngx_http_complex_value(r, local->value, &val) != NGX_OK) { - return NULL; + return NGX_ERROR; } if (val.len == 0) { - return NULL; + return NGX_OK; } addr = ngx_palloc(r->pool, sizeof(ngx_addr_t)); if (addr == NULL) { - return NULL; + return NGX_ERROR; } - rc = ngx_parse_addr(r->pool, addr, val.data, val.len); + rc = ngx_parse_addr_port(r->pool, addr, val.data, val.len); + if (rc == NGX_ERROR) { + return NGX_ERROR; + } - switch (rc) { - case NGX_OK: - addr->name = val; - return addr; - - case NGX_DECLINED: + if (rc != NGX_OK) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "invalid local address \"%V\"", &val); - /* fall through */ - - default: - return NULL; + return NGX_OK; } + + addr->name = val; + u->peer.local = addr; + + return NGX_OK; } diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h index 7595dcf..b288f28 100644 --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -133,6 +133,9 @@ struct ngx_http_upstream_srv_conf_s { typedef struct { ngx_addr_t *addr; ngx_http_complex_value_t *value; +#if (NGX_HAVE_TRANSPARENT_PROXY) + ngx_uint_t transparent; /* unsigned transparent:1; */ +#endif } ngx_http_upstream_local_t; diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c index 8342dc8..8479c42 100644 --- a/src/http/ngx_http_upstream_round_robin.c +++ b/src/http/ngx_http_upstream_round_robin.c @@ -354,16 +354,7 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r, } ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen); - - switch (sockaddr->sa_family) { -#if (NGX_HAVE_INET6) - case AF_INET6: - ((struct sockaddr_in6 *) sockaddr)->sin6_port = htons(ur->port); - break; -#endif - default: /* AF_INET */ - ((struct sockaddr_in *) sockaddr)->sin_port = htons(ur->port); - } + ngx_inet_set_port(sockaddr, ur->port); p = ngx_pnalloc(r->pool, NGX_SOCKADDR_STRLEN); if (p == NULL) { diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c index f8271ab..7e65b2e 100644 --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -58,6 +58,8 @@ static ngx_int_t ngx_http_variable_remote_port(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_proxy_protocol_addr(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_proxy_protocol_port(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r, @@ -98,6 +100,8 @@ static ngx_int_t ngx_http_variable_request_length(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_request_time(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_request_id(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_status(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); @@ -192,6 +196,9 @@ static ngx_http_variable_t ngx_http_core_variables[] = { { ngx_string("proxy_protocol_addr"), NULL, ngx_http_variable_proxy_protocol_addr, 0, 0, 0 }, + { ngx_string("proxy_protocol_port"), NULL, + ngx_http_variable_proxy_protocol_port, 0, 0, 0 }, + { ngx_string("server_addr"), NULL, ngx_http_variable_server_addr, 0, 0, 0 }, { ngx_string("server_port"), NULL, ngx_http_variable_server_port, 0, 0, 0 }, @@ -274,6 +281,10 @@ static ngx_http_variable_t ngx_http_core_variables[] = { { ngx_string("request_time"), NULL, ngx_http_variable_request_time, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + { ngx_string("request_id"), NULL, + ngx_http_variable_request_id, + 0, 0, 0 }, + { ngx_string("status"), NULL, ngx_http_variable_status, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, @@ -1190,11 +1201,7 @@ static ngx_int_t ngx_http_variable_remote_port(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - ngx_uint_t port; - struct sockaddr_in *sin; -#if (NGX_HAVE_INET6) - struct sockaddr_in6 *sin6; -#endif + ngx_uint_t port; v->len = 0; v->valid = 1; @@ -1206,26 +1213,7 @@ ngx_http_variable_remote_port(ngx_http_request_t *r, return NGX_ERROR; } - switch (r->connection->sockaddr->sa_family) { - -#if (NGX_HAVE_INET6) - case AF_INET6: - sin6 = (struct sockaddr_in6 *) r->connection->sockaddr; - port = ntohs(sin6->sin6_port); - break; -#endif - -#if (NGX_HAVE_UNIX_DOMAIN) - case AF_UNIX: - port = 0; - break; -#endif - - default: /* AF_INET */ - sin = (struct sockaddr_in *) r->connection->sockaddr; - port = ntohs(sin->sin_port); - break; - } + port = ngx_inet_get_port(r->connection->sockaddr); if (port > 0 && port < 65536) { v->len = ngx_sprintf(v->data, "%ui", port) - v->data; @@ -1249,6 +1237,32 @@ ngx_http_variable_proxy_protocol_addr(ngx_http_request_t *r, } +static ngx_int_t +ngx_http_variable_proxy_protocol_port(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_uint_t port; + + v->len = 0; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1); + if (v->data == NULL) { + return NGX_ERROR; + } + + port = r->connection->proxy_protocol_port; + + if (port > 0 && port < 65536) { + v->len = ngx_sprintf(v->data, "%ui", port) - v->data; + } + + return NGX_OK; +} + + static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) @@ -1284,11 +1298,7 @@ static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - ngx_uint_t port; - struct sockaddr_in *sin; -#if (NGX_HAVE_INET6) - struct sockaddr_in6 *sin6; -#endif + ngx_uint_t port; v->len = 0; v->valid = 1; @@ -1304,26 +1314,7 @@ ngx_http_variable_server_port(ngx_http_request_t *r, return NGX_ERROR; } - switch (r->connection->local_sockaddr->sa_family) { - -#if (NGX_HAVE_INET6) - case AF_INET6: - sin6 = (struct sockaddr_in6 *) r->connection->local_sockaddr; - port = ntohs(sin6->sin6_port); - break; -#endif - -#if (NGX_HAVE_UNIX_DOMAIN) - case AF_UNIX: - port = 0; - break; -#endif - - default: /* AF_INET */ - sin = (struct sockaddr_in *) r->connection->local_sockaddr; - port = ntohs(sin->sin_port); - break; - } + port = ngx_inet_get_port(r->connection->local_sockaddr); if (port > 0 && port < 65536) { v->len = ngx_sprintf(v->data, "%ui", port) - v->data; @@ -2067,6 +2058,47 @@ ngx_http_variable_request_time(ngx_http_request_t *r, } +static ngx_int_t +ngx_http_variable_request_id(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *id; + +#if (NGX_OPENSSL) + u_char random_bytes[16]; +#endif + + id = ngx_pnalloc(r->pool, 32); + if (id == NULL) { + return NGX_ERROR; + } + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + v->len = 32; + v->data = id; + +#if (NGX_OPENSSL) + + if (RAND_bytes(random_bytes, 16) == 1) { + ngx_hex_dump(id, random_bytes, 16); + return NGX_OK; + } + + ngx_ssl_error(NGX_LOG_ERR, r->connection->log, 0, "RAND_bytes() failed"); + +#endif + + ngx_sprintf(id, "%08xD%08xD%08xD%08xD", + (uint32_t) ngx_random(), (uint32_t) ngx_random(), + (uint32_t) ngx_random(), (uint32_t) ngx_random()); + + return NGX_OK; +} + + static ngx_int_t ngx_http_variable_connection(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index 278c9ab..d0cd2ab 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -48,11 +48,6 @@ #define NGX_HTTP_V2_DEFAULT_FRAME_SIZE (1 << 14) -#define NGX_HTTP_V2_MAX_WINDOW ((1U << 31) - 1) -#define NGX_HTTP_V2_DEFAULT_WINDOW 65535 - -#define NGX_HTTP_V2_INITIAL_WINDOW 0 - #define NGX_HTTP_V2_ROOT (void *) -1 @@ -415,6 +410,16 @@ ngx_http_v2_write_handler(ngx_event_t *wev) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http2 write handler"); + if (h2c->last_out == NULL && !c->buffered) { + + if (wev->timer_set) { + ngx_del_timer(wev); + } + + ngx_http_v2_handle_connection(h2c); + return; + } + h2c->blocked = 1; rc = ngx_http_v2_send_output_queue(h2c); @@ -473,7 +478,7 @@ ngx_http_v2_send_output_queue(ngx_http_v2_connection_t *h2c) wev = c->write; if (!wev->ready) { - return NGX_OK; + return NGX_AGAIN; } cl = NULL; @@ -544,15 +549,6 @@ ngx_http_v2_send_output_queue(ngx_http_v2_connection_t *h2c) c->tcp_nodelay = NGX_TCP_NODELAY_SET; } - if (cl) { - ngx_add_timer(wev, clcf->send_timeout); - - } else { - if (wev->timer_set) { - ngx_del_timer(wev); - } - } - for ( /* void */ ; out; out = fn) { fn = out->next; @@ -577,6 +573,15 @@ ngx_http_v2_send_output_queue(ngx_http_v2_connection_t *h2c) h2c->last_out = frame; + if (!wev->ready) { + ngx_add_timer(wev, clcf->send_timeout); + return NGX_AGAIN; + } + + if (wev->timer_set) { + ngx_del_timer(wev); + } + return NGX_OK; error: @@ -594,7 +599,8 @@ error: static void ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c) { - ngx_connection_t *c; + ngx_int_t rc; + ngx_connection_t *c; ngx_http_v2_srv_conf_t *h2scf; if (h2c->last_out || h2c->processing) { @@ -609,7 +615,22 @@ ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c) } if (c->buffered) { - return; + h2c->blocked = 1; + + rc = ngx_http_v2_send_output_queue(h2c); + + h2c->blocked = 0; + + if (rc == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } + + if (rc == NGX_AGAIN) { + return; + } + + /* rc == NGX_OK */ } h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, @@ -620,7 +641,7 @@ ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c) } if (ngx_terminate || ngx_exiting) { - ngx_http_close_connection(c); + ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_NO_ERROR); return; } @@ -879,8 +900,6 @@ ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) return ngx_http_v2_state_skip_padded(h2c, pos, end); } - stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG; - h2c->state.stream = stream; return ngx_http_v2_state_read_data(h2c, pos, end); @@ -891,10 +910,12 @@ static u_char * ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) { - size_t size; - ngx_int_t rc; - ngx_uint_t last; - ngx_http_v2_stream_t *stream; + size_t size; + ngx_buf_t *buf; + ngx_int_t rc; + ngx_http_request_t *r; + ngx_http_v2_stream_t *stream; + ngx_http_v2_srv_conf_t *h2scf; stream = h2c->state.stream; @@ -913,17 +934,42 @@ ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos, if (size >= h2c->state.length) { size = h2c->state.length; - last = stream->in_closed; - - } else { - last = 0; + stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG; } - rc = ngx_http_v2_process_request_body(stream->request, pos, size, last); + r = stream->request; - if (rc != NGX_OK) { - stream->skip_data = 1; - ngx_http_finalize_request(stream->request, rc); + if (r->request_body) { + rc = ngx_http_v2_process_request_body(r, pos, size, stream->in_closed); + + if (rc != NGX_OK) { + stream->skip_data = 1; + ngx_http_finalize_request(r, rc); + } + + } else if (size) { + buf = stream->preread; + + if (buf == NULL) { + h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module); + + buf = ngx_create_temp_buf(r->pool, h2scf->preread_size); + if (buf == NULL) { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + stream->preread = buf; + } + + if (size > (size_t) (buf->end - buf->last)) { + ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0, + "http2 preread buffer overflow"); + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + buf->last = ngx_cpymem(buf->last, pos, size); } pos += size; @@ -1058,7 +1104,9 @@ ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos, goto rst_stream; } - if (!h2c->settings_ack && !(h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG)) + if (!h2c->settings_ack + && !(h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG) + && h2scf->preread_size < NGX_HTTP_V2_DEFAULT_WINDOW) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, "client sent stream with data " @@ -2434,8 +2482,7 @@ ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c, ngx_uint_t ack) buf->last = ngx_http_v2_write_uint16(buf->last, NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING); - buf->last = ngx_http_v2_write_uint32(buf->last, - NGX_HTTP_V2_INITIAL_WINDOW); + buf->last = ngx_http_v2_write_uint32(buf->last, h2scf->preread_size); buf->last = ngx_http_v2_write_uint16(buf->last, NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING); @@ -2643,6 +2690,7 @@ ngx_http_v2_create_stream(ngx_http_v2_connection_t *h2c) ngx_http_log_ctx_t *ctx; ngx_http_request_t *r; ngx_http_v2_stream_t *stream; + ngx_http_v2_srv_conf_t *h2scf; ngx_http_core_srv_conf_t *cscf; fc = h2c->free_fake_connections; @@ -2756,8 +2804,10 @@ ngx_http_v2_create_stream(ngx_http_v2_connection_t *h2c) stream->request = r; stream->connection = h2c; + h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module); + stream->send_window = h2c->init_window; - stream->recv_window = NGX_HTTP_V2_INITIAL_WINDOW; + stream->recv_window = h2scf->preread_size; h2c->processing++; @@ -3400,7 +3450,9 @@ ngx_http_v2_run_request(ngx_http_request_t *r) return; } - r->headers_in.chunked = (r->headers_in.content_length_n == -1); + if (r->headers_in.content_length_n == -1 && !r->stream->in_closed) { + r->headers_in.chunked = 1; + } ngx_http_process_request(r); } @@ -3411,7 +3463,11 @@ ngx_http_v2_read_request_body(ngx_http_request_t *r, ngx_http_client_body_handler_pt post_handler) { off_t len; + size_t size; + ngx_buf_t *buf; + ngx_int_t rc; ngx_http_v2_stream_t *stream; + ngx_http_v2_srv_conf_t *h2scf; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; ngx_http_v2_connection_t *h2c; @@ -3444,24 +3500,34 @@ ngx_http_v2_read_request_body(ngx_http_request_t *r, r->request_body = rb; + h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); len = r->headers_in.content_length_n; if (r->request_body_no_buffering && !stream->in_closed) { - r->request_body_in_file_only = 0; if (len < 0 || len > (off_t) clcf->client_body_buffer_size) { len = clcf->client_body_buffer_size; } + /* + * We need a room to store data up to the stream's initial window size, + * at least until this window will be exhausted. + */ + + if (len < (off_t) h2scf->preread_size) { + len = h2scf->preread_size; + } + if (len > NGX_HTTP_V2_MAX_WINDOW) { len = NGX_HTTP_V2_MAX_WINDOW; } - } - if (len >= 0 && len <= (off_t) clcf->client_body_buffer_size - && !r->request_body_in_file_only) + rb->buf = ngx_create_temp_buf(r->pool, (size_t) len); + + } else if (len >= 0 && len <= (off_t) clcf->client_body_buffer_size + && !r->request_body_in_file_only) { rb->buf = ngx_create_temp_buf(r->pool, (size_t) len); @@ -3478,22 +3544,44 @@ ngx_http_v2_read_request_body(ngx_http_request_t *r, return NGX_HTTP_INTERNAL_SERVER_ERROR; } + buf = stream->preread; + if (stream->in_closed) { r->request_body_no_buffering = 0; + + if (buf) { + rc = ngx_http_v2_process_request_body(r, buf->pos, + buf->last - buf->pos, 1); + ngx_pfree(r->pool, buf->start); + return rc; + } + return ngx_http_v2_process_request_body(r, NULL, 0, 1); } - if (len) { - if (r->request_body_no_buffering) { - stream->recv_window = (size_t) len; + if (buf) { + rc = ngx_http_v2_process_request_body(r, buf->pos, + buf->last - buf->pos, 0); - } else { - stream->no_flow_control = 1; - stream->recv_window = NGX_HTTP_V2_MAX_WINDOW; + ngx_pfree(r->pool, buf->start); + + if (rc != NGX_OK) { + stream->skip_data = 1; + return rc; } + } - if (ngx_http_v2_send_window_update(stream->connection, stream->node->id, - stream->recv_window) + if (r->request_body_no_buffering) { + size = (size_t) len - h2scf->preread_size; + + } else { + stream->no_flow_control = 1; + size = NGX_HTTP_V2_MAX_WINDOW - stream->recv_window; + } + + if (size) { + if (ngx_http_v2_send_window_update(stream->connection, + stream->node->id, size) == NGX_ERROR) { stream->skip_data = 1; @@ -3508,9 +3596,13 @@ ngx_http_v2_read_request_body(ngx_http_request_t *r, return NGX_HTTP_INTERNAL_SERVER_ERROR; } } + + stream->recv_window += size; } - ngx_add_timer(r->connection->read, clcf->client_body_timeout); + if (!buf) { + ngx_add_timer(r->connection->read, clcf->client_body_timeout); + } r->read_event_handler = ngx_http_v2_read_client_request_body_handler; r->write_event_handler = ngx_http_request_empty_handler; @@ -3529,13 +3621,8 @@ ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos, ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; - rb = r->request_body; - - if (rb == NULL) { - return NGX_OK; - } - fc = r->connection; + rb = r->request_body; buf = rb->buf; if (size) { @@ -3579,7 +3666,7 @@ ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos, rb->buf = NULL; } - if (r->headers_in.content_length_n == -1) { + if (r->headers_in.chunked) { r->headers_in.content_length_n = rb->received; } @@ -3789,7 +3876,14 @@ ngx_http_v2_read_unbuffered_request_body(ngx_http_request_t *r) window -= h2c->state.length; } - if (window == stream->recv_window) { + if (window <= stream->recv_window) { + if (window < stream->recv_window) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "http2 negative window update"); + stream->skip_data = 1; + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + return NGX_AGAIN; } @@ -3824,6 +3918,10 @@ ngx_http_v2_terminate_stream(ngx_http_v2_connection_t *h2c, ngx_event_t *rev; ngx_connection_t *fc; + if (stream->rst_sent) { + return NGX_OK; + } + if (ngx_http_v2_send_rst_stream(h2c, stream->node->id, status) == NGX_ERROR) { @@ -3831,6 +3929,7 @@ ngx_http_v2_terminate_stream(ngx_http_v2_connection_t *h2c, } stream->rst_sent = 1; + stream->skip_data = 1; fc = stream->request->connection; fc->error = 1; @@ -3862,6 +3961,7 @@ ngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc) if (stream->queued) { fc->write->handler = ngx_http_v2_close_stream_handler; + fc->read->handler = ngx_http_empty_handler; return; } @@ -4103,10 +4203,6 @@ ngx_http_v2_finalize_connection(ngx_http_v2_connection_t *h2c, c->error = 1; - if (h2c->state.stream) { - ngx_http_v2_close_stream(h2c->state.stream, NGX_HTTP_BAD_REQUEST); - } - if (!h2c->processing) { ngx_http_close_connection(c); return; diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h index 1adf8de..d712d38 100644 --- a/src/http/v2/ngx_http_v2.h +++ b/src/http/v2/ngx_http_v2.h @@ -46,6 +46,9 @@ #define NGX_HTTP_V2_PADDED_FLAG 0x08 #define NGX_HTTP_V2_PRIORITY_FLAG 0x20 +#define NGX_HTTP_V2_MAX_WINDOW ((1U << 31) - 1) +#define NGX_HTTP_V2_DEFAULT_WINDOW 65535 + typedef struct ngx_http_v2_connection_s ngx_http_v2_connection_t; typedef struct ngx_http_v2_node_s ngx_http_v2_node_t; @@ -174,6 +177,8 @@ struct ngx_http_v2_stream_s { ssize_t send_window; size_t recv_window; + ngx_buf_t *preread; + ngx_http_v2_out_frame_t *free_frames; ngx_chain_t *free_frame_headers; ngx_chain_t *free_bufs; @@ -293,7 +298,7 @@ size_t ngx_http_v2_huff_encode(u_char *src, size_t len, u_char *dst, #define ngx_http_v2_parse_uint16(p) ((p)[0] << 8 | (p)[1]) #define ngx_http_v2_parse_uint32(p) \ - ((p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3]) + ((uint32_t) (p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3]) #endif diff --git a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c index caa835d..4ab7791 100644 --- a/src/http/v2/ngx_http_v2_filter_module.c +++ b/src/http/v2/ngx_http_v2_filter_module.c @@ -137,10 +137,6 @@ ngx_http_v2_header_filter(ngx_http_request_t *r) ngx_http_v2_out_frame_t *frame; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; - struct sockaddr_in *sin; -#if (NGX_HAVE_INET6) - struct sockaddr_in6 *sin6; -#endif u_char addr[NGX_SOCKADDR_STRLEN]; static const u_char nginx[5] = "\x84\xaa\x63\x55\xe7"; @@ -169,6 +165,12 @@ ngx_http_v2_header_filter(ngx_http_request_t *r) return NGX_OK; } + fc = r->connection; + + if (fc->error) { + return NGX_ERROR; + } + if (r->method == NGX_HTTP_HEAD) { r->header_only = 1; } @@ -259,8 +261,6 @@ ngx_http_v2_header_filter(ngx_http_request_t *r) len += 1 + ngx_http_v2_literal_size("Wed, 31 Dec 1986 18:00:00 GMT"); } - fc = r->connection; - if (r->headers_out.location && r->headers_out.location->value.len) { if (r->headers_out.location->value.data[0] == '/') { @@ -280,24 +280,7 @@ ngx_http_v2_header_filter(ngx_http_request_t *r) } } - switch (fc->local_sockaddr->sa_family) { - -#if (NGX_HAVE_INET6) - case AF_INET6: - sin6 = (struct sockaddr_in6 *) fc->local_sockaddr; - port = ntohs(sin6->sin6_port); - break; -#endif -#if (NGX_HAVE_UNIX_DOMAIN) - case AF_UNIX: - port = 0; - break; -#endif - default: /* AF_INET */ - sin = (struct sockaddr_in *) fc->local_sockaddr; - port = ntohs(sin->sin_port); - break; - } + port = ngx_inet_get_port(fc->local_sockaddr); location.len = sizeof("https://") - 1 + host.len + r->headers_out.location->value.len; @@ -995,12 +978,11 @@ static ngx_http_v2_out_frame_t * ngx_http_v2_filter_get_data_frame(ngx_http_v2_stream_t *stream, size_t len, ngx_chain_t *first, ngx_chain_t *last) { - u_char flags; - ngx_buf_t *buf; - ngx_chain_t *cl; + u_char flags; + ngx_buf_t *buf; + ngx_chain_t *cl; ngx_http_v2_out_frame_t *frame; - frame = stream->free_frames; if (frame) { @@ -1028,7 +1010,7 @@ ngx_http_v2_filter_get_data_frame(ngx_http_v2_stream_t *stream, buf = cl->buf; - if (!buf->start) { + if (buf->start == NULL) { buf->start = ngx_palloc(stream->request->pool, NGX_HTTP_V2_FRAME_HEADER_SIZE); if (buf->start == NULL) { @@ -1203,7 +1185,6 @@ ngx_http_v2_data_frame_handler(ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream; stream = frame->stream; - cl = frame->first; if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) { @@ -1313,18 +1294,20 @@ static ngx_inline void ngx_http_v2_handle_stream(ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream) { - ngx_event_t *wev; + ngx_connection_t *fc; - if (stream->handled || stream->blocked || stream->exhausted) { + if (stream->handled || stream->blocked) { return; } - wev = stream->request->connection->write; + fc = stream->request->connection; - if (!wev->delayed) { - stream->handled = 1; - ngx_queue_insert_tail(&h2c->posted, &stream->queue); + if (!fc->error && (stream->exhausted || fc->write->delayed)) { + return; } + + stream->handled = 1; + ngx_queue_insert_tail(&h2c->posted, &stream->queue); } diff --git a/src/http/v2/ngx_http_v2_module.c b/src/http/v2/ngx_http_v2_module.c index 5a4561c..b7d99e0 100644 --- a/src/http/v2/ngx_http_v2_module.c +++ b/src/http/v2/ngx_http_v2_module.c @@ -30,6 +30,7 @@ static char *ngx_http_v2_merge_loc_conf(ngx_conf_t *cf, void *parent, static char *ngx_http_v2_recv_buffer_size(ngx_conf_t *cf, void *post, void *data); static char *ngx_http_v2_pool_size(ngx_conf_t *cf, void *post, void *data); +static char *ngx_http_v2_preread_size(ngx_conf_t *cf, void *post, void *data); 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); @@ -41,6 +42,8 @@ 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 = { ngx_http_v2_pool_size }; +static ngx_conf_post_t ngx_http_v2_preread_size_post = + { ngx_http_v2_preread_size }; static ngx_conf_post_t ngx_http_v2_streams_index_mask_post = { ngx_http_v2_streams_index_mask }; static ngx_conf_post_t ngx_http_v2_chunk_size_post = @@ -84,6 +87,13 @@ static ngx_command_t ngx_http_v2_commands[] = { offsetof(ngx_http_v2_srv_conf_t, max_header_size), NULL }, + { ngx_string("http2_body_preread_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, preread_size), + &ngx_http_v2_preread_size_post }, + { ngx_string("http2_streams_index_size"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, @@ -316,6 +326,8 @@ ngx_http_v2_create_srv_conf(ngx_conf_t *cf) 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; @@ -341,6 +353,8 @@ ngx_http_v2_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) 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); @@ -419,6 +433,23 @@ ngx_http_v2_pool_size(ngx_conf_t *cf, void *post, void *data) } +static char * +ngx_http_v2_preread_size(ngx_conf_t *cf, void *post, void *data) +{ + size_t *sp = data; + + if (*sp > NGX_HTTP_V2_MAX_WINDOW) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the maximum body preread buffer size is %uz", + NGX_HTTP_V2_MAX_WINDOW); + + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + static char * ngx_http_v2_streams_index_mask(ngx_conf_t *cf, void *post, void *data) { diff --git a/src/http/v2/ngx_http_v2_module.h b/src/http/v2/ngx_http_v2_module.h index 95cc7d8..91f97c2 100644 --- a/src/http/v2/ngx_http_v2_module.h +++ b/src/http/v2/ngx_http_v2_module.h @@ -25,6 +25,7 @@ typedef struct { ngx_uint_t concurrent_streams; 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; diff --git a/src/mail/ngx_mail.c b/src/mail/ngx_mail.c index 6ad5a67..e5a77b0 100644 --- a/src/mail/ngx_mail.c +++ b/src/mail/ngx_mail.c @@ -228,35 +228,11 @@ ngx_mail_add_ports(ngx_conf_t *cf, ngx_array_t *ports, in_port_t p; ngx_uint_t i; struct sockaddr *sa; - struct sockaddr_in *sin; ngx_mail_conf_port_t *port; ngx_mail_conf_addr_t *addr; -#if (NGX_HAVE_INET6) - struct sockaddr_in6 *sin6; -#endif - sa = &listen->u.sockaddr; - - switch (sa->sa_family) { - -#if (NGX_HAVE_INET6) - case AF_INET6: - sin6 = &listen->u.sockaddr_in6; - p = sin6->sin6_port; - break; -#endif - -#if (NGX_HAVE_UNIX_DOMAIN) - case AF_UNIX: - p = 0; - break; -#endif - - default: /* AF_INET */ - sin = &listen->u.sockaddr_in; - p = sin->sin_port; - break; - } + sa = &listen->sockaddr.sockaddr; + p = ngx_inet_get_port(sa); port = ports->elts; for (i = 0; i < ports->nelts; i++) { @@ -340,7 +316,7 @@ ngx_mail_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports) continue; } - ls = ngx_create_listening(cf, &addr[i].opt.u.sockaddr, + ls = ngx_create_listening(cf, &addr[i].opt.sockaddr.sockaddr, addr[i].opt.socklen); if (ls == NULL) { return NGX_CONF_ERROR; @@ -423,7 +399,7 @@ ngx_mail_add_addrs(ngx_conf_t *cf, ngx_mail_port_t *mport, for (i = 0; i < mport->naddrs; i++) { - sin = &addr[i].opt.u.sockaddr_in; + sin = &addr[i].opt.sockaddr.sockaddr_in; addrs[i].addr = sin->sin_addr.s_addr; addrs[i].conf.ctx = addr[i].opt.ctx; @@ -431,8 +407,8 @@ ngx_mail_add_addrs(ngx_conf_t *cf, ngx_mail_port_t *mport, addrs[i].conf.ssl = addr[i].opt.ssl; #endif - len = ngx_sock_ntop(&addr[i].opt.u.sockaddr, addr[i].opt.socklen, buf, - NGX_SOCKADDR_STRLEN, 1); + len = ngx_sock_ntop(&addr[i].opt.sockaddr.sockaddr, addr[i].opt.socklen, + buf, NGX_SOCKADDR_STRLEN, 1); p = ngx_pnalloc(cf->pool, len); if (p == NULL) { @@ -472,7 +448,7 @@ ngx_mail_add_addrs6(ngx_conf_t *cf, ngx_mail_port_t *mport, for (i = 0; i < mport->naddrs; i++) { - sin6 = &addr[i].opt.u.sockaddr_in6; + sin6 = &addr[i].opt.sockaddr.sockaddr_in6; addrs6[i].addr6 = sin6->sin6_addr; addrs6[i].conf.ctx = addr[i].opt.ctx; @@ -480,8 +456,8 @@ ngx_mail_add_addrs6(ngx_conf_t *cf, ngx_mail_port_t *mport, addrs6[i].conf.ssl = addr[i].opt.ssl; #endif - len = ngx_sock_ntop(&addr[i].opt.u.sockaddr, addr[i].opt.socklen, buf, - NGX_SOCKADDR_STRLEN, 1); + len = ngx_sock_ntop(&addr[i].opt.sockaddr.sockaddr, addr[i].opt.socklen, + buf, NGX_SOCKADDR_STRLEN, 1); p = ngx_pnalloc(cf->pool, len); if (p == NULL) { diff --git a/src/mail/ngx_mail.h b/src/mail/ngx_mail.h index bfbf768..ae1aa41 100644 --- a/src/mail/ngx_mail.h +++ b/src/mail/ngx_mail.h @@ -27,18 +27,7 @@ typedef struct { typedef struct { - union { - struct sockaddr sockaddr; - struct sockaddr_in sockaddr_in; -#if (NGX_HAVE_INET6) - struct sockaddr_in6 sockaddr_in6; -#endif -#if (NGX_HAVE_UNIX_DOMAIN) - struct sockaddr_un sockaddr_un; -#endif - u_char sockaddr_data[NGX_SOCKADDRLEN]; - } u; - + ngx_sockaddr_t sockaddr; socklen_t socklen; /* server ctx */ diff --git a/src/mail/ngx_mail_auth_http_module.c b/src/mail/ngx_mail_auth_http_module.c index 39f9b17..a94434a 100644 --- a/src/mail/ngx_mail_auth_http_module.c +++ b/src/mail/ngx_mail_auth_http_module.c @@ -462,15 +462,11 @@ static void ngx_mail_auth_http_process_headers(ngx_mail_session_t *s, ngx_mail_auth_http_ctx_t *ctx) { - u_char *p; - time_t timer; - size_t len, size; - ngx_int_t rc, port, n; - ngx_addr_t *peer; - struct sockaddr_in *sin; -#if (NGX_HAVE_INET6) - struct sockaddr_in6 *sin6; -#endif + u_char *p; + time_t timer; + size_t len, size; + ngx_int_t rc, port, n; + ngx_addr_t *peer; ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0, "mail auth http process headers"); @@ -813,20 +809,7 @@ ngx_mail_auth_http_process_headers(ngx_mail_session_t *s, return; } - switch (peer->sockaddr->sa_family) { - -#if (NGX_HAVE_INET6) - case AF_INET6: - sin6 = (struct sockaddr_in6 *) peer->sockaddr; - sin6->sin6_port = htons((in_port_t) port); - break; -#endif - - default: /* AF_INET */ - sin = (struct sockaddr_in *) peer->sockaddr; - sin->sin_port = htons((in_port_t) port); - break; - } + ngx_inet_set_port(peer->sockaddr, (in_port_t) port); len = ctx->addr.len + 1 + ctx->port.len; diff --git a/src/mail/ngx_mail_core_module.c b/src/mail/ngx_mail_core_module.c index 8ea8ea9..d992402 100644 --- a/src/mail/ngx_mail_core_module.c +++ b/src/mail/ngx_mail_core_module.c @@ -288,19 +288,12 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_mail_core_srv_conf_t *cscf = conf; - size_t len, off; - in_port_t port; ngx_str_t *value; ngx_url_t u; ngx_uint_t i, m; - struct sockaddr *sa; ngx_mail_listen_t *ls; ngx_mail_module_t *module; - struct sockaddr_in *sin; ngx_mail_core_main_conf_t *cmcf; -#if (NGX_HAVE_INET6) - struct sockaddr_in6 *sin6; -#endif value = cf->args->elts; @@ -325,49 +318,13 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) for (i = 0; i < cmcf->listen.nelts; i++) { - sa = &ls[i].u.sockaddr; - - if (sa->sa_family != u.family) { - continue; - } - - switch (sa->sa_family) { - -#if (NGX_HAVE_INET6) - case AF_INET6: - off = offsetof(struct sockaddr_in6, sin6_addr); - len = 16; - sin6 = &ls[i].u.sockaddr_in6; - port = ntohs(sin6->sin6_port); - break; -#endif - -#if (NGX_HAVE_UNIX_DOMAIN) - case AF_UNIX: - off = offsetof(struct sockaddr_un, sun_path); - len = sizeof(((struct sockaddr_un *) sa)->sun_path); - port = 0; - break; -#endif - - default: /* AF_INET */ - off = offsetof(struct sockaddr_in, sin_addr); - len = 4; - sin = &ls[i].u.sockaddr_in; - port = ntohs(sin->sin_port); - break; - } - - if (ngx_memcmp(ls[i].u.sockaddr_data + off, u.sockaddr + off, len) - != 0) + if (ngx_cmp_sockaddr(&ls[i].sockaddr.sockaddr, ls[i].socklen, + (struct sockaddr *) &u.sockaddr, u.socklen, 1) + != NGX_OK) { continue; } - if (port != u.port) { - continue; - } - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "duplicate \"%V\" address and port pair", &u.url); return NGX_CONF_ERROR; @@ -380,7 +337,7 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_memzero(ls, sizeof(ngx_mail_listen_t)); - ngx_memcpy(&ls->u.sockaddr, u.sockaddr, u.socklen); + ngx_memcpy(&ls->sockaddr.sockaddr, &u.sockaddr, u.socklen); ls->socklen = u.socklen; ls->backlog = NGX_LISTEN_BACKLOG; @@ -434,11 +391,10 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (ngx_strncmp(value[i].data, "ipv6only=o", 10) == 0) { #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + size_t len; u_char buf[NGX_SOCKADDR_STRLEN]; - sa = &ls->u.sockaddr; - - if (sa->sa_family == AF_INET6) { + if (ls->sockaddr.sockaddr.sa_family == AF_INET6) { if (ngx_strcmp(&value[i].data[10], "n") == 0) { ls->ipv6only = 1; @@ -456,7 +412,7 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ls->bind = 1; } else { - len = ngx_sock_ntop(sa, ls->socklen, buf, + len = ngx_sock_ntop(&ls->sockaddr.sockaddr, ls->socklen, buf, NGX_SOCKADDR_STRLEN, 1); ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c index ff5c141..11e428c 100644 --- a/src/mail/ngx_mail_ssl_module.c +++ b/src/mail/ngx_mail_ssl_module.c @@ -11,7 +11,7 @@ #define NGX_DEFAULT_CIPHERS "HIGH:!aNULL:!MD5" -#define NGX_DEFAULT_ECDH_CURVE "prime256v1" +#define NGX_DEFAULT_ECDH_CURVE "auto" static void *ngx_mail_ssl_create_conf(ngx_conf_t *cf); @@ -73,16 +73,16 @@ static ngx_command_t ngx_mail_ssl_commands[] = { { ngx_string("ssl_certificate"), NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_str_slot, + ngx_conf_set_str_array_slot, NGX_MAIL_SRV_CONF_OFFSET, - offsetof(ngx_mail_ssl_conf_t, certificate), + offsetof(ngx_mail_ssl_conf_t, certificates), NULL }, { ngx_string("ssl_certificate_key"), NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_str_slot, + ngx_conf_set_str_array_slot, NGX_MAIL_SRV_CONF_OFFSET, - offsetof(ngx_mail_ssl_conf_t, certificate_key), + offsetof(ngx_mail_ssl_conf_t, certificate_keys), NULL }, { ngx_string("ssl_password_file"), @@ -238,8 +238,6 @@ ngx_mail_ssl_create_conf(ngx_conf_t *cf) * set by ngx_pcalloc(): * * scf->protocols = 0; - * scf->certificate = { 0, NULL }; - * scf->certificate_key = { 0, NULL }; * scf->dhparam = { 0, NULL }; * scf->ecdh_curve = { 0, NULL }; * scf->client_certificate = { 0, NULL }; @@ -251,6 +249,8 @@ ngx_mail_ssl_create_conf(ngx_conf_t *cf) scf->enable = NGX_CONF_UNSET; scf->starttls = NGX_CONF_UNSET_UINT; + scf->certificates = NGX_CONF_UNSET_PTR; + scf->certificate_keys = NGX_CONF_UNSET_PTR; scf->passwords = NGX_CONF_UNSET_PTR; scf->prefer_server_ciphers = NGX_CONF_UNSET; scf->verify = NGX_CONF_UNSET_UINT; @@ -290,8 +290,9 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_uint_value(conf->verify, prev->verify, 0); ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1); - ngx_conf_merge_str_value(conf->certificate, prev->certificate, ""); - ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, ""); + ngx_conf_merge_ptr_value(conf->certificates, prev->certificates, NULL); + ngx_conf_merge_ptr_value(conf->certificate_keys, prev->certificate_keys, + NULL); ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL); @@ -328,7 +329,7 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) if (*mode) { - if (conf->certificate.len == 0) { + if (conf->certificates == NULL) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "no \"ssl_certificate\" is defined for " "the \"%s\" directive in %s:%ui", @@ -336,7 +337,7 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_ERROR; } - if (conf->certificate_key.len == 0) { + if (conf->certificate_keys == NULL) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "no \"ssl_certificate_key\" is defined for " "the \"%s\" directive in %s:%ui", @@ -344,17 +345,31 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) 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; + } + } else { - if (conf->certificate.len == 0) { + if (conf->certificates == NULL) { return NGX_CONF_OK; } - if (conf->certificate_key.len == 0) { + if (conf->certificate_keys == NULL + || 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\"", - &conf->certificate); + ((ngx_str_t *) conf->certificates->elts) + + conf->certificates->nelts - 1); return NGX_CONF_ERROR; } } @@ -371,8 +386,8 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) cln->handler = ngx_ssl_cleanup_ctx; cln->data = &conf->ssl; - if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate, - &conf->certificate_key, conf->passwords) + if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates, + conf->certificate_keys, conf->passwords) != NGX_OK) { return NGX_CONF_ERROR; @@ -407,24 +422,13 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) } } - if (SSL_CTX_set_cipher_list(conf->ssl.ctx, - (const char *) conf->ciphers.data) - == 0) + if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers, + conf->prefer_server_ciphers) + != NGX_OK) { - ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, - "SSL_CTX_set_cipher_list(\"%V\") failed", - &conf->ciphers); return NGX_CONF_ERROR; } - if (conf->prefer_server_ciphers) { - SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); - } - -#if (OPENSSL_VERSION_NUMBER < 0x10100001L && !defined LIBRESSL_VERSION_NUMBER) - SSL_CTX_set_tmp_rsa_callback(conf->ssl.ctx, ngx_ssl_rsa512_key_callback); -#endif - if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) { return NGX_CONF_ERROR; } diff --git a/src/mail/ngx_mail_ssl_module.h b/src/mail/ngx_mail_ssl_module.h index 296a6a2..26628d5 100644 --- a/src/mail/ngx_mail_ssl_module.h +++ b/src/mail/ngx_mail_ssl_module.h @@ -35,8 +35,9 @@ typedef struct { time_t session_timeout; - ngx_str_t certificate; - ngx_str_t certificate_key; + ngx_array_t *certificates; + ngx_array_t *certificate_keys; + ngx_str_t dhparam; ngx_str_t ecdh_curve; ngx_str_t client_certificate; diff --git a/src/os/unix/ngx_readv_chain.c b/src/os/unix/ngx_readv_chain.c index d23508e..454cfdc 100644 --- a/src/os/unix/ngx_readv_chain.c +++ b/src/os/unix/ngx_readv_chain.c @@ -51,6 +51,20 @@ ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *chain, off_t limit) } } +#endif + +#if (NGX_HAVE_EPOLLRDHUP) + + if (ngx_event_flags & NGX_USE_EPOLL_EVENT) { + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "readv: eof:%d, avail:%d", + rev->pending_eof, rev->available); + + if (!rev->available && !rev->pending_eof) { + return NGX_AGAIN; + } + } + #endif prev = NULL; @@ -149,6 +163,24 @@ ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *chain, off_t limit) return n; } +#endif + +#if (NGX_HAVE_EPOLLRDHUP) + + if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) + && ngx_use_epoll_rdhup) + { + if (n < size) { + if (!rev->pending_eof) { + rev->ready = 0; + } + + rev->available = 0; + } + + return n; + } + #endif if (n < size && !(ngx_event_flags & NGX_USE_GREEDY_EVENT)) { diff --git a/src/os/unix/ngx_recv.c b/src/os/unix/ngx_recv.c index 5013ae3..c85fd45 100644 --- a/src/os/unix/ngx_recv.c +++ b/src/os/unix/ngx_recv.c @@ -48,6 +48,21 @@ ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size) } } +#endif + +#if (NGX_HAVE_EPOLLRDHUP) + + if (ngx_event_flags & NGX_USE_EPOLL_EVENT) { + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "recv: eof:%d, avail:%d", + rev->pending_eof, rev->available); + + if (!rev->available && !rev->pending_eof) { + rev->ready = 0; + return NGX_AGAIN; + } + } + #endif do { @@ -99,6 +114,24 @@ ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size) return n; } +#endif + +#if (NGX_HAVE_EPOLLRDHUP) + + if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) + && ngx_use_epoll_rdhup) + { + if ((size_t) n < size) { + if (!rev->pending_eof) { + rev->ready = 0; + } + + rev->available = 0; + } + + return n; + } + #endif if ((size_t) n < size diff --git a/src/os/unix/ngx_thread_mutex.c b/src/os/unix/ngx_thread_mutex.c index 6e8385e..a0ef693 100644 --- a/src/os/unix/ngx_thread_mutex.c +++ b/src/os/unix/ngx_thread_mutex.c @@ -49,7 +49,7 @@ * for each mutex from 10 to 100 based on spin count taken * previously. * FreeBSD: Deadlock detection. The default spin count is 2000. - * It can be overriden using LIBPTHREAD_SPINLOOPS environment + * It can be overridden using LIBPTHREAD_SPINLOOPS environment * variable or by pthread_mutex_setspinloops_np(). If a lock * is still busy, sched_yield() can be called on both UP and * SMP systems. The default yield loop count is zero, but diff --git a/src/stream/ngx_stream.c b/src/stream/ngx_stream.c index 3bd8f6d..c195171 100644 --- a/src/stream/ngx_stream.c +++ b/src/stream/ngx_stream.c @@ -143,11 +143,26 @@ ngx_stream_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } - /* parse inside the stream{} block */ - pcf = *cf; cf->ctx = ctx; + for (m = 0; cf->cycle->modules[m]; m++) { + if (cf->cycle->modules[m]->type != NGX_STREAM_MODULE) { + continue; + } + + module = cf->cycle->modules[m]->ctx; + + if (module->preconfiguration) { + if (module->preconfiguration(cf) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + } + + + /* parse inside the stream{} block */ + cf->module_type = NGX_STREAM_MODULE; cf->cmd_type = NGX_STREAM_MAIN_CONF; rv = ngx_conf_parse(cf, NULL); @@ -215,6 +230,10 @@ ngx_stream_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } } + if (ngx_stream_variables_init_vars(cf) != NGX_OK) { + return NGX_CONF_ERROR; + } + *cf = pcf; @@ -243,35 +262,11 @@ ngx_stream_add_ports(ngx_conf_t *cf, ngx_array_t *ports, in_port_t p; ngx_uint_t i; struct sockaddr *sa; - struct sockaddr_in *sin; ngx_stream_conf_port_t *port; ngx_stream_conf_addr_t *addr; -#if (NGX_HAVE_INET6) - struct sockaddr_in6 *sin6; -#endif - sa = &listen->u.sockaddr; - - switch (sa->sa_family) { - -#if (NGX_HAVE_INET6) - case AF_INET6: - sin6 = &listen->u.sockaddr_in6; - p = sin6->sin6_port; - break; -#endif - -#if (NGX_HAVE_UNIX_DOMAIN) - case AF_UNIX: - p = 0; - break; -#endif - - default: /* AF_INET */ - sin = &listen->u.sockaddr_in; - p = sin->sin_port; - break; - } + sa = &listen->sockaddr.sockaddr; + p = ngx_inet_get_port(sa); port = ports->elts; for (i = 0; i < ports->nelts; i++) { @@ -359,7 +354,7 @@ ngx_stream_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports) continue; } - ls = ngx_create_listening(cf, &addr[i].opt.u.sockaddr, + ls = ngx_create_listening(cf, &addr[i].opt.sockaddr.sockaddr, addr[i].opt.socklen); if (ls == NULL) { return NGX_CONF_ERROR; @@ -453,7 +448,7 @@ ngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *stport, for (i = 0; i < stport->naddrs; i++) { - sin = &addr[i].opt.u.sockaddr_in; + sin = &addr[i].opt.sockaddr.sockaddr_in; addrs[i].addr = sin->sin_addr.s_addr; addrs[i].conf.ctx = addr[i].opt.ctx; @@ -461,8 +456,8 @@ ngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *stport, addrs[i].conf.ssl = addr[i].opt.ssl; #endif - len = ngx_sock_ntop(&addr[i].opt.u.sockaddr, addr[i].opt.socklen, buf, - NGX_SOCKADDR_STRLEN, 1); + len = ngx_sock_ntop(&addr[i].opt.sockaddr.sockaddr, addr[i].opt.socklen, + buf, NGX_SOCKADDR_STRLEN, 1); p = ngx_pnalloc(cf->pool, len); if (p == NULL) { @@ -502,7 +497,7 @@ ngx_stream_add_addrs6(ngx_conf_t *cf, ngx_stream_port_t *stport, for (i = 0; i < stport->naddrs; i++) { - sin6 = &addr[i].opt.u.sockaddr_in6; + sin6 = &addr[i].opt.sockaddr.sockaddr_in6; addrs6[i].addr6 = sin6->sin6_addr; addrs6[i].conf.ctx = addr[i].opt.ctx; @@ -510,8 +505,8 @@ ngx_stream_add_addrs6(ngx_conf_t *cf, ngx_stream_port_t *stport, addrs6[i].conf.ssl = addr[i].opt.ssl; #endif - len = ngx_sock_ntop(&addr[i].opt.u.sockaddr, addr[i].opt.socklen, buf, - NGX_SOCKADDR_STRLEN, 1); + len = ngx_sock_ntop(&addr[i].opt.sockaddr.sockaddr, addr[i].opt.socklen, + buf, NGX_SOCKADDR_STRLEN, 1); p = ngx_pnalloc(cf->pool, len); if (p == NULL) { diff --git a/src/stream/ngx_stream.h b/src/stream/ngx_stream.h index 49efa45..807ab5b 100644 --- a/src/stream/ngx_stream.h +++ b/src/stream/ngx_stream.h @@ -20,75 +20,66 @@ typedef struct ngx_stream_session_s ngx_stream_session_t; +#include +#include #include #include typedef struct { - void **main_conf; - void **srv_conf; + void **main_conf; + void **srv_conf; } ngx_stream_conf_ctx_t; typedef struct { - union { - struct sockaddr sockaddr; - struct sockaddr_in sockaddr_in; -#if (NGX_HAVE_INET6) - struct sockaddr_in6 sockaddr_in6; -#endif -#if (NGX_HAVE_UNIX_DOMAIN) - struct sockaddr_un sockaddr_un; -#endif - u_char sockaddr_data[NGX_SOCKADDRLEN]; - } u; - - socklen_t socklen; + ngx_sockaddr_t sockaddr; + socklen_t socklen; /* server ctx */ - ngx_stream_conf_ctx_t *ctx; + ngx_stream_conf_ctx_t *ctx; - unsigned bind:1; - unsigned wildcard:1; + unsigned bind:1; + unsigned wildcard:1; #if (NGX_STREAM_SSL) - unsigned ssl:1; + unsigned ssl:1; #endif #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) - unsigned ipv6only:1; + unsigned ipv6only:1; #endif #if (NGX_HAVE_REUSEPORT) - unsigned reuseport:1; + unsigned reuseport:1; #endif - unsigned so_keepalive:2; + unsigned so_keepalive:2; #if (NGX_HAVE_KEEPALIVE_TUNABLE) - int tcp_keepidle; - int tcp_keepintvl; - int tcp_keepcnt; + int tcp_keepidle; + int tcp_keepintvl; + int tcp_keepcnt; #endif - int backlog; - int type; + int backlog; + int type; } ngx_stream_listen_t; typedef struct { - ngx_stream_conf_ctx_t *ctx; - ngx_str_t addr_text; + ngx_stream_conf_ctx_t *ctx; + ngx_str_t addr_text; #if (NGX_STREAM_SSL) - ngx_uint_t ssl; /* unsigned ssl:1; */ + ngx_uint_t ssl; /* unsigned ssl:1; */ #endif } ngx_stream_addr_conf_t; typedef struct { - in_addr_t addr; - ngx_stream_addr_conf_t conf; + in_addr_t addr; + ngx_stream_addr_conf_t conf; } ngx_stream_in_addr_t; #if (NGX_HAVE_INET6) typedef struct { - struct in6_addr addr6; - ngx_stream_addr_conf_t conf; + struct in6_addr addr6; + ngx_stream_addr_conf_t conf; } ngx_stream_in6_addr_t; #endif @@ -96,21 +87,21 @@ typedef struct { typedef struct { /* ngx_stream_in_addr_t or ngx_stream_in6_addr_t */ - void *addrs; - ngx_uint_t naddrs; + void *addrs; + ngx_uint_t naddrs; } ngx_stream_port_t; typedef struct { - int family; - int type; - in_port_t port; - ngx_array_t addrs; /* array of ngx_stream_conf_addr_t */ + int family; + int type; + in_port_t port; + ngx_array_t addrs; /* array of ngx_stream_conf_addr_t */ } ngx_stream_conf_port_t; typedef struct { - ngx_stream_listen_t opt; + ngx_stream_listen_t opt; } ngx_stream_conf_addr_t; @@ -118,10 +109,21 @@ typedef ngx_int_t (*ngx_stream_access_pt)(ngx_stream_session_t *s); typedef struct { - ngx_array_t servers; /* ngx_stream_core_srv_conf_t */ - ngx_array_t listen; /* ngx_stream_listen_t */ - ngx_stream_access_pt limit_conn_handler; - ngx_stream_access_pt access_handler; + ngx_array_t servers; /* ngx_stream_core_srv_conf_t */ + ngx_array_t listen; /* ngx_stream_listen_t */ + + ngx_stream_access_pt limit_conn_handler; + ngx_stream_access_pt access_handler; + + ngx_hash_t variables_hash; + + ngx_array_t variables; /* ngx_stream_variable_t */ + ngx_uint_t ncaptures; + + ngx_uint_t variables_hash_max_size; + ngx_uint_t variables_hash_bucket_size; + + ngx_hash_keys_arrays_t *variables_keys; } ngx_stream_core_main_conf_t; @@ -129,41 +131,57 @@ typedef void (*ngx_stream_handler_pt)(ngx_stream_session_t *s); typedef struct { - ngx_stream_handler_pt handler; - ngx_stream_conf_ctx_t *ctx; - u_char *file_name; - ngx_int_t line; - ngx_log_t *error_log; - ngx_flag_t tcp_nodelay; + ngx_stream_handler_pt handler; + + ngx_stream_conf_ctx_t *ctx; + + u_char *file_name; + ngx_int_t line; + + ngx_flag_t tcp_nodelay; + + ngx_log_t *error_log; + + ngx_msec_t resolver_timeout; + ngx_resolver_t *resolver; } ngx_stream_core_srv_conf_t; struct ngx_stream_session_s { - uint32_t signature; /* "STRM" */ + uint32_t signature; /* "STRM" */ - ngx_connection_t *connection; + ngx_connection_t *connection; - off_t received; + off_t received; - ngx_log_handler_pt log_handler; + ngx_log_handler_pt log_handler; - void **ctx; - void **main_conf; - void **srv_conf; + void **ctx; + void **main_conf; + void **srv_conf; - ngx_stream_upstream_t *upstream; + ngx_stream_upstream_t *upstream; + + ngx_stream_variable_value_t *variables; + +#if (NGX_PCRE) + ngx_uint_t ncaptures; + int *captures; + u_char *captures_data; +#endif }; typedef struct { - ngx_int_t (*postconfiguration)(ngx_conf_t *cf); + ngx_int_t (*preconfiguration)(ngx_conf_t *cf); + ngx_int_t (*postconfiguration)(ngx_conf_t *cf); - void *(*create_main_conf)(ngx_conf_t *cf); - char *(*init_main_conf)(ngx_conf_t *cf, void *conf); + void *(*create_main_conf)(ngx_conf_t *cf); + char *(*init_main_conf)(ngx_conf_t *cf, void *conf); - void *(*create_srv_conf)(ngx_conf_t *cf); - char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, - void *conf); + void *(*create_srv_conf)(ngx_conf_t *cf); + char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, + void *conf); } ngx_stream_module_t; diff --git a/src/stream/ngx_stream_access_module.c b/src/stream/ngx_stream_access_module.c index 64869d2..6985d36 100644 --- a/src/stream/ngx_stream_access_module.c +++ b/src/stream/ngx_stream_access_module.c @@ -88,6 +88,7 @@ static ngx_command_t ngx_stream_access_commands[] = { static ngx_stream_module_t ngx_stream_access_module_ctx = { + NULL, /* preconfiguration */ ngx_stream_access_init, /* postconfiguration */ NULL, /* create main configuration */ diff --git a/src/stream/ngx_stream_core_module.c b/src/stream/ngx_stream_core_module.c index ebc2b1c..70b9e2d 100644 --- a/src/stream/ngx_stream_core_module.c +++ b/src/stream/ngx_stream_core_module.c @@ -10,7 +10,9 @@ #include +static ngx_int_t ngx_stream_core_preconfiguration(ngx_conf_t *cf); static void *ngx_stream_core_create_main_conf(ngx_conf_t *cf); +static char *ngx_stream_core_init_main_conf(ngx_conf_t *cf, void *conf); static void *ngx_stream_core_create_srv_conf(ngx_conf_t *cf); static char *ngx_stream_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child); @@ -20,10 +22,26 @@ static char *ngx_stream_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_stream_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static ngx_command_t ngx_stream_core_commands[] = { + { ngx_string("variables_hash_max_size"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_MAIN_CONF_OFFSET, + offsetof(ngx_stream_core_main_conf_t, variables_hash_max_size), + NULL }, + + { ngx_string("variables_hash_bucket_size"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_MAIN_CONF_OFFSET, + offsetof(ngx_stream_core_main_conf_t, variables_hash_bucket_size), + NULL }, + { ngx_string("server"), NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, ngx_stream_core_server, @@ -45,6 +63,20 @@ static ngx_command_t ngx_stream_core_commands[] = { 0, NULL }, + { ngx_string("resolver"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE, + ngx_stream_core_resolver, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("resolver_timeout"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_core_srv_conf_t, resolver_timeout), + NULL }, + { ngx_string("tcp_nodelay"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, @@ -57,10 +89,11 @@ static ngx_command_t ngx_stream_core_commands[] = { static ngx_stream_module_t ngx_stream_core_module_ctx = { + ngx_stream_core_preconfiguration, /* preconfiguration */ NULL, /* postconfiguration */ ngx_stream_core_create_main_conf, /* create main configuration */ - NULL, /* init main configuration */ + ngx_stream_core_init_main_conf, /* init main configuration */ ngx_stream_core_create_srv_conf, /* create server configuration */ ngx_stream_core_merge_srv_conf /* merge server configuration */ @@ -83,6 +116,13 @@ ngx_module_t ngx_stream_core_module = { }; +static ngx_int_t +ngx_stream_core_preconfiguration(ngx_conf_t *cf) +{ + return ngx_stream_variables_add_core_vars(cf); +} + + static void * ngx_stream_core_create_main_conf(ngx_conf_t *cf) { @@ -106,10 +146,32 @@ ngx_stream_core_create_main_conf(ngx_conf_t *cf) return NULL; } + cmcf->variables_hash_max_size = NGX_CONF_UNSET_UINT; + cmcf->variables_hash_bucket_size = NGX_CONF_UNSET_UINT; + return cmcf; } +static char * +ngx_stream_core_init_main_conf(ngx_conf_t *cf, void *conf) +{ + ngx_stream_core_main_conf_t *cmcf = conf; + + ngx_conf_init_uint_value(cmcf->variables_hash_max_size, 1024); + ngx_conf_init_uint_value(cmcf->variables_hash_bucket_size, 64); + + cmcf->variables_hash_bucket_size = + ngx_align(cmcf->variables_hash_bucket_size, ngx_cacheline_size); + + if (cmcf->ncaptures) { + cmcf->ncaptures = (cmcf->ncaptures + 1) * 3; + } + + return NGX_CONF_OK; +} + + static void * ngx_stream_core_create_srv_conf(ngx_conf_t *cf) { @@ -129,6 +191,7 @@ ngx_stream_core_create_srv_conf(ngx_conf_t *cf) cscf->file_name = cf->conf_file->file.name.data; cscf->line = cf->conf_file->line; + cscf->resolver_timeout = NGX_CONF_UNSET_MSEC; cscf->tcp_nodelay = NGX_CONF_UNSET; return cscf; @@ -141,6 +204,27 @@ ngx_stream_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_stream_core_srv_conf_t *prev = parent; ngx_stream_core_srv_conf_t *conf = child; + ngx_conf_merge_msec_value(conf->resolver_timeout, + prev->resolver_timeout, 30000); + + if (conf->resolver == NULL) { + + if (prev->resolver == NULL) { + + /* + * create dummy resolver in stream {} context + * to inherit it in all servers + */ + + prev->resolver = ngx_resolver_create(cf, NULL, 0); + if (prev->resolver == NULL) { + return NGX_CONF_ERROR; + } + } + + conf->resolver = prev->resolver; + } + if (conf->handler == NULL) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "no handler for server in %s:%ui", @@ -248,18 +332,11 @@ ngx_stream_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) static char * ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - size_t len, off; - in_port_t port; ngx_str_t *value; ngx_url_t u; ngx_uint_t i, backlog; - struct sockaddr *sa; - struct sockaddr_in *sin; - ngx_stream_listen_t *ls; + ngx_stream_listen_t *ls, *als; ngx_stream_core_main_conf_t *cmcf; -#if (NGX_HAVE_INET6) - struct sockaddr_in6 *sin6; -#endif value = cf->args->elts; @@ -280,58 +357,6 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); - ls = cmcf->listen.elts; - - for (i = 0; i < cmcf->listen.nelts; i++) { - - sa = &ls[i].u.sockaddr; - - if (sa->sa_family != u.family) { - continue; - } - - switch (sa->sa_family) { - -#if (NGX_HAVE_INET6) - case AF_INET6: - off = offsetof(struct sockaddr_in6, sin6_addr); - len = 16; - sin6 = &ls[i].u.sockaddr_in6; - port = sin6->sin6_port; - break; -#endif - -#if (NGX_HAVE_UNIX_DOMAIN) - case AF_UNIX: - off = offsetof(struct sockaddr_un, sun_path); - len = sizeof(((struct sockaddr_un *) sa)->sun_path); - port = 0; - break; -#endif - - default: /* AF_INET */ - off = offsetof(struct sockaddr_in, sin_addr); - len = 4; - sin = &ls[i].u.sockaddr_in; - port = sin->sin_port; - break; - } - - if (ngx_memcmp(ls[i].u.sockaddr_data + off, u.sockaddr + off, len) - != 0) - { - continue; - } - - if (port != u.port) { - continue; - } - - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "duplicate \"%V\" address and port pair", &u.url); - return NGX_CONF_ERROR; - } - ls = ngx_array_push(&cmcf->listen); if (ls == NULL) { return NGX_CONF_ERROR; @@ -339,7 +364,7 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_memzero(ls, sizeof(ngx_stream_listen_t)); - ngx_memcpy(&ls->u.sockaddr, u.sockaddr, u.socklen); + ngx_memcpy(&ls->sockaddr.sockaddr, &u.sockaddr, u.socklen); ls->socklen = u.socklen; ls->backlog = NGX_LISTEN_BACKLOG; @@ -384,11 +409,10 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (ngx_strncmp(value[i].data, "ipv6only=o", 10) == 0) { #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + size_t len; u_char buf[NGX_SOCKADDR_STRLEN]; - sa = &ls->u.sockaddr; - - if (sa->sa_family == AF_INET6) { + if (ls->sockaddr.sockaddr.sa_family == AF_INET6) { if (ngx_strcmp(&value[i].data[10], "n") == 0) { ls->ipv6only = 1; @@ -406,7 +430,7 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ls->bind = 1; } else { - len = ngx_sock_ntop(sa, ls->socklen, buf, + len = ngx_sock_ntop(&ls->sockaddr.sockaddr, ls->socklen, buf, NGX_SOCKADDR_STRLEN, 1); ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, @@ -558,5 +582,46 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } } + als = cmcf->listen.elts; + + for (i = 0; i < cmcf->listen.nelts - 1; i++) { + if (ls->type != als[i].type) { + continue; + } + + if (ngx_cmp_sockaddr(&als[i].sockaddr.sockaddr, als[i].socklen, + &ls->sockaddr.sockaddr, ls->socklen, 1) + != NGX_OK) + { + continue; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate \"%V\" address and port pair", &u.url); + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_stream_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_core_srv_conf_t *cscf = conf; + + ngx_str_t *value; + + if (cscf->resolver) { + return "is duplicate"; + } + + value = cf->args->elts; + + cscf->resolver = ngx_resolver_create(cf, &value[1], cf->args->nelts - 1); + if (cscf->resolver == NULL) { + return NGX_CONF_ERROR; + } + return NGX_CONF_OK; } diff --git a/src/stream/ngx_stream_geo_module.c b/src/stream/ngx_stream_geo_module.c new file mode 100644 index 0000000..ed1a488 --- /dev/null +++ b/src/stream/ngx_stream_geo_module.c @@ -0,0 +1,1572 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + ngx_stream_variable_value_t *value; + u_short start; + u_short end; +} ngx_stream_geo_range_t; + + +typedef struct { + ngx_radix_tree_t *tree; +#if (NGX_HAVE_INET6) + ngx_radix_tree_t *tree6; +#endif +} ngx_stream_geo_trees_t; + + +typedef struct { + ngx_stream_geo_range_t **low; + ngx_stream_variable_value_t *default_value; +} ngx_stream_geo_high_ranges_t; + + +typedef struct { + ngx_str_node_t sn; + ngx_stream_variable_value_t *value; + size_t offset; +} ngx_stream_geo_variable_value_node_t; + + +typedef struct { + ngx_stream_variable_value_t *value; + ngx_str_t *net; + ngx_stream_geo_high_ranges_t high; + ngx_radix_tree_t *tree; +#if (NGX_HAVE_INET6) + ngx_radix_tree_t *tree6; +#endif + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; + ngx_pool_t *pool; + ngx_pool_t *temp_pool; + + size_t data_size; + + ngx_str_t include_name; + ngx_uint_t includes; + ngx_uint_t entries; + + unsigned ranges:1; + unsigned outside_entries:1; + unsigned allow_binary_include:1; + unsigned binary_include:1; +} ngx_stream_geo_conf_ctx_t; + + +typedef struct { + union { + ngx_stream_geo_trees_t trees; + ngx_stream_geo_high_ranges_t high; + } u; + + ngx_int_t index; +} ngx_stream_geo_ctx_t; + + +static ngx_int_t ngx_stream_geo_addr(ngx_stream_session_t *s, + ngx_stream_geo_ctx_t *ctx, ngx_addr_t *addr); + +static char *ngx_stream_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); +static char *ngx_stream_geo_range(ngx_conf_t *cf, + ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *value); +static char *ngx_stream_geo_add_range(ngx_conf_t *cf, + ngx_stream_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end); +static ngx_uint_t ngx_stream_geo_delete_range(ngx_conf_t *cf, + ngx_stream_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end); +static char *ngx_stream_geo_cidr(ngx_conf_t *cf, + ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *value); +static char *ngx_stream_geo_cidr_add(ngx_conf_t *cf, + ngx_stream_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr, ngx_str_t *value, + ngx_str_t *net); +static ngx_stream_variable_value_t *ngx_stream_geo_value(ngx_conf_t *cf, + ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *value); +static ngx_int_t ngx_stream_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, + ngx_cidr_t *cidr); +static char *ngx_stream_geo_include(ngx_conf_t *cf, + ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *name); +static ngx_int_t ngx_stream_geo_include_binary_base(ngx_conf_t *cf, + ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *name); +static void ngx_stream_geo_create_binary_base(ngx_stream_geo_conf_ctx_t *ctx); +static u_char *ngx_stream_geo_copy_values(u_char *base, u_char *p, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); + + +static ngx_command_t ngx_stream_geo_commands[] = { + + { ngx_string("geo"), + NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12, + ngx_stream_geo_block, + 0, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_geo_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_geo_module = { + NGX_MODULE_V1, + &ngx_stream_geo_module_ctx, /* module context */ + ngx_stream_geo_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 +}; + + +typedef struct { + u_char GEORNG[6]; + u_char version; + u_char ptr_size; + uint32_t endianness; + uint32_t crc32; +} ngx_stream_geo_header_t; + + +static ngx_stream_geo_header_t ngx_stream_geo_header = { + { 'G', 'E', 'O', 'R', 'N', 'G' }, 0, sizeof(void *), 0x12345678, 0 +}; + + +/* geo range is AF_INET only */ + +static ngx_int_t +ngx_stream_geo_cidr_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_stream_geo_ctx_t *ctx = (ngx_stream_geo_ctx_t *) data; + + in_addr_t inaddr; + ngx_addr_t addr; + struct sockaddr_in *sin; + ngx_stream_variable_value_t *vv; +#if (NGX_HAVE_INET6) + u_char *p; + struct in6_addr *inaddr6; +#endif + + if (ngx_stream_geo_addr(s, ctx, &addr) != NGX_OK) { + vv = (ngx_stream_variable_value_t *) + ngx_radix32tree_find(ctx->u.trees.tree, INADDR_NONE); + goto done; + } + + switch (addr.sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; + p = inaddr6->s6_addr; + + if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { + inaddr = p[12] << 24; + inaddr += p[13] << 16; + inaddr += p[14] << 8; + inaddr += p[15]; + + vv = (ngx_stream_variable_value_t *) + ngx_radix32tree_find(ctx->u.trees.tree, inaddr); + + } else { + vv = (ngx_stream_variable_value_t *) + ngx_radix128tree_find(ctx->u.trees.tree6, p); + } + + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) addr.sockaddr; + inaddr = ntohl(sin->sin_addr.s_addr); + + vv = (ngx_stream_variable_value_t *) + ngx_radix32tree_find(ctx->u.trees.tree, inaddr); + + break; + } + +done: + + *v = *vv; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream geo: %v", v); + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_geo_range_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_stream_geo_ctx_t *ctx = (ngx_stream_geo_ctx_t *) data; + + in_addr_t inaddr; + ngx_addr_t addr; + ngx_uint_t n; + struct sockaddr_in *sin; + ngx_stream_geo_range_t *range; +#if (NGX_HAVE_INET6) + u_char *p; + struct in6_addr *inaddr6; +#endif + + *v = *ctx->u.high.default_value; + + if (ngx_stream_geo_addr(s, ctx, &addr) == NGX_OK) { + + switch (addr.sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; + + if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { + p = inaddr6->s6_addr; + + inaddr = p[12] << 24; + inaddr += p[13] << 16; + inaddr += p[14] << 8; + inaddr += p[15]; + + } else { + inaddr = INADDR_NONE; + } + + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) addr.sockaddr; + inaddr = ntohl(sin->sin_addr.s_addr); + break; + } + + } else { + inaddr = INADDR_NONE; + } + + if (ctx->u.high.low) { + range = ctx->u.high.low[inaddr >> 16]; + + if (range) { + n = inaddr & 0xffff; + do { + if (n >= (ngx_uint_t) range->start + && n <= (ngx_uint_t) range->end) + { + *v = *range->value; + break; + } + } while ((++range)->value); + } + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream geo: %v", v); + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_geo_addr(ngx_stream_session_t *s, ngx_stream_geo_ctx_t *ctx, + ngx_addr_t *addr) +{ + ngx_stream_variable_value_t *v; + + if (ctx->index == -1) { + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream geo started: %V", &s->connection->addr_text); + + addr->sockaddr = s->connection->sockaddr; + addr->socklen = s->connection->socklen; + /* addr->name = s->connection->addr_text; */ + + return NGX_OK; + } + + v = ngx_stream_get_flushed_variable(s, ctx->index); + + if (v == NULL || v->not_found) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream geo not found"); + + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream geo started: %v", v); + + if (ngx_parse_addr(s->connection->pool, addr, v->data, v->len) == NGX_OK) { + return NGX_OK; + } + + return NGX_ERROR; +} + + +static char * +ngx_stream_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *rv; + size_t len; + ngx_str_t *value, name; + ngx_uint_t i; + ngx_conf_t save; + ngx_pool_t *pool; + ngx_array_t *a; + ngx_stream_variable_t *var; + ngx_stream_geo_ctx_t *geo; + ngx_stream_geo_conf_ctx_t ctx; +#if (NGX_HAVE_INET6) + static struct in6_addr zero; +#endif + + value = cf->args->elts; + + geo = ngx_palloc(cf->pool, sizeof(ngx_stream_geo_ctx_t)); + if (geo == NULL) { + return NGX_CONF_ERROR; + } + + name = value[1]; + + if (name.data[0] != '$') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"%V\"", &name); + return NGX_CONF_ERROR; + } + + name.len--; + name.data++; + + if (cf->args->nelts == 3) { + + geo->index = ngx_stream_get_variable_index(cf, &name); + if (geo->index == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + name = value[2]; + + if (name.data[0] != '$') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"%V\"", &name); + return NGX_CONF_ERROR; + } + + name.len--; + name.data++; + + } else { + geo->index = -1; + } + + var = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE); + if (var == NULL) { + return NGX_CONF_ERROR; + } + + pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log); + if (pool == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(&ctx, sizeof(ngx_stream_geo_conf_ctx_t)); + + ctx.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log); + if (ctx.temp_pool == NULL) { + return NGX_CONF_ERROR; + } + + ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, ngx_str_rbtree_insert_value); + + ctx.pool = cf->pool; + ctx.data_size = sizeof(ngx_stream_geo_header_t) + + sizeof(ngx_stream_variable_value_t) + + 0x10000 * sizeof(ngx_stream_geo_range_t *); + ctx.allow_binary_include = 1; + + save = *cf; + cf->pool = pool; + cf->ctx = &ctx; + cf->handler = ngx_stream_geo; + cf->handler_conf = conf; + + rv = ngx_conf_parse(cf, NULL); + + *cf = save; + + if (ctx.ranges) { + + if (ctx.high.low && !ctx.binary_include) { + for (i = 0; i < 0x10000; i++) { + a = (ngx_array_t *) ctx.high.low[i]; + + if (a == NULL || a->nelts == 0) { + continue; + } + + len = a->nelts * sizeof(ngx_stream_geo_range_t); + + ctx.high.low[i] = ngx_palloc(cf->pool, len + sizeof(void *)); + if (ctx.high.low[i] == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memcpy(ctx.high.low[i], a->elts, len); + ctx.high.low[i][a->nelts].value = NULL; + ctx.data_size += len + sizeof(void *); + } + + if (ctx.allow_binary_include + && !ctx.outside_entries + && ctx.entries > 100000 + && ctx.includes == 1) + { + ngx_stream_geo_create_binary_base(&ctx); + } + } + + if (ctx.high.default_value == NULL) { + ctx.high.default_value = &ngx_stream_variable_null_value; + } + + geo->u.high = ctx.high; + + var->get_handler = ngx_stream_geo_range_variable; + var->data = (uintptr_t) geo; + + ngx_destroy_pool(ctx.temp_pool); + ngx_destroy_pool(pool); + + } else { + if (ctx.tree == NULL) { + ctx.tree = ngx_radix_tree_create(cf->pool, -1); + if (ctx.tree == NULL) { + return NGX_CONF_ERROR; + } + } + + geo->u.trees.tree = ctx.tree; + +#if (NGX_HAVE_INET6) + if (ctx.tree6 == NULL) { + ctx.tree6 = ngx_radix_tree_create(cf->pool, -1); + if (ctx.tree6 == NULL) { + return NGX_CONF_ERROR; + } + } + + geo->u.trees.tree6 = ctx.tree6; +#endif + + var->get_handler = ngx_stream_geo_cidr_variable; + var->data = (uintptr_t) geo; + + ngx_destroy_pool(ctx.temp_pool); + ngx_destroy_pool(pool); + + if (ngx_radix32tree_insert(ctx.tree, 0, 0, + (uintptr_t) &ngx_stream_variable_null_value) + == NGX_ERROR) + { + return NGX_CONF_ERROR; + } + + /* NGX_BUSY is okay (default was set explicitly) */ + +#if (NGX_HAVE_INET6) + if (ngx_radix128tree_insert(ctx.tree6, zero.s6_addr, zero.s6_addr, + (uintptr_t) &ngx_stream_variable_null_value) + == NGX_ERROR) + { + return NGX_CONF_ERROR; + } +#endif + } + + return rv; +} + + +static char * +ngx_stream_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) +{ + char *rv; + ngx_str_t *value; + ngx_stream_geo_conf_ctx_t *ctx; + + ctx = cf->ctx; + + value = cf->args->elts; + + if (cf->args->nelts == 1) { + + if (ngx_strcmp(value[0].data, "ranges") == 0) { + + if (ctx->tree +#if (NGX_HAVE_INET6) + || ctx->tree6 +#endif + ) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"ranges\" directive must be " + "the first directive inside \"geo\" block"); + goto failed; + } + + ctx->ranges = 1; + + rv = NGX_CONF_OK; + + goto done; + } + } + + if (cf->args->nelts != 2) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid number of the geo parameters"); + goto failed; + } + + if (ngx_strcmp(value[0].data, "include") == 0) { + + rv = ngx_stream_geo_include(cf, ctx, &value[1]); + + goto done; + } + + if (ctx->ranges) { + rv = ngx_stream_geo_range(cf, ctx, value); + + } else { + rv = ngx_stream_geo_cidr(cf, ctx, value); + } + +done: + + ngx_reset_pool(cf->pool); + + return rv; + +failed: + + ngx_reset_pool(cf->pool); + + return NGX_CONF_ERROR; +} + + +static char * +ngx_stream_geo_range(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx, + ngx_str_t *value) +{ + u_char *p, *last; + in_addr_t start, end; + ngx_str_t *net; + ngx_uint_t del; + + if (ngx_strcmp(value[0].data, "default") == 0) { + + if (ctx->high.default_value) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "duplicate default geo range value: \"%V\", old value: \"%v\"", + &value[1], ctx->high.default_value); + } + + ctx->high.default_value = ngx_stream_geo_value(cf, ctx, &value[1]); + if (ctx->high.default_value == NULL) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; + } + + if (ctx->binary_include) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "binary geo range base \"%s\" cannot be mixed with usual entries", + ctx->include_name.data); + return NGX_CONF_ERROR; + } + + if (ctx->high.low == NULL) { + ctx->high.low = ngx_pcalloc(ctx->pool, + 0x10000 * sizeof(ngx_stream_geo_range_t *)); + if (ctx->high.low == NULL) { + return NGX_CONF_ERROR; + } + } + + ctx->entries++; + ctx->outside_entries = 1; + + if (ngx_strcmp(value[0].data, "delete") == 0) { + net = &value[1]; + del = 1; + + } else { + net = &value[0]; + del = 0; + } + + last = net->data + net->len; + + p = ngx_strlchr(net->data, last, '-'); + + if (p == NULL) { + goto invalid; + } + + start = ngx_inet_addr(net->data, p - net->data); + + if (start == INADDR_NONE) { + goto invalid; + } + + start = ntohl(start); + + p++; + + end = ngx_inet_addr(p, last - p); + + if (end == INADDR_NONE) { + goto invalid; + } + + end = ntohl(end); + + if (start > end) { + goto invalid; + } + + if (del) { + if (ngx_stream_geo_delete_range(cf, ctx, start, end)) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "no address range \"%V\" to delete", net); + } + + return NGX_CONF_OK; + } + + ctx->value = ngx_stream_geo_value(cf, ctx, &value[1]); + + if (ctx->value == NULL) { + return NGX_CONF_ERROR; + } + + ctx->net = net; + + return ngx_stream_geo_add_range(cf, ctx, start, end); + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid range \"%V\"", net); + + return NGX_CONF_ERROR; +} + + +/* the add procedure is optimized to add a growing up sequence */ + +static char * +ngx_stream_geo_add_range(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx, + in_addr_t start, in_addr_t end) +{ + in_addr_t n; + ngx_uint_t h, i, s, e; + ngx_array_t *a; + ngx_stream_geo_range_t *range; + + for (n = start; n <= end; n = (n + 0x10000) & 0xffff0000) { + + h = n >> 16; + + if (n == start) { + s = n & 0xffff; + } else { + s = 0; + } + + if ((n | 0xffff) > end) { + e = end & 0xffff; + + } else { + e = 0xffff; + } + + a = (ngx_array_t *) ctx->high.low[h]; + + if (a == NULL) { + a = ngx_array_create(ctx->temp_pool, 64, + sizeof(ngx_stream_geo_range_t)); + if (a == NULL) { + return NGX_CONF_ERROR; + } + + ctx->high.low[h] = (ngx_stream_geo_range_t *) a; + } + + i = a->nelts; + range = a->elts; + + while (i) { + + i--; + + if (e < (ngx_uint_t) range[i].start) { + continue; + } + + if (s > (ngx_uint_t) range[i].end) { + + /* add after the range */ + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; + } + + range = a->elts; + + ngx_memmove(&range[i + 2], &range[i + 1], + (a->nelts - 2 - i) * sizeof(ngx_stream_geo_range_t)); + + range[i + 1].start = (u_short) s; + range[i + 1].end = (u_short) e; + range[i + 1].value = ctx->value; + + goto next; + } + + if (s == (ngx_uint_t) range[i].start + && e == (ngx_uint_t) range[i].end) + { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "duplicate range \"%V\", value: \"%v\", old value: \"%v\"", + ctx->net, ctx->value, range[i].value); + + range[i].value = ctx->value; + + goto next; + } + + if (s > (ngx_uint_t) range[i].start + && e < (ngx_uint_t) range[i].end) + { + /* split the range and insert the new one */ + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; + } + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; + } + + range = a->elts; + + ngx_memmove(&range[i + 3], &range[i + 1], + (a->nelts - 3 - i) * sizeof(ngx_stream_geo_range_t)); + + range[i + 2].start = (u_short) (e + 1); + range[i + 2].end = range[i].end; + range[i + 2].value = range[i].value; + + range[i + 1].start = (u_short) s; + range[i + 1].end = (u_short) e; + range[i + 1].value = ctx->value; + + range[i].end = (u_short) (s - 1); + + goto next; + } + + if (s == (ngx_uint_t) range[i].start + && e < (ngx_uint_t) range[i].end) + { + /* shift the range start and insert the new range */ + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; + } + + range = a->elts; + + ngx_memmove(&range[i + 1], &range[i], + (a->nelts - 1 - i) * sizeof(ngx_stream_geo_range_t)); + + range[i + 1].start = (u_short) (e + 1); + + range[i].start = (u_short) s; + range[i].end = (u_short) e; + range[i].value = ctx->value; + + goto next; + } + + if (s > (ngx_uint_t) range[i].start + && e == (ngx_uint_t) range[i].end) + { + /* shift the range end and insert the new range */ + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; + } + + range = a->elts; + + ngx_memmove(&range[i + 2], &range[i + 1], + (a->nelts - 2 - i) * sizeof(ngx_stream_geo_range_t)); + + range[i + 1].start = (u_short) s; + range[i + 1].end = (u_short) e; + range[i + 1].value = ctx->value; + + range[i].end = (u_short) (s - 1); + + goto next; + } + + s = (ngx_uint_t) range[i].start; + e = (ngx_uint_t) range[i].end; + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "range \"%V\" overlaps \"%d.%d.%d.%d-%d.%d.%d.%d\"", + ctx->net, + h >> 8, h & 0xff, s >> 8, s & 0xff, + h >> 8, h & 0xff, e >> 8, e & 0xff); + + return NGX_CONF_ERROR; + } + + /* add the first range */ + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; + } + + range->start = (u_short) s; + range->end = (u_short) e; + range->value = ctx->value; + + next: + + continue; + } + + return NGX_CONF_OK; +} + + +static ngx_uint_t +ngx_stream_geo_delete_range(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx, + in_addr_t start, in_addr_t end) +{ + in_addr_t n; + ngx_uint_t h, i, s, e, warn; + ngx_array_t *a; + ngx_stream_geo_range_t *range; + + warn = 0; + + for (n = start; n <= end; n += 0x10000) { + + h = n >> 16; + + if (n == start) { + s = n & 0xffff; + } else { + s = 0; + } + + if ((n | 0xffff) > end) { + e = end & 0xffff; + + } else { + e = 0xffff; + } + + a = (ngx_array_t *) ctx->high.low[h]; + + if (a == NULL) { + warn = 1; + continue; + } + + range = a->elts; + for (i = 0; i < a->nelts; i++) { + + if (s == (ngx_uint_t) range[i].start + && e == (ngx_uint_t) range[i].end) + { + ngx_memmove(&range[i], &range[i + 1], + (a->nelts - 1 - i) * sizeof(ngx_stream_geo_range_t)); + + a->nelts--; + + break; + } + + if (s != (ngx_uint_t) range[i].start + && e != (ngx_uint_t) range[i].end) + { + continue; + } + + warn = 1; + } + } + + return warn; +} + + +static char * +ngx_stream_geo_cidr(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx, + ngx_str_t *value) +{ + char *rv; + ngx_int_t rc, del; + ngx_str_t *net; + ngx_cidr_t cidr; + + if (ctx->tree == NULL) { + ctx->tree = ngx_radix_tree_create(ctx->pool, -1); + if (ctx->tree == NULL) { + return NGX_CONF_ERROR; + } + } + +#if (NGX_HAVE_INET6) + if (ctx->tree6 == NULL) { + ctx->tree6 = ngx_radix_tree_create(ctx->pool, -1); + if (ctx->tree6 == NULL) { + return NGX_CONF_ERROR; + } + } +#endif + + if (ngx_strcmp(value[0].data, "default") == 0) { + cidr.family = AF_INET; + cidr.u.in.addr = 0; + cidr.u.in.mask = 0; + + rv = ngx_stream_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]); + + if (rv != NGX_CONF_OK) { + return rv; + } + +#if (NGX_HAVE_INET6) + cidr.family = AF_INET6; + ngx_memzero(&cidr.u.in6, sizeof(ngx_in6_cidr_t)); + + rv = ngx_stream_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]); + + if (rv != NGX_CONF_OK) { + return rv; + } +#endif + + return NGX_CONF_OK; + } + + if (ngx_strcmp(value[0].data, "delete") == 0) { + net = &value[1]; + del = 1; + + } else { + net = &value[0]; + del = 0; + } + + if (ngx_stream_geo_cidr_value(cf, net, &cidr) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (cidr.family == AF_INET) { + cidr.u.in.addr = ntohl(cidr.u.in.addr); + cidr.u.in.mask = ntohl(cidr.u.in.mask); + } + + if (del) { + switch (cidr.family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + rc = ngx_radix128tree_delete(ctx->tree6, + cidr.u.in6.addr.s6_addr, + cidr.u.in6.mask.s6_addr); + break; +#endif + + default: /* AF_INET */ + rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr, + cidr.u.in.mask); + break; + } + + if (rc != NGX_OK) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "no network \"%V\" to delete", net); + } + + return NGX_CONF_OK; + } + + return ngx_stream_geo_cidr_add(cf, ctx, &cidr, &value[1], net); +} + + +static char * +ngx_stream_geo_cidr_add(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx, + ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net) +{ + ngx_int_t rc; + ngx_stream_variable_value_t *val, *old; + + val = ngx_stream_geo_value(cf, ctx, value); + + if (val == NULL) { + return NGX_CONF_ERROR; + } + + switch (cidr->family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr, + cidr->u.in6.mask.s6_addr, + (uintptr_t) val); + + if (rc == NGX_OK) { + return NGX_CONF_OK; + } + + if (rc == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + /* rc == NGX_BUSY */ + + old = (ngx_stream_variable_value_t *) + ngx_radix128tree_find(ctx->tree6, + cidr->u.in6.addr.s6_addr); + + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "duplicate network \"%V\", value: \"%v\", old value: \"%v\"", + net, val, old); + + rc = ngx_radix128tree_delete(ctx->tree6, + cidr->u.in6.addr.s6_addr, + cidr->u.in6.mask.s6_addr); + + if (rc == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree"); + return NGX_CONF_ERROR; + } + + rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr, + cidr->u.in6.mask.s6_addr, + (uintptr_t) val); + + break; +#endif + + default: /* AF_INET */ + rc = ngx_radix32tree_insert(ctx->tree, cidr->u.in.addr, + cidr->u.in.mask, (uintptr_t) val); + + if (rc == NGX_OK) { + return NGX_CONF_OK; + } + + if (rc == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + /* rc == NGX_BUSY */ + + old = (ngx_stream_variable_value_t *) + ngx_radix32tree_find(ctx->tree, cidr->u.in.addr); + + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "duplicate network \"%V\", value: \"%v\", old value: \"%v\"", + net, val, old); + + rc = ngx_radix32tree_delete(ctx->tree, + cidr->u.in.addr, cidr->u.in.mask); + + if (rc == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree"); + return NGX_CONF_ERROR; + } + + rc = ngx_radix32tree_insert(ctx->tree, cidr->u.in.addr, + cidr->u.in.mask, (uintptr_t) val); + + break; + } + + if (rc == NGX_OK) { + return NGX_CONF_OK; + } + + return NGX_CONF_ERROR; +} + + +static ngx_stream_variable_value_t * +ngx_stream_geo_value(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx, + ngx_str_t *value) +{ + uint32_t hash; + ngx_stream_variable_value_t *val; + ngx_stream_geo_variable_value_node_t *gvvn; + + hash = ngx_crc32_long(value->data, value->len); + + gvvn = (ngx_stream_geo_variable_value_node_t *) + ngx_str_rbtree_lookup(&ctx->rbtree, value, hash); + + if (gvvn) { + return gvvn->value; + } + + val = ngx_palloc(ctx->pool, sizeof(ngx_stream_variable_value_t)); + if (val == NULL) { + return NULL; + } + + val->len = value->len; + val->data = ngx_pstrdup(ctx->pool, value); + if (val->data == NULL) { + return NULL; + } + + val->valid = 1; + val->no_cacheable = 0; + val->not_found = 0; + + gvvn = ngx_palloc(ctx->temp_pool, + sizeof(ngx_stream_geo_variable_value_node_t)); + if (gvvn == NULL) { + return NULL; + } + + gvvn->sn.node.key = hash; + gvvn->sn.str.len = val->len; + gvvn->sn.str.data = val->data; + gvvn->value = val; + gvvn->offset = 0; + + ngx_rbtree_insert(&ctx->rbtree, &gvvn->sn.node); + + ctx->data_size += ngx_align(sizeof(ngx_stream_variable_value_t) + + value->len, sizeof(void *)); + + return val; +} + + +static ngx_int_t +ngx_stream_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr) +{ + ngx_int_t rc; + + if (ngx_strcmp(net->data, "255.255.255.255") == 0) { + cidr->family = AF_INET; + cidr->u.in.addr = 0xffffffff; + cidr->u.in.mask = 0xffffffff; + + return NGX_OK; + } + + rc = ngx_ptocidr(net, cidr); + + if (rc == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net); + return NGX_ERROR; + } + + if (rc == NGX_DONE) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "low address bits of %V are meaningless", net); + } + + return NGX_OK; +} + + +static char * +ngx_stream_geo_include(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx, + ngx_str_t *name) +{ + char *rv; + ngx_str_t file; + + file.len = name->len + 4; + file.data = ngx_pnalloc(ctx->temp_pool, name->len + 5); + if (file.data == NULL) { + return NGX_CONF_ERROR; + } + + ngx_sprintf(file.data, "%V.bin%Z", name); + + if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (ctx->ranges) { + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); + + switch (ngx_stream_geo_include_binary_base(cf, ctx, &file)) { + case NGX_OK: + return NGX_CONF_OK; + case NGX_ERROR: + return NGX_CONF_ERROR; + default: + break; + } + } + + file.len -= 4; + file.data[file.len] = '\0'; + + ctx->include_name = file; + + if (ctx->outside_entries) { + ctx->allow_binary_include = 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); + + rv = ngx_conf_parse(cf, &file); + + ctx->includes++; + ctx->outside_entries = 0; + + return rv; +} + + +static ngx_int_t +ngx_stream_geo_include_binary_base(ngx_conf_t *cf, + ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *name) +{ + u_char *base, ch; + time_t mtime; + size_t size, len; + ssize_t n; + uint32_t crc32; + ngx_err_t err; + ngx_int_t rc; + ngx_uint_t i; + ngx_file_t file; + ngx_file_info_t fi; + ngx_stream_geo_range_t *range, **ranges; + ngx_stream_geo_header_t *header; + ngx_stream_variable_value_t *vv; + + ngx_memzero(&file, sizeof(ngx_file_t)); + file.name = *name; + file.log = cf->log; + + file.fd = ngx_open_file(name->data, NGX_FILE_RDONLY, 0, 0); + if (file.fd == NGX_INVALID_FILE) { + err = ngx_errno; + if (err != NGX_ENOENT) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, err, + ngx_open_file_n " \"%s\" failed", name->data); + } + return NGX_DECLINED; + } + + if (ctx->outside_entries) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "binary geo range base \"%s\" cannot be mixed with usual entries", + name->data); + rc = NGX_ERROR; + goto done; + } + + if (ctx->binary_include) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "second binary geo range base \"%s\" cannot be mixed with \"%s\"", + name->data, ctx->include_name.data); + rc = NGX_ERROR; + goto done; + } + + if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, + ngx_fd_info_n " \"%s\" failed", name->data); + goto failed; + } + + size = (size_t) ngx_file_size(&fi); + mtime = ngx_file_mtime(&fi); + + ch = name->data[name->len - 4]; + name->data[name->len - 4] = '\0'; + + if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, + ngx_file_info_n " \"%s\" failed", name->data); + goto failed; + } + + name->data[name->len - 4] = ch; + + if (mtime < ngx_file_mtime(&fi)) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "stale binary geo range base \"%s\"", name->data); + goto failed; + } + + base = ngx_palloc(ctx->pool, size); + if (base == NULL) { + goto failed; + } + + n = ngx_read_file(&file, base, size, 0); + + if (n == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, + ngx_read_file_n " \"%s\" failed", name->data); + goto failed; + } + + if ((size_t) n != size) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, 0, + ngx_read_file_n " \"%s\" returned only %z bytes instead of %z", + name->data, n, size); + goto failed; + } + + header = (ngx_stream_geo_header_t *) base; + + if (size < 16 || ngx_memcmp(&ngx_stream_geo_header, header, 12) != 0) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "incompatible binary geo range base \"%s\"", name->data); + goto failed; + } + + ngx_crc32_init(crc32); + + vv = (ngx_stream_variable_value_t *) + (base + sizeof(ngx_stream_geo_header_t)); + + while (vv->data) { + len = ngx_align(sizeof(ngx_stream_variable_value_t) + vv->len, + sizeof(void *)); + ngx_crc32_update(&crc32, (u_char *) vv, len); + vv->data += (size_t) base; + vv = (ngx_stream_variable_value_t *) ((u_char *) vv + len); + } + ngx_crc32_update(&crc32, (u_char *) vv, + sizeof(ngx_stream_variable_value_t)); + vv++; + + ranges = (ngx_stream_geo_range_t **) vv; + + for (i = 0; i < 0x10000; i++) { + ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *)); + if (ranges[i]) { + ranges[i] = (ngx_stream_geo_range_t *) + ((u_char *) ranges[i] + (size_t) base); + } + } + + range = (ngx_stream_geo_range_t *) &ranges[0x10000]; + + while ((u_char *) range < base + size) { + while (range->value) { + ngx_crc32_update(&crc32, (u_char *) range, + sizeof(ngx_stream_geo_range_t)); + range->value = (ngx_stream_variable_value_t *) + ((u_char *) range->value + (size_t) base); + range++; + } + ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *)); + range = (ngx_stream_geo_range_t *) ((u_char *) range + sizeof(void *)); + } + + ngx_crc32_final(crc32); + + if (crc32 != header->crc32) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "CRC32 mismatch in binary geo range base \"%s\"", name->data); + goto failed; + } + + ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0, + "using binary geo range base \"%s\"", name->data); + + ctx->include_name = *name; + ctx->binary_include = 1; + ctx->high.low = ranges; + rc = NGX_OK; + + goto done; + +failed: + + rc = NGX_DECLINED; + +done: + + if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", name->data); + } + + return rc; +} + + +static void +ngx_stream_geo_create_binary_base(ngx_stream_geo_conf_ctx_t *ctx) +{ + u_char *p; + uint32_t hash; + ngx_str_t s; + ngx_uint_t i; + ngx_file_mapping_t fm; + ngx_stream_geo_range_t *r, *range, **ranges; + ngx_stream_geo_header_t *header; + ngx_stream_geo_variable_value_node_t *gvvn; + + fm.name = ngx_pnalloc(ctx->temp_pool, ctx->include_name.len + 5); + if (fm.name == NULL) { + return; + } + + ngx_sprintf(fm.name, "%V.bin%Z", &ctx->include_name); + + fm.size = ctx->data_size; + fm.log = ctx->pool->log; + + ngx_log_error(NGX_LOG_NOTICE, fm.log, 0, + "creating binary geo range base \"%s\"", fm.name); + + if (ngx_create_file_mapping(&fm) != NGX_OK) { + return; + } + + p = ngx_cpymem(fm.addr, &ngx_stream_geo_header, + sizeof(ngx_stream_geo_header_t)); + + p = ngx_stream_geo_copy_values(fm.addr, p, ctx->rbtree.root, + ctx->rbtree.sentinel); + + p += sizeof(ngx_stream_variable_value_t); + + ranges = (ngx_stream_geo_range_t **) p; + + p += 0x10000 * sizeof(ngx_stream_geo_range_t *); + + for (i = 0; i < 0x10000; i++) { + r = ctx->high.low[i]; + if (r == NULL) { + continue; + } + + range = (ngx_stream_geo_range_t *) p; + ranges[i] = (ngx_stream_geo_range_t *) (p - (u_char *) fm.addr); + + do { + s.len = r->value->len; + s.data = r->value->data; + hash = ngx_crc32_long(s.data, s.len); + gvvn = (ngx_stream_geo_variable_value_node_t *) + ngx_str_rbtree_lookup(&ctx->rbtree, &s, hash); + + range->value = (ngx_stream_variable_value_t *) gvvn->offset; + range->start = r->start; + range->end = r->end; + range++; + + } while ((++r)->value); + + range->value = NULL; + + p = (u_char *) range + sizeof(void *); + } + + header = fm.addr; + header->crc32 = ngx_crc32_long((u_char *) fm.addr + + sizeof(ngx_stream_geo_header_t), + fm.size - sizeof(ngx_stream_geo_header_t)); + + ngx_close_file_mapping(&fm); +} + + +static u_char * +ngx_stream_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node, + ngx_rbtree_node_t *sentinel) +{ + ngx_stream_variable_value_t *vv; + ngx_stream_geo_variable_value_node_t *gvvn; + + if (node == sentinel) { + return p; + } + + gvvn = (ngx_stream_geo_variable_value_node_t *) node; + gvvn->offset = p - base; + + vv = (ngx_stream_variable_value_t *) p; + *vv = *gvvn->value; + p += sizeof(ngx_stream_variable_value_t); + vv->data = (u_char *) (p - base); + + p = ngx_cpymem(p, gvvn->sn.str.data, gvvn->sn.str.len); + + p = ngx_align_ptr(p, sizeof(void *)); + + p = ngx_stream_geo_copy_values(base, p, node->left, sentinel); + + return ngx_stream_geo_copy_values(base, p, node->right, sentinel); +} diff --git a/src/stream/ngx_stream_geoip_module.c b/src/stream/ngx_stream_geoip_module.c new file mode 100644 index 0000000..f694033 --- /dev/null +++ b/src/stream/ngx_stream_geoip_module.c @@ -0,0 +1,814 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + +#include +#include + + +#define NGX_GEOIP_COUNTRY_CODE 0 +#define NGX_GEOIP_COUNTRY_CODE3 1 +#define NGX_GEOIP_COUNTRY_NAME 2 + + +typedef struct { + GeoIP *country; + GeoIP *org; + GeoIP *city; +#if (NGX_HAVE_GEOIP_V6) + unsigned country_v6:1; + unsigned org_v6:1; + unsigned city_v6:1; +#endif +} ngx_stream_geoip_conf_t; + + +typedef struct { + ngx_str_t *name; + uintptr_t data; +} ngx_stream_geoip_var_t; + + +typedef const char *(*ngx_stream_geoip_variable_handler_pt)(GeoIP *, + u_long addr); + + +ngx_stream_geoip_variable_handler_pt ngx_stream_geoip_country_functions[] = { + GeoIP_country_code_by_ipnum, + GeoIP_country_code3_by_ipnum, + GeoIP_country_name_by_ipnum, +}; + + +#if (NGX_HAVE_GEOIP_V6) + +typedef const char *(*ngx_stream_geoip_variable_handler_v6_pt)(GeoIP *, + geoipv6_t addr); + + +ngx_stream_geoip_variable_handler_v6_pt + ngx_stream_geoip_country_v6_functions[] = +{ + GeoIP_country_code_by_ipnum_v6, + GeoIP_country_code3_by_ipnum_v6, + GeoIP_country_name_by_ipnum_v6, +}; + +#endif + + +static ngx_int_t ngx_stream_geoip_country_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_geoip_org_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_geoip_city_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_geoip_region_name_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_geoip_city_float_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_geoip_city_int_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static GeoIPRecord *ngx_stream_geoip_get_city_record(ngx_stream_session_t *s); + +static ngx_int_t ngx_stream_geoip_add_variables(ngx_conf_t *cf); +static void *ngx_stream_geoip_create_conf(ngx_conf_t *cf); +static char *ngx_stream_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static void ngx_stream_geoip_cleanup(void *data); + + +static ngx_command_t ngx_stream_geoip_commands[] = { + + { ngx_string("geoip_country"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE12, + ngx_stream_geoip_country, + NGX_STREAM_MAIN_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("geoip_org"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE12, + ngx_stream_geoip_org, + NGX_STREAM_MAIN_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("geoip_city"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE12, + ngx_stream_geoip_city, + NGX_STREAM_MAIN_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_geoip_module_ctx = { + ngx_stream_geoip_add_variables, /* preconfiguration */ + NULL, /* postconfiguration */ + + ngx_stream_geoip_create_conf, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_geoip_module = { + NGX_MODULE_V1, + &ngx_stream_geoip_module_ctx, /* module context */ + ngx_stream_geoip_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_stream_variable_t ngx_stream_geoip_vars[] = { + + { ngx_string("geoip_country_code"), NULL, + ngx_stream_geoip_country_variable, + NGX_GEOIP_COUNTRY_CODE, 0, 0 }, + + { ngx_string("geoip_country_code3"), NULL, + ngx_stream_geoip_country_variable, + NGX_GEOIP_COUNTRY_CODE3, 0, 0 }, + + { ngx_string("geoip_country_name"), NULL, + ngx_stream_geoip_country_variable, + NGX_GEOIP_COUNTRY_NAME, 0, 0 }, + + { ngx_string("geoip_org"), NULL, + ngx_stream_geoip_org_variable, + 0, 0, 0 }, + + { ngx_string("geoip_city_continent_code"), NULL, + ngx_stream_geoip_city_variable, + offsetof(GeoIPRecord, continent_code), 0, 0 }, + + { ngx_string("geoip_city_country_code"), NULL, + ngx_stream_geoip_city_variable, + offsetof(GeoIPRecord, country_code), 0, 0 }, + + { ngx_string("geoip_city_country_code3"), NULL, + ngx_stream_geoip_city_variable, + offsetof(GeoIPRecord, country_code3), 0, 0 }, + + { ngx_string("geoip_city_country_name"), NULL, + ngx_stream_geoip_city_variable, + offsetof(GeoIPRecord, country_name), 0, 0 }, + + { ngx_string("geoip_region"), NULL, + ngx_stream_geoip_city_variable, + offsetof(GeoIPRecord, region), 0, 0 }, + + { ngx_string("geoip_region_name"), NULL, + ngx_stream_geoip_region_name_variable, + 0, 0, 0 }, + + { ngx_string("geoip_city"), NULL, + ngx_stream_geoip_city_variable, + offsetof(GeoIPRecord, city), 0, 0 }, + + { ngx_string("geoip_postal_code"), NULL, + ngx_stream_geoip_city_variable, + offsetof(GeoIPRecord, postal_code), 0, 0 }, + + { ngx_string("geoip_latitude"), NULL, + ngx_stream_geoip_city_float_variable, + offsetof(GeoIPRecord, latitude), 0, 0 }, + + { ngx_string("geoip_longitude"), NULL, + ngx_stream_geoip_city_float_variable, + offsetof(GeoIPRecord, longitude), 0, 0 }, + + { ngx_string("geoip_dma_code"), NULL, + ngx_stream_geoip_city_int_variable, + offsetof(GeoIPRecord, dma_code), 0, 0 }, + + { ngx_string("geoip_area_code"), NULL, + ngx_stream_geoip_city_int_variable, + offsetof(GeoIPRecord, area_code), 0, 0 }, + + { ngx_null_string, NULL, NULL, 0, 0, 0 } +}; + + +static u_long +ngx_stream_geoip_addr(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf) +{ + ngx_addr_t addr; + struct sockaddr_in *sin; + + addr.sockaddr = s->connection->sockaddr; + addr.socklen = s->connection->socklen; + /* addr.name = s->connection->addr_text; */ + +#if (NGX_HAVE_INET6) + + if (addr.sockaddr->sa_family == AF_INET6) { + u_char *p; + in_addr_t inaddr; + struct in6_addr *inaddr6; + + inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; + + if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { + p = inaddr6->s6_addr; + + inaddr = p[12] << 24; + inaddr += p[13] << 16; + inaddr += p[14] << 8; + inaddr += p[15]; + + return inaddr; + } + } + +#endif + + if (addr.sockaddr->sa_family != AF_INET) { + return INADDR_NONE; + } + + sin = (struct sockaddr_in *) addr.sockaddr; + return ntohl(sin->sin_addr.s_addr); +} + + +#if (NGX_HAVE_GEOIP_V6) + +static geoipv6_t +ngx_stream_geoip_addr_v6(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf) +{ + ngx_addr_t addr; + in_addr_t addr4; + struct in6_addr addr6; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + addr.sockaddr = s->connection->sockaddr; + addr.socklen = s->connection->socklen; + /* addr.name = s->connection->addr_text; */ + + switch (addr.sockaddr->sa_family) { + + case AF_INET: + /* Produce IPv4-mapped IPv6 address. */ + sin = (struct sockaddr_in *) addr.sockaddr; + addr4 = ntohl(sin->sin_addr.s_addr); + + ngx_memzero(&addr6, sizeof(struct in6_addr)); + addr6.s6_addr[10] = 0xff; + addr6.s6_addr[11] = 0xff; + addr6.s6_addr[12] = addr4 >> 24; + addr6.s6_addr[13] = addr4 >> 16; + addr6.s6_addr[14] = addr4 >> 8; + addr6.s6_addr[15] = addr4; + return addr6; + + case AF_INET6: + sin6 = (struct sockaddr_in6 *) addr.sockaddr; + return sin6->sin6_addr; + + default: + return in6addr_any; + } +} + +#endif + + +static ngx_int_t +ngx_stream_geoip_country_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_stream_geoip_variable_handler_pt handler = + ngx_stream_geoip_country_functions[data]; +#if (NGX_HAVE_GEOIP_V6) + ngx_stream_geoip_variable_handler_v6_pt handler_v6 = + ngx_stream_geoip_country_v6_functions[data]; +#endif + + const char *val; + ngx_stream_geoip_conf_t *gcf; + + gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module); + + if (gcf->country == NULL) { + goto not_found; + } + +#if (NGX_HAVE_GEOIP_V6) + val = gcf->country_v6 + ? handler_v6(gcf->country, ngx_stream_geoip_addr_v6(s, gcf)) + : handler(gcf->country, ngx_stream_geoip_addr(s, gcf)); +#else + val = handler(gcf->country, ngx_stream_geoip_addr(s, gcf)); +#endif + + if (val == NULL) { + goto not_found; + } + + v->len = ngx_strlen(val); + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) val; + + return NGX_OK; + +not_found: + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_geoip_org_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + size_t len; + char *val; + ngx_stream_geoip_conf_t *gcf; + + gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module); + + if (gcf->org == NULL) { + goto not_found; + } + +#if (NGX_HAVE_GEOIP_V6) + val = gcf->org_v6 + ? GeoIP_name_by_ipnum_v6(gcf->org, + ngx_stream_geoip_addr_v6(s, gcf)) + : GeoIP_name_by_ipnum(gcf->org, + ngx_stream_geoip_addr(s, gcf)); +#else + val = GeoIP_name_by_ipnum(gcf->org, ngx_stream_geoip_addr(s, gcf)); +#endif + + if (val == NULL) { + goto not_found; + } + + len = ngx_strlen(val); + v->data = ngx_pnalloc(s->connection->pool, len); + if (v->data == NULL) { + ngx_free(val); + return NGX_ERROR; + } + + ngx_memcpy(v->data, val, len); + + v->len = len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + ngx_free(val); + + return NGX_OK; + +not_found: + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_geoip_city_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + char *val; + size_t len; + GeoIPRecord *gr; + + gr = ngx_stream_geoip_get_city_record(s); + if (gr == NULL) { + goto not_found; + } + + val = *(char **) ((char *) gr + data); + if (val == NULL) { + goto no_value; + } + + len = ngx_strlen(val); + v->data = ngx_pnalloc(s->connection->pool, len); + if (v->data == NULL) { + GeoIPRecord_delete(gr); + return NGX_ERROR; + } + + ngx_memcpy(v->data, val, len); + + v->len = len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + GeoIPRecord_delete(gr); + + return NGX_OK; + +no_value: + + GeoIPRecord_delete(gr); + +not_found: + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_geoip_region_name_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + size_t len; + const char *val; + GeoIPRecord *gr; + + gr = ngx_stream_geoip_get_city_record(s); + if (gr == NULL) { + goto not_found; + } + + val = GeoIP_region_name_by_code(gr->country_code, gr->region); + + GeoIPRecord_delete(gr); + + if (val == NULL) { + goto not_found; + } + + len = ngx_strlen(val); + v->data = ngx_pnalloc(s->connection->pool, len); + if (v->data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(v->data, val, len); + + v->len = len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; + +not_found: + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_geoip_city_float_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + float val; + GeoIPRecord *gr; + + gr = ngx_stream_geoip_get_city_record(s); + if (gr == NULL) { + v->not_found = 1; + return NGX_OK; + } + + v->data = ngx_pnalloc(s->connection->pool, NGX_INT64_LEN + 5); + if (v->data == NULL) { + GeoIPRecord_delete(gr); + return NGX_ERROR; + } + + val = *(float *) ((char *) gr + data); + + v->len = ngx_sprintf(v->data, "%.4f", val) - v->data; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + GeoIPRecord_delete(gr); + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_geoip_city_int_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + int val; + GeoIPRecord *gr; + + gr = ngx_stream_geoip_get_city_record(s); + if (gr == NULL) { + v->not_found = 1; + return NGX_OK; + } + + v->data = ngx_pnalloc(s->connection->pool, NGX_INT64_LEN); + if (v->data == NULL) { + GeoIPRecord_delete(gr); + return NGX_ERROR; + } + + val = *(int *) ((char *) gr + data); + + v->len = ngx_sprintf(v->data, "%d", val) - v->data; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + GeoIPRecord_delete(gr); + + return NGX_OK; +} + + +static GeoIPRecord * +ngx_stream_geoip_get_city_record(ngx_stream_session_t *s) +{ + ngx_stream_geoip_conf_t *gcf; + + gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module); + + if (gcf->city) { +#if (NGX_HAVE_GEOIP_V6) + return gcf->city_v6 + ? GeoIP_record_by_ipnum_v6(gcf->city, + ngx_stream_geoip_addr_v6(s, gcf)) + : GeoIP_record_by_ipnum(gcf->city, + ngx_stream_geoip_addr(s, gcf)); +#else + return GeoIP_record_by_ipnum(gcf->city, ngx_stream_geoip_addr(s, gcf)); +#endif + } + + return NULL; +} + + +static ngx_int_t +ngx_stream_geoip_add_variables(ngx_conf_t *cf) +{ + ngx_stream_variable_t *var, *v; + + for (v = ngx_stream_geoip_vars; v->name.len; v++) { + var = ngx_stream_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + +static void * +ngx_stream_geoip_create_conf(ngx_conf_t *cf) +{ + ngx_pool_cleanup_t *cln; + ngx_stream_geoip_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_geoip_conf_t)); + if (conf == NULL) { + return NULL; + } + + cln = ngx_pool_cleanup_add(cf->pool, 0); + if (cln == NULL) { + return NULL; + } + + cln->handler = ngx_stream_geoip_cleanup; + cln->data = conf; + + return conf; +} + + +static char * +ngx_stream_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_geoip_conf_t *gcf = conf; + + ngx_str_t *value; + + if (gcf->country) { + return "is duplicate"; + } + + value = cf->args->elts; + + gcf->country = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE); + + if (gcf->country == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "GeoIP_open(\"%V\") failed", &value[1]); + + return NGX_CONF_ERROR; + } + + if (cf->args->nelts == 3) { + if (ngx_strcmp(value[2].data, "utf8") == 0) { + GeoIP_set_charset(gcf->country, GEOIP_CHARSET_UTF8); + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } + } + + switch (gcf->country->databaseType) { + + case GEOIP_COUNTRY_EDITION: + + return NGX_CONF_OK; + +#if (NGX_HAVE_GEOIP_V6) + case GEOIP_COUNTRY_EDITION_V6: + + gcf->country_v6 = 1; + return NGX_CONF_OK; +#endif + + default: + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid GeoIP database \"%V\" type:%d", + &value[1], gcf->country->databaseType); + return NGX_CONF_ERROR; + } +} + + +static char * +ngx_stream_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_geoip_conf_t *gcf = conf; + + ngx_str_t *value; + + if (gcf->org) { + return "is duplicate"; + } + + value = cf->args->elts; + + gcf->org = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE); + + if (gcf->org == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "GeoIP_open(\"%V\") failed", &value[1]); + + return NGX_CONF_ERROR; + } + + if (cf->args->nelts == 3) { + if (ngx_strcmp(value[2].data, "utf8") == 0) { + GeoIP_set_charset(gcf->org, GEOIP_CHARSET_UTF8); + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } + } + + switch (gcf->org->databaseType) { + + case GEOIP_ISP_EDITION: + case GEOIP_ORG_EDITION: + case GEOIP_DOMAIN_EDITION: + case GEOIP_ASNUM_EDITION: + + return NGX_CONF_OK; + +#if (NGX_HAVE_GEOIP_V6) + case GEOIP_ISP_EDITION_V6: + case GEOIP_ORG_EDITION_V6: + case GEOIP_DOMAIN_EDITION_V6: + case GEOIP_ASNUM_EDITION_V6: + + gcf->org_v6 = 1; + return NGX_CONF_OK; +#endif + + default: + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid GeoIP database \"%V\" type:%d", + &value[1], gcf->org->databaseType); + return NGX_CONF_ERROR; + } +} + + +static char * +ngx_stream_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_geoip_conf_t *gcf = conf; + + ngx_str_t *value; + + if (gcf->city) { + return "is duplicate"; + } + + value = cf->args->elts; + + gcf->city = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE); + + if (gcf->city == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "GeoIP_open(\"%V\") failed", &value[1]); + + return NGX_CONF_ERROR; + } + + if (cf->args->nelts == 3) { + if (ngx_strcmp(value[2].data, "utf8") == 0) { + GeoIP_set_charset(gcf->city, GEOIP_CHARSET_UTF8); + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } + } + + switch (gcf->city->databaseType) { + + case GEOIP_CITY_EDITION_REV0: + case GEOIP_CITY_EDITION_REV1: + + return NGX_CONF_OK; + +#if (NGX_HAVE_GEOIP_V6) + case GEOIP_CITY_EDITION_REV0_V6: + case GEOIP_CITY_EDITION_REV1_V6: + + gcf->city_v6 = 1; + return NGX_CONF_OK; +#endif + + default: + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid GeoIP City database \"%V\" type:%d", + &value[1], gcf->city->databaseType); + return NGX_CONF_ERROR; + } +} + + +static void +ngx_stream_geoip_cleanup(void *data) +{ + ngx_stream_geoip_conf_t *gcf = data; + + if (gcf->country) { + GeoIP_delete(gcf->country); + } + + if (gcf->org) { + GeoIP_delete(gcf->org); + } + + if (gcf->city) { + GeoIP_delete(gcf->city); + } +} diff --git a/src/stream/ngx_stream_handler.c b/src/stream/ngx_stream_handler.c index aa69e44..61169e1 100644 --- a/src/stream/ngx_stream_handler.c +++ b/src/stream/ngx_stream_handler.c @@ -149,6 +149,15 @@ ngx_stream_init_connection(ngx_connection_t *c) cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); + s->variables = ngx_pcalloc(s->connection->pool, + cmcf->variables.nelts + * sizeof(ngx_stream_variable_value_t)); + + if (s->variables == NULL) { + ngx_stream_close_connection(c); + return; + } + if (cmcf->limit_conn_handler) { rc = cmcf->limit_conn_handler(s); diff --git a/src/stream/ngx_stream_limit_conn_module.c b/src/stream/ngx_stream_limit_conn_module.c index f1d8a37..40eca94 100644 --- a/src/stream/ngx_stream_limit_conn_module.c +++ b/src/stream/ngx_stream_limit_conn_module.c @@ -11,33 +11,34 @@ typedef struct { - u_char color; - u_char len; - u_short conn; - u_char data[1]; + u_char color; + u_char len; + u_short conn; + u_char data[1]; } ngx_stream_limit_conn_node_t; typedef struct { - ngx_shm_zone_t *shm_zone; - ngx_rbtree_node_t *node; + ngx_shm_zone_t *shm_zone; + ngx_rbtree_node_t *node; } ngx_stream_limit_conn_cleanup_t; typedef struct { - ngx_rbtree_t *rbtree; + ngx_rbtree_t *rbtree; + ngx_stream_complex_value_t key; } ngx_stream_limit_conn_ctx_t; typedef struct { - ngx_shm_zone_t *shm_zone; - ngx_uint_t conn; + ngx_shm_zone_t *shm_zone; + ngx_uint_t conn; } ngx_stream_limit_conn_limit_t; typedef struct { - ngx_array_t limits; - ngx_uint_t log_level; + ngx_array_t limits; + ngx_uint_t log_level; } ngx_stream_limit_conn_conf_t; @@ -93,28 +94,29 @@ static ngx_command_t ngx_stream_limit_conn_commands[] = { static ngx_stream_module_t ngx_stream_limit_conn_module_ctx = { + NULL, /* preconfiguration */ ngx_stream_limit_conn_init, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ ngx_stream_limit_conn_create_conf, /* create server configuration */ - ngx_stream_limit_conn_merge_conf, /* merge server configuration */ + ngx_stream_limit_conn_merge_conf /* merge server configuration */ }; ngx_module_t ngx_stream_limit_conn_module = { NGX_MODULE_V1, - &ngx_stream_limit_conn_module_ctx, /* module context */ - ngx_stream_limit_conn_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_stream_limit_conn_module_ctx, /* module context */ + ngx_stream_limit_conn_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 }; @@ -129,48 +131,36 @@ ngx_stream_limit_conn_handler(ngx_stream_session_t *s) ngx_slab_pool_t *shpool; ngx_rbtree_node_t *node; ngx_pool_cleanup_t *cln; - struct sockaddr_in *sin; -#if (NGX_HAVE_INET6) - struct sockaddr_in6 *sin6; -#endif ngx_stream_limit_conn_ctx_t *ctx; ngx_stream_limit_conn_node_t *lc; ngx_stream_limit_conn_conf_t *lccf; ngx_stream_limit_conn_limit_t *limits; ngx_stream_limit_conn_cleanup_t *lccln; - switch (s->connection->sockaddr->sa_family) { - - case AF_INET: - sin = (struct sockaddr_in *) s->connection->sockaddr; - - key.len = sizeof(in_addr_t); - key.data = (u_char *) &sin->sin_addr; - - break; - -#if (NGX_HAVE_INET6) - case AF_INET6: - sin6 = (struct sockaddr_in6 *) s->connection->sockaddr; - - key.len = sizeof(struct in6_addr); - key.data = sin6->sin6_addr.s6_addr; - - break; -#endif - - default: - return NGX_DECLINED; - } - - hash = ngx_crc32_short(key.data, key.len); - lccf = ngx_stream_get_module_srv_conf(s, ngx_stream_limit_conn_module); limits = lccf->limits.elts; for (i = 0; i < lccf->limits.nelts; i++) { ctx = limits[i].shm_zone->data; + if (ngx_stream_complex_value(s, &ctx->key, &key) != NGX_OK) { + return NGX_ERROR; + } + + if (key.len == 0) { + continue; + } + + if (key.len > 255) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "the value of the \"%V\" key " + "is more than 255 bytes: \"%V\"", + &ctx->key.value, &key); + continue; + } + + hash = ngx_crc32_short(key.data, key.len); + shpool = (ngx_slab_pool_t *) limits[i].shm_zone->shm.addr; ngx_shmtx_lock(&shpool->mutex); @@ -382,6 +372,19 @@ ngx_stream_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data) ctx = shm_zone->data; if (octx) { + if (ctx->key.value.len != octx->key.value.len + || ngx_strncmp(ctx->key.value.data, octx->key.value.data, + ctx->key.value.len) + != 0) + { + ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, + "limit_conn_zone \"%V\" uses the \"%V\" key " + "while previously it used the \"%V\" key", + &shm_zone->shm.name, &ctx->key.value, + &octx->key.value); + return NGX_ERROR; + } + ctx->rbtree = octx->rbtree; return NGX_OK; @@ -465,12 +468,13 @@ ngx_stream_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child) static char * ngx_stream_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - u_char *p; - ssize_t size; - ngx_str_t *value, name, s; - ngx_uint_t i; - ngx_shm_zone_t *shm_zone; - ngx_stream_limit_conn_ctx_t *ctx; + u_char *p; + ssize_t size; + ngx_str_t *value, name, s; + ngx_uint_t i; + ngx_shm_zone_t *shm_zone; + ngx_stream_limit_conn_ctx_t *ctx; + ngx_stream_compile_complex_value_t ccv; value = cf->args->elts; @@ -479,6 +483,16 @@ ngx_stream_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_ERROR; } + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &ctx->key; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + size = 0; name.len = 0; @@ -537,17 +551,11 @@ ngx_stream_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } if (shm_zone->data) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "%V \"%V\" is already bound to key " - "\"$binary_remote_addr\"", - &cmd->name, &name); - return NGX_CONF_ERROR; - } + ctx = shm_zone->data; - if (ngx_strcmp(value[1].data, "$binary_remote_addr") != 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "unsupported key \"%V\", use " - "$binary_remote_addr", &value[1]); + "%V \"%V\" is already bound to key \"%V\"", + &cmd->name, &name, &ctx->key.value); return NGX_CONF_ERROR; } diff --git a/src/stream/ngx_stream_map_module.c b/src/stream/ngx_stream_map_module.c new file mode 100644 index 0000000..47a15be --- /dev/null +++ b/src/stream/ngx_stream_map_module.c @@ -0,0 +1,574 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + ngx_uint_t hash_max_size; + ngx_uint_t hash_bucket_size; +} ngx_stream_map_conf_t; + + +typedef struct { + ngx_hash_keys_arrays_t keys; + + ngx_array_t *values_hash; +#if (NGX_PCRE) + ngx_array_t regexes; +#endif + + ngx_stream_variable_value_t *default_value; + ngx_conf_t *cf; + ngx_uint_t hostnames; /* unsigned hostnames:1 */ +} ngx_stream_map_conf_ctx_t; + + +typedef struct { + ngx_stream_map_t map; + ngx_stream_complex_value_t value; + ngx_stream_variable_value_t *default_value; + ngx_uint_t hostnames; /* unsigned hostnames:1 */ +} ngx_stream_map_ctx_t; + + +static int ngx_libc_cdecl ngx_stream_map_cmp_dns_wildcards(const void *one, + const void *two); +static void *ngx_stream_map_create_conf(ngx_conf_t *cf); +static char *ngx_stream_map_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); + + +static ngx_command_t ngx_stream_map_commands[] = { + + { ngx_string("map"), + NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2, + ngx_stream_map_block, + NGX_STREAM_MAIN_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("map_hash_max_size"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_MAIN_CONF_OFFSET, + offsetof(ngx_stream_map_conf_t, hash_max_size), + NULL }, + + { ngx_string("map_hash_bucket_size"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_MAIN_CONF_OFFSET, + offsetof(ngx_stream_map_conf_t, hash_bucket_size), + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_map_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + ngx_stream_map_create_conf, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_map_module = { + NGX_MODULE_V1, + &ngx_stream_map_module_ctx, /* module context */ + ngx_stream_map_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_map_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, + uintptr_t data) +{ + ngx_stream_map_ctx_t *map = (ngx_stream_map_ctx_t *) data; + + ngx_str_t val, str; + ngx_stream_complex_value_t *cv; + ngx_stream_variable_value_t *value; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream map started"); + + if (ngx_stream_complex_value(s, &map->value, &val) != NGX_OK) { + return NGX_ERROR; + } + + if (map->hostnames && val.len > 0 && val.data[val.len - 1] == '.') { + val.len--; + } + + value = ngx_stream_map_find(s, &map->map, &val); + + if (value == NULL) { + value = map->default_value; + } + + if (!value->valid) { + cv = (ngx_stream_complex_value_t *) value->data; + + if (ngx_stream_complex_value(s, cv, &str) != NGX_OK) { + return NGX_ERROR; + } + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->len = str.len; + v->data = str.data; + + } else { + *v = *value; + } + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream map: \"%V\" \"%v\"", &val, v); + + return NGX_OK; +} + + +static void * +ngx_stream_map_create_conf(ngx_conf_t *cf) +{ + ngx_stream_map_conf_t *mcf; + + mcf = ngx_palloc(cf->pool, sizeof(ngx_stream_map_conf_t)); + if (mcf == NULL) { + return NULL; + } + + mcf->hash_max_size = NGX_CONF_UNSET_UINT; + mcf->hash_bucket_size = NGX_CONF_UNSET_UINT; + + return mcf; +} + + +static char * +ngx_stream_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_map_conf_t *mcf = conf; + + char *rv; + ngx_str_t *value, name; + ngx_conf_t save; + ngx_pool_t *pool; + ngx_hash_init_t hash; + ngx_stream_map_ctx_t *map; + ngx_stream_variable_t *var; + ngx_stream_map_conf_ctx_t ctx; + ngx_stream_compile_complex_value_t ccv; + + if (mcf->hash_max_size == NGX_CONF_UNSET_UINT) { + mcf->hash_max_size = 2048; + } + + if (mcf->hash_bucket_size == NGX_CONF_UNSET_UINT) { + mcf->hash_bucket_size = ngx_cacheline_size; + + } else { + mcf->hash_bucket_size = ngx_align(mcf->hash_bucket_size, + ngx_cacheline_size); + } + + map = ngx_pcalloc(cf->pool, sizeof(ngx_stream_map_ctx_t)); + if (map == NULL) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &map->value; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + name = value[2]; + + if (name.data[0] != '$') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"%V\"", &name); + return NGX_CONF_ERROR; + } + + name.len--; + name.data++; + + var = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE); + if (var == NULL) { + return NGX_CONF_ERROR; + } + + var->get_handler = ngx_stream_map_variable; + var->data = (uintptr_t) map; + + pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log); + if (pool == NULL) { + return NGX_CONF_ERROR; + } + + ctx.keys.pool = cf->pool; + ctx.keys.temp_pool = pool; + + if (ngx_hash_keys_array_init(&ctx.keys, NGX_HASH_LARGE) != NGX_OK) { + ngx_destroy_pool(pool); + return NGX_CONF_ERROR; + } + + ctx.values_hash = ngx_pcalloc(pool, sizeof(ngx_array_t) * ctx.keys.hsize); + if (ctx.values_hash == NULL) { + ngx_destroy_pool(pool); + return NGX_CONF_ERROR; + } + +#if (NGX_PCRE) + if (ngx_array_init(&ctx.regexes, cf->pool, 2, + sizeof(ngx_stream_map_regex_t)) + != NGX_OK) + { + ngx_destroy_pool(pool); + return NGX_CONF_ERROR; + } +#endif + + ctx.default_value = NULL; + ctx.cf = &save; + ctx.hostnames = 0; + + save = *cf; + cf->pool = pool; + cf->ctx = &ctx; + cf->handler = ngx_stream_map; + cf->handler_conf = conf; + + rv = ngx_conf_parse(cf, NULL); + + *cf = save; + + if (rv != NGX_CONF_OK) { + ngx_destroy_pool(pool); + return rv; + } + + map->default_value = ctx.default_value ? ctx.default_value: + &ngx_stream_variable_null_value; + + map->hostnames = ctx.hostnames; + + hash.key = ngx_hash_key_lc; + hash.max_size = mcf->hash_max_size; + hash.bucket_size = mcf->hash_bucket_size; + hash.name = "map_hash"; + hash.pool = cf->pool; + + if (ctx.keys.keys.nelts) { + hash.hash = &map->map.hash.hash; + hash.temp_pool = NULL; + + if (ngx_hash_init(&hash, ctx.keys.keys.elts, ctx.keys.keys.nelts) + != NGX_OK) + { + ngx_destroy_pool(pool); + return NGX_CONF_ERROR; + } + } + + if (ctx.keys.dns_wc_head.nelts) { + + ngx_qsort(ctx.keys.dns_wc_head.elts, + (size_t) ctx.keys.dns_wc_head.nelts, + sizeof(ngx_hash_key_t), ngx_stream_map_cmp_dns_wildcards); + + hash.hash = NULL; + hash.temp_pool = pool; + + if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_head.elts, + ctx.keys.dns_wc_head.nelts) + != NGX_OK) + { + ngx_destroy_pool(pool); + return NGX_CONF_ERROR; + } + + map->map.hash.wc_head = (ngx_hash_wildcard_t *) hash.hash; + } + + if (ctx.keys.dns_wc_tail.nelts) { + + ngx_qsort(ctx.keys.dns_wc_tail.elts, + (size_t) ctx.keys.dns_wc_tail.nelts, + sizeof(ngx_hash_key_t), ngx_stream_map_cmp_dns_wildcards); + + hash.hash = NULL; + hash.temp_pool = pool; + + if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_tail.elts, + ctx.keys.dns_wc_tail.nelts) + != NGX_OK) + { + ngx_destroy_pool(pool); + return NGX_CONF_ERROR; + } + + map->map.hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash; + } + +#if (NGX_PCRE) + + if (ctx.regexes.nelts) { + map->map.regex = ctx.regexes.elts; + map->map.nregex = ctx.regexes.nelts; + } + +#endif + + ngx_destroy_pool(pool); + + return rv; +} + + +static int ngx_libc_cdecl +ngx_stream_map_cmp_dns_wildcards(const void *one, const void *two) +{ + ngx_hash_key_t *first, *second; + + first = (ngx_hash_key_t *) one; + second = (ngx_hash_key_t *) two; + + return ngx_dns_strcmp(first->key.data, second->key.data); +} + + +static char * +ngx_stream_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) +{ + u_char *data; + size_t len; + ngx_int_t rv; + ngx_str_t *value, v; + ngx_uint_t i, key; + ngx_stream_map_conf_ctx_t *ctx; + ngx_stream_complex_value_t cv, *cvp; + ngx_stream_variable_value_t *var, **vp; + ngx_stream_compile_complex_value_t ccv; + + ctx = cf->ctx; + + value = cf->args->elts; + + if (cf->args->nelts == 1 + && ngx_strcmp(value[0].data, "hostnames") == 0) + { + ctx->hostnames = 1; + return NGX_CONF_OK; + + } else if (cf->args->nelts != 2) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid number of the map parameters"); + return NGX_CONF_ERROR; + } + + if (ngx_strcmp(value[0].data, "include") == 0) { + return ngx_conf_include(cf, dummy, conf); + } + + key = 0; + + for (i = 0; i < value[1].len; i++) { + key = ngx_hash(key, value[1].data[i]); + } + + key %= ctx->keys.hsize; + + vp = ctx->values_hash[key].elts; + + if (vp) { + for (i = 0; i < ctx->values_hash[key].nelts; i++) { + + if (vp[i]->valid) { + data = vp[i]->data; + len = vp[i]->len; + + } else { + cvp = (ngx_stream_complex_value_t *) vp[i]->data; + data = cvp->value.data; + len = cvp->value.len; + } + + if (value[1].len != len) { + continue; + } + + if (ngx_strncmp(value[1].data, data, len) == 0) { + var = vp[i]; + goto found; + } + } + + } else { + if (ngx_array_init(&ctx->values_hash[key], cf->pool, 4, + sizeof(ngx_stream_variable_value_t *)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + } + + var = ngx_palloc(ctx->keys.pool, sizeof(ngx_stream_variable_value_t)); + if (var == NULL) { + return NGX_CONF_ERROR; + } + + v.len = value[1].len; + v.data = ngx_pstrdup(ctx->keys.pool, &value[1]); + if (v.data == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = ctx->cf; + ccv.value = &v; + ccv.complex_value = &cv; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (cv.lengths != NULL) { + cvp = ngx_palloc(ctx->keys.pool, sizeof(ngx_stream_complex_value_t)); + if (cvp == NULL) { + return NGX_CONF_ERROR; + } + + *cvp = cv; + + var->len = 0; + var->data = (u_char *) cvp; + var->valid = 0; + + } else { + var->len = v.len; + var->data = v.data; + var->valid = 1; + } + + var->no_cacheable = 0; + var->not_found = 0; + + vp = ngx_array_push(&ctx->values_hash[key]); + if (vp == NULL) { + return NGX_CONF_ERROR; + } + + *vp = var; + +found: + + if (ngx_strcmp(value[0].data, "default") == 0) { + + if (ctx->default_value) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate default map parameter"); + return NGX_CONF_ERROR; + } + + ctx->default_value = var; + + return NGX_CONF_OK; + } + +#if (NGX_PCRE) + + if (value[0].len && value[0].data[0] == '~') { + ngx_regex_compile_t rc; + ngx_stream_map_regex_t *regex; + u_char errstr[NGX_MAX_CONF_ERRSTR]; + + regex = ngx_array_push(&ctx->regexes); + if (regex == NULL) { + return NGX_CONF_ERROR; + } + + value[0].len--; + value[0].data++; + + ngx_memzero(&rc, sizeof(ngx_regex_compile_t)); + + if (value[0].data[0] == '*') { + value[0].len--; + value[0].data++; + rc.options = NGX_REGEX_CASELESS; + } + + rc.pattern = value[0]; + rc.err.len = NGX_MAX_CONF_ERRSTR; + rc.err.data = errstr; + + regex->regex = ngx_stream_regex_compile(ctx->cf, &rc); + if (regex->regex == NULL) { + return NGX_CONF_ERROR; + } + + regex->value = var; + + return NGX_CONF_OK; + } + +#endif + + if (value[0].len && value[0].data[0] == '\\') { + value[0].len--; + value[0].data++; + } + + rv = ngx_hash_add_key(&ctx->keys, &value[0], var, + (ctx->hostnames) ? NGX_HASH_WILDCARD_KEY : 0); + + if (rv == NGX_OK) { + return NGX_CONF_OK; + } + + if (rv == NGX_DECLINED) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid hostname or wildcard \"%V\"", &value[0]); + } + + if (rv == NGX_BUSY) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "conflicting parameter \"%V\"", &value[0]); + } + + return NGX_CONF_ERROR; +} diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c index 6c535fd..9d43109 100644 --- a/src/stream/ngx_stream_proxy_module.c +++ b/src/stream/ngx_stream_proxy_module.c @@ -10,6 +10,15 @@ #include +typedef struct { + ngx_addr_t *addr; + ngx_stream_complex_value_t *value; +#if (NGX_HAVE_TRANSPARENT_PROXY) + ngx_uint_t transparent; /* unsigned transparent:1; */ +#endif +} ngx_stream_upstream_local_t; + + typedef struct { ngx_msec_t connect_timeout; ngx_msec_t timeout; @@ -21,14 +30,14 @@ typedef struct { ngx_uint_t next_upstream_tries; ngx_flag_t next_upstream; ngx_flag_t proxy_protocol; - ngx_addr_t *local; + ngx_stream_upstream_local_t *local; #if (NGX_STREAM_SSL) ngx_flag_t ssl_enable; ngx_flag_t ssl_session_reuse; ngx_uint_t ssl_protocols; ngx_str_t ssl_ciphers; - ngx_str_t ssl_name; + ngx_stream_complex_value_t *ssl_name; ngx_flag_t ssl_server_name; ngx_flag_t ssl_verify; @@ -43,12 +52,18 @@ typedef struct { #endif ngx_stream_upstream_srv_conf_t *upstream; + ngx_stream_complex_value_t *upstream_value; } ngx_stream_proxy_srv_conf_t; static void ngx_stream_proxy_handler(ngx_stream_session_t *s); +static ngx_int_t ngx_stream_proxy_eval(ngx_stream_session_t *s, + ngx_stream_proxy_srv_conf_t *pscf); +static ngx_int_t ngx_stream_proxy_set_local(ngx_stream_session_t *s, + ngx_stream_upstream_t *u, ngx_stream_upstream_local_t *local); static void ngx_stream_proxy_connect(ngx_stream_session_t *s); static void ngx_stream_proxy_init_upstream(ngx_stream_session_t *s); +static void ngx_stream_proxy_resolve_handler(ngx_resolver_ctx_t *ctx); static void ngx_stream_proxy_upstream_handler(ngx_event_t *ev); static void ngx_stream_proxy_downstream_handler(ngx_event_t *ev); static void ngx_stream_proxy_process_connection(ngx_event_t *ev, @@ -113,7 +128,7 @@ static ngx_command_t ngx_stream_proxy_commands[] = { NULL }, { ngx_string("proxy_bind"), - NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE12, ngx_stream_proxy_bind, NGX_STREAM_SRV_CONF_OFFSET, 0, @@ -235,7 +250,7 @@ static ngx_command_t ngx_stream_proxy_commands[] = { { ngx_string("proxy_ssl_name"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_str_slot, + ngx_stream_set_complex_value_slot, NGX_STREAM_SRV_CONF_OFFSET, offsetof(ngx_stream_proxy_srv_conf_t, ssl_name), NULL }, @@ -303,6 +318,7 @@ static ngx_command_t ngx_stream_proxy_commands[] = { static ngx_stream_module_t ngx_stream_proxy_module_ctx = { + NULL, /* preconfiguration */ NULL, /* postconfiguration */ NULL, /* create main configuration */ @@ -332,11 +348,16 @@ ngx_module_t ngx_stream_proxy_module = { static void ngx_stream_proxy_handler(ngx_stream_session_t *s) { - u_char *p; - ngx_connection_t *c; - ngx_stream_upstream_t *u; - ngx_stream_proxy_srv_conf_t *pscf; - ngx_stream_upstream_srv_conf_t *uscf; + u_char *p; + ngx_str_t *host; + ngx_uint_t i; + ngx_connection_t *c; + ngx_resolver_ctx_t *ctx, temp; + ngx_stream_upstream_t *u; + ngx_stream_core_srv_conf_t *cscf; + ngx_stream_proxy_srv_conf_t *pscf; + ngx_stream_upstream_srv_conf_t *uscf, **uscfp; + ngx_stream_upstream_main_conf_t *umcf; c = s->connection; @@ -358,10 +379,168 @@ ngx_stream_proxy_handler(ngx_stream_session_t *s) u->peer.log = c->log; u->peer.log_error = NGX_ERROR_ERR; - u->peer.local = pscf->local; + if (ngx_stream_proxy_set_local(s, u, pscf->local) != NGX_OK) { + ngx_stream_proxy_finalize(s, NGX_ERROR); + return; + } + u->peer.type = c->type; - uscf = pscf->upstream; + u->proxy_protocol = pscf->proxy_protocol; + u->start_sec = ngx_time(); + + c->write->handler = ngx_stream_proxy_downstream_handler; + c->read->handler = ngx_stream_proxy_downstream_handler; + + if (c->type == SOCK_STREAM) { + p = ngx_pnalloc(c->pool, pscf->buffer_size); + if (p == NULL) { + ngx_stream_proxy_finalize(s, NGX_ERROR); + return; + } + + u->downstream_buf.start = p; + u->downstream_buf.end = p + pscf->buffer_size; + u->downstream_buf.pos = p; + u->downstream_buf.last = p; + + if (u->proxy_protocol +#if (NGX_STREAM_SSL) + && pscf->ssl == NULL +#endif + && pscf->buffer_size >= NGX_PROXY_PROTOCOL_MAX_HEADER) + { + /* optimization for a typical case */ + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream proxy send PROXY protocol header"); + + p = ngx_proxy_protocol_write(c, u->downstream_buf.last, + u->downstream_buf.end); + if (p == NULL) { + ngx_stream_proxy_finalize(s, NGX_ERROR); + return; + } + + u->downstream_buf.last = p; + u->proxy_protocol = 0; + } + + if (c->read->ready) { + ngx_post_event(c->read, &ngx_posted_events); + } + } + + if (pscf->upstream_value) { + if (ngx_stream_proxy_eval(s, pscf) != NGX_OK) { + ngx_stream_proxy_finalize(s, NGX_ERROR); + return; + } + } + + if (u->resolved == NULL) { + + uscf = pscf->upstream; + + } else { + +#if (NGX_STREAM_SSL) + u->ssl_name = u->resolved->host; +#endif + + host = &u->resolved->host; + + if (u->resolved->sockaddr) { + + if (u->resolved->port == 0 + && u->resolved->sockaddr->sa_family != AF_UNIX) + { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "no port in upstream \"%V\"", host); + ngx_stream_proxy_finalize(s, NGX_ERROR); + return; + } + + if (ngx_stream_upstream_create_round_robin_peer(s, u->resolved) + != NGX_OK) + { + ngx_stream_proxy_finalize(s, NGX_ERROR); + return; + } + + ngx_stream_proxy_connect(s); + + return; + } + + umcf = ngx_stream_get_module_main_conf(s, ngx_stream_upstream_module); + + uscfp = umcf->upstreams.elts; + + for (i = 0; i < umcf->upstreams.nelts; i++) { + + uscf = uscfp[i]; + + if (uscf->host.len == host->len + && ((uscf->port == 0 && u->resolved->no_port) + || uscf->port == u->resolved->port) + && ngx_strncasecmp(uscf->host.data, host->data, host->len) == 0) + { + goto found; + } + } + + if (u->resolved->port == 0) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "no port in upstream \"%V\"", host); + ngx_stream_proxy_finalize(s, NGX_ERROR); + return; + } + + temp.name = *host; + + cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); + + ctx = ngx_resolve_start(cscf->resolver, &temp); + if (ctx == NULL) { + ngx_stream_proxy_finalize(s, NGX_ERROR); + return; + } + + if (ctx == NGX_NO_RESOLVER) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "no resolver defined to resolve %V", host); + ngx_stream_proxy_finalize(s, NGX_ERROR); + return; + } + + ctx->name = *host; + ctx->handler = ngx_stream_proxy_resolve_handler; + ctx->data = s; + ctx->timeout = cscf->resolver_timeout; + + u->resolved->ctx = ctx; + + if (ngx_resolve_name(ctx) != NGX_OK) { + u->resolved->ctx = NULL; + ngx_stream_proxy_finalize(s, NGX_ERROR); + return; + } + + return; + } + +found: + + if (uscf == NULL) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, "no upstream configuration"); + ngx_stream_proxy_finalize(s, NGX_ERROR); + return; + } + +#if (NGX_HTTP_SSL) + u->ssl_name = uscf->host; +#endif if (uscf->peer.init(s, uscf) != NGX_OK) { ngx_stream_proxy_finalize(s, NGX_ERROR); @@ -376,55 +555,111 @@ ngx_stream_proxy_handler(ngx_stream_session_t *s) u->peer.tries = pscf->next_upstream_tries; } - u->proxy_protocol = pscf->proxy_protocol; - u->start_sec = ngx_time(); + ngx_stream_proxy_connect(s); +} - c->write->handler = ngx_stream_proxy_downstream_handler; - c->read->handler = ngx_stream_proxy_downstream_handler; - if (c->type == SOCK_DGRAM) { - ngx_stream_proxy_connect(s); - return; +static ngx_int_t +ngx_stream_proxy_eval(ngx_stream_session_t *s, + ngx_stream_proxy_srv_conf_t *pscf) +{ + ngx_str_t host; + ngx_url_t url; + ngx_stream_upstream_t *u; + + if (ngx_stream_complex_value(s, pscf->upstream_value, &host) != NGX_OK) { + return NGX_ERROR; } - p = ngx_pnalloc(c->pool, pscf->buffer_size); - if (p == NULL) { - ngx_stream_proxy_finalize(s, NGX_ERROR); - return; - } + ngx_memzero(&url, sizeof(ngx_url_t)); - u->downstream_buf.start = p; - u->downstream_buf.end = p + pscf->buffer_size; - u->downstream_buf.pos = p; - u->downstream_buf.last = p; + url.url = host; + url.no_resolve = 1; - if (u->proxy_protocol -#if (NGX_STREAM_SSL) - && pscf->ssl == NULL -#endif - && pscf->buffer_size >= NGX_PROXY_PROTOCOL_MAX_HEADER) - { - /* optimization for a typical case */ - - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, - "stream proxy send PROXY protocol header"); - - p = ngx_proxy_protocol_write(c, u->downstream_buf.last, - u->downstream_buf.end); - if (p == NULL) { - ngx_stream_proxy_finalize(s, NGX_ERROR); - return; + if (ngx_parse_url(s->connection->pool, &url) != NGX_OK) { + if (url.err) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "%s in upstream \"%V\"", url.err, &url.url); } - u->downstream_buf.last = p; - u->proxy_protocol = 0; + return NGX_ERROR; } - if (c->read->ready) { - ngx_post_event(c->read, &ngx_posted_events); + u = s->upstream; + + u->resolved = ngx_pcalloc(s->connection->pool, + sizeof(ngx_stream_upstream_resolved_t)); + if (u->resolved == NULL) { + return NGX_ERROR; } - ngx_stream_proxy_connect(s); + if (url.addrs && url.addrs[0].sockaddr) { + u->resolved->sockaddr = url.addrs[0].sockaddr; + u->resolved->socklen = url.addrs[0].socklen; + u->resolved->naddrs = 1; + u->resolved->host = url.addrs[0].name; + + } else { + u->resolved->host = url.host; + } + + u->resolved->port = url.port; + u->resolved->no_port = url.no_port; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_proxy_set_local(ngx_stream_session_t *s, ngx_stream_upstream_t *u, + ngx_stream_upstream_local_t *local) +{ + ngx_int_t rc; + ngx_str_t val; + ngx_addr_t *addr; + + if (local == NULL) { + u->peer.local = NULL; + return NGX_OK; + } + +#if (NGX_HAVE_TRANSPARENT_PROXY) + u->peer.transparent = local->transparent; +#endif + + if (local->value == NULL) { + u->peer.local = local->addr; + return NGX_OK; + } + + if (ngx_stream_complex_value(s, local->value, &val) != NGX_OK) { + return NGX_ERROR; + } + + if (val.len == 0) { + return NGX_OK; + } + + addr = ngx_palloc(s->connection->pool, sizeof(ngx_addr_t)); + if (addr == NULL) { + return NGX_ERROR; + } + + rc = ngx_parse_addr_port(s->connection->pool, addr, val.data, val.len); + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc != NGX_OK) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "invalid local address \"%V\"", &val); + return NGX_OK; + } + + addr->name = val; + u->peer.local = addr; + + return NGX_OK; } @@ -814,10 +1049,13 @@ ngx_stream_proxy_ssl_name(ngx_stream_session_t *s) u = s->upstream; - name = pscf->ssl_name; + if (pscf->ssl_name) { + if (ngx_stream_complex_value(s, pscf->ssl_name, &name) != NGX_OK) { + return NGX_ERROR; + } - if (name.len == 0) { - name = pscf->upstream->host; + } else { + name = u->ssl_name; } if (name.len == 0) { @@ -906,6 +1144,75 @@ ngx_stream_proxy_downstream_handler(ngx_event_t *ev) } +static void +ngx_stream_proxy_resolve_handler(ngx_resolver_ctx_t *ctx) +{ + ngx_stream_session_t *s; + ngx_stream_upstream_t *u; + ngx_stream_proxy_srv_conf_t *pscf; + ngx_stream_upstream_resolved_t *ur; + + s = ctx->data; + + u = s->upstream; + ur = u->resolved; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, + "stream upstream resolve"); + + if (ctx->state) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "%V could not be resolved (%i: %s)", + &ctx->name, ctx->state, + ngx_resolver_strerror(ctx->state)); + + ngx_stream_proxy_finalize(s, NGX_ERROR); + return; + } + + ur->naddrs = ctx->naddrs; + ur->addrs = ctx->addrs; + +#if (NGX_DEBUG) + { + u_char text[NGX_SOCKADDR_STRLEN]; + ngx_str_t addr; + ngx_uint_t i; + + addr.data = text; + + for (i = 0; i < ctx->naddrs; i++) { + addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen, + text, NGX_SOCKADDR_STRLEN, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "name was resolved to %V", &addr); + } + } +#endif + + if (ngx_stream_upstream_create_round_robin_peer(s, ur) != NGX_OK) { + ngx_stream_proxy_finalize(s, NGX_ERROR); + return; + } + + ngx_resolve_name_done(ctx); + ur->ctx = NULL; + + u->peer.start_time = ngx_current_msec; + + pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); + + if (pscf->next_upstream_tries + && u->peer.tries > pscf->next_upstream_tries) + { + u->peer.tries = pscf->next_upstream_tries; + } + + ngx_stream_proxy_connect(s); +} + + static void ngx_stream_proxy_upstream_handler(ngx_event_t *ev) { @@ -1328,6 +1635,11 @@ ngx_stream_proxy_finalize(ngx_stream_session_t *s, ngx_int_t rc) goto noupstream; } + if (u->resolved && u->resolved->ctx) { + ngx_resolve_name_done(u->resolved->ctx); + u->resolved->ctx = NULL; + } + if (u->peer.free && u->peer.sockaddr) { u->peer.free(&u->peer, u->peer.data, 0); u->peer.sockaddr = NULL; @@ -1402,7 +1714,7 @@ ngx_stream_proxy_create_srv_conf(ngx_conf_t *cf) * * conf->ssl_protocols = 0; * conf->ssl_ciphers = { 0, NULL }; - * conf->ssl_name = { 0, NULL }; + * conf->ssl_name = NULL; * conf->ssl_trusted_certificate = { 0, NULL }; * conf->ssl_crl = { 0, NULL }; * conf->ssl_certificate = { 0, NULL }; @@ -1410,6 +1722,7 @@ ngx_stream_proxy_create_srv_conf(ngx_conf_t *cf) * * conf->ssl = NULL; * conf->upstream = NULL; + * conf->upstream_value = NULL; */ conf->connect_timeout = NGX_CONF_UNSET_MSEC; @@ -1486,7 +1799,9 @@ ngx_stream_proxy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); - ngx_conf_merge_str_value(conf->ssl_name, prev->ssl_name, ""); + if (conf->ssl_name == NULL) { + conf->ssl_name = prev->ssl_name; + } ngx_conf_merge_value(conf->ssl_server_name, prev->ssl_server_name, 0); @@ -1561,13 +1876,7 @@ ngx_stream_proxy_set_ssl(ngx_conf_t *cf, ngx_stream_proxy_srv_conf_t *pscf) } } - if (SSL_CTX_set_cipher_list(pscf->ssl->ctx, - (const char *) pscf->ssl_ciphers.data) - == 0) - { - ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, - "SSL_CTX_set_cipher_list(\"%V\") failed", - &pscf->ssl_ciphers); + if (ngx_ssl_ciphers(cf, pscf->ssl, &pscf->ssl_ciphers, 0) != NGX_OK) { return NGX_ERROR; } @@ -1602,11 +1911,13 @@ ngx_stream_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_stream_proxy_srv_conf_t *pscf = conf; - ngx_url_t u; - ngx_str_t *value, *url; - ngx_stream_core_srv_conf_t *cscf; + ngx_url_t u; + ngx_str_t *value, *url; + ngx_stream_complex_value_t cv; + ngx_stream_core_srv_conf_t *cscf; + ngx_stream_compile_complex_value_t ccv; - if (pscf->upstream) { + if (pscf->upstream || pscf->upstream_value) { return "is duplicate"; } @@ -1618,6 +1929,28 @@ ngx_stream_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) url = &value[1]; + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = url; + ccv.complex_value = &cv; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (cv.lengths) { + pscf->upstream_value = ngx_palloc(cf->pool, + sizeof(ngx_stream_complex_value_t)); + if (pscf->upstream_value == NULL) { + return NGX_CONF_ERROR; + } + + *pscf->upstream_value = cv; + + return NGX_CONF_OK; + } + ngx_memzero(&u, sizeof(ngx_url_t)); u.url = *url; @@ -1637,8 +1970,11 @@ ngx_stream_proxy_bind(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_stream_proxy_srv_conf_t *pscf = conf; - ngx_int_t rc; - ngx_str_t *value; + ngx_int_t rc; + ngx_str_t *value; + ngx_stream_complex_value_t cv; + ngx_stream_upstream_local_t *local; + ngx_stream_compile_complex_value_t ccv; if (pscf->local != NGX_CONF_UNSET_PTR) { return "is duplicate"; @@ -1646,29 +1982,75 @@ ngx_stream_proxy_bind(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) value = cf->args->elts; - if (ngx_strcmp(value[1].data, "off") == 0) { + if (cf->args->nelts == 2 && ngx_strcmp(value[1].data, "off") == 0) { pscf->local = NULL; return NGX_CONF_OK; } - pscf->local = ngx_palloc(cf->pool, sizeof(ngx_addr_t)); - if (pscf->local == NULL) { + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &cv; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } - rc = ngx_parse_addr(cf->pool, pscf->local, value[1].data, value[1].len); - - switch (rc) { - case NGX_OK: - pscf->local->name = value[1]; - return NGX_CONF_OK; - - case NGX_DECLINED: - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid address \"%V\"", &value[1]); - /* fall through */ - - default: + local = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_local_t)); + if (local == NULL) { return NGX_CONF_ERROR; } + + pscf->local = local; + + if (cv.lengths) { + local->value = ngx_palloc(cf->pool, sizeof(ngx_stream_complex_value_t)); + if (local->value == NULL) { + return NGX_CONF_ERROR; + } + + *local->value = cv; + + } else { + local->addr = ngx_palloc(cf->pool, sizeof(ngx_addr_t)); + if (local->addr == NULL) { + return NGX_CONF_ERROR; + } + + rc = ngx_parse_addr_port(cf->pool, local->addr, value[1].data, + value[1].len); + + switch (rc) { + case NGX_OK: + local->addr->name = value[1]; + break; + + case NGX_DECLINED: + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid address \"%V\"", &value[1]); + /* fall through */ + + default: + return NGX_CONF_ERROR; + } + } + + if (cf->args->nelts > 2) { + if (ngx_strcmp(value[2].data, "transparent") == 0) { +#if (NGX_HAVE_TRANSPARENT_PROXY) + local->transparent = 1; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "transparent proxying is not supported " + "on this platform, ignored"); +#endif + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } + } + + return NGX_CONF_OK; } diff --git a/src/stream/ngx_stream_return_module.c b/src/stream/ngx_stream_return_module.c new file mode 100644 index 0000000..3ab9bdf --- /dev/null +++ b/src/stream/ngx_stream_return_module.c @@ -0,0 +1,207 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + ngx_stream_complex_value_t text; +} ngx_stream_return_srv_conf_t; + + +typedef struct { + ngx_buf_t buf; +} ngx_stream_return_ctx_t; + + +static void ngx_stream_return_handler(ngx_stream_session_t *s); +static void ngx_stream_return_write_handler(ngx_event_t *ev); + +static void *ngx_stream_return_create_srv_conf(ngx_conf_t *cf); +static char *ngx_stream_return(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + + +static ngx_command_t ngx_stream_return_commands[] = { + + { ngx_string("return"), + NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_return, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_return_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_stream_return_create_srv_conf, /* create server configuration */ + NULL /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_return_module = { + NGX_MODULE_V1, + &ngx_stream_return_module_ctx, /* module context */ + ngx_stream_return_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 void +ngx_stream_return_handler(ngx_stream_session_t *s) +{ + ngx_str_t text; + ngx_connection_t *c; + ngx_stream_return_ctx_t *ctx; + ngx_stream_return_srv_conf_t *rscf; + + c = s->connection; + + c->log->action = "returning text"; + + rscf = ngx_stream_get_module_srv_conf(s, ngx_stream_return_module); + + if (ngx_stream_complex_value(s, &rscf->text, &text) != NGX_OK) { + ngx_stream_close_connection(c); + return; + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream return text: \"%V\"", &text); + + if (text.len == 0) { + ngx_stream_close_connection(c); + return; + } + + ctx = ngx_pcalloc(c->pool, sizeof(ngx_stream_return_ctx_t)); + if (ctx == NULL) { + ngx_stream_close_connection(c); + return; + } + + ngx_stream_set_ctx(s, ctx, ngx_stream_return_module); + + ctx->buf.pos = text.data; + ctx->buf.last = text.data + text.len; + + c->write->handler = ngx_stream_return_write_handler; + + ngx_stream_return_write_handler(c->write); +} + + +static void +ngx_stream_return_write_handler(ngx_event_t *ev) +{ + ssize_t n; + ngx_buf_t *b; + ngx_connection_t *c; + ngx_stream_session_t *s; + ngx_stream_return_ctx_t *ctx; + + c = ev->data; + s = c->data; + + if (ev->timedout) { + ngx_connection_error(c, NGX_ETIMEDOUT, "connection timed out"); + ngx_stream_close_connection(c); + return; + } + + if (ev->ready) { + ctx = ngx_stream_get_module_ctx(s, ngx_stream_return_module); + + b = &ctx->buf; + + n = c->send(c, b->pos, b->last - b->pos); + if (n == NGX_ERROR) { + ngx_stream_close_connection(c); + return; + } + + if (n > 0) { + b->pos += n; + + if (b->pos == b->last) { + ngx_stream_close_connection(c); + return; + } + } + } + + if (ngx_handle_write_event(ev, 0) != NGX_OK) { + ngx_stream_close_connection(c); + return; + } + + ngx_add_timer(ev, 5000); +} + + +static void * +ngx_stream_return_create_srv_conf(ngx_conf_t *cf) +{ + ngx_stream_return_srv_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_return_srv_conf_t)); + if (conf == NULL) { + return NULL; + } + + return conf; +} + + +static char * +ngx_stream_return(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_return_srv_conf_t *rscf = conf; + + ngx_str_t *value; + ngx_stream_core_srv_conf_t *cscf; + ngx_stream_compile_complex_value_t ccv; + + if (rscf->text.value.data) { + return "is duplicate"; + } + + value = cf->args->elts; + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &rscf->text; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + cscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_core_module); + + cscf->handler = ngx_stream_return_handler; + + return NGX_CONF_OK; +} diff --git a/src/stream/ngx_stream_script.c b/src/stream/ngx_stream_script.c new file mode 100644 index 0000000..8130f92 --- /dev/null +++ b/src/stream/ngx_stream_script.c @@ -0,0 +1,854 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +static ngx_int_t ngx_stream_script_init_arrays( + ngx_stream_script_compile_t *sc); +static ngx_int_t ngx_stream_script_done(ngx_stream_script_compile_t *sc); +static ngx_int_t ngx_stream_script_add_copy_code( + ngx_stream_script_compile_t *sc, ngx_str_t *value, ngx_uint_t last); +static ngx_int_t ngx_stream_script_add_var_code( + ngx_stream_script_compile_t *sc, ngx_str_t *name); +#if (NGX_PCRE) +static ngx_int_t ngx_stream_script_add_capture_code( + ngx_stream_script_compile_t *sc, ngx_uint_t n); +#endif +static ngx_int_t ngx_stream_script_add_full_name_code( + ngx_stream_script_compile_t *sc); +static size_t ngx_stream_script_full_name_len_code( + ngx_stream_script_engine_t *e); +static void ngx_stream_script_full_name_code(ngx_stream_script_engine_t *e); + + +#define ngx_stream_script_exit (u_char *) &ngx_stream_script_exit_code + +static uintptr_t ngx_stream_script_exit_code = (uintptr_t) NULL; + + +void +ngx_stream_script_flush_complex_value(ngx_stream_session_t *s, + ngx_stream_complex_value_t *val) +{ + ngx_uint_t *index; + + index = val->flushes; + + if (index) { + while (*index != (ngx_uint_t) -1) { + + if (s->variables[*index].no_cacheable) { + s->variables[*index].valid = 0; + s->variables[*index].not_found = 0; + } + + index++; + } + } +} + + +ngx_int_t +ngx_stream_complex_value(ngx_stream_session_t *s, + ngx_stream_complex_value_t *val, ngx_str_t *value) +{ + size_t len; + ngx_stream_script_code_pt code; + ngx_stream_script_engine_t e; + ngx_stream_script_len_code_pt lcode; + + if (val->lengths == NULL) { + *value = val->value; + return NGX_OK; + } + + ngx_stream_script_flush_complex_value(s, val); + + ngx_memzero(&e, sizeof(ngx_stream_script_engine_t)); + + e.ip = val->lengths; + e.session = s; + e.flushed = 1; + + len = 0; + + while (*(uintptr_t *) e.ip) { + lcode = *(ngx_stream_script_len_code_pt *) e.ip; + len += lcode(&e); + } + + value->len = len; + value->data = ngx_pnalloc(s->connection->pool, len); + if (value->data == NULL) { + return NGX_ERROR; + } + + e.ip = val->values; + e.pos = value->data; + e.buf = *value; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_stream_script_code_pt *) e.ip; + code((ngx_stream_script_engine_t *) &e); + } + + *value = e.buf; + + return NGX_OK; +} + + +ngx_int_t +ngx_stream_compile_complex_value(ngx_stream_compile_complex_value_t *ccv) +{ + ngx_str_t *v; + ngx_uint_t i, n, nv, nc; + ngx_array_t flushes, lengths, values, *pf, *pl, *pv; + ngx_stream_script_compile_t sc; + + v = ccv->value; + + nv = 0; + nc = 0; + + for (i = 0; i < v->len; i++) { + if (v->data[i] == '$') { + if (v->data[i + 1] >= '1' && v->data[i + 1] <= '9') { + nc++; + + } else { + nv++; + } + } + } + + if ((v->len == 0 || v->data[0] != '$') + && (ccv->conf_prefix || ccv->root_prefix)) + { + if (ngx_conf_full_name(ccv->cf->cycle, v, ccv->conf_prefix) != NGX_OK) { + return NGX_ERROR; + } + + ccv->conf_prefix = 0; + ccv->root_prefix = 0; + } + + ccv->complex_value->value = *v; + ccv->complex_value->flushes = NULL; + ccv->complex_value->lengths = NULL; + ccv->complex_value->values = NULL; + + if (nv == 0 && nc == 0) { + return NGX_OK; + } + + n = nv + 1; + + if (ngx_array_init(&flushes, ccv->cf->pool, n, sizeof(ngx_uint_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + n = nv * (2 * sizeof(ngx_stream_script_copy_code_t) + + sizeof(ngx_stream_script_var_code_t)) + + sizeof(uintptr_t); + + if (ngx_array_init(&lengths, ccv->cf->pool, n, 1) != NGX_OK) { + return NGX_ERROR; + } + + n = (nv * (2 * sizeof(ngx_stream_script_copy_code_t) + + sizeof(ngx_stream_script_var_code_t)) + + sizeof(uintptr_t) + + v->len + + sizeof(uintptr_t) - 1) + & ~(sizeof(uintptr_t) - 1); + + if (ngx_array_init(&values, ccv->cf->pool, n, 1) != NGX_OK) { + return NGX_ERROR; + } + + pf = &flushes; + pl = &lengths; + pv = &values; + + ngx_memzero(&sc, sizeof(ngx_stream_script_compile_t)); + + sc.cf = ccv->cf; + sc.source = v; + sc.flushes = &pf; + sc.lengths = &pl; + sc.values = &pv; + sc.complete_lengths = 1; + sc.complete_values = 1; + sc.zero = ccv->zero; + sc.conf_prefix = ccv->conf_prefix; + sc.root_prefix = ccv->root_prefix; + + if (ngx_stream_script_compile(&sc) != NGX_OK) { + return NGX_ERROR; + } + + if (flushes.nelts) { + ccv->complex_value->flushes = flushes.elts; + ccv->complex_value->flushes[flushes.nelts] = (ngx_uint_t) -1; + } + + ccv->complex_value->lengths = lengths.elts; + ccv->complex_value->values = values.elts; + + return NGX_OK; +} + + +char * +ngx_stream_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *p = conf; + + ngx_str_t *value; + ngx_stream_complex_value_t **cv; + ngx_stream_compile_complex_value_t ccv; + + cv = (ngx_stream_complex_value_t **) (p + cmd->offset); + + if (*cv != NULL) { + return "duplicate"; + } + + *cv = ngx_palloc(cf->pool, sizeof(ngx_stream_complex_value_t)); + if (*cv == NULL) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = *cv; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +ngx_uint_t +ngx_stream_script_variables_count(ngx_str_t *value) +{ + ngx_uint_t i, n; + + for (n = 0, i = 0; i < value->len; i++) { + if (value->data[i] == '$') { + n++; + } + } + + return n; +} + + +ngx_int_t +ngx_stream_script_compile(ngx_stream_script_compile_t *sc) +{ + u_char ch; + ngx_str_t name; + ngx_uint_t i, bracket; + + if (ngx_stream_script_init_arrays(sc) != NGX_OK) { + return NGX_ERROR; + } + + for (i = 0; i < sc->source->len; /* void */ ) { + + name.len = 0; + + if (sc->source->data[i] == '$') { + + if (++i == sc->source->len) { + goto invalid_variable; + } + + if (sc->source->data[i] >= '1' && sc->source->data[i] <= '9') { +#if (NGX_PCRE) + ngx_uint_t n; + + n = sc->source->data[i] - '0'; + + if (ngx_stream_script_add_capture_code(sc, n) != NGX_OK) { + return NGX_ERROR; + } + + i++; + + continue; +#else + ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0, + "using variable \"$%c\" requires " + "PCRE library", sc->source->data[i]); + return NGX_ERROR; +#endif + } + + if (sc->source->data[i] == '{') { + bracket = 1; + + if (++i == sc->source->len) { + goto invalid_variable; + } + + name.data = &sc->source->data[i]; + + } else { + bracket = 0; + name.data = &sc->source->data[i]; + } + + for ( /* void */ ; i < sc->source->len; i++, name.len++) { + ch = sc->source->data[i]; + + if (ch == '}' && bracket) { + i++; + bracket = 0; + break; + } + + if ((ch >= 'A' && ch <= 'Z') + || (ch >= 'a' && ch <= 'z') + || (ch >= '0' && ch <= '9') + || ch == '_') + { + continue; + } + + break; + } + + if (bracket) { + ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0, + "the closing bracket in \"%V\" " + "variable is missing", &name); + return NGX_ERROR; + } + + if (name.len == 0) { + goto invalid_variable; + } + + sc->variables++; + + if (ngx_stream_script_add_var_code(sc, &name) != NGX_OK) { + return NGX_ERROR; + } + + continue; + } + + name.data = &sc->source->data[i]; + + while (i < sc->source->len) { + + if (sc->source->data[i] == '$') { + break; + } + + i++; + name.len++; + } + + sc->size += name.len; + + if (ngx_stream_script_add_copy_code(sc, &name, (i == sc->source->len)) + != NGX_OK) + { + return NGX_ERROR; + } + } + + return ngx_stream_script_done(sc); + +invalid_variable: + + ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0, "invalid variable name"); + + return NGX_ERROR; +} + + +static ngx_int_t +ngx_stream_script_init_arrays(ngx_stream_script_compile_t *sc) +{ + ngx_uint_t n; + + if (sc->flushes && *sc->flushes == NULL) { + n = sc->variables ? sc->variables : 1; + *sc->flushes = ngx_array_create(sc->cf->pool, n, sizeof(ngx_uint_t)); + if (*sc->flushes == NULL) { + return NGX_ERROR; + } + } + + if (*sc->lengths == NULL) { + n = sc->variables * (2 * sizeof(ngx_stream_script_copy_code_t) + + sizeof(ngx_stream_script_var_code_t)) + + sizeof(uintptr_t); + + *sc->lengths = ngx_array_create(sc->cf->pool, n, 1); + if (*sc->lengths == NULL) { + return NGX_ERROR; + } + } + + if (*sc->values == NULL) { + n = (sc->variables * (2 * sizeof(ngx_stream_script_copy_code_t) + + sizeof(ngx_stream_script_var_code_t)) + + sizeof(uintptr_t) + + sc->source->len + + sizeof(uintptr_t) - 1) + & ~(sizeof(uintptr_t) - 1); + + *sc->values = ngx_array_create(sc->cf->pool, n, 1); + if (*sc->values == NULL) { + return NGX_ERROR; + } + } + + sc->variables = 0; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_script_done(ngx_stream_script_compile_t *sc) +{ + ngx_str_t zero; + uintptr_t *code; + + if (sc->zero) { + + zero.len = 1; + zero.data = (u_char *) "\0"; + + if (ngx_stream_script_add_copy_code(sc, &zero, 0) != NGX_OK) { + return NGX_ERROR; + } + } + + if (sc->conf_prefix || sc->root_prefix) { + if (ngx_stream_script_add_full_name_code(sc) != NGX_OK) { + return NGX_ERROR; + } + } + + if (sc->complete_lengths) { + code = ngx_stream_script_add_code(*sc->lengths, sizeof(uintptr_t), + NULL); + if (code == NULL) { + return NGX_ERROR; + } + + *code = (uintptr_t) NULL; + } + + if (sc->complete_values) { + code = ngx_stream_script_add_code(*sc->values, sizeof(uintptr_t), + &sc->main); + if (code == NULL) { + return NGX_ERROR; + } + + *code = (uintptr_t) NULL; + } + + return NGX_OK; +} + + +void * +ngx_stream_script_add_code(ngx_array_t *codes, size_t size, void *code) +{ + u_char *elts, **p; + void *new; + + elts = codes->elts; + + new = ngx_array_push_n(codes, size); + if (new == NULL) { + return NULL; + } + + if (code) { + if (elts != codes->elts) { + p = code; + *p += (u_char *) codes->elts - elts; + } + } + + return new; +} + + +static ngx_int_t +ngx_stream_script_add_copy_code(ngx_stream_script_compile_t *sc, + ngx_str_t *value, ngx_uint_t last) +{ + u_char *p; + size_t size, len, zero; + ngx_stream_script_copy_code_t *code; + + zero = (sc->zero && last); + len = value->len + zero; + + code = ngx_stream_script_add_code(*sc->lengths, + sizeof(ngx_stream_script_copy_code_t), + NULL); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = (ngx_stream_script_code_pt) ngx_stream_script_copy_len_code; + code->len = len; + + size = (sizeof(ngx_stream_script_copy_code_t) + len + sizeof(uintptr_t) - 1) + & ~(sizeof(uintptr_t) - 1); + + code = ngx_stream_script_add_code(*sc->values, size, &sc->main); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = ngx_stream_script_copy_code; + code->len = len; + + p = ngx_cpymem((u_char *) code + sizeof(ngx_stream_script_copy_code_t), + value->data, value->len); + + if (zero) { + *p = '\0'; + sc->zero = 0; + } + + return NGX_OK; +} + + +size_t +ngx_stream_script_copy_len_code(ngx_stream_script_engine_t *e) +{ + ngx_stream_script_copy_code_t *code; + + code = (ngx_stream_script_copy_code_t *) e->ip; + + e->ip += sizeof(ngx_stream_script_copy_code_t); + + return code->len; +} + + +void +ngx_stream_script_copy_code(ngx_stream_script_engine_t *e) +{ + u_char *p; + ngx_stream_script_copy_code_t *code; + + code = (ngx_stream_script_copy_code_t *) e->ip; + + p = e->pos; + + if (!e->skip) { + e->pos = ngx_copy(p, e->ip + sizeof(ngx_stream_script_copy_code_t), + code->len); + } + + e->ip += sizeof(ngx_stream_script_copy_code_t) + + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1)); + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, e->session->connection->log, 0, + "stream script copy: \"%*s\"", e->pos - p, p); +} + + +static ngx_int_t +ngx_stream_script_add_var_code(ngx_stream_script_compile_t *sc, ngx_str_t *name) +{ + ngx_int_t index, *p; + ngx_stream_script_var_code_t *code; + + index = ngx_stream_get_variable_index(sc->cf, name); + + if (index == NGX_ERROR) { + return NGX_ERROR; + } + + if (sc->flushes) { + p = ngx_array_push(*sc->flushes); + if (p == NULL) { + return NGX_ERROR; + } + + *p = index; + } + + code = ngx_stream_script_add_code(*sc->lengths, + sizeof(ngx_stream_script_var_code_t), + NULL); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = (ngx_stream_script_code_pt) + ngx_stream_script_copy_var_len_code; + code->index = (uintptr_t) index; + + code = ngx_stream_script_add_code(*sc->values, + sizeof(ngx_stream_script_var_code_t), + &sc->main); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = ngx_stream_script_copy_var_code; + code->index = (uintptr_t) index; + + return NGX_OK; +} + + +size_t +ngx_stream_script_copy_var_len_code(ngx_stream_script_engine_t *e) +{ + ngx_stream_variable_value_t *value; + ngx_stream_script_var_code_t *code; + + code = (ngx_stream_script_var_code_t *) e->ip; + + e->ip += sizeof(ngx_stream_script_var_code_t); + + if (e->flushed) { + value = ngx_stream_get_indexed_variable(e->session, code->index); + + } else { + value = ngx_stream_get_flushed_variable(e->session, code->index); + } + + if (value && !value->not_found) { + return value->len; + } + + return 0; +} + + +void +ngx_stream_script_copy_var_code(ngx_stream_script_engine_t *e) +{ + u_char *p; + ngx_stream_variable_value_t *value; + ngx_stream_script_var_code_t *code; + + code = (ngx_stream_script_var_code_t *) e->ip; + + e->ip += sizeof(ngx_stream_script_var_code_t); + + if (!e->skip) { + + if (e->flushed) { + value = ngx_stream_get_indexed_variable(e->session, code->index); + + } else { + value = ngx_stream_get_flushed_variable(e->session, code->index); + } + + if (value && !value->not_found) { + p = e->pos; + e->pos = ngx_copy(p, value->data, value->len); + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, + e->session->connection->log, 0, + "stream script var: \"%*s\"", e->pos - p, p); + } + } +} + + +#if (NGX_PCRE) + +static ngx_int_t +ngx_stream_script_add_capture_code(ngx_stream_script_compile_t *sc, + ngx_uint_t n) +{ + ngx_stream_script_copy_capture_code_t *code; + + code = ngx_stream_script_add_code(*sc->lengths, + sizeof(ngx_stream_script_copy_capture_code_t), + NULL); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = (ngx_stream_script_code_pt) + ngx_stream_script_copy_capture_len_code; + code->n = 2 * n; + + + code = ngx_stream_script_add_code(*sc->values, + sizeof(ngx_stream_script_copy_capture_code_t), + &sc->main); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = ngx_stream_script_copy_capture_code; + code->n = 2 * n; + + if (sc->ncaptures < n) { + sc->ncaptures = n; + } + + return NGX_OK; +} + + +size_t +ngx_stream_script_copy_capture_len_code(ngx_stream_script_engine_t *e) +{ + int *cap; + ngx_uint_t n; + ngx_stream_session_t *s; + ngx_stream_script_copy_capture_code_t *code; + + s = e->session; + + code = (ngx_stream_script_copy_capture_code_t *) e->ip; + + e->ip += sizeof(ngx_stream_script_copy_capture_code_t); + + n = code->n; + + if (n < s->ncaptures) { + cap = s->captures; + return cap[n + 1] - cap[n]; + } + + return 0; +} + + +void +ngx_stream_script_copy_capture_code(ngx_stream_script_engine_t *e) +{ + int *cap; + u_char *p, *pos; + ngx_uint_t n; + ngx_stream_session_t *s; + ngx_stream_script_copy_capture_code_t *code; + + s = e->session; + + code = (ngx_stream_script_copy_capture_code_t *) e->ip; + + e->ip += sizeof(ngx_stream_script_copy_capture_code_t); + + n = code->n; + + pos = e->pos; + + if (n < s->ncaptures) { + cap = s->captures; + p = s->captures_data; + e->pos = ngx_copy(pos, &p[cap[n]], cap[n + 1] - cap[n]); + } + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, e->session->connection->log, 0, + "stream script capture: \"%*s\"", e->pos - pos, pos); +} + +#endif + + +static ngx_int_t +ngx_stream_script_add_full_name_code(ngx_stream_script_compile_t *sc) +{ + ngx_stream_script_full_name_code_t *code; + + code = ngx_stream_script_add_code(*sc->lengths, + sizeof(ngx_stream_script_full_name_code_t), + NULL); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = (ngx_stream_script_code_pt) + ngx_stream_script_full_name_len_code; + code->conf_prefix = sc->conf_prefix; + + code = ngx_stream_script_add_code(*sc->values, + sizeof(ngx_stream_script_full_name_code_t), &sc->main); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = ngx_stream_script_full_name_code; + code->conf_prefix = sc->conf_prefix; + + return NGX_OK; +} + + +static size_t +ngx_stream_script_full_name_len_code(ngx_stream_script_engine_t *e) +{ + ngx_stream_script_full_name_code_t *code; + + code = (ngx_stream_script_full_name_code_t *) e->ip; + + e->ip += sizeof(ngx_stream_script_full_name_code_t); + + return code->conf_prefix ? ngx_cycle->conf_prefix.len: + ngx_cycle->prefix.len; +} + + +static void +ngx_stream_script_full_name_code(ngx_stream_script_engine_t *e) +{ + ngx_stream_script_full_name_code_t *code; + + ngx_str_t value, *prefix; + + code = (ngx_stream_script_full_name_code_t *) e->ip; + + value.data = e->buf.data; + value.len = e->pos - e->buf.data; + + prefix = code->conf_prefix ? (ngx_str_t *) &ngx_cycle->conf_prefix: + (ngx_str_t *) &ngx_cycle->prefix; + + if (ngx_get_full_name(e->session->connection->pool, prefix, &value) + != NGX_OK) + { + e->ip = ngx_stream_script_exit; + return; + } + + e->buf = value; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, e->session->connection->log, 0, + "stream script fullname: \"%V\"", &value); + + e->ip += sizeof(ngx_stream_script_full_name_code_t); +} diff --git a/src/stream/ngx_stream_script.h b/src/stream/ngx_stream_script.h new file mode 100644 index 0000000..0abe50e --- /dev/null +++ b/src/stream/ngx_stream_script.h @@ -0,0 +1,123 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_STREAM_SCRIPT_H_INCLUDED_ +#define _NGX_STREAM_SCRIPT_H_INCLUDED_ + + +#include +#include +#include + + +typedef struct { + u_char *ip; + u_char *pos; + ngx_stream_variable_value_t *sp; + + ngx_str_t buf; + ngx_str_t line; + + unsigned flushed:1; + unsigned skip:1; + + ngx_stream_session_t *session; +} ngx_stream_script_engine_t; + + +typedef struct { + ngx_conf_t *cf; + ngx_str_t *source; + + ngx_array_t **flushes; + ngx_array_t **lengths; + ngx_array_t **values; + + ngx_uint_t variables; + ngx_uint_t ncaptures; + ngx_uint_t size; + + void *main; + + unsigned complete_lengths:1; + unsigned complete_values:1; + unsigned zero:1; + unsigned conf_prefix:1; + unsigned root_prefix:1; +} ngx_stream_script_compile_t; + + +typedef struct { + ngx_str_t value; + ngx_uint_t *flushes; + void *lengths; + void *values; +} ngx_stream_complex_value_t; + + +typedef struct { + ngx_conf_t *cf; + ngx_str_t *value; + ngx_stream_complex_value_t *complex_value; + + unsigned zero:1; + unsigned conf_prefix:1; + unsigned root_prefix:1; +} ngx_stream_compile_complex_value_t; + + +typedef void (*ngx_stream_script_code_pt) (ngx_stream_script_engine_t *e); +typedef size_t (*ngx_stream_script_len_code_pt) (ngx_stream_script_engine_t *e); + + +typedef struct { + ngx_stream_script_code_pt code; + uintptr_t len; +} ngx_stream_script_copy_code_t; + + +typedef struct { + ngx_stream_script_code_pt code; + uintptr_t index; +} ngx_stream_script_var_code_t; + + +typedef struct { + ngx_stream_script_code_pt code; + uintptr_t n; +} ngx_stream_script_copy_capture_code_t; + + +typedef struct { + ngx_stream_script_code_pt code; + uintptr_t conf_prefix; +} ngx_stream_script_full_name_code_t; + + +void ngx_stream_script_flush_complex_value(ngx_stream_session_t *s, + ngx_stream_complex_value_t *val); +ngx_int_t ngx_stream_complex_value(ngx_stream_session_t *s, + ngx_stream_complex_value_t *val, ngx_str_t *value); +ngx_int_t ngx_stream_compile_complex_value( + ngx_stream_compile_complex_value_t *ccv); +char *ngx_stream_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + + +ngx_uint_t ngx_stream_script_variables_count(ngx_str_t *value); +ngx_int_t ngx_stream_script_compile(ngx_stream_script_compile_t *sc); + +void *ngx_stream_script_add_code(ngx_array_t *codes, size_t size, void *code); + +size_t ngx_stream_script_copy_len_code(ngx_stream_script_engine_t *e); +void ngx_stream_script_copy_code(ngx_stream_script_engine_t *e); +size_t ngx_stream_script_copy_var_len_code(ngx_stream_script_engine_t *e); +void ngx_stream_script_copy_var_code(ngx_stream_script_engine_t *e); +size_t ngx_stream_script_copy_capture_len_code(ngx_stream_script_engine_t *e); +void ngx_stream_script_copy_capture_code(ngx_stream_script_engine_t *e); + +#endif /* _NGX_STREAM_SCRIPT_H_INCLUDED_ */ diff --git a/src/stream/ngx_stream_split_clients_module.c b/src/stream/ngx_stream_split_clients_module.c new file mode 100644 index 0000000..af6c8a1 --- /dev/null +++ b/src/stream/ngx_stream_split_clients_module.c @@ -0,0 +1,244 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + uint32_t percent; + ngx_stream_variable_value_t value; +} ngx_stream_split_clients_part_t; + + +typedef struct { + ngx_stream_complex_value_t value; + ngx_array_t parts; +} ngx_stream_split_clients_ctx_t; + + +static char *ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_split_clients(ngx_conf_t *cf, ngx_command_t *dummy, + void *conf); + +static ngx_command_t ngx_stream_split_clients_commands[] = { + + { ngx_string("split_clients"), + NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2, + ngx_conf_split_clients_block, + NGX_STREAM_MAIN_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_split_clients_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_split_clients_module = { + NGX_MODULE_V1, + &ngx_stream_split_clients_module_ctx, /* module context */ + ngx_stream_split_clients_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_split_clients_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_stream_split_clients_ctx_t *ctx = + (ngx_stream_split_clients_ctx_t *) data; + + uint32_t hash; + ngx_str_t val; + ngx_uint_t i; + ngx_stream_split_clients_part_t *part; + + *v = ngx_stream_variable_null_value; + + if (ngx_stream_complex_value(s, &ctx->value, &val) != NGX_OK) { + return NGX_OK; + } + + hash = ngx_murmur_hash2(val.data, val.len); + + part = ctx->parts.elts; + + for (i = 0; i < ctx->parts.nelts; i++) { + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream split: %uD %uD", hash, part[i].percent); + + if (hash < part[i].percent || part[i].percent == 0) { + *v = part[i].value; + return NGX_OK; + } + } + + return NGX_OK; +} + + +static char * +ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *rv; + uint32_t sum, last; + ngx_str_t *value, name; + ngx_uint_t i; + ngx_conf_t save; + ngx_stream_variable_t *var; + ngx_stream_split_clients_ctx_t *ctx; + ngx_stream_split_clients_part_t *part; + ngx_stream_compile_complex_value_t ccv; + + ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_split_clients_ctx_t)); + if (ctx == NULL) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &ctx->value; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + name = value[2]; + + if (name.data[0] != '$') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"%V\"", &name); + return NGX_CONF_ERROR; + } + + name.len--; + name.data++; + + var = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE); + if (var == NULL) { + return NGX_CONF_ERROR; + } + + var->get_handler = ngx_stream_split_clients_variable; + var->data = (uintptr_t) ctx; + + if (ngx_array_init(&ctx->parts, cf->pool, 2, + sizeof(ngx_stream_split_clients_part_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + save = *cf; + cf->ctx = ctx; + cf->handler = ngx_stream_split_clients; + cf->handler_conf = conf; + + rv = ngx_conf_parse(cf, NULL); + + *cf = save; + + if (rv != NGX_CONF_OK) { + return rv; + } + + sum = 0; + last = 0; + part = ctx->parts.elts; + + for (i = 0; i < ctx->parts.nelts; i++) { + sum = part[i].percent ? sum + part[i].percent : 10000; + if (sum > 10000) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "percent total is greater than 100%%"); + return NGX_CONF_ERROR; + } + + if (part[i].percent) { + last += part[i].percent * (uint64_t) 0xffffffff / 10000; + part[i].percent = last; + } + } + + return rv; +} + + +static char * +ngx_stream_split_clients(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) +{ + ngx_int_t n; + ngx_str_t *value; + ngx_stream_split_clients_ctx_t *ctx; + ngx_stream_split_clients_part_t *part; + + ctx = cf->ctx; + value = cf->args->elts; + + part = ngx_array_push(&ctx->parts); + if (part == NULL) { + return NGX_CONF_ERROR; + } + + if (value[0].len == 1 && value[0].data[0] == '*') { + part->percent = 0; + + } else { + if (value[0].len == 0 || value[0].data[value[0].len - 1] != '%') { + goto invalid; + } + + n = ngx_atofp(value[0].data, value[0].len - 1, 2); + if (n == NGX_ERROR || n == 0) { + goto invalid; + } + + part->percent = (uint32_t) n; + } + + part->value.len = value[1].len; + part->value.valid = 1; + part->value.no_cacheable = 0; + part->value.not_found = 0; + part->value.data = value[1].data; + + return NGX_CONF_OK; + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid percent value \"%V\"", &value[0]); + return NGX_CONF_ERROR; +} diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c index e12da1b..2661220 100644 --- a/src/stream/ngx_stream_ssl_module.c +++ b/src/stream/ngx_stream_ssl_module.c @@ -10,10 +10,20 @@ #include +typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c, + ngx_pool_t *pool, ngx_str_t *s); + + #define NGX_DEFAULT_CIPHERS "HIGH:!aNULL:!MD5" -#define NGX_DEFAULT_ECDH_CURVE "prime256v1" +#define NGX_DEFAULT_ECDH_CURVE "auto" +static ngx_int_t ngx_stream_ssl_static_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_ssl_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); + +static ngx_int_t ngx_stream_ssl_add_variables(ngx_conf_t *cf); static void *ngx_stream_ssl_create_conf(ngx_conf_t *cf); static char *ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child); @@ -45,16 +55,16 @@ static ngx_command_t ngx_stream_ssl_commands[] = { { ngx_string("ssl_certificate"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_str_slot, + ngx_conf_set_str_array_slot, NGX_STREAM_SRV_CONF_OFFSET, - offsetof(ngx_stream_ssl_conf_t, certificate), + offsetof(ngx_stream_ssl_conf_t, certificates), NULL }, { ngx_string("ssl_certificate_key"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_str_slot, + ngx_conf_set_str_array_slot, NGX_STREAM_SRV_CONF_OFFSET, - offsetof(ngx_stream_ssl_conf_t, certificate_key), + offsetof(ngx_stream_ssl_conf_t, certificate_keys), NULL }, { ngx_string("ssl_password_file"), @@ -132,6 +142,7 @@ static ngx_command_t ngx_stream_ssl_commands[] = { static ngx_stream_module_t ngx_stream_ssl_module_ctx = { + ngx_stream_ssl_add_variables, /* preconfiguration */ NULL, /* postconfiguration */ NULL, /* create main configuration */ @@ -158,9 +169,112 @@ ngx_module_t ngx_stream_ssl_module = { }; +static ngx_stream_variable_t ngx_stream_ssl_vars[] = { + + { ngx_string("ssl_protocol"), NULL, ngx_stream_ssl_static_variable, + (uintptr_t) ngx_ssl_get_protocol, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_cipher"), NULL, ngx_stream_ssl_static_variable, + (uintptr_t) ngx_ssl_get_cipher_name, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_session_id"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_session_id, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_session_reused"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_session_reused, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_server_name"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_server_name, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_null_string, NULL, NULL, 0, 0, 0 } +}; + + static ngx_str_t ngx_stream_ssl_sess_id_ctx = ngx_string("STREAM"); +static ngx_int_t +ngx_stream_ssl_static_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_ssl_variable_handler_pt handler = (ngx_ssl_variable_handler_pt) data; + + size_t len; + ngx_str_t str; + + if (s->connection->ssl) { + + (void) handler(s->connection, NULL, &str); + + v->data = str.data; + + for (len = 0; v->data[len]; len++) { /* void */ } + + v->len = len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; + } + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_ssl_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_ssl_variable_handler_pt handler = (ngx_ssl_variable_handler_pt) data; + + ngx_str_t str; + + if (s->connection->ssl) { + + if (handler(s->connection, s->connection->pool, &str) != NGX_OK) { + return NGX_ERROR; + } + + v->len = str.len; + v->data = str.data; + + if (v->len) { + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; + } + } + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_ssl_add_variables(ngx_conf_t *cf) +{ + ngx_stream_variable_t *var, *v; + + for (v = ngx_stream_ssl_vars; v->name.len; v++) { + var = ngx_stream_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + static void * ngx_stream_ssl_create_conf(ngx_conf_t *cf) { @@ -175,8 +289,6 @@ ngx_stream_ssl_create_conf(ngx_conf_t *cf) * set by ngx_pcalloc(): * * scf->protocols = 0; - * scf->certificate = { 0, NULL }; - * scf->certificate_key = { 0, NULL }; * scf->dhparam = { 0, NULL }; * scf->ecdh_curve = { 0, NULL }; * scf->ciphers = { 0, NULL }; @@ -184,6 +296,8 @@ ngx_stream_ssl_create_conf(ngx_conf_t *cf) */ scf->handshake_timeout = NGX_CONF_UNSET_MSEC; + scf->certificates = NGX_CONF_UNSET_PTR; + scf->certificate_keys = NGX_CONF_UNSET_PTR; scf->passwords = NGX_CONF_UNSET_PTR; scf->prefer_server_ciphers = NGX_CONF_UNSET; scf->builtin_session_cache = NGX_CONF_UNSET; @@ -216,8 +330,9 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); - ngx_conf_merge_str_value(conf->certificate, prev->certificate, ""); - ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, ""); + ngx_conf_merge_ptr_value(conf->certificates, prev->certificates, NULL); + ngx_conf_merge_ptr_value(conf->certificate_keys, prev->certificate_keys, + NULL); ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL); @@ -231,15 +346,18 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) conf->ssl.log = cf->log; - if (conf->certificate.len == 0) { + if (conf->certificates == NULL) { return NGX_CONF_OK; } - if (conf->certificate_key.len == 0) { + if (conf->certificate_keys == NULL + || 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\"", - &conf->certificate); + ((ngx_str_t *) conf->certificates->elts) + + conf->certificates->nelts - 1); return NGX_CONF_ERROR; } @@ -255,31 +373,20 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) cln->handler = ngx_ssl_cleanup_ctx; cln->data = &conf->ssl; - if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate, - &conf->certificate_key, conf->passwords) + if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates, + conf->certificate_keys, conf->passwords) != NGX_OK) { return NGX_CONF_ERROR; } - if (SSL_CTX_set_cipher_list(conf->ssl.ctx, - (const char *) conf->ciphers.data) - == 0) + if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers, + conf->prefer_server_ciphers) + != NGX_OK) { - ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, - "SSL_CTX_set_cipher_list(\"%V\") failed", - &conf->ciphers); return NGX_CONF_ERROR; } - if (conf->prefer_server_ciphers) { - SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); - } - -#if (OPENSSL_VERSION_NUMBER < 0x10100001L && !defined LIBRESSL_VERSION_NUMBER) - SSL_CTX_set_tmp_rsa_callback(conf->ssl.ctx, ngx_ssl_rsa512_key_callback); -#endif - if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) { return NGX_CONF_ERROR; } diff --git a/src/stream/ngx_stream_ssl_module.h b/src/stream/ngx_stream_ssl_module.h index 85e8b6e..9b1c41a 100644 --- a/src/stream/ngx_stream_ssl_module.h +++ b/src/stream/ngx_stream_ssl_module.h @@ -27,8 +27,9 @@ typedef struct { time_t session_timeout; - ngx_str_t certificate; - ngx_str_t certificate_key; + ngx_array_t *certificates; + ngx_array_t *certificate_keys; + ngx_str_t dhparam; ngx_str_t ecdh_curve; diff --git a/src/stream/ngx_stream_upstream.c b/src/stream/ngx_stream_upstream.c index 69dddc5..2ea5eeb 100644 --- a/src/stream/ngx_stream_upstream.c +++ b/src/stream/ngx_stream_upstream.c @@ -39,13 +39,14 @@ static ngx_command_t ngx_stream_upstream_commands[] = { static ngx_stream_module_t ngx_stream_upstream_module_ctx = { + NULL, /* preconfiguration */ NULL, /* postconfiguration */ ngx_stream_upstream_create_main_conf, /* create main configuration */ ngx_stream_upstream_init_main_conf, /* init main configuration */ NULL, /* create server configuration */ - NULL, /* merge server configuration */ + NULL /* merge server configuration */ }; diff --git a/src/stream/ngx_stream_upstream.h b/src/stream/ngx_stream_upstream.h index 1f4810c..5f067c0 100644 --- a/src/stream/ngx_stream_upstream.h +++ b/src/stream/ngx_stream_upstream.h @@ -78,6 +78,21 @@ struct ngx_stream_upstream_srv_conf_s { }; +typedef struct { + ngx_str_t host; + in_port_t port; + ngx_uint_t no_port; /* unsigned no_port:1 */ + + ngx_uint_t naddrs; + ngx_resolver_addr_t *addrs; + + struct sockaddr *sockaddr; + socklen_t socklen; + + ngx_resolver_ctx_t *ctx; +} ngx_stream_upstream_resolved_t; + + typedef struct { ngx_peer_connection_t peer; ngx_buf_t downstream_buf; @@ -88,6 +103,7 @@ typedef struct { #if (NGX_STREAM_SSL) ngx_str_t ssl_name; #endif + ngx_stream_upstream_resolved_t *resolved; unsigned connected:1; unsigned proxy_protocol:1; } ngx_stream_upstream_t; diff --git a/src/stream/ngx_stream_upstream_hash_module.c b/src/stream/ngx_stream_upstream_hash_module.c index 56ff7d6..88185eb 100644 --- a/src/stream/ngx_stream_upstream_hash_module.c +++ b/src/stream/ngx_stream_upstream_hash_module.c @@ -23,6 +23,7 @@ typedef struct { typedef struct { + ngx_stream_complex_value_t key; ngx_stream_upstream_chash_points_t *points; } ngx_stream_upstream_hash_srv_conf_t; @@ -76,13 +77,14 @@ static ngx_command_t ngx_stream_upstream_hash_commands[] = { static ngx_stream_module_t ngx_stream_upstream_hash_module_ctx = { + NULL, /* preconfiguration */ NULL, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ ngx_stream_upstream_hash_create_conf, /* create server configuration */ - NULL, /* merge server configuration */ + NULL /* merge server configuration */ }; @@ -140,7 +142,9 @@ ngx_stream_upstream_init_hash_peer(ngx_stream_session_t *s, hcf = ngx_stream_conf_upstream_srv_conf(us, ngx_stream_upstream_hash_module); - hp->key = s->connection->addr_text; + if (ngx_stream_complex_value(s, &hcf->key, &hp->key) != NGX_OK) { + return NGX_ERROR; + } ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "upstream hash key:\"%V\"", &hp->key); @@ -615,15 +619,21 @@ ngx_stream_upstream_hash_create_conf(ngx_conf_t *cf) static char * ngx_stream_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - ngx_str_t *value; - ngx_stream_upstream_srv_conf_t *uscf; + ngx_stream_upstream_hash_srv_conf_t *hcf = conf; + + ngx_str_t *value; + ngx_stream_upstream_srv_conf_t *uscf; + ngx_stream_compile_complex_value_t ccv; value = cf->args->elts; - if (ngx_strcmp(value[1].data, "$remote_addr")) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "unsupported hash key \"%V\", use $remote_addr", - &value[1]); + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &hcf->key; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } diff --git a/src/stream/ngx_stream_upstream_least_conn_module.c b/src/stream/ngx_stream_upstream_least_conn_module.c index c9719f9..e884975 100644 --- a/src/stream/ngx_stream_upstream_least_conn_module.c +++ b/src/stream/ngx_stream_upstream_least_conn_module.c @@ -32,13 +32,14 @@ static ngx_command_t ngx_stream_upstream_least_conn_commands[] = { static ngx_stream_module_t ngx_stream_upstream_least_conn_module_ctx = { + NULL, /* preconfiguration */ NULL, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ - NULL, /* merge server configuration */ + NULL /* merge server configuration */ }; diff --git a/src/stream/ngx_stream_upstream_round_robin.c b/src/stream/ngx_stream_upstream_round_robin.c index e1ab592..7aced0f 100644 --- a/src/stream/ngx_stream_upstream_round_robin.c +++ b/src/stream/ngx_stream_upstream_round_robin.c @@ -23,6 +23,10 @@ static ngx_int_t ngx_stream_upstream_set_round_robin_peer_session( ngx_peer_connection_t *pc, void *data); static void ngx_stream_upstream_save_round_robin_peer_session( ngx_peer_connection_t *pc, void *data); +static ngx_int_t ngx_stream_upstream_empty_set_session( + ngx_peer_connection_t *pc, void *data); +static void ngx_stream_upstream_empty_save_session(ngx_peer_connection_t *pc, + void *data); #endif @@ -292,6 +296,123 @@ ngx_stream_upstream_init_round_robin_peer(ngx_stream_session_t *s, } +ngx_int_t +ngx_stream_upstream_create_round_robin_peer(ngx_stream_session_t *s, + ngx_stream_upstream_resolved_t *ur) +{ + u_char *p; + size_t len; + socklen_t socklen; + ngx_uint_t i, n; + struct sockaddr *sockaddr; + ngx_stream_upstream_rr_peer_t *peer, **peerp; + ngx_stream_upstream_rr_peers_t *peers; + ngx_stream_upstream_rr_peer_data_t *rrp; + + rrp = s->upstream->peer.data; + + if (rrp == NULL) { + rrp = ngx_palloc(s->connection->pool, + sizeof(ngx_stream_upstream_rr_peer_data_t)); + if (rrp == NULL) { + return NGX_ERROR; + } + + s->upstream->peer.data = rrp; + } + + peers = ngx_pcalloc(s->connection->pool, + sizeof(ngx_stream_upstream_rr_peers_t)); + if (peers == NULL) { + return NGX_ERROR; + } + + peer = ngx_pcalloc(s->connection->pool, + sizeof(ngx_stream_upstream_rr_peer_t) * ur->naddrs); + if (peer == NULL) { + return NGX_ERROR; + } + + peers->single = (ur->naddrs == 1); + peers->number = ur->naddrs; + peers->name = &ur->host; + + if (ur->sockaddr) { + peer[0].sockaddr = ur->sockaddr; + peer[0].socklen = ur->socklen; + peer[0].name = ur->host; + peer[0].weight = 1; + peer[0].effective_weight = 1; + peer[0].current_weight = 0; + peer[0].max_fails = 1; + peer[0].fail_timeout = 10; + peers->peer = peer; + + } else { + peerp = &peers->peer; + + for (i = 0; i < ur->naddrs; i++) { + + socklen = ur->addrs[i].socklen; + + sockaddr = ngx_palloc(s->connection->pool, socklen); + if (sockaddr == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen); + ngx_inet_set_port(sockaddr, ur->port); + + p = ngx_pnalloc(s->connection->pool, NGX_SOCKADDR_STRLEN); + if (p == NULL) { + return NGX_ERROR; + } + + len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1); + + peer[i].sockaddr = sockaddr; + peer[i].socklen = socklen; + peer[i].name.len = len; + peer[i].name.data = p; + peer[i].weight = 1; + peer[i].effective_weight = 1; + peer[i].current_weight = 0; + peer[i].max_fails = 1; + peer[i].fail_timeout = 10; + *peerp = &peer[i]; + peerp = &peer[i].next; + } + } + + rrp->peers = peers; + rrp->current = NULL; + + if (rrp->peers->number <= 8 * sizeof(uintptr_t)) { + rrp->tried = &rrp->data; + rrp->data = 0; + + } else { + n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1)) + / (8 * sizeof(uintptr_t)); + + rrp->tried = ngx_pcalloc(s->connection->pool, n * sizeof(uintptr_t)); + if (rrp->tried == NULL) { + return NGX_ERROR; + } + } + + s->upstream->peer.get = ngx_stream_upstream_get_round_robin_peer; + s->upstream->peer.free = ngx_stream_upstream_free_round_robin_peer; + s->upstream->peer.tries = ngx_stream_upstream_tries(rrp->peers); +#if (NGX_STREAM_SSL) + s->upstream->peer.set_session = ngx_stream_upstream_empty_set_session; + s->upstream->peer.save_session = ngx_stream_upstream_empty_save_session; +#endif + + return NGX_OK; +} + + ngx_int_t ngx_stream_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data) { @@ -699,4 +820,18 @@ ngx_stream_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc, } } + +static ngx_int_t +ngx_stream_upstream_empty_set_session(ngx_peer_connection_t *pc, void *data) +{ + return NGX_OK; +} + + +static void +ngx_stream_upstream_empty_save_session(ngx_peer_connection_t *pc, void *data) +{ + return; +} + #endif diff --git a/src/stream/ngx_stream_upstream_round_robin.h b/src/stream/ngx_stream_upstream_round_robin.h index 77ee0ab..452c2e9 100644 --- a/src/stream/ngx_stream_upstream_round_robin.h +++ b/src/stream/ngx_stream_upstream_round_robin.h @@ -130,6 +130,8 @@ ngx_int_t ngx_stream_upstream_init_round_robin(ngx_conf_t *cf, ngx_stream_upstream_srv_conf_t *us); ngx_int_t ngx_stream_upstream_init_round_robin_peer(ngx_stream_session_t *s, ngx_stream_upstream_srv_conf_t *us); +ngx_int_t ngx_stream_upstream_create_round_robin_peer(ngx_stream_session_t *s, + ngx_stream_upstream_resolved_t *ur); ngx_int_t ngx_stream_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data); void ngx_stream_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, diff --git a/src/stream/ngx_stream_upstream_zone_module.c b/src/stream/ngx_stream_upstream_zone_module.c index ffc9e8a..07ab88d 100644 --- a/src/stream/ngx_stream_upstream_zone_module.c +++ b/src/stream/ngx_stream_upstream_zone_module.c @@ -32,13 +32,14 @@ static ngx_command_t ngx_stream_upstream_zone_commands[] = { static ngx_stream_module_t ngx_stream_upstream_zone_module_ctx = { + NULL, /* preconfiguration */ NULL, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ - NULL, /* merge server configuration */ + NULL /* merge server configuration */ }; diff --git a/src/stream/ngx_stream_variables.c b/src/stream/ngx_stream_variables.c new file mode 100644 index 0000000..10f9c7e --- /dev/null +++ b/src/stream/ngx_stream_variables.c @@ -0,0 +1,971 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include + + +static ngx_int_t ngx_stream_variable_binary_remote_addr( + ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_remote_addr(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_remote_port(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_server_addr(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_server_port(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_bytes_sent(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_connection(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); + +static ngx_int_t ngx_stream_variable_nginx_version(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_hostname(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_pid(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_msec(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_time_iso8601(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_time_local(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); + + +static ngx_stream_variable_t ngx_stream_core_variables[] = { + + { ngx_string("binary_remote_addr"), NULL, + ngx_stream_variable_binary_remote_addr, 0, 0, 0 }, + + { ngx_string("remote_addr"), NULL, + ngx_stream_variable_remote_addr, 0, 0, 0 }, + + { ngx_string("remote_port"), NULL, + ngx_stream_variable_remote_port, 0, 0, 0 }, + + { ngx_string("server_addr"), NULL, + ngx_stream_variable_server_addr, 0, 0, 0 }, + + { ngx_string("server_port"), NULL, + ngx_stream_variable_server_port, 0, 0, 0 }, + + { ngx_string("bytes_sent"), NULL, ngx_stream_variable_bytes_sent, + 0, 0, 0 }, + + { ngx_string("connection"), NULL, + ngx_stream_variable_connection, 0, 0, 0 }, + + { ngx_string("nginx_version"), NULL, ngx_stream_variable_nginx_version, + 0, 0, 0 }, + + { ngx_string("hostname"), NULL, ngx_stream_variable_hostname, + 0, 0, 0 }, + + { ngx_string("pid"), NULL, ngx_stream_variable_pid, + 0, 0, 0 }, + + { ngx_string("msec"), NULL, ngx_stream_variable_msec, + 0, NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + { ngx_string("time_iso8601"), NULL, ngx_stream_variable_time_iso8601, + 0, NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + { ngx_string("time_local"), NULL, ngx_stream_variable_time_local, + 0, NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + { ngx_null_string, NULL, NULL, 0, 0, 0 } +}; + + +ngx_stream_variable_value_t ngx_stream_variable_null_value = + ngx_stream_variable(""); +ngx_stream_variable_value_t ngx_stream_variable_true_value = + ngx_stream_variable("1"); + + +ngx_stream_variable_t * +ngx_stream_add_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags) +{ + ngx_int_t rc; + ngx_uint_t i; + ngx_hash_key_t *key; + ngx_stream_variable_t *v; + ngx_stream_core_main_conf_t *cmcf; + + if (name->len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"$\""); + return NULL; + } + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + + key = cmcf->variables_keys->keys.elts; + for (i = 0; i < cmcf->variables_keys->keys.nelts; i++) { + if (name->len != key[i].key.len + || ngx_strncasecmp(name->data, key[i].key.data, name->len) != 0) + { + continue; + } + + v = key[i].value; + + if (!(v->flags & NGX_STREAM_VAR_CHANGEABLE)) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the duplicate \"%V\" variable", name); + return NULL; + } + + return v; + } + + v = ngx_palloc(cf->pool, sizeof(ngx_stream_variable_t)); + if (v == NULL) { + return NULL; + } + + v->name.len = name->len; + v->name.data = ngx_pnalloc(cf->pool, name->len); + if (v->name.data == NULL) { + return NULL; + } + + ngx_strlow(v->name.data, name->data, name->len); + + v->set_handler = NULL; + v->get_handler = NULL; + v->data = 0; + v->flags = flags; + v->index = 0; + + rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v, 0); + + if (rc == NGX_ERROR) { + return NULL; + } + + if (rc == NGX_BUSY) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "conflicting variable name \"%V\"", name); + return NULL; + } + + return v; +} + + +ngx_int_t +ngx_stream_get_variable_index(ngx_conf_t *cf, ngx_str_t *name) +{ + ngx_uint_t i; + ngx_stream_variable_t *v; + ngx_stream_core_main_conf_t *cmcf; + + if (name->len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"$\""); + return NGX_ERROR; + } + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + + v = cmcf->variables.elts; + + if (v == NULL) { + if (ngx_array_init(&cmcf->variables, cf->pool, 4, + sizeof(ngx_stream_variable_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + } else { + for (i = 0; i < cmcf->variables.nelts; i++) { + if (name->len != v[i].name.len + || ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0) + { + continue; + } + + return i; + } + } + + v = ngx_array_push(&cmcf->variables); + if (v == NULL) { + return NGX_ERROR; + } + + v->name.len = name->len; + v->name.data = ngx_pnalloc(cf->pool, name->len); + if (v->name.data == NULL) { + return NGX_ERROR; + } + + ngx_strlow(v->name.data, name->data, name->len); + + v->set_handler = NULL; + v->get_handler = NULL; + v->data = 0; + v->flags = 0; + v->index = cmcf->variables.nelts - 1; + + return v->index; +} + + +ngx_stream_variable_value_t * +ngx_stream_get_indexed_variable(ngx_stream_session_t *s, ngx_uint_t index) +{ + ngx_stream_variable_t *v; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); + + if (cmcf->variables.nelts <= index) { + ngx_log_error(NGX_LOG_ALERT, s->connection->log, 0, + "unknown variable index: %ui", index); + return NULL; + } + + if (s->variables[index].not_found || s->variables[index].valid) { + return &s->variables[index]; + } + + v = cmcf->variables.elts; + + if (v[index].get_handler(s, &s->variables[index], v[index].data) + == NGX_OK) + { + if (v[index].flags & NGX_STREAM_VAR_NOCACHEABLE) { + s->variables[index].no_cacheable = 1; + } + + return &s->variables[index]; + } + + s->variables[index].valid = 0; + s->variables[index].not_found = 1; + + return NULL; +} + + +ngx_stream_variable_value_t * +ngx_stream_get_flushed_variable(ngx_stream_session_t *s, ngx_uint_t index) +{ + ngx_stream_variable_value_t *v; + + v = &s->variables[index]; + + if (v->valid || v->not_found) { + if (!v->no_cacheable) { + return v; + } + + v->valid = 0; + v->not_found = 0; + } + + return ngx_stream_get_indexed_variable(s, index); +} + + +ngx_stream_variable_value_t * +ngx_stream_get_variable(ngx_stream_session_t *s, ngx_str_t *name, + ngx_uint_t key) +{ + ngx_stream_variable_t *v; + ngx_stream_variable_value_t *vv; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); + + v = ngx_hash_find(&cmcf->variables_hash, key, name->data, name->len); + + if (v) { + if (v->flags & NGX_STREAM_VAR_INDEXED) { + return ngx_stream_get_flushed_variable(s, v->index); + + } else { + + vv = ngx_palloc(s->connection->pool, + sizeof(ngx_stream_variable_value_t)); + + if (vv && v->get_handler(s, vv, v->data) == NGX_OK) { + return vv; + } + + return NULL; + } + } + + vv = ngx_palloc(s->connection->pool, sizeof(ngx_stream_variable_value_t)); + if (vv == NULL) { + return NULL; + } + + vv->not_found = 1; + + return vv; +} + + +static ngx_int_t +ngx_stream_variable_binary_remote_addr(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) + { + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + switch (s->connection->sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) s->connection->sockaddr; + + v->len = sizeof(struct in6_addr); + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = sin6->sin6_addr.s6_addr; + + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) s->connection->sockaddr; + + v->len = sizeof(in_addr_t); + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) &sin->sin_addr; + + break; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_remote_addr(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + v->len = s->connection->addr_text.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = s->connection->addr_text.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_remote_port(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_uint_t port; + + v->len = 0; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + v->data = ngx_pnalloc(s->connection->pool, sizeof("65535") - 1); + if (v->data == NULL) { + return NGX_ERROR; + } + + port = ngx_inet_get_port(s->connection->sockaddr); + + if (port > 0 && port < 65536) { + v->len = ngx_sprintf(v->data, "%ui", port) - v->data; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_server_addr(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_str_t str; + u_char addr[NGX_SOCKADDR_STRLEN]; + + str.len = NGX_SOCKADDR_STRLEN; + str.data = addr; + + if (ngx_connection_local_sockaddr(s->connection, &str, 0) != NGX_OK) { + return NGX_ERROR; + } + + str.data = ngx_pnalloc(s->connection->pool, str.len); + if (str.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(str.data, addr, str.len); + + v->len = str.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = str.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_server_port(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_uint_t port; + + v->len = 0; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + if (ngx_connection_local_sockaddr(s->connection, NULL, 0) != NGX_OK) { + return NGX_ERROR; + } + + v->data = ngx_pnalloc(s->connection->pool, sizeof("65535") - 1); + if (v->data == NULL) { + return NGX_ERROR; + } + + port = ngx_inet_get_port(s->connection->local_sockaddr); + + if (port > 0 && port < 65536) { + v->len = ngx_sprintf(v->data, "%ui", port) - v->data; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_bytes_sent(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + p = ngx_pnalloc(s->connection->pool, NGX_OFF_T_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(p, "%O", s->connection->sent) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_connection(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + p = ngx_pnalloc(s->connection->pool, NGX_ATOMIC_T_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(p, "%uA", s->connection->number) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_nginx_version(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + v->len = sizeof(NGINX_VERSION) - 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) NGINX_VERSION; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_hostname(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + v->len = ngx_cycle->hostname.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = ngx_cycle->hostname.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_pid(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + p = ngx_pnalloc(s->connection->pool, NGX_INT64_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(p, "%P", ngx_pid) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_msec(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + u_char *p; + ngx_time_t *tp; + + p = ngx_pnalloc(s->connection->pool, NGX_TIME_T_LEN + 4); + if (p == NULL) { + return NGX_ERROR; + } + + tp = ngx_timeofday(); + + v->len = ngx_sprintf(p, "%T.%03M", tp->sec, tp->msec) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_time_iso8601(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + p = ngx_pnalloc(s->connection->pool, ngx_cached_http_log_iso8601.len); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(p, ngx_cached_http_log_iso8601.data, + ngx_cached_http_log_iso8601.len); + + v->len = ngx_cached_http_log_iso8601.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_time_local(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + p = ngx_pnalloc(s->connection->pool, ngx_cached_http_log_time.len); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(p, ngx_cached_http_log_time.data, ngx_cached_http_log_time.len); + + v->len = ngx_cached_http_log_time.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +void * +ngx_stream_map_find(ngx_stream_session_t *s, ngx_stream_map_t *map, + ngx_str_t *match) +{ + void *value; + u_char *low; + size_t len; + ngx_uint_t key; + + len = match->len; + + if (len) { + low = ngx_pnalloc(s->connection->pool, len); + if (low == NULL) { + return NULL; + } + + } else { + low = NULL; + } + + key = ngx_hash_strlow(low, match->data, len); + + value = ngx_hash_find_combined(&map->hash, key, low, len); + if (value) { + return value; + } + +#if (NGX_PCRE) + + if (len && map->nregex) { + ngx_int_t n; + ngx_uint_t i; + ngx_stream_map_regex_t *reg; + + reg = map->regex; + + for (i = 0; i < map->nregex; i++) { + + n = ngx_stream_regex_exec(s, reg[i].regex, match); + + if (n == NGX_OK) { + return reg[i].value; + } + + if (n == NGX_DECLINED) { + continue; + } + + /* NGX_ERROR */ + + return NULL; + } + } + +#endif + + return NULL; +} + + +#if (NGX_PCRE) + +static ngx_int_t +ngx_stream_variable_not_found(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + v->not_found = 1; + return NGX_OK; +} + + +ngx_stream_regex_t * +ngx_stream_regex_compile(ngx_conf_t *cf, ngx_regex_compile_t *rc) +{ + u_char *p; + size_t size; + ngx_str_t name; + ngx_uint_t i, n; + ngx_stream_variable_t *v; + ngx_stream_regex_t *re; + ngx_stream_regex_variable_t *rv; + ngx_stream_core_main_conf_t *cmcf; + + rc->pool = cf->pool; + + if (ngx_regex_compile(rc) != NGX_OK) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc->err); + return NULL; + } + + re = ngx_pcalloc(cf->pool, sizeof(ngx_stream_regex_t)); + if (re == NULL) { + return NULL; + } + + re->regex = rc->regex; + re->ncaptures = rc->captures; + re->name = rc->pattern; + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + cmcf->ncaptures = ngx_max(cmcf->ncaptures, re->ncaptures); + + n = (ngx_uint_t) rc->named_captures; + + if (n == 0) { + return re; + } + + rv = ngx_palloc(rc->pool, n * sizeof(ngx_stream_regex_variable_t)); + if (rv == NULL) { + return NULL; + } + + re->variables = rv; + re->nvariables = n; + + size = rc->name_size; + p = rc->names; + + for (i = 0; i < n; i++) { + rv[i].capture = 2 * ((p[0] << 8) + p[1]); + + name.data = &p[2]; + name.len = ngx_strlen(name.data); + + v = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE); + if (v == NULL) { + return NULL; + } + + rv[i].index = ngx_stream_get_variable_index(cf, &name); + if (rv[i].index == NGX_ERROR) { + return NULL; + } + + v->get_handler = ngx_stream_variable_not_found; + + p += size; + } + + return re; +} + + +ngx_int_t +ngx_stream_regex_exec(ngx_stream_session_t *s, ngx_stream_regex_t *re, + ngx_str_t *str) +{ + ngx_int_t rc, index; + ngx_uint_t i, n, len; + ngx_stream_variable_value_t *vv; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); + + if (re->ncaptures) { + len = cmcf->ncaptures; + + if (s->captures == NULL) { + s->captures = ngx_palloc(s->connection->pool, len * sizeof(int)); + if (s->captures == NULL) { + return NGX_ERROR; + } + } + + } else { + len = 0; + } + + rc = ngx_regex_exec(re->regex, str, s->captures, len); + + if (rc == NGX_REGEX_NO_MATCHED) { + return NGX_DECLINED; + } + + if (rc < 0) { + ngx_log_error(NGX_LOG_ALERT, s->connection->log, 0, + ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"", + rc, str, &re->name); + return NGX_ERROR; + } + + for (i = 0; i < re->nvariables; i++) { + + n = re->variables[i].capture; + index = re->variables[i].index; + vv = &s->variables[index]; + + vv->len = s->captures[n + 1] - s->captures[n]; + vv->valid = 1; + vv->no_cacheable = 0; + vv->not_found = 0; + vv->data = &str->data[s->captures[n]]; + +#if (NGX_DEBUG) + { + ngx_stream_variable_t *v; + + v = cmcf->variables.elts; + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream regex set $%V to \"%v\"", &v[index].name, vv); + } +#endif + } + + s->ncaptures = rc * 2; + s->captures_data = str->data; + + return NGX_OK; +} + +#endif + + +ngx_int_t +ngx_stream_variables_add_core_vars(ngx_conf_t *cf) +{ + ngx_int_t rc; + ngx_stream_variable_t *cv, *v; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + + cmcf->variables_keys = ngx_pcalloc(cf->temp_pool, + sizeof(ngx_hash_keys_arrays_t)); + if (cmcf->variables_keys == NULL) { + return NGX_ERROR; + } + + cmcf->variables_keys->pool = cf->pool; + cmcf->variables_keys->temp_pool = cf->pool; + + if (ngx_hash_keys_array_init(cmcf->variables_keys, NGX_HASH_SMALL) + != NGX_OK) + { + return NGX_ERROR; + } + + for (cv = ngx_stream_core_variables; cv->name.len; cv++) { + v = ngx_palloc(cf->pool, sizeof(ngx_stream_variable_t)); + if (v == NULL) { + return NGX_ERROR; + } + + *v = *cv; + + rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v, + NGX_HASH_READONLY_KEY); + + if (rc == NGX_OK) { + continue; + } + + if (rc == NGX_BUSY) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "conflicting variable name \"%V\"", &v->name); + } + + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_stream_variables_init_vars(ngx_conf_t *cf) +{ + ngx_uint_t i, n; + ngx_hash_key_t *key; + ngx_hash_init_t hash; + ngx_stream_variable_t *v, *av; + ngx_stream_core_main_conf_t *cmcf; + + /* set the handlers for the indexed stream variables */ + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + + v = cmcf->variables.elts; + key = cmcf->variables_keys->keys.elts; + + for (i = 0; i < cmcf->variables.nelts; i++) { + + for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) { + + av = key[n].value; + + if (v[i].name.len == key[n].key.len + && ngx_strncmp(v[i].name.data, key[n].key.data, v[i].name.len) + == 0) + { + v[i].get_handler = av->get_handler; + v[i].data = av->data; + + av->flags |= NGX_STREAM_VAR_INDEXED; + v[i].flags = av->flags; + + av->index = i; + + if (av->get_handler == NULL) { + break; + } + + goto next; + } + } + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "unknown \"%V\" variable", &v[i].name); + + return NGX_ERROR; + + next: + continue; + } + + + for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) { + av = key[n].value; + + if (av->flags & NGX_STREAM_VAR_NOHASH) { + key[n].key.data = NULL; + } + } + + + hash.hash = &cmcf->variables_hash; + hash.key = ngx_hash_key; + hash.max_size = cmcf->variables_hash_max_size; + hash.bucket_size = cmcf->variables_hash_bucket_size; + hash.name = "variables_hash"; + hash.pool = cf->pool; + hash.temp_pool = NULL; + + if (ngx_hash_init(&hash, cmcf->variables_keys->keys.elts, + cmcf->variables_keys->keys.nelts) + != NGX_OK) + { + return NGX_ERROR; + } + + cmcf->variables_keys = NULL; + + return NGX_OK; +} diff --git a/src/stream/ngx_stream_variables.h b/src/stream/ngx_stream_variables.h new file mode 100644 index 0000000..e4151e2 --- /dev/null +++ b/src/stream/ngx_stream_variables.h @@ -0,0 +1,109 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_STREAM_VARIABLES_H_INCLUDED_ +#define _NGX_STREAM_VARIABLES_H_INCLUDED_ + + +#include +#include +#include + + +typedef ngx_variable_value_t ngx_stream_variable_value_t; + +#define ngx_stream_variable(v) { sizeof(v) - 1, 1, 0, 0, 0, (u_char *) v } + +typedef struct ngx_stream_variable_s ngx_stream_variable_t; + +typedef void (*ngx_stream_set_variable_pt) (ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +typedef ngx_int_t (*ngx_stream_get_variable_pt) (ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); + + +#define NGX_STREAM_VAR_CHANGEABLE 1 +#define NGX_STREAM_VAR_NOCACHEABLE 2 +#define NGX_STREAM_VAR_INDEXED 4 +#define NGX_STREAM_VAR_NOHASH 8 + + +struct ngx_stream_variable_s { + ngx_str_t name; /* must be first to build the hash */ + ngx_stream_set_variable_pt set_handler; + ngx_stream_get_variable_pt get_handler; + uintptr_t data; + ngx_uint_t flags; + ngx_uint_t index; +}; + + +ngx_stream_variable_t *ngx_stream_add_variable(ngx_conf_t *cf, ngx_str_t *name, + ngx_uint_t flags); +ngx_int_t ngx_stream_get_variable_index(ngx_conf_t *cf, ngx_str_t *name); +ngx_stream_variable_value_t *ngx_stream_get_indexed_variable( + ngx_stream_session_t *s, ngx_uint_t index); +ngx_stream_variable_value_t *ngx_stream_get_flushed_variable( + ngx_stream_session_t *s, ngx_uint_t index); + +ngx_stream_variable_value_t *ngx_stream_get_variable(ngx_stream_session_t *s, + ngx_str_t *name, ngx_uint_t key); + + +#if (NGX_PCRE) + +typedef struct { + ngx_uint_t capture; + ngx_int_t index; +} ngx_stream_regex_variable_t; + + +typedef struct { + ngx_regex_t *regex; + ngx_uint_t ncaptures; + ngx_stream_regex_variable_t *variables; + ngx_uint_t nvariables; + ngx_str_t name; +} ngx_stream_regex_t; + + +typedef struct { + ngx_stream_regex_t *regex; + void *value; +} ngx_stream_map_regex_t; + + +ngx_stream_regex_t *ngx_stream_regex_compile(ngx_conf_t *cf, + ngx_regex_compile_t *rc); +ngx_int_t ngx_stream_regex_exec(ngx_stream_session_t *s, ngx_stream_regex_t *re, + ngx_str_t *str); + +#endif + + +typedef struct { + ngx_hash_combined_t hash; +#if (NGX_PCRE) + ngx_stream_map_regex_t *regex; + ngx_uint_t nregex; +#endif +} ngx_stream_map_t; + + +void *ngx_stream_map_find(ngx_stream_session_t *s, ngx_stream_map_t *map, + ngx_str_t *match); + + +ngx_int_t ngx_stream_variables_add_core_vars(ngx_conf_t *cf); +ngx_int_t ngx_stream_variables_init_vars(ngx_conf_t *cf); + + +extern ngx_stream_variable_value_t ngx_stream_variable_null_value; +extern ngx_stream_variable_value_t ngx_stream_variable_true_value; + + +#endif /* _NGX_STREAM_VARIABLES_H_INCLUDED_ */ From 42f5c9e7b475ee71e5bda337dd18474c54a101ac Mon Sep 17 00:00:00 2001 From: Christos Trochalakis Date: Fri, 16 Sep 2016 12:54:01 +0300 Subject: [PATCH 002/475] New upstream version 1.11.4 --- CHANGES | 30 + CHANGES.ru | 29 + auto/modules | 12 + auto/options | 3 + contrib/unicode2nginx/unicode-to-nginx.pl | 7 +- src/core/nginx.h | 4 +- src/core/ngx_inet.c | 87 + src/core/ngx_inet.h | 1 + src/core/ngx_thread_pool.c | 7 + src/event/ngx_event_connect.c | 2 +- src/event/ngx_event_openssl.c | 63 +- src/event/ngx_event_openssl_stapling.c | 3 + src/event/ngx_event_pipe.c | 12 +- src/http/modules/ngx_http_geo_module.c | 50 +- src/http/modules/ngx_http_limit_req_module.c | 14 +- src/http/modules/ngx_http_realip_module.c | 2 +- src/http/modules/ngx_http_ssi_filter_module.c | 10 +- src/http/ngx_http_core_module.c | 134 +- src/http/ngx_http_upstream.c | 22 +- src/http/ngx_http_upstream.h | 1 + src/mail/ngx_mail.h | 4 +- src/mail/ngx_mail_core_module.c | 9 + src/os/unix/ngx_posix_init.c | 6 +- src/os/unix/ngx_process_cycle.c | 4 +- src/stream/ngx_stream.c | 2 + src/stream/ngx_stream.h | 33 +- src/stream/ngx_stream_core_module.c | 31 + src/stream/ngx_stream_geo_module.c | 40 +- src/stream/ngx_stream_handler.c | 238 ++- src/stream/ngx_stream_log_module.c | 1474 +++++++++++++++++ src/stream/ngx_stream_proxy_module.c | 129 +- src/stream/ngx_stream_realip_module.c | 342 ++++ src/stream/ngx_stream_return_module.c | 14 +- src/stream/ngx_stream_script.c | 67 + src/stream/ngx_stream_script.h | 4 + src/stream/ngx_stream_upstream.c | 236 ++- src/stream/ngx_stream_upstream.h | 12 + src/stream/ngx_stream_variables.c | 143 +- 38 files changed, 3005 insertions(+), 276 deletions(-) create mode 100644 src/stream/ngx_stream_log_module.c create mode 100644 src/stream/ngx_stream_realip_module.c diff --git a/CHANGES b/CHANGES index a795ef2..2ea8d8d 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,34 @@ +Changes with nginx 1.11.4 13 Sep 2016 + + *) Feature: the $upstream_bytes_received variable. + + *) Feature: the $bytes_received, $session_time, $protocol, $status, + $upstream_addr, $upstream_bytes_sent, $upstream_bytes_received, + $upstream_connect_time, $upstream_first_byte_time, and + $upstream_session_time variables in the stream module. + + *) Feature: the ngx_stream_log_module. + + *) Feature: the "proxy_protocol" parameter of the "listen" directive, + the $proxy_protocol_addr and $proxy_protocol_port variables in the + stream module. + + *) Feature: the ngx_stream_realip_module. + + *) Bugfix: nginx could not be built with the stream module and the + ngx_http_ssl_module, but without ngx_stream_ssl_module; the bug had + appeared in 1.11.3. + + *) Feature: the IP_BIND_ADDRESS_NO_PORT socket option was not used; the + bug had appeared in 1.11.2. + + *) Bugfix: in the "ranges" parameter of the "geo" directive. + + *) Bugfix: an incorrect response might be returned when using the "aio + threads" and "sendfile" directives; the bug had appeared in 1.9.13. + + Changes with nginx 1.11.3 26 Jul 2016 *) Change: now the "accept_mutex" directive is turned off by default. diff --git a/CHANGES.ru b/CHANGES.ru index cd424e9..28ac2d8 100644 --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,33 @@ +Изменения в nginx 1.11.4 13.09.2016 + + *) Добавление: переменная $upstream_bytes_received. + + *) Добавление: переменные $bytes_received, $session_time, $protocol, + $status, $upstream_addr, $upstream_bytes_sent, + $upstream_bytes_received, $upstream_connect_time, + $upstream_first_byte_time и $upstream_session_time в модуле stream. + + *) Добавление: модуль ngx_stream_log_module. + + *) Добавление: параметр proxy_protocol в директиве listen, переменные + $proxy_protocol_addr и $proxy_protocol_port в модуле stream. + + *) Добавление: модуль ngx_stream_realip_module. + + *) Исправление: nginx не собирался с модулем stream и модулем + ngx_http_ssl_module, но без модуля ngx_stream_ssl_module; ошибка + появилась в 1.11.3. + + *) Добавление: опция сокета IP_BIND_ADDRESS_NO_PORT не использовалась; + ошибка появилась в 1.11.2. + + *) Исправление: в параметре ranges директивы geo. + + *) Исправление: при использовании директив "aio threads" и sendfile мог + возвращаться некорректный ответ; ошибка появилась в 1.9.13. + + Изменения в nginx 1.11.3 26.07.2016 *) Изменение: теперь accept_mutex по умолчанию выключен. diff --git a/auto/modules b/auto/modules index 614037c..433767a 100644 --- a/auto/modules +++ b/auto/modules @@ -971,6 +971,7 @@ if [ $STREAM != NO ]; then ngx_module_name="ngx_stream_module \ ngx_stream_core_module \ + ngx_stream_log_module \ ngx_stream_proxy_module \ ngx_stream_upstream_module" ngx_module_incs="src/stream" @@ -984,6 +985,7 @@ if [ $STREAM != NO ]; then src/stream/ngx_stream_script.c \ src/stream/ngx_stream_handler.c \ src/stream/ngx_stream_core_module.c \ + src/stream/ngx_stream_log_module.c \ src/stream/ngx_stream_proxy_module.c \ src/stream/ngx_stream_upstream.c \ src/stream/ngx_stream_upstream_round_robin.c" @@ -1005,6 +1007,16 @@ if [ $STREAM != NO ]; then . auto/module fi + if [ $STREAM_REALIP = YES ]; then + ngx_module_name=ngx_stream_realip_module + ngx_module_deps= + ngx_module_srcs=src/stream/ngx_stream_realip_module.c + ngx_module_libs= + ngx_module_link=$STREAM_REALIP + + . auto/module + fi + if [ $STREAM_LIMIT_CONN = YES ]; then ngx_module_name=ngx_stream_limit_conn_module ngx_module_deps= diff --git a/auto/options b/auto/options index a8fce30..73149d9 100644 --- a/auto/options +++ b/auto/options @@ -115,6 +115,7 @@ MAIL_SMTP=YES STREAM=NO STREAM_SSL=NO +STREAM_REALIP=NO STREAM_LIMIT_CONN=YES STREAM_ACCESS=YES STREAM_GEO=YES @@ -296,6 +297,7 @@ use the \"--with-mail_ssl_module\" option instead" --with-stream) STREAM=YES ;; --with-stream=dynamic) STREAM=DYNAMIC ;; --with-stream_ssl_module) STREAM_SSL=YES ;; + --with-stream_realip_module) STREAM_REALIP=YES ;; --with-stream_geoip_module) STREAM_GEOIP=YES ;; --with-stream_geoip_module=dynamic) STREAM_GEOIP=DYNAMIC ;; @@ -503,6 +505,7 @@ cat << END --with-stream enable TCP/UDP proxy module --with-stream=dynamic enable dynamic TCP/UDP proxy module --with-stream_ssl_module enable ngx_stream_ssl_module + --with-stream_realip_module enable ngx_stream_realip_module --with-stream_geoip_module enable ngx_stream_geoip_module --with-stream_geoip_module=dynamic enable dynamic ngx_stream_geoip_module --without-stream_limit_conn_module disable ngx_stream_limit_conn_module diff --git a/contrib/unicode2nginx/unicode-to-nginx.pl b/contrib/unicode2nginx/unicode-to-nginx.pl index daaf354..d113fed 100755 --- a/contrib/unicode2nginx/unicode-to-nginx.pl +++ b/contrib/unicode2nginx/unicode-to-nginx.pl @@ -10,7 +10,7 @@ # Needs perl 5.6 or later. -# Written by Maxim Dounin, mdounin@rambler-co.ru +# Written by Maxim Dounin, mdounin@mdounin.ru ############################################################################### @@ -33,7 +33,10 @@ while (<>) { # Produce UTF-8 sequence from character code; - my $un_utf8 = join('', map { sprintf("%02X", $_) } unpack("C*", pack("U", hex($un_code)))); + my $un_utf8 = join('', + map { sprintf("%02X", $_) } + unpack("U0C*", pack("U", hex($un_code))) + ); print " $cs_code $un_utf8 ; $un_name\n"; diff --git a/src/core/nginx.h b/src/core/nginx.h index e303e66..7e9aae6 100644 --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1011003 -#define NGINX_VERSION "1.11.3" +#define nginx_version 1011004 +#define NGINX_VERSION "1.11.4" #define NGINX_VER "nginx/" NGINX_VERSION #ifdef NGX_BUILD diff --git a/src/core/ngx_inet.c b/src/core/ngx_inet.c index c4aaf3a..dbd1f46 100644 --- a/src/core/ngx_inet.c +++ b/src/core/ngx_inet.c @@ -465,6 +465,93 @@ ngx_ptocidr(ngx_str_t *text, ngx_cidr_t *cidr) } +ngx_int_t +ngx_cidr_match(struct sockaddr *sa, ngx_array_t *cidrs) +{ +#if (NGX_HAVE_INET6) + u_char *p; +#endif + in_addr_t inaddr; + ngx_cidr_t *cidr; + ngx_uint_t family, i; +#if (NGX_HAVE_INET6) + ngx_uint_t n; + struct in6_addr *inaddr6; +#endif + +#if (NGX_SUPPRESS_WARN) + inaddr = 0; +#if (NGX_HAVE_INET6) + inaddr6 = NULL; +#endif +#endif + + family = sa->sa_family; + + if (family == AF_INET) { + inaddr = ((struct sockaddr_in *) sa)->sin_addr.s_addr; + } + +#if (NGX_HAVE_INET6) + else if (family == AF_INET6) { + inaddr6 = &((struct sockaddr_in6 *) sa)->sin6_addr; + + if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { + family = AF_INET; + + p = inaddr6->s6_addr; + + inaddr = p[12] << 24; + inaddr += p[13] << 16; + inaddr += p[14] << 8; + inaddr += p[15]; + + inaddr = htonl(inaddr); + } + } +#endif + + for (cidr = cidrs->elts, i = 0; i < cidrs->nelts; i++) { + if (cidr[i].family != family) { + goto next; + } + + switch (family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + for (n = 0; n < 16; n++) { + if ((inaddr6->s6_addr[n] & cidr[i].u.in6.mask.s6_addr[n]) + != cidr[i].u.in6.addr.s6_addr[n]) + { + goto next; + } + } + break; +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + break; +#endif + + default: /* AF_INET */ + if ((inaddr & cidr[i].u.in.mask) != cidr[i].u.in.addr) { + goto next; + } + break; + } + + return NGX_OK; + + next: + continue; + } + + return NGX_DECLINED; +} + + ngx_int_t ngx_parse_addr(ngx_pool_t *pool, ngx_addr_t *addr, u_char *text, size_t len) { diff --git a/src/core/ngx_inet.h b/src/core/ngx_inet.h index 97dc354..538771e 100644 --- a/src/core/ngx_inet.h +++ b/src/core/ngx_inet.h @@ -113,6 +113,7 @@ size_t ngx_sock_ntop(struct sockaddr *sa, socklen_t socklen, u_char *text, size_t len, ngx_uint_t port); size_t ngx_inet_ntop(int family, void *addr, u_char *text, size_t len); ngx_int_t ngx_ptocidr(ngx_str_t *text, ngx_cidr_t *cidr); +ngx_int_t ngx_cidr_match(struct sockaddr *sa, ngx_array_t *cidrs); ngx_int_t ngx_parse_addr(ngx_pool_t *pool, ngx_addr_t *addr, u_char *text, size_t len); ngx_int_t ngx_parse_addr_port(ngx_pool_t *pool, ngx_addr_t *addr, diff --git a/src/core/ngx_thread_pool.c b/src/core/ngx_thread_pool.c index f3655aa..7fb0f7f 100644 --- a/src/core/ngx_thread_pool.c +++ b/src/core/ngx_thread_pool.c @@ -137,6 +137,13 @@ ngx_thread_pool_init(ngx_thread_pool_t *tp, ngx_log_t *log, ngx_pool_t *pool) return NGX_ERROR; } + err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + if (err) { + ngx_log_error(NGX_LOG_ALERT, log, err, + "pthread_attr_setdetachstate() failed"); + return NGX_ERROR; + } + #if 0 err = pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN); if (err) { diff --git a/src/event/ngx_event_connect.c b/src/event/ngx_event_connect.c index 30cb59a..06534ef 100644 --- a/src/event/ngx_event_connect.c +++ b/src/event/ngx_event_connect.c @@ -91,7 +91,7 @@ ngx_event_connect_peer(ngx_peer_connection_t *pc) #endif #if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT || NGX_LINUX) - port = ngx_inet_get_port(pc->sockaddr); + port = ngx_inet_get_port(pc->local->sockaddr); #endif #if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index bb9a900..1cbfdf2 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -118,9 +118,7 @@ ngx_ssl_init(ngx_log_t *log) #else -#ifndef OPENSSL_IS_BORINGSSL OPENSSL_config(NULL); -#endif SSL_library_init(); SSL_load_error_strings(); @@ -2023,7 +2021,9 @@ ngx_ssl_connection_error(ngx_connection_t *c, int sslerr, ngx_err_t err, || n == SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST /* 151 */ || n == SSL_R_EXCESSIVE_MESSAGE_SIZE /* 152 */ || n == SSL_R_LENGTH_MISMATCH /* 159 */ +#ifdef SSL_R_NO_CIPHERS_PASSED || n == SSL_R_NO_CIPHERS_PASSED /* 182 */ +#endif || n == SSL_R_NO_CIPHERS_SPECIFIED /* 183 */ || n == SSL_R_NO_COMPRESSION_SPECIFIED /* 187 */ || n == SSL_R_NO_SHARED_CIPHER /* 193 */ @@ -2941,13 +2941,6 @@ failed: } -#ifdef OPENSSL_NO_SHA256 -#define ngx_ssl_session_ticket_md EVP_sha1 -#else -#define ngx_ssl_session_ticket_md EVP_sha256 -#endif - - static int ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn, unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx, @@ -2958,6 +2951,8 @@ ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn, ngx_array_t *keys; ngx_connection_t *c; ngx_ssl_session_ticket_key_t *key; + const EVP_MD *digest; + const EVP_CIPHER *cipher; #if (NGX_DEBUG) u_char buf[32]; #endif @@ -2965,6 +2960,13 @@ ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn, c = ngx_ssl_get_connection(ssl_conn); ssl_ctx = c->ssl->session_ctx; + cipher = EVP_aes_128_cbc(); +#ifdef OPENSSL_NO_SHA256 + digest = EVP_sha1(); +#else + digest = EVP_sha256(); +#endif + keys = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_ticket_keys_index); if (keys == NULL) { return -1; @@ -2980,13 +2982,29 @@ ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn, ngx_hex_dump(buf, key[0].name, 16) - buf, buf, SSL_session_reused(ssl_conn) ? "reused" : "new"); - RAND_bytes(iv, 16); - EVP_EncryptInit_ex(ectx, EVP_aes_128_cbc(), NULL, key[0].aes_key, iv); - HMAC_Init_ex(hctx, key[0].hmac_key, 16, - ngx_ssl_session_ticket_md(), NULL); + if (RAND_bytes(iv, EVP_CIPHER_iv_length(cipher)) != 1) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "RAND_bytes() failed"); + return -1; + } + + if (EVP_EncryptInit_ex(ectx, cipher, NULL, key[0].aes_key, iv) != 1) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, + "EVP_EncryptInit_ex() failed"); + return -1; + } + +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + if (HMAC_Init_ex(hctx, key[0].hmac_key, 16, digest, NULL) != 1) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "HMAC_Init_ex() failed"); + return -1; + } +#else + HMAC_Init_ex(hctx, key[0].hmac_key, 16, digest, NULL); +#endif + ngx_memcpy(name, key[0].name, 16); - return 0; + return 1; } else { /* decrypt session ticket */ @@ -3010,9 +3028,20 @@ ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn, ngx_hex_dump(buf, key[i].name, 16) - buf, buf, (i == 0) ? " (default)" : ""); - HMAC_Init_ex(hctx, key[i].hmac_key, 16, - ngx_ssl_session_ticket_md(), NULL); - EVP_DecryptInit_ex(ectx, EVP_aes_128_cbc(), NULL, key[i].aes_key, iv); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + if (HMAC_Init_ex(hctx, key[i].hmac_key, 16, digest, NULL) != 1) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "HMAC_Init_ex() failed"); + return -1; + } +#else + HMAC_Init_ex(hctx, key[i].hmac_key, 16, digest, NULL); +#endif + + if (EVP_DecryptInit_ex(ectx, cipher, NULL, key[i].aes_key, iv) != 1) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, + "EVP_DecryptInit_ex() failed"); + return -1; + } return (i == 0) ? 1 : 2 /* renew */; } diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c index cce8e9e..09fab76 100644 --- a/src/event/ngx_event_openssl_stapling.c +++ b/src/event/ngx_event_openssl_stapling.c @@ -376,6 +376,7 @@ ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl, { ngx_url_t u; char *s; + ngx_str_t rsp; STACK_OF(OPENSSL_STRING) *aia; if (responder->len == 0) { @@ -403,6 +404,8 @@ ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl, return NGX_DECLINED; } + responder = &rsp; + responder->len = ngx_strlen(s); responder->data = ngx_palloc(cf->pool, responder->len); if (responder->data == NULL) { diff --git a/src/event/ngx_event_pipe.c b/src/event/ngx_event_pipe.c index 5ce59ae..249433a 100644 --- a/src/event/ngx_event_pipe.c +++ b/src/event/ngx_event_pipe.c @@ -300,7 +300,7 @@ ngx_event_pipe_read_upstream(ngx_event_pipe_t *p) if (n == NGX_ERROR) { p->upstream_error = 1; - return NGX_ERROR; + break; } if (n == NGX_AGAIN) { @@ -815,10 +815,12 @@ ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p) } #if (NGX_THREADS) - p->temp_file->thread_write = p->thread_handler ? 1 : 0; - p->temp_file->file.thread_task = p->thread_task; - p->temp_file->file.thread_handler = p->thread_handler; - p->temp_file->file.thread_ctx = p->thread_ctx; + if (p->thread_handler) { + p->temp_file->thread_write = 1; + p->temp_file->file.thread_task = p->thread_task; + p->temp_file->file.thread_handler = p->thread_handler; + p->temp_file->file.thread_ctx = p->thread_ctx; + } #endif n = ngx_write_chain_to_temp_file(p->temp_file, out); diff --git a/src/http/modules/ngx_http_geo_module.c b/src/http/modules/ngx_http_geo_module.c index 9b3c6cb..46a8d7c 100644 --- a/src/http/modules/ngx_http_geo_module.c +++ b/src/http/modules/ngx_http_geo_module.c @@ -469,7 +469,12 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) for (i = 0; i < 0x10000; i++) { a = (ngx_array_t *) ctx.high.low[i]; - if (a == NULL || a->nelts == 0) { + if (a == NULL) { + continue; + } + + if (a->nelts == 0) { + ctx.high.low[i] = NULL; continue; } @@ -814,7 +819,7 @@ ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, range = a->elts; ngx_memmove(&range[i + 2], &range[i + 1], - (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t)); + (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t)); range[i + 1].start = (u_short) s; range[i + 1].end = (u_short) e; @@ -853,7 +858,7 @@ ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, range = a->elts; ngx_memmove(&range[i + 3], &range[i + 1], - (a->nelts - 3 - i) * sizeof(ngx_http_geo_range_t)); + (a->nelts - 3 - i) * sizeof(ngx_http_geo_range_t)); range[i + 2].start = (u_short) (e + 1); range[i + 2].end = range[i].end; @@ -881,7 +886,7 @@ ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, range = a->elts; ngx_memmove(&range[i + 1], &range[i], - (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t)); + (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t)); range[i + 1].start = (u_short) (e + 1); @@ -905,7 +910,7 @@ ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, range = a->elts; ngx_memmove(&range[i + 2], &range[i + 1], - (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t)); + (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t)); range[i + 1].start = (u_short) s; range[i + 1].end = (u_short) e; @@ -935,13 +940,20 @@ ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, return NGX_CONF_ERROR; } - range->start = (u_short) s; - range->end = (u_short) e; - range->value = ctx->value; + range = a->elts; + + ngx_memmove(&range[1], &range[0], + (a->nelts - 1) * sizeof(ngx_http_geo_range_t)); + + range[0].start = (u_short) s; + range[0].end = (u_short) e; + range[0].value = ctx->value; next: - continue; + if (h == 0xffff) { + break; + } } return NGX_CONF_OK; @@ -959,7 +971,7 @@ ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, warn = 0; - for (n = start; n <= end; n += 0x10000) { + for (n = start; n <= end; n = (n + 0x10000) & 0xffff0000) { h = n >> 16; @@ -978,9 +990,9 @@ ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, a = (ngx_array_t *) ctx->high.low[h]; - if (a == NULL) { + if (a == NULL || a->nelts == 0) { warn = 1; - continue; + goto next; } range = a->elts; @@ -990,20 +1002,22 @@ ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, && e == (ngx_uint_t) range[i].end) { ngx_memmove(&range[i], &range[i + 1], - (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t)); + (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t)); a->nelts--; break; } - if (s != (ngx_uint_t) range[i].start - && e != (ngx_uint_t) range[i].end) - { - continue; + if (i == a->nelts - 1) { + warn = 1; } + } - warn = 1; + next: + + if (h == 0xffff) { + break; } } diff --git a/src/http/modules/ngx_http_limit_req_module.c b/src/http/modules/ngx_http_limit_req_module.c index 9059ac3..2f695f2 100644 --- a/src/http/modules/ngx_http_limit_req_module.c +++ b/src/http/modules/ngx_http_limit_req_module.c @@ -362,15 +362,13 @@ ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit, ngx_uint_t hash, { size_t size; ngx_int_t rc, excess; - ngx_time_t *tp; ngx_msec_t now; ngx_msec_int_t ms; ngx_rbtree_node_t *node, *sentinel; ngx_http_limit_req_ctx_t *ctx; ngx_http_limit_req_node_t *lr; - tp = ngx_timeofday(); - now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); + now = ngx_current_msec; ctx = limit->shm_zone->data; @@ -483,7 +481,6 @@ 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) { ngx_int_t excess; - ngx_time_t *tp; ngx_msec_t now, delay, max_delay; ngx_msec_int_t ms; ngx_http_limit_req_ctx_t *ctx; @@ -509,9 +506,7 @@ ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits, ngx_uint_t n, ngx_shmtx_lock(&ctx->shpool->mutex); - tp = ngx_timeofday(); - - now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); + now = ngx_current_msec; ms = (ngx_msec_int_t) (now - lr->last); excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; @@ -549,16 +544,13 @@ static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n) { ngx_int_t excess; - ngx_time_t *tp; ngx_msec_t now; ngx_queue_t *q; ngx_msec_int_t ms; ngx_rbtree_node_t *node; ngx_http_limit_req_node_t *lr; - tp = ngx_timeofday(); - - now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); + now = ngx_current_msec; /* * n == 1 deletes one or two zero rate entries diff --git a/src/http/modules/ngx_http_realip_module.c b/src/http/modules/ngx_http_realip_module.c index 490a53d..dba3c52 100644 --- a/src/http/modules/ngx_http_realip_module.c +++ b/src/http/modules/ngx_http_realip_module.c @@ -264,7 +264,6 @@ ngx_http_realip_set_addr(ngx_http_request_t *r, ngx_addr_t *addr) } ctx = cln->data; - ngx_http_set_ctx(r, ctx, ngx_http_realip_module); c = r->connection; @@ -282,6 +281,7 @@ ngx_http_realip_set_addr(ngx_http_request_t *r, ngx_addr_t *addr) ngx_memcpy(p, text, len); cln->handler = ngx_http_realip_cleanup; + ngx_http_set_ctx(r, ctx, ngx_http_realip_module); ctx->connection = c; ctx->sockaddr = c->sockaddr; diff --git a/src/http/modules/ngx_http_ssi_filter_module.c b/src/http/modules/ngx_http_ssi_filter_module.c index fc6e65b..6fb1fbe 100644 --- a/src/http/modules/ngx_http_ssi_filter_module.c +++ b/src/http/modules/ngx_http_ssi_filter_module.c @@ -2722,8 +2722,8 @@ static ngx_int_t ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t gmt) { + time_t now; ngx_http_ssi_ctx_t *ctx; - ngx_time_t *tp; ngx_str_t *timefmt; struct tm tm; char buf[NGX_HTTP_SSI_DATE_LEN]; @@ -2732,7 +2732,7 @@ ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r, v->no_cacheable = 0; v->not_found = 0; - tp = ngx_timeofday(); + now = ngx_time(); ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module); @@ -2746,15 +2746,15 @@ ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r, return NGX_ERROR; } - v->len = ngx_sprintf(v->data, "%T", tp->sec) - v->data; + v->len = ngx_sprintf(v->data, "%T", now) - v->data; return NGX_OK; } if (gmt) { - ngx_libc_gmtime(tp->sec, &tm); + ngx_libc_gmtime(now, &tm); } else { - ngx_libc_localtime(tp->sec, &tm); + ngx_libc_localtime(now, &tm); } v->len = strftime(buf, NGX_HTTP_SSI_DATE_LEN, diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index 76917bb..e26c3f7 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -2823,120 +2823,46 @@ static ngx_int_t ngx_http_get_forwarded_addr_internal(ngx_http_request_t *r, ngx_addr_t *addr, u_char *xff, size_t xfflen, ngx_array_t *proxies, int recursive) { - u_char *p; - in_addr_t inaddr; - ngx_int_t rc; - ngx_addr_t paddr; - ngx_cidr_t *cidr; - ngx_uint_t family, i; -#if (NGX_HAVE_INET6) - ngx_uint_t n; - struct in6_addr *inaddr6; -#endif + u_char *p; + ngx_int_t rc; + ngx_addr_t paddr; -#if (NGX_SUPPRESS_WARN) - inaddr = 0; -#if (NGX_HAVE_INET6) - inaddr6 = NULL; -#endif -#endif - - family = addr->sockaddr->sa_family; - - if (family == AF_INET) { - inaddr = ((struct sockaddr_in *) addr->sockaddr)->sin_addr.s_addr; + if (ngx_cidr_match(addr->sockaddr, proxies) != NGX_OK) { + return NGX_DECLINED; } -#if (NGX_HAVE_INET6) - else if (family == AF_INET6) { - inaddr6 = &((struct sockaddr_in6 *) addr->sockaddr)->sin6_addr; - - if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { - family = AF_INET; - - p = inaddr6->s6_addr; - - inaddr = p[12] << 24; - inaddr += p[13] << 16; - inaddr += p[14] << 8; - inaddr += p[15]; - - inaddr = htonl(inaddr); + for (p = xff + xfflen - 1; p > xff; p--, xfflen--) { + if (*p != ' ' && *p != ',') { + break; } } -#endif - for (cidr = proxies->elts, i = 0; i < proxies->nelts; i++) { - if (cidr[i].family != family) { - goto next; - } - - switch (family) { - -#if (NGX_HAVE_INET6) - case AF_INET6: - for (n = 0; n < 16; n++) { - if ((inaddr6->s6_addr[n] & cidr[i].u.in6.mask.s6_addr[n]) - != cidr[i].u.in6.addr.s6_addr[n]) - { - goto next; - } - } - break; -#endif - -#if (NGX_HAVE_UNIX_DOMAIN) - case AF_UNIX: - break; -#endif - - default: /* AF_INET */ - if ((inaddr & cidr[i].u.in.mask) != cidr[i].u.in.addr) { - goto next; - } + for ( /* void */ ; p > xff; p--) { + if (*p == ' ' || *p == ',') { + p++; break; } - - for (p = xff + xfflen - 1; p > xff; p--, xfflen--) { - if (*p != ' ' && *p != ',') { - break; - } - } - - for ( /* void */ ; p > xff; p--) { - if (*p == ' ' || *p == ',') { - p++; - break; - } - } - - if (ngx_parse_addr_port(r->pool, &paddr, p, xfflen - (p - xff)) - != NGX_OK) - { - return NGX_DECLINED; - } - - *addr = paddr; - - if (recursive && p > xff) { - rc = ngx_http_get_forwarded_addr_internal(r, addr, xff, p - 1 - xff, - proxies, 1); - - if (rc == NGX_DECLINED) { - return NGX_DONE; - } - - /* rc == NGX_OK || rc == NGX_DONE */ - return rc; - } - - return NGX_OK; - - next: - continue; } - return NGX_DECLINED; + if (ngx_parse_addr_port(r->pool, &paddr, p, xfflen - (p - xff)) != NGX_OK) { + return NGX_DECLINED; + } + + *addr = paddr; + + if (recursive && p > xff) { + rc = ngx_http_get_forwarded_addr_internal(r, addr, xff, p - 1 - xff, + proxies, 1); + + if (rc == NGX_DECLINED) { + return NGX_DONE; + } + + /* rc == NGX_OK || rc == NGX_DONE */ + return rc; + } + + return NGX_OK; } diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index 0f6b3ae..7e4b3c5 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -391,6 +391,10 @@ static ngx_http_variable_t ngx_http_upstream_vars[] = { ngx_http_upstream_response_length_variable, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + { ngx_string("upstream_bytes_received"), NULL, + ngx_http_upstream_response_length_variable, 1, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + #if (NGX_HTTP_CACHE) { ngx_string("upstream_cache_status"), NULL, @@ -2136,6 +2140,8 @@ ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u) return; } + u->state->bytes_received += n; + u->buffer.last += n; #if 0 @@ -2642,6 +2648,7 @@ ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r, return; } + u->state->bytes_received += n; u->state->response_length += n; if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) { @@ -3215,6 +3222,10 @@ ngx_http_upstream_process_upgraded(ngx_http_request_t *r, do_write = 1; b->last += n; + if (from_upstream) { + u->state->bytes_received += n; + } + continue; } @@ -3411,6 +3422,7 @@ ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r, } if (n > 0) { + u->state->bytes_received += n; u->state->response_length += n; if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) { @@ -4095,6 +4107,8 @@ ngx_http_upstream_finalize_request(ngx_http_request_t *r, u->state->response_time = ngx_current_msec - u->state->response_time; if (u->pipe && u->pipe->read_length) { + u->state->bytes_received += u->pipe->read_length + - u->pipe->preread_size; u->state->response_length = u->pipe->read_length; } } @@ -5242,7 +5256,13 @@ ngx_http_upstream_response_length_variable(ngx_http_request_t *r, state = r->upstream_states->elts; for ( ;; ) { - p = ngx_sprintf(p, "%O", state[i].response_length); + + if (data == 1) { + p = ngx_sprintf(p, "%O", state[i].bytes_received); + + } else { + p = ngx_sprintf(p, "%O", state[i].response_length); + } if (++i == r->upstream_states->nelts) { break; diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h index b288f28..ef861f4 100644 --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -63,6 +63,7 @@ typedef struct { ngx_msec_t connect_time; ngx_msec_t header_time; off_t response_length; + off_t bytes_received; ngx_str_t *peer; } ngx_http_upstream_state_t; diff --git a/src/mail/ngx_mail.h b/src/mail/ngx_mail.h index ae1aa41..1068bb3 100644 --- a/src/mail/ngx_mail.h +++ b/src/mail/ngx_mail.h @@ -117,13 +117,15 @@ typedef struct { ngx_str_t server_name; u_char *file_name; - ngx_int_t line; + ngx_uint_t line; ngx_resolver_t *resolver; ngx_log_t *error_log; /* server ctx */ ngx_mail_conf_ctx_t *ctx; + + ngx_uint_t listen; /* unsigned listen:1; */ } ngx_mail_core_srv_conf_t; diff --git a/src/mail/ngx_mail_core_module.c b/src/mail/ngx_mail_core_module.c index d992402..48eacfa 100644 --- a/src/mail/ngx_mail_core_module.c +++ b/src/mail/ngx_mail_core_module.c @@ -279,6 +279,13 @@ ngx_mail_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) *cf = pcf; + if (rv == NGX_CONF_OK && !cscf->listen) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"listen\" is defined for server in %s:%ui", + cscf->file_name, cscf->line); + return NGX_CONF_ERROR; + } + return rv; } @@ -295,6 +302,8 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_mail_module_t *module; ngx_mail_core_main_conf_t *cmcf; + cscf->listen = 1; + value = cf->args->elts; ngx_memzero(&u, sizeof(ngx_url_t)); diff --git a/src/os/unix/ngx_posix_init.c b/src/os/unix/ngx_posix_init.c index 76ed94e..7e6e79d 100644 --- a/src/os/unix/ngx_posix_init.c +++ b/src/os/unix/ngx_posix_init.c @@ -33,7 +33,8 @@ ngx_os_io_t ngx_os_io = { ngx_int_t ngx_os_init(ngx_log_t *log) { - ngx_uint_t n; + ngx_time_t *tp; + ngx_uint_t n; #if (NGX_HAVE_OS_SPECIFIC_INIT) if (ngx_os_specific_init(log) != NGX_OK) { @@ -76,7 +77,8 @@ ngx_os_init(ngx_log_t *log) ngx_inherited_nonblocking = 0; #endif - srandom(ngx_time()); + tp = ngx_timeofday(); + srandom(((unsigned) ngx_pid << 16) ^ tp->sec ^ tp->msec); return NGX_OK; } diff --git a/src/os/unix/ngx_process_cycle.c b/src/os/unix/ngx_process_cycle.c index 7cee1c5..83b04ee 100644 --- a/src/os/unix/ngx_process_cycle.c +++ b/src/os/unix/ngx_process_cycle.c @@ -785,6 +785,7 @@ ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker) { sigset_t set; ngx_int_t n; + ngx_time_t *tp; ngx_uint_t i; ngx_cpuset_t *cpu_affinity; struct rlimit rlmt; @@ -884,7 +885,8 @@ ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker) "sigprocmask() failed"); } - srandom((ngx_pid << 16) ^ ngx_time()); + tp = ngx_timeofday(); + srandom(((unsigned) ngx_pid << 16) ^ tp->sec ^ tp->msec); /* * disable deleting previous events for the listening sockets because diff --git a/src/stream/ngx_stream.c b/src/stream/ngx_stream.c index c195171..873e102 100644 --- a/src/stream/ngx_stream.c +++ b/src/stream/ngx_stream.c @@ -455,6 +455,7 @@ ngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *stport, #if (NGX_STREAM_SSL) addrs[i].conf.ssl = addr[i].opt.ssl; #endif + addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; len = ngx_sock_ntop(&addr[i].opt.sockaddr.sockaddr, addr[i].opt.socklen, buf, NGX_SOCKADDR_STRLEN, 1); @@ -504,6 +505,7 @@ ngx_stream_add_addrs6(ngx_conf_t *cf, ngx_stream_port_t *stport, #if (NGX_STREAM_SSL) addrs6[i].conf.ssl = addr[i].opt.ssl; #endif + addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; len = ngx_sock_ntop(&addr[i].opt.sockaddr.sockaddr, addr[i].opt.socklen, buf, NGX_SOCKADDR_STRLEN, 1); diff --git a/src/stream/ngx_stream.h b/src/stream/ngx_stream.h index 807ab5b..6251cc7 100644 --- a/src/stream/ngx_stream.h +++ b/src/stream/ngx_stream.h @@ -26,6 +26,14 @@ typedef struct ngx_stream_session_s ngx_stream_session_t; #include +#define NGX_STREAM_OK 200 +#define NGX_STREAM_BAD_REQUEST 400 +#define NGX_STREAM_FORBIDDEN 403 +#define NGX_STREAM_INTERNAL_SERVER_ERROR 500 +#define NGX_STREAM_BAD_GATEWAY 502 +#define NGX_STREAM_SERVICE_UNAVAILABLE 503 + + typedef struct { void **main_conf; void **srv_conf; @@ -51,6 +59,7 @@ typedef struct { unsigned reuseport:1; #endif unsigned so_keepalive:2; + unsigned proxy_protocol:1; #if (NGX_HAVE_KEEPALIVE_TUNABLE) int tcp_keepidle; int tcp_keepintvl; @@ -65,8 +74,9 @@ typedef struct { ngx_stream_conf_ctx_t *ctx; ngx_str_t addr_text; #if (NGX_STREAM_SSL) - ngx_uint_t ssl; /* unsigned ssl:1; */ + unsigned ssl:1; #endif + unsigned proxy_protocol:1; } ngx_stream_addr_conf_t; typedef struct { @@ -112,8 +122,10 @@ typedef struct { ngx_array_t servers; /* ngx_stream_core_srv_conf_t */ ngx_array_t listen; /* ngx_stream_listen_t */ + ngx_stream_access_pt realip_handler; ngx_stream_access_pt limit_conn_handler; ngx_stream_access_pt access_handler; + ngx_stream_access_pt access_log_handler; ngx_hash_t variables_hash; @@ -136,7 +148,7 @@ typedef struct { ngx_stream_conf_ctx_t *ctx; u_char *file_name; - ngx_int_t line; + ngx_uint_t line; ngx_flag_t tcp_nodelay; @@ -144,6 +156,10 @@ typedef struct { ngx_msec_t resolver_timeout; ngx_resolver_t *resolver; + + ngx_msec_t proxy_protocol_timeout; + + ngx_uint_t listen; /* unsigned listen:1; */ } ngx_stream_core_srv_conf_t; @@ -153,6 +169,8 @@ struct ngx_stream_session_s { ngx_connection_t *connection; off_t received; + time_t start_sec; + ngx_msec_t start_msec; ngx_log_handler_pt log_handler; @@ -161,7 +179,8 @@ struct ngx_stream_session_s { void **srv_conf; ngx_stream_upstream_t *upstream; - + ngx_array_t *upstream_states; + /* of ngx_stream_upstream_state_t */ ngx_stream_variable_value_t *variables; #if (NGX_PCRE) @@ -169,6 +188,12 @@ struct ngx_stream_session_s { int *captures; u_char *captures_data; #endif + + ngx_uint_t status; + +#if (NGX_STREAM_SSL) + ngx_uint_t ssl; /* unsigned ssl:1; */ +#endif }; @@ -219,7 +244,7 @@ typedef struct { void ngx_stream_init_connection(ngx_connection_t *c); -void ngx_stream_close_connection(ngx_connection_t *c); +void ngx_stream_finalize_session(ngx_stream_session_t *s, ngx_uint_t rc); extern ngx_module_t ngx_stream_module; diff --git a/src/stream/ngx_stream_core_module.c b/src/stream/ngx_stream_core_module.c index 70b9e2d..1c808ad 100644 --- a/src/stream/ngx_stream_core_module.c +++ b/src/stream/ngx_stream_core_module.c @@ -77,6 +77,13 @@ static ngx_command_t ngx_stream_core_commands[] = { offsetof(ngx_stream_core_srv_conf_t, resolver_timeout), NULL }, + { ngx_string("proxy_protocol_timeout"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_core_srv_conf_t, proxy_protocol_timeout), + NULL }, + { ngx_string("tcp_nodelay"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, @@ -192,6 +199,7 @@ ngx_stream_core_create_srv_conf(ngx_conf_t *cf) cscf->file_name = cf->conf_file->file.name.data; cscf->line = cf->conf_file->line; cscf->resolver_timeout = NGX_CONF_UNSET_MSEC; + cscf->proxy_protocol_timeout = NGX_CONF_UNSET_MSEC; cscf->tcp_nodelay = NGX_CONF_UNSET; return cscf; @@ -240,6 +248,9 @@ ngx_stream_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) } } + ngx_conf_merge_msec_value(conf->proxy_protocol_timeout, + prev->proxy_protocol_timeout, 30000); + ngx_conf_merge_value(conf->tcp_nodelay, prev->tcp_nodelay, 1); return NGX_CONF_OK; @@ -325,6 +336,13 @@ ngx_stream_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) *cf = pcf; + if (rv == NGX_CONF_OK && !cscf->listen) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"listen\" is defined for server in %s:%ui", + cscf->file_name, cscf->line); + return NGX_CONF_ERROR; + } + return rv; } @@ -332,12 +350,16 @@ ngx_stream_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) static char * ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { + ngx_stream_core_srv_conf_t *cscf = conf; + ngx_str_t *value; ngx_url_t u; ngx_uint_t i, backlog; ngx_stream_listen_t *ls, *als; ngx_stream_core_main_conf_t *cmcf; + cscf->listen = 1; + value = cf->args->elts; ngx_memzero(&u, sizeof(ngx_url_t)); @@ -561,6 +583,11 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) #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; @@ -580,6 +607,10 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (ls->so_keepalive) { return "\"so_keepalive\" parameter is incompatible with \"udp\""; } + + if (ls->proxy_protocol) { + return "\"proxy_protocol\" parameter is incompatible with \"udp\""; + } } als = cmcf->listen.elts; diff --git a/src/stream/ngx_stream_geo_module.c b/src/stream/ngx_stream_geo_module.c index ed1a488..2204546 100644 --- a/src/stream/ngx_stream_geo_module.c +++ b/src/stream/ngx_stream_geo_module.c @@ -436,7 +436,12 @@ ngx_stream_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) for (i = 0; i < 0x10000; i++) { a = (ngx_array_t *) ctx.high.low[i]; - if (a == NULL || a->nelts == 0) { + if (a == NULL) { + continue; + } + + if (a->nelts == 0) { + ctx.high.low[i] = NULL; continue; } @@ -885,13 +890,20 @@ ngx_stream_geo_add_range(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx, return NGX_CONF_ERROR; } - range->start = (u_short) s; - range->end = (u_short) e; - range->value = ctx->value; + range = a->elts; + + ngx_memmove(&range[1], &range[0], + (a->nelts - 1) * sizeof(ngx_stream_geo_range_t)); + + range[0].start = (u_short) s; + range[0].end = (u_short) e; + range[0].value = ctx->value; next: - continue; + if (h == 0xffff) { + break; + } } return NGX_CONF_OK; @@ -909,7 +921,7 @@ ngx_stream_geo_delete_range(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx, warn = 0; - for (n = start; n <= end; n += 0x10000) { + for (n = start; n <= end; n = (n + 0x10000) & 0xffff0000) { h = n >> 16; @@ -928,9 +940,9 @@ ngx_stream_geo_delete_range(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx, a = (ngx_array_t *) ctx->high.low[h]; - if (a == NULL) { + if (a == NULL || a->nelts == 0) { warn = 1; - continue; + goto next; } range = a->elts; @@ -947,13 +959,15 @@ ngx_stream_geo_delete_range(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx, break; } - if (s != (ngx_uint_t) range[i].start - && e != (ngx_uint_t) range[i].end) - { - continue; + if (i == a->nelts - 1) { + warn = 1; } + } - warn = 1; + next: + + if (h == 0xffff) { + break; } } diff --git a/src/stream/ngx_stream_handler.c b/src/stream/ngx_stream_handler.c index 61169e1..6e2ed82 100644 --- a/src/stream/ngx_stream_handler.c +++ b/src/stream/ngx_stream_handler.c @@ -11,8 +11,10 @@ #include +static void ngx_stream_close_connection(ngx_connection_t *c); static u_char *ngx_stream_log_error(ngx_log_t *log, u_char *buf, size_t len); -static void ngx_stream_init_session(ngx_connection_t *c); +static void ngx_stream_proxy_protocol_handler(ngx_event_t *rev); +static void ngx_stream_init_session_handler(ngx_event_t *rev); #if (NGX_STREAM_SSL) static void ngx_stream_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c); @@ -23,11 +25,11 @@ static void ngx_stream_ssl_handshake_handler(ngx_connection_t *c); void ngx_stream_init_connection(ngx_connection_t *c) { - int tcp_nodelay; u_char text[NGX_SOCKADDR_STRLEN]; size_t len; - ngx_int_t rc; ngx_uint_t i; + ngx_time_t *tp; + ngx_event_t *rev; struct sockaddr *sa; ngx_stream_port_t *port; struct sockaddr_in *sin; @@ -128,6 +130,10 @@ ngx_stream_init_connection(ngx_connection_t *c) s->main_conf = addr_conf->ctx->main_conf; s->srv_conf = addr_conf->ctx->srv_conf; +#if (NGX_STREAM_SSL) + s->ssl = addr_conf->ssl; +#endif + s->connection = c; c->data = s; @@ -147,6 +153,12 @@ ngx_stream_init_connection(ngx_connection_t *c) c->log->action = "initializing connection"; c->log_error = NGX_ERROR_INFO; + s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_stream_max_module); + if (s->ctx == NULL) { + ngx_stream_close_connection(c); + return; + } + cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); s->variables = ngx_pcalloc(s->connection->pool, @@ -158,11 +170,151 @@ ngx_stream_init_connection(ngx_connection_t *c) return; } + tp = ngx_timeofday(); + s->start_sec = tp->sec; + s->start_msec = tp->msec; + + rev = c->read; + rev->handler = ngx_stream_init_session_handler; + + if (addr_conf->proxy_protocol) { + c->log->action = "reading PROXY protocol"; + + rev->handler = ngx_stream_proxy_protocol_handler; + + if (!rev->ready) { + ngx_add_timer(rev, cscf->proxy_protocol_timeout); + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_stream_finalize_session(s, + NGX_STREAM_INTERNAL_SERVER_ERROR); + } + + return; + } + } + + if (ngx_use_accept_mutex) { + ngx_post_event(rev, &ngx_posted_events); + return; + } + + rev->handler(rev); +} + + +static void +ngx_stream_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_stream_session_t *s; + ngx_stream_core_srv_conf_t *cscf; + + c = rev->data; + s = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream PROXY protocol handler"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + ngx_stream_finalize_session(s, NGX_STREAM_OK); + return; + } + + n = recv(c->fd, (char *) buf, sizeof(buf), MSG_PEEK); + + err = ngx_socket_errno; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, "recv(): %z", n); + + if (n == -1) { + if (err == NGX_EAGAIN) { + rev->ready = 0; + + if (!rev->timer_set) { + cscf = ngx_stream_get_module_srv_conf(s, + ngx_stream_core_module); + + ngx_add_timer(rev, cscf->proxy_protocol_timeout); + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_stream_finalize_session(s, + NGX_STREAM_INTERNAL_SERVER_ERROR); + } + + return; + } + + ngx_connection_error(c, err, "recv() failed"); + + ngx_stream_finalize_session(s, NGX_STREAM_OK); + return; + } + + if (rev->timer_set) { + ngx_del_timer(rev); + } + + p = ngx_proxy_protocol_read(c, buf, buf + n); + + if (p == NULL) { + ngx_stream_finalize_session(s, NGX_STREAM_BAD_REQUEST); + return; + } + + size = p - buf; + + if (c->recv(c, buf, size) != (ssize_t) size) { + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + ngx_stream_init_session_handler(rev); +} + + +static void +ngx_stream_init_session_handler(ngx_event_t *rev) +{ + int tcp_nodelay; + ngx_int_t rc; + ngx_connection_t *c; + ngx_stream_session_t *s; + ngx_stream_core_srv_conf_t *cscf; + ngx_stream_core_main_conf_t *cmcf; + + c = rev->data; + s = c->data; + + c->log->action = "initializing session"; + + cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); + + if (cmcf->realip_handler) { + rc = cmcf->realip_handler(s); + + if (rc == NGX_ERROR) { + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + } + if (cmcf->limit_conn_handler) { rc = cmcf->limit_conn_handler(s); - if (rc != NGX_DECLINED) { - ngx_stream_close_connection(c); + if (rc == NGX_ERROR) { + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + if (rc == NGX_ABORT) { + ngx_stream_finalize_session(s, NGX_STREAM_SERVICE_UNAVAILABLE); return; } } @@ -170,12 +322,19 @@ ngx_stream_init_connection(ngx_connection_t *c) if (cmcf->access_handler) { rc = cmcf->access_handler(s); - if (rc != NGX_OK && rc != NGX_DECLINED) { - ngx_stream_close_connection(c); + if (rc == NGX_ERROR) { + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + if (rc == NGX_ABORT) { + ngx_stream_finalize_session(s, NGX_STREAM_FORBIDDEN); return; } } + cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); + if (c->type == SOCK_STREAM && cscf->tcp_nodelay && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) @@ -189,7 +348,7 @@ ngx_stream_init_connection(ngx_connection_t *c) { ngx_connection_error(c, ngx_socket_errno, "setsockopt(TCP_NODELAY) failed"); - ngx_stream_close_connection(c); + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } @@ -203,14 +362,14 @@ ngx_stream_init_connection(ngx_connection_t *c) sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module); - if (addr_conf->ssl) { + if (s->ssl) { c->log->action = "SSL handshaking"; if (sslcf->ssl.ctx == NULL) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "no \"ssl_certificate\" is defined " "in server listening on SSL port"); - ngx_stream_close_connection(c); + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } @@ -220,27 +379,8 @@ ngx_stream_init_connection(ngx_connection_t *c) } #endif - ngx_stream_init_session(c); -} - - -static void -ngx_stream_init_session(ngx_connection_t *c) -{ - ngx_stream_session_t *s; - ngx_stream_core_srv_conf_t *cscf; - - s = c->data; c->log->action = "handling client connection"; - cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); - - s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_stream_max_module); - if (s->ctx == NULL) { - ngx_stream_close_connection(c); - return; - } - cscf->handler(s); } @@ -253,15 +393,14 @@ ngx_stream_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c) ngx_stream_session_t *s; ngx_stream_ssl_conf_t *sslcf; + s = c->data; + if (ngx_ssl_create_connection(ssl, c, 0) == NGX_ERROR) { - ngx_stream_close_connection(c); + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } if (ngx_ssl_handshake(c) == NGX_AGAIN) { - - s = c->data; - sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module); ngx_add_timer(c->read, sslcf->handshake_timeout); @@ -278,8 +417,11 @@ ngx_stream_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c) static void ngx_stream_ssl_handshake_handler(ngx_connection_t *c) { + ngx_stream_session_t *s; + ngx_stream_core_srv_conf_t *cscf; + if (!c->ssl->handshaked) { - ngx_stream_close_connection(c); + ngx_stream_finalize_session(c->data, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } @@ -287,13 +429,39 @@ ngx_stream_ssl_handshake_handler(ngx_connection_t *c) ngx_del_timer(c->read); } - ngx_stream_init_session(c); + c->log->action = "handling client connection"; + + s = c->data; + + cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); + + cscf->handler(s); } #endif void +ngx_stream_finalize_session(ngx_stream_session_t *s, ngx_uint_t rc) +{ + ngx_stream_core_main_conf_t *cmcf; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "finalize stream session: %i", rc); + + s->status = rc; + + cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); + + if (cmcf->access_log_handler) { + (void) cmcf->access_log_handler(s); + } + + ngx_stream_close_connection(s->connection); +} + + +static void ngx_stream_close_connection(ngx_connection_t *c) { ngx_pool_t *pool; diff --git a/src/stream/ngx_stream_log_module.c b/src/stream/ngx_stream_log_module.c new file mode 100644 index 0000000..4affbdf --- /dev/null +++ b/src/stream/ngx_stream_log_module.c @@ -0,0 +1,1474 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + +#if (NGX_ZLIB) +#include +#endif + + +typedef struct ngx_stream_log_op_s ngx_stream_log_op_t; + +typedef u_char *(*ngx_stream_log_op_run_pt) (ngx_stream_session_t *s, + u_char *buf, ngx_stream_log_op_t *op); + +typedef size_t (*ngx_stream_log_op_getlen_pt) (ngx_stream_session_t *s, + uintptr_t data); + + +struct ngx_stream_log_op_s { + size_t len; + ngx_stream_log_op_getlen_pt getlen; + ngx_stream_log_op_run_pt run; + uintptr_t data; +}; + + +typedef struct { + ngx_str_t name; + ngx_array_t *flushes; + ngx_array_t *ops; /* array of ngx_stream_log_op_t */ +} ngx_stream_log_fmt_t; + + +typedef struct { + ngx_array_t formats; /* array of ngx_stream_log_fmt_t */ +} ngx_stream_log_main_conf_t; + + +typedef struct { + u_char *start; + u_char *pos; + u_char *last; + + ngx_event_t *event; + ngx_msec_t flush; + ngx_int_t gzip; +} ngx_stream_log_buf_t; + + +typedef struct { + ngx_array_t *lengths; + ngx_array_t *values; +} ngx_stream_log_script_t; + + +typedef struct { + ngx_open_file_t *file; + ngx_stream_log_script_t *script; + time_t disk_full_time; + time_t error_log_time; + ngx_syslog_peer_t *syslog_peer; + ngx_stream_log_fmt_t *format; + ngx_stream_complex_value_t *filter; +} ngx_stream_log_t; + + +typedef struct { + ngx_array_t *logs; /* array of ngx_stream_log_t */ + + ngx_open_file_cache_t *open_file_cache; + time_t open_file_cache_valid; + ngx_uint_t open_file_cache_min_uses; + + ngx_uint_t off; /* unsigned off:1 */ +} ngx_stream_log_srv_conf_t; + + +typedef struct { + ngx_str_t name; + size_t len; + ngx_stream_log_op_run_pt run; +} ngx_stream_log_var_t; + + +static void ngx_stream_log_write(ngx_stream_session_t *s, ngx_stream_log_t *log, + u_char *buf, size_t len); +static ssize_t ngx_stream_log_script_write(ngx_stream_session_t *s, + ngx_stream_log_script_t *script, u_char **name, u_char *buf, size_t len); + +#if (NGX_ZLIB) +static ssize_t ngx_stream_log_gzip(ngx_fd_t fd, u_char *buf, size_t len, + ngx_int_t level, ngx_log_t *log); + +static void *ngx_stream_log_gzip_alloc(void *opaque, u_int items, u_int size); +static void ngx_stream_log_gzip_free(void *opaque, void *address); +#endif + +static void ngx_stream_log_flush(ngx_open_file_t *file, ngx_log_t *log); +static void ngx_stream_log_flush_handler(ngx_event_t *ev); + +static ngx_int_t ngx_stream_log_variable_compile(ngx_conf_t *cf, + ngx_stream_log_op_t *op, ngx_str_t *value); +static size_t ngx_stream_log_variable_getlen(ngx_stream_session_t *s, + uintptr_t data); +static u_char *ngx_stream_log_variable(ngx_stream_session_t *s, u_char *buf, + ngx_stream_log_op_t *op); +static uintptr_t ngx_stream_log_escape(u_char *dst, u_char *src, size_t size); + + +static void *ngx_stream_log_create_main_conf(ngx_conf_t *cf); +static void *ngx_stream_log_create_srv_conf(ngx_conf_t *cf); +static char *ngx_stream_log_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); +static char *ngx_stream_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_log_compile_format(ngx_conf_t *cf, + ngx_array_t *flushes, ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s); +static char *ngx_stream_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static ngx_int_t ngx_stream_log_init(ngx_conf_t *cf); + + +static ngx_command_t ngx_stream_log_commands[] = { + + { ngx_string("log_format"), + NGX_STREAM_MAIN_CONF|NGX_CONF_2MORE, + ngx_stream_log_set_format, + NGX_STREAM_MAIN_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("access_log"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE, + ngx_stream_log_set_log, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("open_log_file_cache"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1234, + ngx_stream_log_open_file_cache, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_log_module_ctx = { + NULL, /* preconfiguration */ + ngx_stream_log_init, /* postconfiguration */ + + ngx_stream_log_create_main_conf, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_stream_log_create_srv_conf, /* create server configuration */ + ngx_stream_log_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_log_module = { + NGX_MODULE_V1, + &ngx_stream_log_module_ctx, /* module context */ + ngx_stream_log_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_log_handler(ngx_stream_session_t *s) +{ + u_char *line, *p; + size_t len, size; + ssize_t n; + ngx_str_t val; + ngx_uint_t i, l; + ngx_stream_log_t *log; + ngx_stream_log_op_t *op; + ngx_stream_log_buf_t *buffer; + ngx_stream_log_srv_conf_t *lscf; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream log handler"); + + lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_log_module); + + if (lscf->off || lscf->logs == NULL) { + return NGX_OK; + } + + log = lscf->logs->elts; + for (l = 0; l < lscf->logs->nelts; l++) { + + if (log[l].filter) { + if (ngx_stream_complex_value(s, log[l].filter, &val) != NGX_OK) { + return NGX_ERROR; + } + + if (val.len == 0 || (val.len == 1 && val.data[0] == '0')) { + continue; + } + } + + if (ngx_time() == log[l].disk_full_time) { + + /* + * on FreeBSD writing to a full filesystem with enabled softupdates + * may block process for much longer time than writing to non-full + * filesystem, so we skip writing to a log for one second + */ + + continue; + } + + ngx_stream_script_flush_no_cacheable_variables(s, + log[l].format->flushes); + + len = 0; + op = log[l].format->ops->elts; + for (i = 0; i < log[l].format->ops->nelts; i++) { + if (op[i].len == 0) { + len += op[i].getlen(s, op[i].data); + + } else { + len += op[i].len; + } + } + + if (log[l].syslog_peer) { + + /* length of syslog's PRI and HEADER message parts */ + len += sizeof("<255>Jan 01 00:00:00 ") - 1 + + ngx_cycle->hostname.len + 1 + + log[l].syslog_peer->tag.len + 2; + + goto alloc_line; + } + + len += NGX_LINEFEED_SIZE; + + buffer = log[l].file ? log[l].file->data : NULL; + + if (buffer) { + + if (len > (size_t) (buffer->last - buffer->pos)) { + + ngx_stream_log_write(s, &log[l], buffer->start, + buffer->pos - buffer->start); + + buffer->pos = buffer->start; + } + + if (len <= (size_t) (buffer->last - buffer->pos)) { + + p = buffer->pos; + + if (buffer->event && p == buffer->start) { + ngx_add_timer(buffer->event, buffer->flush); + } + + for (i = 0; i < log[l].format->ops->nelts; i++) { + p = op[i].run(s, p, &op[i]); + } + + ngx_linefeed(p); + + buffer->pos = p; + + continue; + } + + if (buffer->event && buffer->event->timer_set) { + ngx_del_timer(buffer->event); + } + } + + alloc_line: + + line = ngx_pnalloc(s->connection->pool, len); + if (line == NULL) { + return NGX_ERROR; + } + + p = line; + + if (log[l].syslog_peer) { + p = ngx_syslog_add_header(log[l].syslog_peer, line); + } + + for (i = 0; i < log[l].format->ops->nelts; i++) { + p = op[i].run(s, p, &op[i]); + } + + if (log[l].syslog_peer) { + + size = p - line; + + n = ngx_syslog_send(log[l].syslog_peer, line, size); + + if (n < 0) { + ngx_log_error(NGX_LOG_WARN, s->connection->log, 0, + "send() to syslog failed"); + + } else if ((size_t) n != size) { + ngx_log_error(NGX_LOG_WARN, s->connection->log, 0, + "send() to syslog has written only %z of %uz", + n, size); + } + + continue; + } + + ngx_linefeed(p); + + ngx_stream_log_write(s, &log[l], line, p - line); + } + + return NGX_OK; +} + + +static void +ngx_stream_log_write(ngx_stream_session_t *s, ngx_stream_log_t *log, + u_char *buf, size_t len) +{ + u_char *name; + time_t now; + ssize_t n; + ngx_err_t err; +#if (NGX_ZLIB) + ngx_stream_log_buf_t *buffer; +#endif + + if (log->script == NULL) { + name = log->file->name.data; + +#if (NGX_ZLIB) + buffer = log->file->data; + + if (buffer && buffer->gzip) { + n = ngx_stream_log_gzip(log->file->fd, buf, len, buffer->gzip, + s->connection->log); + } else { + n = ngx_write_fd(log->file->fd, buf, len); + } +#else + n = ngx_write_fd(log->file->fd, buf, len); +#endif + + } else { + name = NULL; + n = ngx_stream_log_script_write(s, log->script, &name, buf, len); + } + + if (n == (ssize_t) len) { + return; + } + + now = ngx_time(); + + if (n == -1) { + err = ngx_errno; + + if (err == NGX_ENOSPC) { + log->disk_full_time = now; + } + + if (now - log->error_log_time > 59) { + ngx_log_error(NGX_LOG_ALERT, s->connection->log, err, + ngx_write_fd_n " to \"%s\" failed", name); + + log->error_log_time = now; + } + + return; + } + + if (now - log->error_log_time > 59) { + ngx_log_error(NGX_LOG_ALERT, s->connection->log, 0, + ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz", + name, n, len); + + log->error_log_time = now; + } +} + + +static ssize_t +ngx_stream_log_script_write(ngx_stream_session_t *s, + ngx_stream_log_script_t *script, u_char **name, u_char *buf, size_t len) +{ + ssize_t n; + ngx_str_t log; + ngx_open_file_info_t of; + ngx_stream_log_srv_conf_t *lscf; + + if (ngx_stream_script_run(s, &log, script->lengths->elts, 1, + script->values->elts) + == NULL) + { + /* simulate successful logging */ + return len; + } + + log.data[log.len - 1] = '\0'; + *name = log.data; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream log \"%s\"", log.data); + + lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_log_module); + + ngx_memzero(&of, sizeof(ngx_open_file_info_t)); + + of.log = 1; + of.valid = lscf->open_file_cache_valid; + of.min_uses = lscf->open_file_cache_min_uses; + of.directio = NGX_OPEN_FILE_DIRECTIO_OFF; + + if (ngx_open_cached_file(lscf->open_file_cache, &log, &of, + s->connection->pool) + != NGX_OK) + { + ngx_log_error(NGX_LOG_CRIT, s->connection->log, ngx_errno, + "%s \"%s\" failed", of.failed, log.data); + /* simulate successful logging */ + return len; + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream log #%d", of.fd); + + n = ngx_write_fd(of.fd, buf, len); + + return n; +} + + +#if (NGX_ZLIB) + +static ssize_t +ngx_stream_log_gzip(ngx_fd_t fd, u_char *buf, size_t len, ngx_int_t level, + ngx_log_t *log) +{ + int rc, wbits, memlevel; + u_char *out; + size_t size; + ssize_t n; + z_stream zstream; + ngx_err_t err; + ngx_pool_t *pool; + + wbits = MAX_WBITS; + memlevel = MAX_MEM_LEVEL - 1; + + while ((ssize_t) len < ((1 << (wbits - 1)) - 262)) { + wbits--; + memlevel--; + } + + /* + * This is a formula from deflateBound() for conservative upper bound of + * compressed data plus 18 bytes of gzip wrapper. + */ + + size = len + ((len + 7) >> 3) + ((len + 63) >> 6) + 5 + 18; + + ngx_memzero(&zstream, sizeof(z_stream)); + + pool = ngx_create_pool(256, log); + if (pool == NULL) { + /* simulate successful logging */ + return len; + } + + pool->log = log; + + zstream.zalloc = ngx_stream_log_gzip_alloc; + zstream.zfree = ngx_stream_log_gzip_free; + zstream.opaque = pool; + + out = ngx_pnalloc(pool, size); + if (out == NULL) { + goto done; + } + + zstream.next_in = buf; + zstream.avail_in = len; + zstream.next_out = out; + zstream.avail_out = size; + + rc = deflateInit2(&zstream, (int) level, Z_DEFLATED, wbits + 16, memlevel, + Z_DEFAULT_STRATEGY); + + if (rc != Z_OK) { + ngx_log_error(NGX_LOG_ALERT, log, 0, "deflateInit2() failed: %d", rc); + goto done; + } + + ngx_log_debug4(NGX_LOG_DEBUG_STREAM, log, 0, + "deflate in: ni:%p no:%p ai:%ud ao:%ud", + zstream.next_in, zstream.next_out, + zstream.avail_in, zstream.avail_out); + + rc = deflate(&zstream, Z_FINISH); + + if (rc != Z_STREAM_END) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "deflate(Z_FINISH) failed: %d", rc); + goto done; + } + + ngx_log_debug5(NGX_LOG_DEBUG_STREAM, log, 0, + "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", + zstream.next_in, zstream.next_out, + zstream.avail_in, zstream.avail_out, + rc); + + size -= zstream.avail_out; + + rc = deflateEnd(&zstream); + + if (rc != Z_OK) { + ngx_log_error(NGX_LOG_ALERT, log, 0, "deflateEnd() failed: %d", rc); + goto done; + } + + n = ngx_write_fd(fd, out, size); + + if (n != (ssize_t) size) { + err = (n == -1) ? ngx_errno : 0; + + ngx_destroy_pool(pool); + + ngx_set_errno(err); + return -1; + } + +done: + + ngx_destroy_pool(pool); + + /* simulate successful logging */ + return len; +} + + +static void * +ngx_stream_log_gzip_alloc(void *opaque, u_int items, u_int size) +{ + ngx_pool_t *pool = opaque; + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pool->log, 0, + "gzip alloc: n:%ud s:%ud", items, size); + + return ngx_palloc(pool, items * size); +} + + +static void +ngx_stream_log_gzip_free(void *opaque, void *address) +{ +#if 0 + ngx_pool_t *pool = opaque; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pool->log, 0, + "gzip free: %p", address); +#endif +} + +#endif + + +static void +ngx_stream_log_flush(ngx_open_file_t *file, ngx_log_t *log) +{ + size_t len; + ssize_t n; + ngx_stream_log_buf_t *buffer; + + buffer = file->data; + + len = buffer->pos - buffer->start; + + if (len == 0) { + return; + } + +#if (NGX_ZLIB) + if (buffer->gzip) { + n = ngx_stream_log_gzip(file->fd, buffer->start, len, buffer->gzip, + log); + } else { + n = ngx_write_fd(file->fd, buffer->start, len); + } +#else + n = ngx_write_fd(file->fd, buffer->start, len); +#endif + + if (n == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_write_fd_n " to \"%s\" failed", + file->name.data); + + } else if ((size_t) n != len) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz", + file->name.data, n, len); + } + + buffer->pos = buffer->start; + + if (buffer->event && buffer->event->timer_set) { + ngx_del_timer(buffer->event); + } +} + + +static void +ngx_stream_log_flush_handler(ngx_event_t *ev) +{ + ngx_open_file_t *file; + ngx_stream_log_buf_t *buffer; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "stream log buffer flush handler"); + + if (ev->timedout) { + ngx_stream_log_flush(ev->data, ev->log); + return; + } + + /* cancel the flush timer for graceful shutdown */ + + file = ev->data; + buffer = file->data; + + buffer->event = NULL; +} + + +static u_char * +ngx_stream_log_copy_short(ngx_stream_session_t *s, u_char *buf, + ngx_stream_log_op_t *op) +{ + size_t len; + uintptr_t data; + + len = op->len; + data = op->data; + + while (len--) { + *buf++ = (u_char) (data & 0xff); + data >>= 8; + } + + return buf; +} + + +static u_char * +ngx_stream_log_copy_long(ngx_stream_session_t *s, u_char *buf, + ngx_stream_log_op_t *op) +{ + return ngx_cpymem(buf, (u_char *) op->data, op->len); +} + + +static ngx_int_t +ngx_stream_log_variable_compile(ngx_conf_t *cf, ngx_stream_log_op_t *op, + ngx_str_t *value) +{ + ngx_int_t index; + + index = ngx_stream_get_variable_index(cf, value); + if (index == NGX_ERROR) { + return NGX_ERROR; + } + + op->len = 0; + op->getlen = ngx_stream_log_variable_getlen; + op->run = ngx_stream_log_variable; + op->data = index; + + return NGX_OK; +} + + +static size_t +ngx_stream_log_variable_getlen(ngx_stream_session_t *s, uintptr_t data) +{ + uintptr_t len; + ngx_stream_variable_value_t *value; + + value = ngx_stream_get_indexed_variable(s, data); + + if (value == NULL || value->not_found) { + return 1; + } + + len = ngx_stream_log_escape(NULL, value->data, value->len); + + value->escape = len ? 1 : 0; + + return value->len + len * 3; +} + + +static u_char * +ngx_stream_log_variable(ngx_stream_session_t *s, u_char *buf, + ngx_stream_log_op_t *op) +{ + ngx_stream_variable_value_t *value; + + value = ngx_stream_get_indexed_variable(s, op->data); + + if (value == NULL || value->not_found) { + *buf = '-'; + return buf + 1; + } + + if (value->escape == 0) { + return ngx_cpymem(buf, value->data, value->len); + + } else { + return (u_char *) ngx_stream_log_escape(buf, value->data, value->len); + } +} + + +static uintptr_t +ngx_stream_log_escape(u_char *dst, u_char *src, size_t size) +{ + ngx_uint_t n; + static u_char hex[] = "0123456789ABCDEF"; + + static uint32_t escape[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x00000004, /* 0000 0000 0000 0000 0000 0000 0000 0100 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x10000000, /* 0001 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + + if (dst == NULL) { + + /* find the number of the characters to be escaped */ + + n = 0; + + while (size) { + if (escape[*src >> 5] & (1U << (*src & 0x1f))) { + n++; + } + src++; + size--; + } + + return (uintptr_t) n; + } + + while (size) { + if (escape[*src >> 5] & (1U << (*src & 0x1f))) { + *dst++ = '\\'; + *dst++ = 'x'; + *dst++ = hex[*src >> 4]; + *dst++ = hex[*src & 0xf]; + src++; + + } else { + *dst++ = *src++; + } + size--; + } + + return (uintptr_t) dst; +} + + +static void * +ngx_stream_log_create_main_conf(ngx_conf_t *cf) +{ + ngx_stream_log_main_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_main_conf_t)); + if (conf == NULL) { + return NULL; + } + + if (ngx_array_init(&conf->formats, cf->pool, 4, + sizeof(ngx_stream_log_fmt_t)) + != NGX_OK) + { + return NULL; + } + + return conf; +} + + +static void * +ngx_stream_log_create_srv_conf(ngx_conf_t *cf) +{ + ngx_stream_log_srv_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_srv_conf_t)); + if (conf == NULL) { + return NULL; + } + + conf->open_file_cache = NGX_CONF_UNSET_PTR; + + return conf; +} + + +static char * +ngx_stream_log_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_stream_log_srv_conf_t *prev = parent; + ngx_stream_log_srv_conf_t *conf = child; + + if (conf->open_file_cache == NGX_CONF_UNSET_PTR) { + + conf->open_file_cache = prev->open_file_cache; + conf->open_file_cache_valid = prev->open_file_cache_valid; + conf->open_file_cache_min_uses = prev->open_file_cache_min_uses; + + if (conf->open_file_cache == NGX_CONF_UNSET_PTR) { + conf->open_file_cache = NULL; + } + } + + if (conf->logs || conf->off) { + return NGX_CONF_OK; + } + + conf->logs = prev->logs; + conf->off = prev->off; + + return NGX_CONF_OK; +} + + +static char * +ngx_stream_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_log_srv_conf_t *lscf = conf; + + ssize_t size; + ngx_int_t gzip; + ngx_uint_t i, n; + ngx_msec_t flush; + ngx_str_t *value, name, s; + ngx_stream_log_t *log; + ngx_syslog_peer_t *peer; + ngx_stream_log_buf_t *buffer; + ngx_stream_log_fmt_t *fmt; + ngx_stream_script_compile_t sc; + ngx_stream_log_main_conf_t *lmcf; + ngx_stream_compile_complex_value_t ccv; + + value = cf->args->elts; + + if (ngx_strcmp(value[1].data, "off") == 0) { + lscf->off = 1; + if (cf->args->nelts == 2) { + return NGX_CONF_OK; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } + + if (lscf->logs == NULL) { + lscf->logs = ngx_array_create(cf->pool, 2, sizeof(ngx_stream_log_t)); + if (lscf->logs == NULL) { + return NGX_CONF_ERROR; + } + } + + lmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_log_module); + + log = ngx_array_push(lscf->logs); + if (log == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(log, sizeof(ngx_stream_log_t)); + + + if (ngx_strncmp(value[1].data, "syslog:", 7) == 0) { + + peer = ngx_pcalloc(cf->pool, sizeof(ngx_syslog_peer_t)); + if (peer == NULL) { + return NGX_CONF_ERROR; + } + + if (ngx_syslog_process_conf(cf, peer) != NGX_CONF_OK) { + return NGX_CONF_ERROR; + } + + log->syslog_peer = peer; + + goto process_formats; + } + + n = ngx_stream_script_variables_count(&value[1]); + + if (n == 0) { + log->file = ngx_conf_open_file(cf->cycle, &value[1]); + if (log->file == NULL) { + return NGX_CONF_ERROR; + } + + } else { + if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + + log->script = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_script_t)); + if (log->script == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(&sc, sizeof(ngx_stream_script_compile_t)); + + sc.cf = cf; + sc.source = &value[1]; + sc.lengths = &log->script->lengths; + sc.values = &log->script->values; + sc.variables = n; + sc.complete_lengths = 1; + sc.complete_values = 1; + + if (ngx_stream_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + +process_formats: + + if (cf->args->nelts >= 3) { + name = value[2]; + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "log format is not specified"); + return NGX_CONF_ERROR; + } + + fmt = lmcf->formats.elts; + for (i = 0; i < lmcf->formats.nelts; i++) { + if (fmt[i].name.len == name.len + && ngx_strcasecmp(fmt[i].name.data, name.data) == 0) + { + log->format = &fmt[i]; + break; + } + } + + if (log->format == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unknown log format \"%V\"", &name); + return NGX_CONF_ERROR; + } + + size = 0; + flush = 0; + gzip = 0; + + for (i = 3; i < cf->args->nelts; i++) { + + if (ngx_strncmp(value[i].data, "buffer=", 7) == 0) { + s.len = value[i].len - 7; + s.data = value[i].data + 7; + + size = ngx_parse_size(&s); + + if (size == NGX_ERROR || size == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid buffer size \"%V\"", &s); + return NGX_CONF_ERROR; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "flush=", 6) == 0) { + s.len = value[i].len - 6; + s.data = value[i].data + 6; + + flush = ngx_parse_time(&s, 0); + + if (flush == (ngx_msec_t) NGX_ERROR || flush == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid flush time \"%V\"", &s); + return NGX_CONF_ERROR; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "gzip", 4) == 0 + && (value[i].len == 4 || value[i].data[4] == '=')) + { +#if (NGX_ZLIB) + if (size == 0) { + size = 64 * 1024; + } + + if (value[i].len == 4) { + gzip = Z_BEST_SPEED; + continue; + } + + s.len = value[i].len - 5; + s.data = value[i].data + 5; + + gzip = ngx_atoi(s.data, s.len); + + if (gzip < 1 || gzip > 9) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid compression level \"%V\"", &s); + return NGX_CONF_ERROR; + } + + continue; + +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "nginx was built without zlib support"); + return NGX_CONF_ERROR; +#endif + } + + if (ngx_strncmp(value[i].data, "if=", 3) == 0) { + s.len = value[i].len - 3; + s.data = value[i].data + 3; + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &s; + ccv.complex_value = ngx_palloc(cf->pool, + sizeof(ngx_stream_complex_value_t)); + if (ccv.complex_value == NULL) { + return NGX_CONF_ERROR; + } + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + log->filter = ccv.complex_value; + + continue; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (flush && size == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "no buffer is defined for access_log \"%V\"", + &value[1]); + return NGX_CONF_ERROR; + } + + if (size) { + + if (log->script) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "buffered logs cannot have variables in name"); + return NGX_CONF_ERROR; + } + + if (log->syslog_peer) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "logs to syslog cannot be buffered"); + return NGX_CONF_ERROR; + } + + if (log->file->data) { + buffer = log->file->data; + + if (buffer->last - buffer->start != size + || buffer->flush != flush + || buffer->gzip != gzip) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "access_log \"%V\" already defined " + "with conflicting parameters", + &value[1]); + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; + } + + buffer = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_buf_t)); + if (buffer == NULL) { + return NGX_CONF_ERROR; + } + + buffer->start = ngx_pnalloc(cf->pool, size); + if (buffer->start == NULL) { + return NGX_CONF_ERROR; + } + + buffer->pos = buffer->start; + buffer->last = buffer->start + size; + + if (flush) { + buffer->event = ngx_pcalloc(cf->pool, sizeof(ngx_event_t)); + if (buffer->event == NULL) { + return NGX_CONF_ERROR; + } + + buffer->event->data = log->file; + buffer->event->handler = ngx_stream_log_flush_handler; + buffer->event->log = &cf->cycle->new_log; + buffer->event->cancelable = 1; + + buffer->flush = flush; + } + + buffer->gzip = gzip; + + log->file->flush = ngx_stream_log_flush; + log->file->data = buffer; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_stream_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_log_main_conf_t *lmcf = conf; + + ngx_str_t *value; + ngx_uint_t i; + ngx_stream_log_fmt_t *fmt; + + value = cf->args->elts; + + fmt = lmcf->formats.elts; + for (i = 0; i < lmcf->formats.nelts; i++) { + if (fmt[i].name.len == value[1].len + && ngx_strcmp(fmt[i].name.data, value[1].data) == 0) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate \"log_format\" name \"%V\"", + &value[1]); + return NGX_CONF_ERROR; + } + } + + fmt = ngx_array_push(&lmcf->formats); + if (fmt == NULL) { + return NGX_CONF_ERROR; + } + + fmt->name = value[1]; + + fmt->flushes = ngx_array_create(cf->pool, 4, sizeof(ngx_int_t)); + if (fmt->flushes == NULL) { + return NGX_CONF_ERROR; + } + + fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_stream_log_op_t)); + if (fmt->ops == NULL) { + return NGX_CONF_ERROR; + } + + return ngx_stream_log_compile_format(cf, fmt->flushes, fmt->ops, + cf->args, 2); +} + + +static char * +ngx_stream_log_compile_format(ngx_conf_t *cf, ngx_array_t *flushes, + ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s) +{ + u_char *data, *p, ch; + size_t i, len; + ngx_str_t *value, var; + ngx_int_t *flush; + ngx_uint_t bracket; + ngx_stream_log_op_t *op; + + value = args->elts; + + for ( /* void */ ; s < args->nelts; s++) { + + i = 0; + + while (i < value[s].len) { + + op = ngx_array_push(ops); + if (op == NULL) { + return NGX_CONF_ERROR; + } + + data = &value[s].data[i]; + + if (value[s].data[i] == '$') { + + if (++i == value[s].len) { + goto invalid; + } + + if (value[s].data[i] == '{') { + bracket = 1; + + if (++i == value[s].len) { + goto invalid; + } + + var.data = &value[s].data[i]; + + } else { + bracket = 0; + var.data = &value[s].data[i]; + } + + for (var.len = 0; i < value[s].len; i++, var.len++) { + ch = value[s].data[i]; + + if (ch == '}' && bracket) { + i++; + bracket = 0; + break; + } + + if ((ch >= 'A' && ch <= 'Z') + || (ch >= 'a' && ch <= 'z') + || (ch >= '0' && ch <= '9') + || ch == '_') + { + continue; + } + + break; + } + + if (bracket) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the closing bracket in \"%V\" " + "variable is missing", &var); + return NGX_CONF_ERROR; + } + + if (var.len == 0) { + goto invalid; + } + + if (ngx_stream_log_variable_compile(cf, op, &var) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (flushes) { + + flush = ngx_array_push(flushes); + if (flush == NULL) { + return NGX_CONF_ERROR; + } + + *flush = op->data; /* variable index */ + } + + continue; + } + + i++; + + while (i < value[s].len && value[s].data[i] != '$') { + i++; + } + + len = &value[s].data[i] - data; + + if (len) { + + op->len = len; + op->getlen = NULL; + + if (len <= sizeof(uintptr_t)) { + op->run = ngx_stream_log_copy_short; + op->data = 0; + + while (len--) { + op->data <<= 8; + op->data |= data[len]; + } + + } else { + op->run = ngx_stream_log_copy_long; + + p = ngx_pnalloc(cf->pool, len); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memcpy(p, data, len); + op->data = (uintptr_t) p; + } + } + } + } + + return NGX_CONF_OK; + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%s\"", data); + + return NGX_CONF_ERROR; +} + + +static char * +ngx_stream_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_log_srv_conf_t *lscf = conf; + + time_t inactive, valid; + ngx_str_t *value, s; + ngx_int_t max, min_uses; + ngx_uint_t i; + + if (lscf->open_file_cache != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + value = cf->args->elts; + + max = 0; + inactive = 10; + valid = 60; + min_uses = 1; + + for (i = 1; i < cf->args->nelts; i++) { + + if (ngx_strncmp(value[i].data, "max=", 4) == 0) { + + max = ngx_atoi(value[i].data + 4, value[i].len - 4); + if (max == NGX_ERROR) { + goto failed; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) { + + s.len = value[i].len - 9; + s.data = value[i].data + 9; + + inactive = ngx_parse_time(&s, 1); + if (inactive == (time_t) NGX_ERROR) { + goto failed; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "min_uses=", 9) == 0) { + + min_uses = ngx_atoi(value[i].data + 9, value[i].len - 9); + if (min_uses == NGX_ERROR) { + goto failed; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "valid=", 6) == 0) { + + s.len = value[i].len - 6; + s.data = value[i].data + 6; + + valid = ngx_parse_time(&s, 1); + if (valid == (time_t) NGX_ERROR) { + goto failed; + } + + continue; + } + + if (ngx_strcmp(value[i].data, "off") == 0) { + + lscf->open_file_cache = NULL; + + continue; + } + + failed: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid \"open_log_file_cache\" parameter \"%V\"", + &value[i]); + return NGX_CONF_ERROR; + } + + if (lscf->open_file_cache == NULL) { + return NGX_CONF_OK; + } + + if (max == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"open_log_file_cache\" must have \"max\" parameter"); + return NGX_CONF_ERROR; + } + + lscf->open_file_cache = ngx_open_file_cache_init(cf->pool, max, inactive); + + if (lscf->open_file_cache) { + + lscf->open_file_cache_valid = valid; + lscf->open_file_cache_min_uses = min_uses; + + return NGX_CONF_OK; + } + + return NGX_CONF_ERROR; +} + + +static ngx_int_t +ngx_stream_log_init(ngx_conf_t *cf) +{ + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + + cmcf->access_log_handler = ngx_stream_log_handler; + + return NGX_OK; +} diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c index 9d43109..ed802e7 100644 --- a/src/stream/ngx_stream_proxy_module.c +++ b/src/stream/ngx_stream_proxy_module.c @@ -73,7 +73,7 @@ static ngx_int_t ngx_stream_proxy_test_connect(ngx_connection_t *c); static void ngx_stream_proxy_process(ngx_stream_session_t *s, ngx_uint_t from_upstream, ngx_uint_t do_write); static void ngx_stream_proxy_next_upstream(ngx_stream_session_t *s); -static void ngx_stream_proxy_finalize(ngx_stream_session_t *s, ngx_int_t rc); +static void ngx_stream_proxy_finalize(ngx_stream_session_t *s, ngx_uint_t rc); static u_char *ngx_stream_proxy_log_error(ngx_log_t *log, u_char *buf, size_t len); @@ -368,7 +368,7 @@ ngx_stream_proxy_handler(ngx_stream_session_t *s) u = ngx_pcalloc(c->pool, sizeof(ngx_stream_upstream_t)); if (u == NULL) { - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } @@ -380,7 +380,7 @@ ngx_stream_proxy_handler(ngx_stream_session_t *s) u->peer.log_error = NGX_ERROR_ERR; if (ngx_stream_proxy_set_local(s, u, pscf->local) != NGX_OK) { - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } @@ -392,10 +392,17 @@ ngx_stream_proxy_handler(ngx_stream_session_t *s) c->write->handler = ngx_stream_proxy_downstream_handler; c->read->handler = ngx_stream_proxy_downstream_handler; + s->upstream_states = ngx_array_create(c->pool, 1, + sizeof(ngx_stream_upstream_state_t)); + if (s->upstream_states == NULL) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + if (c->type == SOCK_STREAM) { p = ngx_pnalloc(c->pool, pscf->buffer_size); if (p == NULL) { - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } @@ -418,7 +425,7 @@ ngx_stream_proxy_handler(ngx_stream_session_t *s) p = ngx_proxy_protocol_write(c, u->downstream_buf.last, u->downstream_buf.end); if (p == NULL) { - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } @@ -433,7 +440,7 @@ ngx_stream_proxy_handler(ngx_stream_session_t *s) if (pscf->upstream_value) { if (ngx_stream_proxy_eval(s, pscf) != NGX_OK) { - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } } @@ -457,14 +464,14 @@ ngx_stream_proxy_handler(ngx_stream_session_t *s) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "no port in upstream \"%V\"", host); - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } if (ngx_stream_upstream_create_round_robin_peer(s, u->resolved) != NGX_OK) { - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } @@ -493,7 +500,7 @@ ngx_stream_proxy_handler(ngx_stream_session_t *s) if (u->resolved->port == 0) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "no port in upstream \"%V\"", host); - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } @@ -503,14 +510,14 @@ ngx_stream_proxy_handler(ngx_stream_session_t *s) ctx = ngx_resolve_start(cscf->resolver, &temp); if (ctx == NULL) { - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } if (ctx == NGX_NO_RESOLVER) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "no resolver defined to resolve %V", host); - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } @@ -523,7 +530,7 @@ ngx_stream_proxy_handler(ngx_stream_session_t *s) if (ngx_resolve_name(ctx) != NGX_OK) { u->resolved->ctx = NULL; - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } @@ -534,16 +541,16 @@ found: if (uscf == NULL) { ngx_log_error(NGX_LOG_ALERT, c->log, 0, "no upstream configuration"); - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } -#if (NGX_HTTP_SSL) +#if (NGX_STREAM_SSL) u->ssl_name = uscf->host; #endif if (uscf->peer.init(s, uscf) != NGX_OK) { - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } @@ -677,18 +684,36 @@ ngx_stream_proxy_connect(ngx_stream_session_t *s) u = s->upstream; + if (u->state) { + u->state->response_time = ngx_current_msec - u->state->response_time; + } + + u->state = ngx_array_push(s->upstream_states); + if (u->state == NULL) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + ngx_memzero(u->state, sizeof(ngx_stream_upstream_state_t)); + + u->state->connect_time = (ngx_msec_t) -1; + u->state->first_byte_time = (ngx_msec_t) -1; + u->state->response_time = ngx_current_msec; + rc = ngx_event_connect_peer(&u->peer); ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, "proxy connect: %i", rc); if (rc == NGX_ERROR) { - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } + u->state->peer = u->peer.name; + if (rc == NGX_BUSY) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "no live upstreams"); - ngx_stream_proxy_finalize(s, NGX_DECLINED); + ngx_stream_proxy_finalize(s, NGX_STREAM_BAD_GATEWAY); return; } @@ -796,12 +821,14 @@ ngx_stream_proxy_init_upstream(ngx_stream_session_t *s) } } + u->state->connect_time = ngx_current_msec - u->state->response_time; + c->log->action = "proxying connection"; if (u->upstream_buf.start == NULL) { p = ngx_pnalloc(c->pool, pscf->buffer_size); if (p == NULL) { - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } @@ -851,7 +878,7 @@ ngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s) p = ngx_proxy_protocol_write(c, buf, buf + NGX_PROXY_PROTOCOL_MAX_HEADER); if (p == NULL) { - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return NGX_ERROR; } @@ -865,7 +892,7 @@ ngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s) if (n == NGX_AGAIN) { if (ngx_handle_write_event(pc->write, 0) != NGX_OK) { - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return NGX_ERROR; } @@ -879,7 +906,7 @@ ngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s) } if (n == NGX_ERROR) { - ngx_stream_proxy_finalize(s, NGX_DECLINED); + ngx_stream_proxy_finalize(s, NGX_STREAM_OK); return NGX_ERROR; } @@ -895,7 +922,7 @@ ngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s) ngx_log_error(NGX_LOG_ERR, c->log, 0, "could not send PROXY protocol header at once"); - ngx_stream_proxy_finalize(s, NGX_DECLINED); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return NGX_ERROR; } @@ -947,20 +974,20 @@ ngx_stream_proxy_ssl_init_connection(ngx_stream_session_t *s) if (ngx_ssl_create_connection(pscf->ssl, pc, NGX_SSL_BUFFER|NGX_SSL_CLIENT) != NGX_OK) { - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } if (pscf->ssl_server_name || pscf->ssl_verify) { if (ngx_stream_proxy_ssl_name(s) != NGX_OK) { - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } } if (pscf->ssl_session_reuse) { if (u->peer.set_session(&u->peer, u->peer.data) != NGX_OK) { - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } } @@ -1157,7 +1184,7 @@ ngx_stream_proxy_resolve_handler(ngx_resolver_ctx_t *ctx) u = s->upstream; ur = u->resolved; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream upstream resolve"); if (ctx->state) { @@ -1166,7 +1193,7 @@ ngx_stream_proxy_resolve_handler(ngx_resolver_ctx_t *ctx) &ctx->name, ctx->state, ngx_resolver_strerror(ctx->state)); - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } @@ -1192,7 +1219,7 @@ ngx_stream_proxy_resolve_handler(ngx_resolver_ctx_t *ctx) #endif if (ngx_stream_upstream_create_round_robin_peer(s, ur) != NGX_OK) { - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } @@ -1245,7 +1272,8 @@ ngx_stream_proxy_process_connection(ngx_event_t *ev, ngx_uint_t from_upstream) if (!ev->ready) { if (ngx_handle_read_event(ev, 0) != NGX_OK) { - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, + NGX_STREAM_INTERNAL_SERVER_ERROR); return; } @@ -1279,7 +1307,7 @@ ngx_stream_proxy_process_connection(ngx_event_t *ev, ngx_uint_t from_upstream) } ngx_connection_error(c, NGX_ETIMEDOUT, "connection timed out"); - ngx_stream_proxy_finalize(s, NGX_DECLINED); + ngx_stream_proxy_finalize(s, NGX_STREAM_OK); return; } @@ -1289,7 +1317,7 @@ ngx_stream_proxy_process_connection(ngx_event_t *ev, ngx_uint_t from_upstream) "stream connection delayed"); if (ngx_handle_read_event(ev, 0) != NGX_OK) { - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); } return; @@ -1407,7 +1435,7 @@ ngx_stream_proxy_process(ngx_stream_session_t *s, ngx_uint_t from_upstream, c->log->handler = handler; - ngx_stream_proxy_finalize(s, NGX_OK); + ngx_stream_proxy_finalize(s, NGX_STREAM_OK); return; } @@ -1449,7 +1477,7 @@ ngx_stream_proxy_process(ngx_stream_session_t *s, ngx_uint_t from_upstream, return; } - ngx_stream_proxy_finalize(s, NGX_DECLINED); + ngx_stream_proxy_finalize(s, NGX_STREAM_OK); return; } @@ -1500,6 +1528,13 @@ ngx_stream_proxy_process(ngx_stream_session_t *s, ngx_uint_t from_upstream, } } + if (from_upstream) { + if (u->state->first_byte_time == (ngx_msec_t) -1) { + u->state->first_byte_time = ngx_current_msec + - u->state->response_time; + } + } + if (c->type == SOCK_DGRAM && ++u->responses == pscf->responses) { src->read->ready = 0; @@ -1540,20 +1575,20 @@ ngx_stream_proxy_process(ngx_stream_session_t *s, ngx_uint_t from_upstream, c->log->handler = handler; - ngx_stream_proxy_finalize(s, NGX_OK); + ngx_stream_proxy_finalize(s, NGX_STREAM_OK); return; } flags = src->read->eof ? NGX_CLOSE_EVENT : 0; if (!src->shared && ngx_handle_read_event(src->read, flags) != NGX_OK) { - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } if (dst) { if (!dst->shared && ngx_handle_write_event(dst->write, 0) != NGX_OK) { - ngx_stream_proxy_finalize(s, NGX_ERROR); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } @@ -1593,7 +1628,7 @@ ngx_stream_proxy_next_upstream(ngx_stream_session_t *s) || !pscf->next_upstream || (timeout && ngx_current_msec - u->peer.start_time >= timeout)) { - ngx_stream_proxy_finalize(s, NGX_DECLINED); + ngx_stream_proxy_finalize(s, NGX_STREAM_BAD_GATEWAY); return; } @@ -1612,6 +1647,9 @@ ngx_stream_proxy_next_upstream(ngx_stream_session_t *s) } #endif + u->state->bytes_received = u->received; + u->state->bytes_sent = pc->sent; + ngx_close_connection(pc); u->peer.connection = NULL; } @@ -1621,7 +1659,7 @@ ngx_stream_proxy_next_upstream(ngx_stream_session_t *s) static void -ngx_stream_proxy_finalize(ngx_stream_session_t *s, ngx_int_t rc) +ngx_stream_proxy_finalize(ngx_stream_session_t *s, ngx_uint_t rc) { ngx_connection_t *pc; ngx_stream_upstream_t *u; @@ -1640,13 +1678,22 @@ ngx_stream_proxy_finalize(ngx_stream_session_t *s, ngx_int_t rc) u->resolved->ctx = NULL; } + pc = u->peer.connection; + + if (u->state) { + u->state->response_time = ngx_current_msec - u->state->response_time; + + if (pc) { + u->state->bytes_received = u->received; + u->state->bytes_sent = pc->sent; + } + } + if (u->peer.free && u->peer.sockaddr) { u->peer.free(&u->peer, u->peer.data, 0); u->peer.sockaddr = NULL; } - pc = u->peer.connection; - if (pc) { ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "close stream proxy upstream connection: %d", pc->fd); @@ -1664,7 +1711,7 @@ ngx_stream_proxy_finalize(ngx_stream_session_t *s, ngx_int_t rc) noupstream: - ngx_stream_close_connection(s->connection); + ngx_stream_finalize_session(s, rc); } diff --git a/src/stream/ngx_stream_realip_module.c b/src/stream/ngx_stream_realip_module.c new file mode 100644 index 0000000..8ce05a0 --- /dev/null +++ b/src/stream/ngx_stream_realip_module.c @@ -0,0 +1,342 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + ngx_array_t *from; /* array of ngx_cidr_t */ +} ngx_stream_realip_srv_conf_t; + + +typedef struct { + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_str_t addr_text; +} ngx_stream_realip_ctx_t; + + +static ngx_int_t ngx_stream_realip_handler(ngx_stream_session_t *s); +static ngx_int_t ngx_stream_realip_set_addr(ngx_stream_session_t *s, + ngx_addr_t *addr); +static char *ngx_stream_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static void *ngx_stream_realip_create_srv_conf(ngx_conf_t *cf); +static char *ngx_stream_realip_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); +static ngx_int_t ngx_stream_realip_add_variables(ngx_conf_t *cf); +static ngx_int_t ngx_stream_realip_init(ngx_conf_t *cf); + + +static ngx_int_t ngx_stream_realip_remote_addr_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_realip_remote_port_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); + + +static ngx_command_t ngx_stream_realip_commands[] = { + + { ngx_string("set_real_ip_from"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_realip_from, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_realip_module_ctx = { + ngx_stream_realip_add_variables, /* preconfiguration */ + ngx_stream_realip_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_stream_realip_create_srv_conf, /* create server configuration */ + ngx_stream_realip_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_realip_module = { + NGX_MODULE_V1, + &ngx_stream_realip_module_ctx, /* module context */ + ngx_stream_realip_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_stream_variable_t ngx_stream_realip_vars[] = { + + { ngx_string("realip_remote_addr"), NULL, + ngx_stream_realip_remote_addr_variable, 0, 0, 0 }, + + { ngx_string("realip_remote_port"), NULL, + ngx_stream_realip_remote_port_variable, 0, 0, 0 }, + + { ngx_null_string, NULL, NULL, 0, 0, 0 } +}; + + +static ngx_int_t +ngx_stream_realip_handler(ngx_stream_session_t *s) +{ + ngx_addr_t addr; + ngx_connection_t *c; + ngx_stream_realip_srv_conf_t *rscf; + + rscf = ngx_stream_get_module_srv_conf(s, ngx_stream_realip_module); + + if (rscf->from == NULL) { + return NGX_DECLINED; + } + + c = s->connection; + + if (c->proxy_protocol_addr.len == 0) { + return NGX_DECLINED; + } + + if (ngx_cidr_match(c->sockaddr, rscf->from) != NGX_OK) { + return NGX_DECLINED; + } + + if (ngx_parse_addr(c->pool, &addr, c->proxy_protocol_addr.data, + c->proxy_protocol_addr.len) + != NGX_OK) + { + return NGX_DECLINED; + } + + ngx_inet_set_port(addr.sockaddr, c->proxy_protocol_port); + + return ngx_stream_realip_set_addr(s, &addr); +} + + +static ngx_int_t +ngx_stream_realip_set_addr(ngx_stream_session_t *s, ngx_addr_t *addr) +{ + size_t len; + u_char *p; + u_char text[NGX_SOCKADDR_STRLEN]; + ngx_connection_t *c; + ngx_stream_realip_ctx_t *ctx; + + c = s->connection; + + ctx = ngx_palloc(c->pool, sizeof(ngx_stream_realip_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } + + 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); + + ngx_stream_set_ctx(s, ctx, ngx_stream_realip_module); + + ctx->sockaddr = c->sockaddr; + ctx->socklen = c->socklen; + ctx->addr_text = c->addr_text; + + c->sockaddr = addr->sockaddr; + c->socklen = addr->socklen; + c->addr_text.len = len; + c->addr_text.data = p; + + return NGX_DECLINED; +} + + +static char * +ngx_stream_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_realip_srv_conf_t *rscf = conf; + + ngx_int_t rc; + ngx_str_t *value; + ngx_cidr_t *cidr; + + 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; + } + } + + cidr = ngx_array_push(rscf->from); + if (cidr == NULL) { + return NGX_CONF_ERROR; + } + +#if (NGX_HAVE_UNIX_DOMAIN) + + if (ngx_strcmp(value[1].data, "unix:") == 0) { + cidr->family = AF_UNIX; + return NGX_CONF_OK; + } + +#endif + + rc = ngx_ptocidr(&value[1], cidr); + + if (rc == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", + &value[1]); + return NGX_CONF_ERROR; + } + + if (rc == NGX_DONE) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "low address bits of %V are meaningless", &value[1]); + } + + return NGX_CONF_OK; +} + + +static void * +ngx_stream_realip_create_srv_conf(ngx_conf_t *cf) +{ + ngx_stream_realip_srv_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_realip_srv_conf_t)); + if (conf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * conf->from = NULL; + */ + + return conf; +} + + +static char * +ngx_stream_realip_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_stream_realip_srv_conf_t *prev = parent; + ngx_stream_realip_srv_conf_t *conf = child; + + if (conf->from == NULL) { + conf->from = prev->from; + } + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_stream_realip_add_variables(ngx_conf_t *cf) +{ + ngx_stream_variable_t *var, *v; + + for (v = ngx_stream_realip_vars; v->name.len; v++) { + var = ngx_stream_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_realip_init(ngx_conf_t *cf) +{ + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + + cmcf->realip_handler = ngx_stream_realip_handler; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_realip_remote_addr_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_str_t *addr_text; + ngx_stream_realip_ctx_t *ctx; + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_realip_module); + + addr_text = ctx ? &ctx->addr_text : &s->connection->addr_text; + + v->len = addr_text->len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = addr_text->data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_realip_remote_port_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_uint_t port; + struct sockaddr *sa; + ngx_stream_realip_ctx_t *ctx; + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_realip_module); + + sa = ctx ? ctx->sockaddr : s->connection->sockaddr; + + v->len = 0; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + v->data = ngx_pnalloc(s->connection->pool, sizeof("65535") - 1); + if (v->data == NULL) { + return NGX_ERROR; + } + + port = ngx_inet_get_port(sa); + + if (port > 0 && port < 65536) { + v->len = ngx_sprintf(v->data, "%ui", port) - v->data; + } + + return NGX_OK; +} diff --git a/src/stream/ngx_stream_return_module.c b/src/stream/ngx_stream_return_module.c index 3ab9bdf..c22087f 100644 --- a/src/stream/ngx_stream_return_module.c +++ b/src/stream/ngx_stream_return_module.c @@ -83,7 +83,7 @@ ngx_stream_return_handler(ngx_stream_session_t *s) rscf = ngx_stream_get_module_srv_conf(s, ngx_stream_return_module); if (ngx_stream_complex_value(s, &rscf->text, &text) != NGX_OK) { - ngx_stream_close_connection(c); + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } @@ -91,13 +91,13 @@ ngx_stream_return_handler(ngx_stream_session_t *s) "stream return text: \"%V\"", &text); if (text.len == 0) { - ngx_stream_close_connection(c); + ngx_stream_finalize_session(s, NGX_STREAM_OK); return; } ctx = ngx_pcalloc(c->pool, sizeof(ngx_stream_return_ctx_t)); if (ctx == NULL) { - ngx_stream_close_connection(c); + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } @@ -126,7 +126,7 @@ ngx_stream_return_write_handler(ngx_event_t *ev) if (ev->timedout) { ngx_connection_error(c, NGX_ETIMEDOUT, "connection timed out"); - ngx_stream_close_connection(c); + ngx_stream_finalize_session(s, NGX_STREAM_OK); return; } @@ -137,7 +137,7 @@ ngx_stream_return_write_handler(ngx_event_t *ev) n = c->send(c, b->pos, b->last - b->pos); if (n == NGX_ERROR) { - ngx_stream_close_connection(c); + ngx_stream_finalize_session(s, NGX_STREAM_OK); return; } @@ -145,14 +145,14 @@ ngx_stream_return_write_handler(ngx_event_t *ev) b->pos += n; if (b->pos == b->last) { - ngx_stream_close_connection(c); + ngx_stream_finalize_session(s, NGX_STREAM_OK); return; } } } if (ngx_handle_write_event(ev, 0) != NGX_OK) { - ngx_stream_close_connection(c); + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } diff --git a/src/stream/ngx_stream_script.c b/src/stream/ngx_stream_script.c index 8130f92..ff8e655 100644 --- a/src/stream/ngx_stream_script.c +++ b/src/stream/ngx_stream_script.c @@ -388,6 +388,73 @@ invalid_variable: } +u_char * +ngx_stream_script_run(ngx_stream_session_t *s, ngx_str_t *value, + void *code_lengths, size_t len, void *code_values) +{ + ngx_uint_t i; + ngx_stream_script_code_pt code; + ngx_stream_script_engine_t e; + ngx_stream_core_main_conf_t *cmcf; + ngx_stream_script_len_code_pt lcode; + + cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); + + for (i = 0; i < cmcf->variables.nelts; i++) { + if (s->variables[i].no_cacheable) { + s->variables[i].valid = 0; + s->variables[i].not_found = 0; + } + } + + ngx_memzero(&e, sizeof(ngx_stream_script_engine_t)); + + e.ip = code_lengths; + e.session = s; + e.flushed = 1; + + while (*(uintptr_t *) e.ip) { + lcode = *(ngx_stream_script_len_code_pt *) e.ip; + len += lcode(&e); + } + + + value->len = len; + value->data = ngx_pnalloc(s->connection->pool, len); + if (value->data == NULL) { + return NULL; + } + + e.ip = code_values; + e.pos = value->data; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_stream_script_code_pt *) e.ip; + code((ngx_stream_script_engine_t *) &e); + } + + return e.pos; +} + + +void +ngx_stream_script_flush_no_cacheable_variables(ngx_stream_session_t *s, + ngx_array_t *indices) +{ + ngx_uint_t n, *index; + + if (indices) { + index = indices->elts; + for (n = 0; n < indices->nelts; n++) { + if (s->variables[index[n]].no_cacheable) { + s->variables[index[n]].valid = 0; + s->variables[index[n]].not_found = 0; + } + } + } +} + + static ngx_int_t ngx_stream_script_init_arrays(ngx_stream_script_compile_t *sc) { diff --git a/src/stream/ngx_stream_script.h b/src/stream/ngx_stream_script.h index 0abe50e..25a450d 100644 --- a/src/stream/ngx_stream_script.h +++ b/src/stream/ngx_stream_script.h @@ -110,6 +110,10 @@ char *ngx_stream_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd, ngx_uint_t ngx_stream_script_variables_count(ngx_str_t *value); ngx_int_t ngx_stream_script_compile(ngx_stream_script_compile_t *sc); +u_char *ngx_stream_script_run(ngx_stream_session_t *s, ngx_str_t *value, + void *code_lengths, size_t reserved, void *code_values); +void ngx_stream_script_flush_no_cacheable_variables(ngx_stream_session_t *s, + ngx_array_t *indices); void *ngx_stream_script_add_code(ngx_array_t *codes, size_t size, void *code); diff --git a/src/stream/ngx_stream_upstream.c b/src/stream/ngx_stream_upstream.c index 2ea5eeb..0c59780 100644 --- a/src/stream/ngx_stream_upstream.c +++ b/src/stream/ngx_stream_upstream.c @@ -10,6 +10,14 @@ #include +static ngx_int_t ngx_stream_upstream_add_variables(ngx_conf_t *cf); +static ngx_int_t ngx_stream_upstream_addr_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_upstream_response_time_variable( + ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_upstream_bytes_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); + static char *ngx_stream_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy); static char *ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, @@ -39,7 +47,7 @@ static ngx_command_t ngx_stream_upstream_commands[] = { static ngx_stream_module_t ngx_stream_upstream_module_ctx = { - NULL, /* preconfiguration */ + ngx_stream_upstream_add_variables, /* preconfiguration */ NULL, /* postconfiguration */ ngx_stream_upstream_create_main_conf, /* create main configuration */ @@ -66,6 +74,232 @@ ngx_module_t ngx_stream_upstream_module = { }; +static ngx_stream_variable_t ngx_stream_upstream_vars[] = { + + { ngx_string("upstream_addr"), NULL, + ngx_stream_upstream_addr_variable, 0, + NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + { ngx_string("upstream_bytes_sent"), NULL, + ngx_stream_upstream_bytes_variable, 0, + NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + { ngx_string("upstream_connect_time"), NULL, + ngx_stream_upstream_response_time_variable, 2, + NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + { ngx_string("upstream_first_byte_time"), NULL, + ngx_stream_upstream_response_time_variable, 1, + NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + { ngx_string("upstream_session_time"), NULL, + ngx_stream_upstream_response_time_variable, 0, + NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + { ngx_string("upstream_bytes_received"), NULL, + ngx_stream_upstream_bytes_variable, 1, + NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + { ngx_null_string, NULL, NULL, 0, 0, 0 } +}; + + +static ngx_int_t +ngx_stream_upstream_add_variables(ngx_conf_t *cf) +{ + ngx_stream_variable_t *var, *v; + + for (v = ngx_stream_upstream_vars; v->name.len; v++) { + var = ngx_stream_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_upstream_addr_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + u_char *p; + size_t len; + ngx_uint_t i; + ngx_stream_upstream_state_t *state; + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + if (s->upstream_states == NULL || s->upstream_states->nelts == 0) { + v->not_found = 1; + return NGX_OK; + } + + len = 0; + state = s->upstream_states->elts; + + for (i = 0; i < s->upstream_states->nelts; i++) { + if (state[i].peer) { + len += state[i].peer->len; + } + + len += 2; + } + + p = ngx_pnalloc(s->connection->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + v->data = p; + + i = 0; + + for ( ;; ) { + if (state[i].peer) { + p = ngx_cpymem(p, state[i].peer->data, state[i].peer->len); + } + + if (++i == s->upstream_states->nelts) { + break; + } + + *p++ = ','; + *p++ = ' '; + } + + v->len = p - v->data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_upstream_bytes_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + u_char *p; + size_t len; + ngx_uint_t i; + ngx_stream_upstream_state_t *state; + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + if (s->upstream_states == NULL || s->upstream_states->nelts == 0) { + v->not_found = 1; + return NGX_OK; + } + + len = s->upstream_states->nelts * (NGX_OFF_T_LEN + 2); + + p = ngx_pnalloc(s->connection->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + v->data = p; + + i = 0; + state = s->upstream_states->elts; + + for ( ;; ) { + + if (data == 1) { + p = ngx_sprintf(p, "%O", state[i].bytes_received); + + } else { + p = ngx_sprintf(p, "%O", state[i].bytes_sent); + } + + if (++i == s->upstream_states->nelts) { + break; + } + + *p++ = ','; + *p++ = ' '; + } + + v->len = p - v->data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_upstream_response_time_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + u_char *p; + size_t len; + ngx_uint_t i; + ngx_msec_int_t ms; + ngx_stream_upstream_state_t *state; + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + if (s->upstream_states == NULL || s->upstream_states->nelts == 0) { + v->not_found = 1; + return NGX_OK; + } + + len = s->upstream_states->nelts * (NGX_TIME_T_LEN + 4 + 2); + + p = ngx_pnalloc(s->connection->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + v->data = p; + + i = 0; + state = s->upstream_states->elts; + + for ( ;; ) { + + if (data == 1) { + if (state[i].first_byte_time == (ngx_msec_t) -1) { + *p++ = '-'; + goto next; + } + + ms = state[i].first_byte_time; + + } else if (data == 2 && state[i].connect_time != (ngx_msec_t) -1) { + ms = state[i].connect_time; + + } else { + ms = state[i].response_time; + } + + ms = ngx_max(ms, 0); + p = ngx_sprintf(p, "%T.%03M", (time_t) ms / 1000, ms % 1000); + + next: + + if (++i == s->upstream_states->nelts) { + break; + } + + *p++ = ','; + *p++ = ' '; + } + + v->len = p - v->data; + + return NGX_OK; +} + + static char * ngx_stream_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) { diff --git a/src/stream/ngx_stream_upstream.h b/src/stream/ngx_stream_upstream.h index 5f067c0..f83b5ba 100644 --- a/src/stream/ngx_stream_upstream.h +++ b/src/stream/ngx_stream_upstream.h @@ -78,6 +78,17 @@ struct ngx_stream_upstream_srv_conf_s { }; +typedef struct { + ngx_msec_t response_time; + ngx_msec_t connect_time; + ngx_msec_t first_byte_time; + off_t bytes_sent; + off_t bytes_received; + + ngx_str_t *peer; +} ngx_stream_upstream_state_t; + + typedef struct { ngx_str_t host; in_port_t port; @@ -104,6 +115,7 @@ typedef struct { ngx_str_t ssl_name; #endif ngx_stream_upstream_resolved_t *resolved; + ngx_stream_upstream_state_t *state; unsigned connected:1; unsigned proxy_protocol:1; } ngx_stream_upstream_t; diff --git a/src/stream/ngx_stream_variables.c b/src/stream/ngx_stream_variables.c index 10f9c7e..aa5361d 100644 --- a/src/stream/ngx_stream_variables.c +++ b/src/stream/ngx_stream_variables.c @@ -17,11 +17,19 @@ static ngx_int_t ngx_stream_variable_remote_addr(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_variable_remote_port(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_proxy_protocol_addr( + ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_proxy_protocol_port( + ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_variable_server_addr(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_variable_server_port(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); -static ngx_int_t ngx_stream_variable_bytes_sent(ngx_stream_session_t *s, +static ngx_int_t ngx_stream_variable_bytes(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_session_time(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_status(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_variable_connection(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); @@ -38,6 +46,8 @@ static ngx_int_t ngx_stream_variable_time_iso8601(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_variable_time_local(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_protocol(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); static ngx_stream_variable_t ngx_stream_core_variables[] = { @@ -51,15 +61,30 @@ static ngx_stream_variable_t ngx_stream_core_variables[] = { { ngx_string("remote_port"), NULL, ngx_stream_variable_remote_port, 0, 0, 0 }, + { ngx_string("proxy_protocol_addr"), NULL, + ngx_stream_variable_proxy_protocol_addr, 0, 0, 0 }, + + { ngx_string("proxy_protocol_port"), NULL, + ngx_stream_variable_proxy_protocol_port, 0, 0, 0 }, + { ngx_string("server_addr"), NULL, ngx_stream_variable_server_addr, 0, 0, 0 }, { ngx_string("server_port"), NULL, ngx_stream_variable_server_port, 0, 0, 0 }, - { ngx_string("bytes_sent"), NULL, ngx_stream_variable_bytes_sent, + { ngx_string("bytes_sent"), NULL, ngx_stream_variable_bytes, 0, 0, 0 }, + { ngx_string("bytes_received"), NULL, ngx_stream_variable_bytes, + 1, 0, 0 }, + + { ngx_string("session_time"), NULL, ngx_stream_variable_session_time, + 0, NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + { ngx_string("status"), NULL, ngx_stream_variable_status, + 0, NGX_STREAM_VAR_NOCACHEABLE, 0 }, + { ngx_string("connection"), NULL, ngx_stream_variable_connection, 0, 0, 0 }, @@ -81,6 +106,9 @@ static ngx_stream_variable_t ngx_stream_core_variables[] = { { ngx_string("time_local"), NULL, ngx_stream_variable_time_local, 0, NGX_STREAM_VAR_NOCACHEABLE, 0 }, + { ngx_string("protocol"), NULL, + ngx_stream_variable_protocol, 0, 0, 0 }, + { ngx_null_string, NULL, NULL, 0, 0, 0 } }; @@ -399,6 +427,46 @@ ngx_stream_variable_remote_port(ngx_stream_session_t *s, } +static ngx_int_t +ngx_stream_variable_proxy_protocol_addr(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + v->len = s->connection->proxy_protocol_addr.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = s->connection->proxy_protocol_addr.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_proxy_protocol_port(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_uint_t port; + + v->len = 0; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + v->data = ngx_pnalloc(s->connection->pool, sizeof("65535") - 1); + if (v->data == NULL) { + return NGX_ERROR; + } + + port = s->connection->proxy_protocol_port; + + if (port > 0 && port < 65536) { + v->len = ngx_sprintf(v->data, "%ui", port) - v->data; + } + + return NGX_OK; +} + + static ngx_int_t ngx_stream_variable_server_addr(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data) @@ -461,7 +529,7 @@ ngx_stream_variable_server_port(ngx_stream_session_t *s, static ngx_int_t -ngx_stream_variable_bytes_sent(ngx_stream_session_t *s, +ngx_stream_variable_bytes(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data) { u_char *p; @@ -471,7 +539,13 @@ ngx_stream_variable_bytes_sent(ngx_stream_session_t *s, return NGX_ERROR; } - v->len = ngx_sprintf(p, "%O", s->connection->sent) - p; + if (data == 1) { + v->len = ngx_sprintf(p, "%O", s->received) - p; + + } else { + v->len = ngx_sprintf(p, "%O", s->connection->sent) - p; + } + v->valid = 1; v->no_cacheable = 0; v->not_found = 0; @@ -481,6 +555,53 @@ ngx_stream_variable_bytes_sent(ngx_stream_session_t *s, } +static ngx_int_t +ngx_stream_variable_session_time(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + u_char *p; + ngx_time_t *tp; + ngx_msec_int_t ms; + + p = ngx_pnalloc(s->connection->pool, NGX_TIME_T_LEN + 4); + if (p == NULL) { + return NGX_ERROR; + } + + tp = ngx_timeofday(); + + ms = (ngx_msec_int_t) + ((tp->sec - s->start_sec) * 1000 + (tp->msec - s->start_msec)); + 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; +} + + +static ngx_int_t +ngx_stream_variable_status(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + v->data = ngx_pnalloc(s->connection->pool, NGX_INT_T_LEN); + if (v->data == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(v->data, "%03ui", s->status) - v->data; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; +} + + static ngx_int_t ngx_stream_variable_connection(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data) @@ -622,6 +743,20 @@ ngx_stream_variable_time_local(ngx_stream_session_t *s, } +static ngx_int_t +ngx_stream_variable_protocol(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + v->len = 3; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) (s->connection->type == SOCK_DGRAM ? "UDP" : "TCP"); + + return NGX_OK; +} + + void * ngx_stream_map_find(ngx_stream_session_t *s, ngx_stream_map_t *map, ngx_str_t *match) From 646704e9fbeace3af6be908c8148c045ad6eca25 Mon Sep 17 00:00:00 2001 From: Christos Trochalakis Date: Wed, 12 Oct 2016 08:07:21 +0300 Subject: [PATCH 003/475] New upstream version 1.11.5 --- CHANGES | 39 ++ CHANGES.ru | 39 ++ auto/lib/geoip/conf | 20 +- auto/lib/perl/conf | 4 +- auto/lib/perl/make | 1 + auto/make | 1 + auto/modules | 28 +- auto/options | 18 +- auto/os/win32 | 4 +- auto/sources | 1 + auto/unix | 26 +- src/core/nginx.h | 4 +- src/core/ngx_buf.h | 12 +- src/core/ngx_conf_file.c | 4 +- src/core/ngx_config.h | 13 + src/core/ngx_connection.c | 5 +- src/core/ngx_connection.h | 14 +- src/core/ngx_core.h | 31 +- src/core/ngx_file.c | 8 +- src/core/ngx_file.h | 8 +- src/core/ngx_inet.c | 19 +- src/core/ngx_module.h | 37 +- src/core/ngx_resolver.c | 1 + src/core/ngx_resolver.h | 1 + src/event/ngx_event.h | 3 +- src/event/ngx_event_accept.c | 1 + src/event/ngx_event_connect.c | 1 + src/event/ngx_event_connect.h | 13 +- src/event/ngx_event_openssl.c | 10 +- src/event/ngx_event_openssl.h | 8 +- src/event/ngx_event_pipe.h | 2 +- .../modules/ngx_http_addition_filter_module.c | 1 + src/http/modules/ngx_http_mp4_module.c | 2 +- .../modules/ngx_http_range_filter_module.c | 3 +- src/http/modules/ngx_http_realip_module.c | 12 +- .../modules/ngx_http_upstream_hash_module.c | 11 +- .../ngx_http_upstream_ip_hash_module.c | 5 + .../ngx_http_upstream_least_conn_module.c | 16 +- src/http/modules/perl/Makefile.PL | 2 + src/http/ngx_http.c | 2 +- src/http/ngx_http.h | 3 - src/http/ngx_http_cache.h | 11 +- src/http/ngx_http_core_module.c | 4 +- src/http/ngx_http_core_module.h | 22 +- src/http/ngx_http_file_cache.c | 103 +++- src/http/ngx_http_parse.c | 4 +- src/http/ngx_http_request.h | 14 +- src/http/ngx_http_special_response.c | 2 +- src/http/ngx_http_upstream.c | 26 +- src/http/ngx_http_upstream.h | 18 +- src/http/ngx_http_upstream_round_robin.c | 22 +- src/http/ngx_http_upstream_round_robin.h | 15 +- src/http/v2/ngx_http_v2.c | 2 +- src/mail/ngx_mail.c | 2 +- src/mail/ngx_mail.h | 6 +- src/mail/ngx_mail_core_module.c | 2 +- src/mail/ngx_mail_ssl_module.c | 4 +- src/os/unix/ngx_darwin_init.c | 1 + src/os/unix/ngx_freebsd_init.c | 1 + src/os/unix/ngx_linux_init.c | 1 + src/os/unix/ngx_os.h | 3 + src/os/unix/ngx_posix_init.c | 1 + src/os/unix/ngx_process_cycle.c | 6 +- src/os/unix/ngx_solaris_init.c | 1 + src/os/unix/ngx_udp_sendmsg_chain.c | 245 ++++++++++ src/stream/ngx_stream.c | 124 ++++- src/stream/ngx_stream.h | 86 +++- src/stream/ngx_stream_access_module.c | 11 +- src/stream/ngx_stream_core_module.c | 232 ++++++++- src/stream/ngx_stream_handler.c | 204 ++------ src/stream/ngx_stream_limit_conn_module.c | 12 +- src/stream/ngx_stream_log_module.c | 8 +- src/stream/ngx_stream_proxy_module.c | 218 ++++++--- src/stream/ngx_stream_realip_module.c | 8 +- src/stream/ngx_stream_return_module.c | 55 ++- src/stream/ngx_stream_ssl_module.c | 108 ++++- src/stream/ngx_stream_ssl_preread_module.c | 449 ++++++++++++++++++ src/stream/ngx_stream_upstream.c | 24 +- src/stream/ngx_stream_upstream.h | 19 +- src/stream/ngx_stream_upstream_hash_module.c | 11 +- .../ngx_stream_upstream_least_conn_module.c | 16 +- src/stream/ngx_stream_upstream_round_robin.c | 22 +- src/stream/ngx_stream_upstream_round_robin.h | 15 +- src/stream/ngx_stream_write_filter_module.c | 273 +++++++++++ 84 files changed, 2297 insertions(+), 547 deletions(-) create mode 100644 src/os/unix/ngx_udp_sendmsg_chain.c create mode 100644 src/stream/ngx_stream_ssl_preread_module.c create mode 100644 src/stream/ngx_stream_write_filter_module.c diff --git a/CHANGES b/CHANGES index 2ea8d8d..6a88ce4 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,43 @@ +Changes with nginx 1.11.5 11 Oct 2016 + + *) Change: the --with-ipv6 configure option was removed, now IPv6 + support is configured automatically. + + *) Change: now if there are no available servers in an upstream, nginx + will not reset number of failures of all servers as it previously + did, but will wait for fail_timeout to expire. + + *) Feature: the ngx_stream_ssl_preread_module. + + *) Feature: the "server" directive in the "upstream" context supports + the "max_conns" parameter. + + *) Feature: the --with-compat configure option. + + *) Feature: "manager_files", "manager_threshold", and "manager_sleep" + parameters of the "proxy_cache_path", "fastcgi_cache_path", + "scgi_cache_path", and "uwsgi_cache_path" directives. + + *) Bugfix: flags passed by the --with-ld-opt configure option were not + used while building perl module. + + *) Bugfix: in the "add_after_body" directive when used with the + "sub_filter" directive. + + *) Bugfix: in the $realip_remote_addr variable. + + *) Bugfix: the "dav_access", "proxy_store_access", + "fastcgi_store_access", "scgi_store_access", and "uwsgi_store_access" + directives ignored permissions specified for user. + + *) Bugfix: unix domain listen sockets might not be inherited during + binary upgrade on Linux. + + *) Bugfix: nginx returned the 400 response on requests with the "-" + character in the HTTP method. + + Changes with nginx 1.11.4 13 Sep 2016 *) Feature: the $upstream_bytes_received variable. diff --git a/CHANGES.ru b/CHANGES.ru index 28ac2d8..f0bdaf5 100644 --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,43 @@ +Изменения в nginx 1.11.5 11.10.2016 + + *) Изменение: параметр configure --with-ipv6 упразднён, поддержка IPv6 + теперь собирается автоматически. + + *) Изменение: теперь, если в блоке upstream не оказалось доступных + серверов, nginx не сбрасывает статистику ошибок всех серверов, как + делал ранее, а ожидает истечения fail_timeout. + + *) Добавление: модуль ngx_stream_ssl_preread_module. + + *) Добавление: директива server в блоке upstream поддерживает параметр + max_conns. + + *) Добавление: параметр configure --with-compat. + + *) Добавление: параметры manager_files, manager_threshold и + manager_sleep директив proxy_cache_path, fastcgi_cache_path, + scgi_cache_path и uwsgi_cache_path. + + *) Исправление: при сборке perl-модуля не использовались флаги, заданные + с помощью параметра configure --with-ld-opt. + + *) Исправление: в директиве add_after_body при использовании совместно с + директивой sub_filter. + + *) Исправление: в переменной $realip_remote_addr. + + *) Исправление: директивы dav_access, proxy_store_access, + fastcgi_store_access, scgi_store_access и uwsgi_store_access + игнорировали права, заданные для пользователя. + + *) Исправление: unix domain listen-сокеты могли не наследоваться при + обновлении исполняемого файла на Linux. + + *) Исправление: nginx возвращал ошибку 400 на запросы с символом "-" в + HTTP-методе. + + Изменения в nginx 1.11.4 13.09.2016 *) Добавление: переменная $upstream_bytes_received. diff --git a/auto/lib/geoip/conf b/auto/lib/geoip/conf index ebd2e15..8302aae 100644 --- a/auto/lib/geoip/conf +++ b/auto/lib/geoip/conf @@ -74,17 +74,15 @@ if [ $ngx_found = yes ]; then NGX_LIB_GEOIP=$ngx_feature_libs - if [ $NGX_IPV6 = YES ]; then - ngx_feature="GeoIP IPv6 support" - ngx_feature_name="NGX_HAVE_GEOIP_V6" - ngx_feature_run=no - ngx_feature_incs="#include - #include " - #ngx_feature_path= - #ngx_feature_libs= - ngx_feature_test="printf(\"%d\", GEOIP_CITY_EDITION_REV0_V6);" - . auto/feature - fi + ngx_feature="GeoIP IPv6 support" + ngx_feature_name="NGX_HAVE_GEOIP_V6" + ngx_feature_run=no + ngx_feature_incs="#include + #include " + #ngx_feature_path= + #ngx_feature_libs= + ngx_feature_test="printf(\"%d\", GEOIP_CITY_EDITION_REV0_V6);" + . auto/feature else diff --git a/auto/lib/perl/conf b/auto/lib/perl/conf index f5f5d3e..d891d82 100644 --- a/auto/lib/perl/conf +++ b/auto/lib/perl/conf @@ -28,8 +28,10 @@ if test -n "$NGX_PERL_VER"; then exit 1; fi - NGX_PERL_CFLAGS="$CFLAGS `$NGX_PERL -MExtUtils::Embed -e ccopts`" NGX_PM_CFLAGS=`$NGX_PERL -MExtUtils::Embed -e ccopts` + NGX_PM_LDFLAGS=`$NGX_PERL -MConfig -e 'print $Config{lddlflags}'` + + NGX_PERL_CFLAGS="$CFLAGS `$NGX_PERL -MExtUtils::Embed -e ccopts`" # gcc 4.1/4.2 warn about unused values in pTHX_ NGX_PERL_CFLAGS=`echo $NGX_PERL_CFLAGS \ diff --git a/auto/lib/perl/make b/auto/lib/perl/make index 8af8902..350090c 100644 --- a/auto/lib/perl/make +++ b/auto/lib/perl/make @@ -35,6 +35,7 @@ $NGX_OBJS/src/http/modules/perl/Makefile: \\ cd $NGX_OBJS/src/http/modules/perl \\ && NGX_PM_CFLAGS="\$(NGX_PM_CFLAGS) -g $NGX_CC_OPT" \\ + NGX_PM_LDFLAGS="$NGX_LD_OPT \$(NGX_PM_LDFLAGS)" \\ NGX_INCS="$CORE_INCS $NGX_OBJS $HTTP_INCS" \\ NGX_DEPS="\$(CORE_DEPS) \$(HTTP_DEPS)" \\ $NGX_PERL Makefile.PL \\ diff --git a/auto/make b/auto/make index 5589bee..84d2668 100644 --- a/auto/make +++ b/auto/make @@ -31,6 +31,7 @@ END if test -n "$NGX_PERL_CFLAGS"; then echo NGX_PERL_CFLAGS = $NGX_PERL_CFLAGS >> $NGX_MAKEFILE echo NGX_PM_CFLAGS = $NGX_PM_CFLAGS >> $NGX_MAKEFILE + echo NGX_PM_LDFLAGS = $NGX_PM_LDFLAGS >> $NGX_MAKEFILE fi diff --git a/auto/modules b/auto/modules index 433767a..89377bf 100644 --- a/auto/modules +++ b/auto/modules @@ -973,7 +973,8 @@ if [ $STREAM != NO ]; then ngx_stream_core_module \ ngx_stream_log_module \ ngx_stream_proxy_module \ - ngx_stream_upstream_module" + ngx_stream_upstream_module \ + ngx_stream_write_filter_module" ngx_module_incs="src/stream" ngx_module_deps="src/stream/ngx_stream.h \ src/stream/ngx_stream_variables.h \ @@ -988,7 +989,8 @@ if [ $STREAM != NO ]; then src/stream/ngx_stream_log_module.c \ src/stream/ngx_stream_proxy_module.c \ src/stream/ngx_stream_upstream.c \ - src/stream/ngx_stream_upstream_round_robin.c" + src/stream/ngx_stream_upstream_round_robin.c \ + src/stream/ngx_stream_write_filter_module.c" . auto/module @@ -1118,6 +1120,16 @@ if [ $STREAM != NO ]; then . auto/module fi + + if [ $STREAM_SSL_PREREAD = YES ]; then + ngx_module_name=ngx_stream_ssl_preread_module + ngx_module_deps= + ngx_module_srcs=src/stream/ngx_stream_ssl_preread_module.c + ngx_module_libs= + ngx_module_link=$STREAM_SSL_PREREAD + + . auto/module + fi fi @@ -1300,6 +1312,18 @@ fi modules="$modules $MISC_MODULES" +if [ $NGX_COMPAT = YES ]; then + have=NGX_COMPAT . auto/have + have=NGX_HTTP_GZIP . auto/have + have=NGX_HTTP_DAV . auto/have + have=NGX_HTTP_REALIP . auto/have + have=NGX_HTTP_X_FORWARDED_FOR . auto/have + have=NGX_HTTP_HEADERS . auto/have + have=NGX_HTTP_UPSTREAM_ZONE . auto/have + have=NGX_STREAM_UPSTREAM_ZONE . auto/have +fi + + cat << END > $NGX_MODULES_C #include diff --git a/auto/options b/auto/options index 73149d9..43724b1 100644 --- a/auto/options +++ b/auto/options @@ -44,7 +44,6 @@ EVENT_POLL=NO USE_THREADS=NO NGX_FILE_AIO=NO -NGX_IPV6=NO HTTP=YES @@ -126,6 +125,7 @@ STREAM_RETURN=YES STREAM_UPSTREAM_HASH=YES STREAM_UPSTREAM_LEAST_CONN=YES STREAM_UPSTREAM_ZONE=YES +STREAM_SSL_PREREAD=NO DYNAMIC_MODULES= @@ -133,6 +133,8 @@ NGX_ADDONS= NGX_ADDON_DEPS= DYNAMIC_ADDONS= +NGX_COMPAT=NO + USE_PCRE=NO PCRE=NONE PCRE_OPT= @@ -201,7 +203,11 @@ do --with-threads) USE_THREADS=YES ;; --with-file-aio) NGX_FILE_AIO=YES ;; - --with-ipv6) NGX_IPV6=YES ;; + + --with-ipv6) + NGX_POST_CONF_MSG="$NGX_POST_CONF_MSG +$0: warning: the \"--with-ipv6\" option is deprecated" + ;; --without-http) HTTP=NO ;; --without-http-cache) HTTP_CACHE=NO ;; @@ -301,6 +307,8 @@ use the \"--with-mail_ssl_module\" option instead" --with-stream_geoip_module) STREAM_GEOIP=YES ;; --with-stream_geoip_module=dynamic) STREAM_GEOIP=DYNAMIC ;; + --with-stream_ssl_preread_module) + STREAM_SSL_PREREAD=YES ;; --without-stream_limit_conn_module) STREAM_LIMIT_CONN=NO ;; --without-stream_access_module) STREAM_ACCESS=NO ;; @@ -322,6 +330,8 @@ use the \"--with-mail_ssl_module\" option instead" --add-module=*) NGX_ADDONS="$NGX_ADDONS $value" ;; --add-dynamic-module=*) DYNAMIC_ADDONS="$DYNAMIC_ADDONS $value" ;; + --with-compat) NGX_COMPAT=YES ;; + --with-cc=*) CC="$value" ;; --with-cpp=*) CPP="$value" ;; --with-cc-opt=*) NGX_CC_OPT="$value" ;; @@ -417,7 +427,6 @@ cat << END --with-threads enable thread pool support --with-file-aio enable file AIO support - --with-ipv6 enable IPv6 support --with-http_ssl_module enable ngx_http_ssl_module --with-http_v2_module enable ngx_http_v2_module @@ -508,6 +517,7 @@ cat << END --with-stream_realip_module enable ngx_stream_realip_module --with-stream_geoip_module enable ngx_stream_geoip_module --with-stream_geoip_module=dynamic enable dynamic ngx_stream_geoip_module + --with-stream_ssl_preread_module enable ngx_stream_ssl_preread_module --without-stream_limit_conn_module disable ngx_stream_limit_conn_module --without-stream_access_module disable ngx_stream_access_module --without-stream_geo_module disable ngx_stream_geo_module @@ -528,6 +538,8 @@ cat << END --add-module=PATH enable external module --add-dynamic-module=PATH enable dynamic external module + --with-compat dynamic modules compatibility + --with-cc=PATH set C compiler pathname --with-cpp=PATH set C preprocessor pathname --with-cc-opt=OPTIONS set additional C compiler options diff --git a/auto/os/win32 b/auto/os/win32 index 14ae3b8..650cf49 100644 --- a/auto/os/win32 +++ b/auto/os/win32 @@ -37,8 +37,6 @@ if [ $EVENT_SELECT = NO ]; then EVENT_MODULES="$EVENT_MODULES $SELECT_MODULE" fi -if [ $NGX_IPV6 = YES ]; then - have=NGX_HAVE_INET6 . auto/have -fi +have=NGX_HAVE_INET6 . auto/have have=NGX_HAVE_IOCP . auto/have diff --git a/auto/sources b/auto/sources index 216e900..1398147 100644 --- a/auto/sources +++ b/auto/sources @@ -167,6 +167,7 @@ UNIX_SRCS="$CORE_SRCS $EVENT_SRCS \ src/os/unix/ngx_send.c \ src/os/unix/ngx_writev_chain.c \ src/os/unix/ngx_udp_send.c \ + src/os/unix/ngx_udp_sendmsg_chain.c \ src/os/unix/ngx_channel.c \ src/os/unix/ngx_shmem.c \ src/os/unix/ngx_process.c \ diff --git a/auto/unix b/auto/unix index dbc0f0e..5ef74d4 100755 --- a/auto/unix +++ b/auto/unix @@ -637,20 +637,18 @@ ngx_param=NGX_MAX_TIME_T_VALUE; ngx_value=$ngx_max_value; . auto/types/value # syscalls, libc calls and some features -if [ $NGX_IPV6 = YES ]; then - ngx_feature="AF_INET6" - ngx_feature_name="NGX_HAVE_INET6" - ngx_feature_run=no - ngx_feature_incs="#include - #include - #include " - ngx_feature_path= - ngx_feature_libs= - ngx_feature_test="struct sockaddr_in6 sin6; - sin6.sin6_family = AF_INET6; - (void) sin6" - . auto/feature -fi +ngx_feature="AF_INET6" +ngx_feature_name="NGX_HAVE_INET6" +ngx_feature_run=no +ngx_feature_incs="#include + #include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="struct sockaddr_in6 sin6; + sin6.sin6_family = AF_INET6; + (void) sin6" +. auto/feature ngx_feature="setproctitle()" diff --git a/src/core/nginx.h b/src/core/nginx.h index 7e9aae6..1446251 100644 --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1011004 -#define NGINX_VERSION "1.11.4" +#define nginx_version 1011005 +#define NGINX_VERSION "1.11.5" #define NGINX_VER "nginx/" NGINX_VERSION #ifdef NGX_BUILD diff --git a/src/core/ngx_buf.h b/src/core/ngx_buf.h index f652b20..12781a7 100644 --- a/src/core/ngx_buf.h +++ b/src/core/ngx_buf.h @@ -72,10 +72,8 @@ typedef struct ngx_output_chain_ctx_s ngx_output_chain_ctx_t; typedef ngx_int_t (*ngx_output_chain_filter_pt)(void *ctx, ngx_chain_t *in); -#if (NGX_HAVE_FILE_AIO) typedef void (*ngx_output_chain_aio_pt)(ngx_output_chain_ctx_t *ctx, ngx_file_t *file); -#endif struct ngx_output_chain_ctx_s { ngx_buf_t *buf; @@ -85,23 +83,19 @@ struct ngx_output_chain_ctx_s { unsigned sendfile:1; unsigned directio:1; -#if (NGX_HAVE_ALIGNED_DIRECTIO) unsigned unaligned:1; -#endif unsigned need_in_memory:1; unsigned need_in_temp:1; -#if (NGX_HAVE_FILE_AIO || NGX_THREADS) unsigned aio:1; -#endif -#if (NGX_HAVE_FILE_AIO) +#if (NGX_HAVE_FILE_AIO || NGX_COMPAT) ngx_output_chain_aio_pt aio_handler; -#if (NGX_HAVE_AIO_SENDFILE) +#if (NGX_HAVE_AIO_SENDFILE || NGX_COMPAT) ssize_t (*aio_preload)(ngx_buf_t *file); #endif #endif -#if (NGX_THREADS) +#if (NGX_THREADS || NGX_COMPAT) ngx_int_t (*thread_handler)(ngx_thread_task_t *task, ngx_file_t *file); ngx_thread_task_t *thread_task; diff --git a/src/core/ngx_conf_file.c b/src/core/ngx_conf_file.c index c60d5fb..ea1dceb 100644 --- a/src/core/ngx_conf_file.c +++ b/src/core/ngx_conf_file.c @@ -1336,7 +1336,7 @@ ngx_conf_set_enum_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_OK; } - ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid value \"%s\"", value[1].data); return NGX_CONF_ERROR; @@ -1378,7 +1378,7 @@ ngx_conf_set_bitmask_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } if (mask[m].name.len == 0) { - ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid value \"%s\"", value[i].data); return NGX_CONF_ERROR; diff --git a/src/core/ngx_config.h b/src/core/ngx_config.h index a0bfa63..1861be6 100644 --- a/src/core/ngx_config.h +++ b/src/core/ngx_config.h @@ -129,4 +129,17 @@ typedef intptr_t ngx_flag_t; #define NGX_MAX_INT32_VALUE (uint32_t) 0x7fffffff +#if (NGX_COMPAT) + +#define NGX_COMPAT_BEGIN(slots) uint64_t spare[slots]; +#define NGX_COMPAT_END + +#else + +#define NGX_COMPAT_BEGIN(slots) +#define NGX_COMPAT_END + +#endif + + #endif /* _NGX_CONFIG_H_INCLUDED_ */ diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c index 16ba630..a789d8c 100644 --- a/src/core/ngx_connection.c +++ b/src/core/ngx_connection.c @@ -1191,10 +1191,7 @@ ngx_close_connection(ngx_connection_t *c) level = NGX_LOG_CRIT; } - /* we use ngx_cycle->log because c->log was in c->pool */ - - ngx_log_error(level, ngx_cycle->log, err, - ngx_close_socket_n " %d failed", fd); + ngx_log_error(level, c->log, err, ngx_close_socket_n " %d failed", fd); } } diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h index e484c81..1d3e3a3 100644 --- a/src/core/ngx_connection.h +++ b/src/core/ngx_connection.h @@ -66,23 +66,19 @@ struct ngx_listening_s { unsigned addr_ntop:1; unsigned wildcard:1; -#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) +#if (NGX_HAVE_INET6) unsigned ipv6only:1; #endif -#if (NGX_HAVE_REUSEPORT) unsigned reuseport:1; unsigned add_reuseport:1; -#endif unsigned keepalive:2; -#if (NGX_HAVE_DEFERRED_ACCEPT) unsigned deferred_accept:1; unsigned delete_deferred:1; unsigned add_deferred:1; -#ifdef SO_ACCEPTFILTER +#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) char *accept_filter; #endif -#endif #if (NGX_HAVE_SETFIB) int setfib; #endif @@ -151,7 +147,7 @@ struct ngx_connection_s { ngx_str_t proxy_protocol_addr; in_port_t proxy_protocol_port; -#if (NGX_SSL) +#if (NGX_SSL || NGX_COMPAT) ngx_ssl_connection_t *ssl; #endif @@ -186,11 +182,11 @@ struct ngx_connection_s { unsigned need_last_buf:1; -#if (NGX_HAVE_AIO_SENDFILE) +#if (NGX_HAVE_AIO_SENDFILE || NGX_COMPAT) unsigned busy_count:2; #endif -#if (NGX_THREADS) +#if (NGX_THREADS || NGX_COMPAT) ngx_thread_task_t *sendfile_task; #endif }; diff --git a/src/core/ngx_core.h b/src/core/ngx_core.h index 2819c1a..2069373 100644 --- a/src/core/ngx_core.h +++ b/src/core/ngx_core.h @@ -12,22 +12,21 @@ #include -typedef struct ngx_module_s ngx_module_t; -typedef struct ngx_conf_s ngx_conf_t; -typedef struct ngx_cycle_s ngx_cycle_t; -typedef struct ngx_pool_s ngx_pool_t; -typedef struct ngx_chain_s ngx_chain_t; -typedef struct ngx_log_s ngx_log_t; -typedef struct ngx_open_file_s ngx_open_file_t; -typedef struct ngx_command_s ngx_command_t; -typedef struct ngx_file_s ngx_file_t; -typedef struct ngx_event_s ngx_event_t; -typedef struct ngx_event_aio_s ngx_event_aio_t; -typedef struct ngx_connection_s ngx_connection_t; - -#if (NGX_THREADS) -typedef struct ngx_thread_task_s ngx_thread_task_t; -#endif +typedef struct ngx_module_s ngx_module_t; +typedef struct ngx_conf_s ngx_conf_t; +typedef struct ngx_cycle_s ngx_cycle_t; +typedef struct ngx_pool_s ngx_pool_t; +typedef struct ngx_chain_s ngx_chain_t; +typedef struct ngx_log_s ngx_log_t; +typedef struct ngx_open_file_s ngx_open_file_t; +typedef struct ngx_command_s ngx_command_t; +typedef struct ngx_file_s ngx_file_t; +typedef struct ngx_event_s ngx_event_t; +typedef struct ngx_event_aio_s ngx_event_aio_t; +typedef struct ngx_connection_s ngx_connection_t; +typedef struct ngx_thread_task_s ngx_thread_task_t; +typedef struct ngx_ssl_s ngx_ssl_t; +typedef struct ngx_ssl_connection_s ngx_ssl_connection_t; typedef void (*ngx_event_handler_pt)(ngx_event_t *ev); typedef void (*ngx_connection_handler_pt)(ngx_connection_t *c); diff --git a/src/core/ngx_file.c b/src/core/ngx_file.c index c1137cc..8359c0f 100644 --- a/src/core/ngx_file.c +++ b/src/core/ngx_file.c @@ -441,7 +441,7 @@ ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) u_char *p; ngx_str_t *value; - ngx_uint_t i, right, shift, *access; + ngx_uint_t i, right, shift, *access, user; access = (ngx_uint_t *) (confp + cmd->offset); @@ -451,7 +451,8 @@ ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) value = cf->args->elts; - *access = 0600; + *access = 0; + user = 0600; for (i = 1; i < cf->args->nelts; i++) { @@ -460,6 +461,7 @@ ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (ngx_strncmp(p, "user:", sizeof("user:") - 1) == 0) { shift = 6; p += sizeof("user:") - 1; + user = 0; } else if (ngx_strncmp(p, "group:", sizeof("group:") - 1) == 0) { shift = 3; @@ -486,6 +488,8 @@ ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) *access |= right << shift; } + *access |= user; + return NGX_CONF_OK; invalid: diff --git a/src/core/ngx_file.h b/src/core/ngx_file.h index a723c3d..320adc2 100644 --- a/src/core/ngx_file.h +++ b/src/core/ngx_file.h @@ -23,14 +23,14 @@ struct ngx_file_s { ngx_log_t *log; -#if (NGX_THREADS) +#if (NGX_THREADS || NGX_COMPAT) ngx_int_t (*thread_handler)(ngx_thread_task_t *task, ngx_file_t *file); void *thread_ctx; ngx_thread_task_t *thread_task; #endif -#if (NGX_HAVE_FILE_AIO) +#if (NGX_HAVE_FILE_AIO || NGX_COMPAT) ngx_event_aio_t *aio; #endif @@ -42,7 +42,8 @@ struct ngx_file_s { #define NGX_MAX_PATH_LEVEL 3 -typedef time_t (*ngx_path_manager_pt) (void *data); +typedef ngx_msec_t (*ngx_path_manager_pt) (void *data); +typedef ngx_msec_t (*ngx_path_purger_pt) (void *data); typedef void (*ngx_path_loader_pt) (void *data); @@ -52,6 +53,7 @@ typedef struct { size_t level[NGX_MAX_PATH_LEVEL]; ngx_path_manager_pt manager; + ngx_path_purger_pt purger; ngx_path_loader_pt loader; void *data; diff --git a/src/core/ngx_inet.c b/src/core/ngx_inet.c index dbd1f46..3bcd3e7 100644 --- a/src/core/ngx_inet.c +++ b/src/core/ngx_inet.c @@ -1364,6 +1364,7 @@ ngx_cmp_sockaddr(struct sockaddr *sa1, socklen_t slen1, struct sockaddr_in6 *sin61, *sin62; #endif #if (NGX_HAVE_UNIX_DOMAIN) + size_t len; struct sockaddr_un *saun1, *saun2; #endif @@ -1393,15 +1394,21 @@ ngx_cmp_sockaddr(struct sockaddr *sa1, socklen_t slen1, #if (NGX_HAVE_UNIX_DOMAIN) case AF_UNIX: - /* TODO length */ - saun1 = (struct sockaddr_un *) sa1; saun2 = (struct sockaddr_un *) sa2; - if (ngx_memcmp(&saun1->sun_path, &saun2->sun_path, - sizeof(saun1->sun_path)) - != 0) - { + if (slen1 < slen2) { + len = slen1 - offsetof(struct sockaddr_un, sun_path); + + } else { + len = slen2 - offsetof(struct sockaddr_un, sun_path); + } + + if (len > sizeof(saun1->sun_path)) { + len = sizeof(saun1->sun_path); + } + + if (ngx_memcmp(&saun1->sun_path, &saun2->sun_path, len) != 0) { return NGX_DECLINED; } diff --git a/src/core/ngx_module.h b/src/core/ngx_module.h index a1a0d6c..8cf3210 100644 --- a/src/core/ngx_module.h +++ b/src/core/ngx_module.h @@ -35,13 +35,13 @@ #define NGX_MODULE_SIGNATURE_2 "0" #endif -#if (NGX_HAVE_FILE_AIO) +#if (NGX_HAVE_FILE_AIO || NGX_COMPAT) #define NGX_MODULE_SIGNATURE_3 "1" #else #define NGX_MODULE_SIGNATURE_3 "0" #endif -#if (NGX_HAVE_AIO_SENDFILE) +#if (NGX_HAVE_AIO_SENDFILE || NGX_COMPAT) #define NGX_MODULE_SIGNATURE_4 "1" #else #define NGX_MODULE_SIGNATURE_4 "0" @@ -71,17 +71,8 @@ #define NGX_MODULE_SIGNATURE_8 "0" #endif -#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) #define NGX_MODULE_SIGNATURE_9 "1" -#else -#define NGX_MODULE_SIGNATURE_9 "0" -#endif - -#if (NGX_HAVE_REUSEPORT) #define NGX_MODULE_SIGNATURE_10 "1" -#else -#define NGX_MODULE_SIGNATURE_10 "0" -#endif #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) #define NGX_MODULE_SIGNATURE_11 "1" @@ -89,11 +80,7 @@ #define NGX_MODULE_SIGNATURE_11 "0" #endif -#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) #define NGX_MODULE_SIGNATURE_12 "1" -#else -#define NGX_MODULE_SIGNATURE_12 "0" -#endif #if (NGX_HAVE_SETFIB) #define NGX_MODULE_SIGNATURE_13 "1" @@ -140,7 +127,7 @@ #define NGX_MODULE_SIGNATURE_21 "0" #endif -#if (NGX_THREADS) +#if (NGX_THREADS || NGX_COMPAT) #define NGX_MODULE_SIGNATURE_22 "1" #else #define NGX_MODULE_SIGNATURE_22 "0" @@ -152,17 +139,13 @@ #define NGX_MODULE_SIGNATURE_23 "0" #endif -#if (NGX_HTTP_SSL) +#if (NGX_HTTP_SSL || NGX_COMPAT) #define NGX_MODULE_SIGNATURE_24 "1" #else #define NGX_MODULE_SIGNATURE_24 "0" #endif -#if (NGX_HTTP_V2) #define NGX_MODULE_SIGNATURE_25 "1" -#else -#define NGX_MODULE_SIGNATURE_25 "0" -#endif #if (NGX_HTTP_GZIP) #define NGX_MODULE_SIGNATURE_26 "1" @@ -170,11 +153,7 @@ #define NGX_MODULE_SIGNATURE_26 "0" #endif -#if (NGX_HTTP_DEGRADATION) #define NGX_MODULE_SIGNATURE_27 "1" -#else -#define NGX_MODULE_SIGNATURE_27 "0" -#endif #if (NGX_HTTP_X_FORWARDED_FOR) #define NGX_MODULE_SIGNATURE_28 "1" @@ -212,6 +191,12 @@ #define NGX_MODULE_SIGNATURE_33 "0" #endif +#if (NGX_COMPAT) +#define NGX_MODULE_SIGNATURE_34 "1" +#else +#define NGX_MODULE_SIGNATURE_34 "0" +#endif + #define NGX_MODULE_SIGNATURE \ NGX_MODULE_SIGNATURE_0 NGX_MODULE_SIGNATURE_1 NGX_MODULE_SIGNATURE_2 \ NGX_MODULE_SIGNATURE_3 NGX_MODULE_SIGNATURE_4 NGX_MODULE_SIGNATURE_5 \ @@ -224,7 +209,7 @@ NGX_MODULE_SIGNATURE_24 NGX_MODULE_SIGNATURE_25 NGX_MODULE_SIGNATURE_26 \ NGX_MODULE_SIGNATURE_27 NGX_MODULE_SIGNATURE_28 NGX_MODULE_SIGNATURE_29 \ NGX_MODULE_SIGNATURE_30 NGX_MODULE_SIGNATURE_31 NGX_MODULE_SIGNATURE_32 \ - NGX_MODULE_SIGNATURE_33 + NGX_MODULE_SIGNATURE_33 NGX_MODULE_SIGNATURE_34 #define NGX_MODULE_V1 \ diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c index 53dae6b..bdfed88 100644 --- a/src/core/ngx_resolver.c +++ b/src/core/ngx_resolver.c @@ -3006,6 +3006,7 @@ ngx_resolver_srv_names_handler(ngx_resolver_ctx_t *cctx) ctx->count--; srv->ctx = NULL; + srv->state = cctx->state; if (cctx->naddrs) { diff --git a/src/core/ngx_resolver.h b/src/core/ngx_resolver.h index e36cfdc..a0d6fc3 100644 --- a/src/core/ngx_resolver.h +++ b/src/core/ngx_resolver.h @@ -82,6 +82,7 @@ typedef struct { u_short port; ngx_resolver_ctx_t *ctx; + ngx_int_t state; ngx_uint_t naddrs; ngx_addr_t *addrs; diff --git a/src/event/ngx_event.h b/src/event/ngx_event.h index 27139ee..053bd16 100644 --- a/src/event/ngx_event.h +++ b/src/event/ngx_event.h @@ -152,7 +152,7 @@ struct ngx_event_aio_s { ngx_event_handler_pt handler; ngx_file_t *file; -#if (NGX_HAVE_AIO_SENDFILE) +#if (NGX_HAVE_AIO_SENDFILE || NGX_COMPAT) ssize_t (*preload_handler)(ngx_buf_t *file); #endif @@ -430,6 +430,7 @@ extern ngx_os_io_t ngx_io; #define ngx_send ngx_io.send #define ngx_send_chain ngx_io.send_chain #define ngx_udp_send ngx_io.udp_send +#define ngx_udp_send_chain ngx_io.udp_send_chain #define NGX_EVENT_MODULE 0x544E5645 /* "EVNT" */ diff --git a/src/event/ngx_event_accept.c b/src/event/ngx_event_accept.c index 4445adc..1fce2e8 100644 --- a/src/event/ngx_event_accept.c +++ b/src/event/ngx_event_accept.c @@ -467,6 +467,7 @@ ngx_event_recvmsg(ngx_event_t *ev) *log = ls->log; c->send = ngx_udp_send; + c->send_chain = ngx_udp_send_chain; c->log = log; c->pool->log = log; diff --git a/src/event/ngx_event_connect.c b/src/event/ngx_event_connect.c index 06534ef..c5bb806 100644 --- a/src/event/ngx_event_connect.c +++ b/src/event/ngx_event_connect.c @@ -166,6 +166,7 @@ ngx_event_connect_peer(ngx_peer_connection_t *pc) } else { /* type == SOCK_DGRAM */ c->recv = ngx_udp_recv; c->send = ngx_send; + c->send_chain = ngx_udp_send_chain; } c->log_error = pc->log_error; diff --git a/src/event/ngx_event_connect.h b/src/event/ngx_event_connect.h index 10b72a1..72d21d7 100644 --- a/src/event/ngx_event_connect.h +++ b/src/event/ngx_event_connect.h @@ -25,13 +25,12 @@ typedef ngx_int_t (*ngx_event_get_peer_pt)(ngx_peer_connection_t *pc, void *data); typedef void (*ngx_event_free_peer_pt)(ngx_peer_connection_t *pc, void *data, ngx_uint_t state); -#if (NGX_SSL) - +typedef void (*ngx_event_notify_peer_pt)(ngx_peer_connection_t *pc, + void *data, ngx_uint_t type); typedef ngx_int_t (*ngx_event_set_peer_session_pt)(ngx_peer_connection_t *pc, void *data); typedef void (*ngx_event_save_peer_session_pt)(ngx_peer_connection_t *pc, void *data); -#endif struct ngx_peer_connection_s { @@ -46,9 +45,10 @@ struct ngx_peer_connection_s { ngx_event_get_peer_pt get; ngx_event_free_peer_pt free; + ngx_event_notify_peer_pt notify; void *data; -#if (NGX_SSL) +#if (NGX_SSL || NGX_COMPAT) ngx_event_set_peer_session_pt set_session; ngx_event_save_peer_session_pt save_session; #endif @@ -61,12 +61,13 @@ struct ngx_peer_connection_s { ngx_log_t *log; unsigned cached:1; -#if (NGX_HAVE_TRANSPARENT_PROXY) unsigned transparent:1; -#endif /* ngx_connection_log_error_e */ unsigned log_error:2; + + NGX_COMPAT_BEGIN(2) + NGX_COMPAT_END }; diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 1cbfdf2..68d02bf 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -55,7 +55,7 @@ static int ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn, HMAC_CTX *hctx, int enc); #endif -#if OPENSSL_VERSION_NUMBER < 0x10002002L +#ifndef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT static ngx_int_t ngx_ssl_check_name(ngx_str_t *name, ASN1_STRING *str); #endif @@ -3092,7 +3092,7 @@ ngx_ssl_check_host(ngx_connection_t *c, ngx_str_t *name) return NGX_ERROR; } -#if OPENSSL_VERSION_NUMBER >= 0x10002002L +#ifdef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT /* X509_check_host() is only available in OpenSSL 1.0.2+ */ @@ -3209,7 +3209,7 @@ found: } -#if OPENSSL_VERSION_NUMBER < 0x10002002L +#ifndef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT static ngx_int_t ngx_ssl_check_name(ngx_str_t *name, ASN1_STRING *pattern) @@ -3656,13 +3656,13 @@ ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) engine = ENGINE_by_id((char *) value[1].data); if (engine == NULL) { - ngx_ssl_error(NGX_LOG_WARN, cf->log, 0, + ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, "ENGINE_by_id(\"%V\") failed", &value[1]); return NGX_CONF_ERROR; } if (ENGINE_set_default(engine, ENGINE_METHOD_ALL) == 0) { - ngx_ssl_error(NGX_LOG_WARN, cf->log, 0, + ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, "ENGINE_set_default(\"%V\", ENGINE_METHOD_ALL) failed", &value[1]); diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index 3367d10..24b812f 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -54,14 +54,14 @@ #define ngx_ssl_conn_t SSL -typedef struct { +struct ngx_ssl_s { SSL_CTX *ctx; ngx_log_t *log; size_t buffer_size; -} ngx_ssl_t; +}; -typedef struct { +struct ngx_ssl_connection_s { ngx_ssl_conn_t *connection; SSL_CTX *session_ctx; @@ -80,7 +80,7 @@ typedef struct { unsigned no_wait_shutdown:1; unsigned no_send_shutdown:1; unsigned handshake_buffer_set:1; -} ngx_ssl_connection_t; +}; #define NGX_SSL_NO_SCACHE -2 diff --git a/src/event/ngx_event_pipe.h b/src/event/ngx_event_pipe.h index ef2e7a0..10a3340 100644 --- a/src/event/ngx_event_pipe.h +++ b/src/event/ngx_event_pipe.h @@ -47,7 +47,7 @@ struct ngx_event_pipe_s { ngx_event_pipe_output_filter_pt output_filter; void *output_ctx; -#if (NGX_THREADS) +#if (NGX_THREADS || NGX_COMPAT) ngx_int_t (*thread_handler)(ngx_thread_task_t *task, ngx_file_t *file); void *thread_ctx; diff --git a/src/http/modules/ngx_http_addition_filter_module.c b/src/http/modules/ngx_http_addition_filter_module.c index db4970b..2fad0e5 100644 --- a/src/http/modules/ngx_http_addition_filter_module.c +++ b/src/http/modules/ngx_http_addition_filter_module.c @@ -171,6 +171,7 @@ ngx_http_addition_body_filter(ngx_http_request_t *r, ngx_chain_t *in) for (cl = in; cl; cl = cl->next) { if (cl->buf->last_buf) { cl->buf->last_buf = 0; + cl->buf->last_in_chain = 1; cl->buf->sync = 1; last = 1; } diff --git a/src/http/modules/ngx_http_mp4_module.c b/src/http/modules/ngx_http_mp4_module.c index 16ef83c..2a68bae 100644 --- a/src/http/modules/ngx_http_mp4_module.c +++ b/src/http/modules/ngx_http_mp4_module.c @@ -1144,7 +1144,7 @@ ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) data = &mp4->mdat_data_buf; data->file = &mp4->file; data->in_file = 1; - data->last_buf = 1; + data->last_buf = (mp4->request == mp4->request->main) ? 1 : 0; data->last_in_chain = 1; data->file_last = mp4->offset + atom_data_size; diff --git a/src/http/modules/ngx_http_range_filter_module.c b/src/http/modules/ngx_http_range_filter_module.c index 57065e1..095ef06 100644 --- a/src/http/modules/ngx_http_range_filter_module.c +++ b/src/http/modules/ngx_http_range_filter_module.c @@ -750,7 +750,8 @@ ngx_http_range_singlepart_body(ngx_http_request_t *r, buf->last -= (size_t) (last - range->end); } - buf->last_buf = 1; + buf->last_buf = (r == r->main) ? 1 : 0; + buf->last_in_chain = 1; *ll = cl; cl->next = NULL; diff --git a/src/http/modules/ngx_http_realip_module.c b/src/http/modules/ngx_http_realip_module.c index dba3c52..5e3355c 100644 --- a/src/http/modules/ngx_http_realip_module.c +++ b/src/http/modules/ngx_http_realip_module.c @@ -141,18 +141,18 @@ ngx_http_realip_handler(ngx_http_request_t *r) ngx_http_realip_ctx_t *ctx; ngx_http_realip_loc_conf_t *rlcf; - ctx = ngx_http_get_module_ctx(r, ngx_http_realip_module); - - if (ctx) { - return NGX_DECLINED; - } - rlcf = ngx_http_get_module_loc_conf(r, ngx_http_realip_module); if (rlcf->from == NULL) { return NGX_DECLINED; } + ctx = ngx_http_realip_get_module_ctx(r); + + if (ctx) { + return NGX_DECLINED; + } + switch (rlcf->type) { case NGX_HTTP_REALIP_XREALIP: diff --git a/src/http/modules/ngx_http_upstream_hash_module.c b/src/http/modules/ngx_http_upstream_hash_module.c index 1e2e05c..6c28c64 100644 --- a/src/http/modules/ngx_http_upstream_hash_module.c +++ b/src/http/modules/ngx_http_upstream_hash_module.c @@ -242,6 +242,10 @@ ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data) goto next; } + if (peer->max_conns && peer->conns >= peer->max_conns) { + goto next; + } + break; next: @@ -523,7 +527,6 @@ ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data) peer; peer = peer->next, i++) { - n = i / (8 * sizeof(uintptr_t)); m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); @@ -549,6 +552,10 @@ ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data) continue; } + if (peer->max_conns && peer->conns >= peer->max_conns) { + continue; + } + peer->current_weight += peer->effective_weight; total += peer->effective_weight; @@ -571,6 +578,7 @@ ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data) hp->tries++; if (hp->tries >= points->number) { + pc->name = hp->rrp.peers->name; ngx_http_upstream_rr_peers_unlock(hp->rrp.peers); return NGX_BUSY; } @@ -647,6 +655,7 @@ ngx_http_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) uscf->flags = NGX_HTTP_UPSTREAM_CREATE |NGX_HTTP_UPSTREAM_WEIGHT + |NGX_HTTP_UPSTREAM_MAX_CONNS |NGX_HTTP_UPSTREAM_MAX_FAILS |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT |NGX_HTTP_UPSTREAM_DOWN; diff --git a/src/http/modules/ngx_http_upstream_ip_hash_module.c b/src/http/modules/ngx_http_upstream_ip_hash_module.c index 8a5f0fa..296108f 100644 --- a/src/http/modules/ngx_http_upstream_ip_hash_module.c +++ b/src/http/modules/ngx_http_upstream_ip_hash_module.c @@ -212,6 +212,10 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data) goto next; } + if (peer->max_conns && peer->conns >= peer->max_conns) { + goto next; + } + break; next: @@ -259,6 +263,7 @@ ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) uscf->flags = NGX_HTTP_UPSTREAM_CREATE |NGX_HTTP_UPSTREAM_WEIGHT + |NGX_HTTP_UPSTREAM_MAX_CONNS |NGX_HTTP_UPSTREAM_MAX_FAILS |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT |NGX_HTTP_UPSTREAM_DOWN; diff --git a/src/http/modules/ngx_http_upstream_least_conn_module.c b/src/http/modules/ngx_http_upstream_least_conn_module.c index 8a300c1..ebe0627 100644 --- a/src/http/modules/ngx_http_upstream_least_conn_module.c +++ b/src/http/modules/ngx_http_upstream_least_conn_module.c @@ -136,7 +136,6 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data) peer; peer = peer->next, i++) { - n = i / (8 * sizeof(uintptr_t)); m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); @@ -155,6 +154,10 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data) continue; } + if (peer->max_conns && peer->conns >= peer->max_conns) { + continue; + } + /* * select peer with least number of connections; if there are * multiple peers with the same number of connections, select @@ -210,6 +213,10 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data) continue; } + if (peer->max_conns && peer->conns >= peer->max_conns) { + continue; + } + peer->current_weight += peer->effective_weight; total += peer->effective_weight; @@ -273,12 +280,6 @@ failed: ngx_http_upstream_rr_peers_wlock(peers); } - /* all peers failed, mark them as live for quick recovery */ - - for (peer = peers->peer; peer; peer = peer->next) { - peer->fails = 0; - } - ngx_http_upstream_rr_peers_unlock(peers); pc->name = peers->name; @@ -303,6 +304,7 @@ ngx_http_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) uscf->flags = NGX_HTTP_UPSTREAM_CREATE |NGX_HTTP_UPSTREAM_WEIGHT + |NGX_HTTP_UPSTREAM_MAX_CONNS |NGX_HTTP_UPSTREAM_MAX_FAILS |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT |NGX_HTTP_UPSTREAM_DOWN diff --git a/src/http/modules/perl/Makefile.PL b/src/http/modules/perl/Makefile.PL index 03348b5..7edadcb 100644 --- a/src/http/modules/perl/Makefile.PL +++ b/src/http/modules/perl/Makefile.PL @@ -16,6 +16,8 @@ WriteMakefile( CCFLAGS => "$ENV{NGX_PM_CFLAGS}", OPTIMIZE => '-O', + LDDLFLAGS => "$ENV{NGX_PM_LDFLAGS}", + INC => join(" ", map { m#^/# ? "-I $_" : "-I ../../../../../$_" } (split /\s+/, $ENV{NGX_INCS})), diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c index 7a46b3e..ba559f2 100644 --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -1756,7 +1756,7 @@ ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr) ls->deferred_accept = addr->opt.deferred_accept; #endif -#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) +#if (NGX_HAVE_INET6) ls->ipv6only = addr->opt.ipv6only; #endif diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h index 19cb680..afab4f6 100644 --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -19,10 +19,7 @@ typedef struct ngx_http_cache_s ngx_http_cache_t; typedef struct ngx_http_file_cache_s ngx_http_file_cache_t; typedef struct ngx_http_log_ctx_s ngx_http_log_ctx_t; typedef struct ngx_http_chunked_s ngx_http_chunked_t; - -#if (NGX_HTTP_V2) typedef struct ngx_http_v2_stream_s ngx_http_v2_stream_t; -#endif typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); diff --git a/src/http/ngx_http_cache.h b/src/http/ngx_http_cache.h index 2667cbb..70342d0 100644 --- a/src/http/ngx_http_cache.h +++ b/src/http/ngx_http_cache.h @@ -50,7 +50,8 @@ typedef struct { unsigned exists:1; unsigned updating:1; unsigned deleting:1; - /* 11 unused bits */ + unsigned purged:1; + /* 10 unused bits */ ngx_file_uniq_t uniq; time_t expire; @@ -85,13 +86,14 @@ struct ngx_http_cache_s { ngx_uint_t min_uses; ngx_uint_t error; ngx_uint_t valid_msec; + ngx_uint_t vary_tag; ngx_buf_t *buf; ngx_http_file_cache_t *file_cache; ngx_http_file_cache_node_t *node; -#if (NGX_THREADS) +#if (NGX_THREADS || NGX_COMPAT) ngx_thread_task_t *thread_task; #endif @@ -109,6 +111,7 @@ struct ngx_http_cache_s { unsigned updating:1; unsigned exists:1; unsigned temp_file:1; + unsigned purged:1; unsigned reading:1; unsigned secondary:1; }; @@ -163,6 +166,10 @@ struct ngx_http_file_cache_s { ngx_msec_t loader_sleep; ngx_msec_t loader_threshold; + ngx_uint_t manager_files; + ngx_msec_t manager_sleep; + ngx_msec_t manager_threshold; + ngx_shm_zone_t *shm_zone; }; diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index e26c3f7..9da5d10 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -3760,10 +3760,8 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->sendfile, prev->sendfile, 0); ngx_conf_merge_size_value(conf->sendfile_max_chunk, prev->sendfile_max_chunk, 0); -#if (NGX_HAVE_FILE_AIO || NGX_THREADS) ngx_conf_merge_value(conf->aio, prev->aio, NGX_HTTP_AIO_OFF); ngx_conf_merge_value(conf->aio_write, prev->aio_write, 0); -#endif #if (NGX_THREADS) ngx_conf_merge_ptr_value(conf->thread_pool, prev->thread_pool, NULL); ngx_conf_merge_ptr_value(conf->thread_pool_value, prev->thread_pool_value, @@ -3939,7 +3937,7 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) lsopt.fastopen = -1; #endif lsopt.wildcard = u.wildcard; -#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) +#if (NGX_HAVE_INET6) lsopt.ipv6only = 1; #endif diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h index 773c215..ade9abb 100644 --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -15,6 +15,8 @@ #if (NGX_THREADS) #include +#elif (NGX_COMPAT) +typedef struct ngx_thread_pool_s ngx_thread_pool_t; #endif @@ -65,18 +67,13 @@ typedef struct { unsigned default_server:1; unsigned bind:1; unsigned wildcard:1; -#if (NGX_HTTP_SSL) unsigned ssl:1; -#endif -#if (NGX_HTTP_V2) unsigned http2:1; -#endif -#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) +#if (NGX_HAVE_INET6) unsigned ipv6only:1; #endif -#if (NGX_HAVE_REUSEPORT) + unsigned deferred_accept:1; unsigned reuseport:1; -#endif unsigned so_keepalive:2; unsigned proxy_protocol:1; @@ -98,9 +95,6 @@ typedef struct { #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) char *accept_filter; #endif -#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) - ngx_uint_t deferred_accept; -#endif u_char addr[NGX_SOCKADDR_STRLEN + 1]; } ngx_http_listen_opt_t; @@ -234,12 +228,8 @@ struct ngx_http_addr_conf_s { ngx_http_virtual_names_t *virtual_names; -#if (NGX_HTTP_SSL) unsigned ssl:1; -#endif -#if (NGX_HTTP_V2) unsigned http2:1; -#endif unsigned proxy_protocol:1; }; @@ -327,9 +317,7 @@ struct ngx_http_core_loc_conf_s { unsigned auto_redirect:1; #if (NGX_HTTP_GZIP) unsigned gzip_disable_msie6:2; -#if (NGX_HTTP_DEGRADATION) unsigned gzip_disable_degradation:2; -#endif #endif ngx_http_location_tree_node_t *static_locations; @@ -419,7 +407,7 @@ struct ngx_http_core_loc_conf_s { #endif #endif -#if (NGX_THREADS) +#if (NGX_THREADS || NGX_COMPAT) ngx_thread_pool_t *thread_pool; ngx_http_complex_value_t *thread_pool_value; #endif diff --git a/src/http/ngx_http_file_cache.c b/src/http/ngx_http_file_cache.c index 4bf0f7f..199a901 100644 --- a/src/http/ngx_http_file_cache.c +++ b/src/http/ngx_http_file_cache.c @@ -1759,6 +1759,7 @@ ngx_http_file_cache_expire(ngx_http_file_cache_t *cache) size_t len; time_t now, wait; ngx_path_t *path; + ngx_msec_t elapsed; ngx_queue_t *q; ngx_http_file_cache_node_t *fcn; u_char key[2 * NGX_HTTP_CACHE_KEY_LEN]; @@ -1810,7 +1811,7 @@ ngx_http_file_cache_expire(ngx_http_file_cache_t *cache) if (fcn->count == 0) { ngx_http_file_cache_delete(cache, q, name); - continue; + goto next; } if (fcn->deleting) { @@ -1836,6 +1837,22 @@ ngx_http_file_cache_expire(ngx_http_file_cache_t *cache) ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "ignore long locked inactive cache entry %*s, count:%d", (size_t) 2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count); + +next: + + if (++cache->files >= cache->manager_files) { + wait = 0; + break; + } + + ngx_time_update(); + + elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last)); + + if (elapsed >= cache->manager_threshold) { + wait = 0; + break; + } } ngx_shmtx_unlock(&cache->shpool->mutex); @@ -1897,20 +1914,25 @@ ngx_http_file_cache_delete(ngx_http_file_cache_t *cache, ngx_queue_t *q, } -static time_t +static ngx_msec_t ngx_http_file_cache_manager(void *data) { ngx_http_file_cache_t *cache = data; off_t size; time_t next, wait; + ngx_msec_t elapsed; ngx_uint_t count, watermark; - next = ngx_http_file_cache_expire(cache); - cache->last = ngx_current_msec; cache->files = 0; + next = ngx_http_file_cache_expire(cache); + + if (next == 0) { + return cache->manager_sleep; + } + for ( ;; ) { ngx_shmtx_lock(&cache->shpool->mutex); @@ -1925,17 +1947,29 @@ ngx_http_file_cache_manager(void *data) size, count, (ngx_int_t) watermark); if (size < cache->max_size && count < watermark) { - return next; + return (ngx_msec_t) next * 1000; } wait = ngx_http_file_cache_forced_expire(cache); if (wait > 0) { - return wait; + return (ngx_msec_t) wait * 1000; } if (ngx_quit || ngx_terminate) { - return next; + return (ngx_msec_t) next * 1000; + } + + if (++cache->files >= cache->manager_files) { + return cache->manager_sleep; + } + + ngx_time_update(); + + elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last)); + + if (elapsed >= cache->manager_threshold) { + return cache->manager_sleep; } } } @@ -2211,8 +2245,9 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) size_t len; ssize_t size; ngx_str_t s, name, *value; - ngx_int_t loader_files; - ngx_msec_t loader_sleep, loader_threshold; + ngx_int_t loader_files, manager_files; + ngx_msec_t loader_sleep, manager_sleep, loader_threshold, + manager_threshold; ngx_uint_t i, n, use_temp_path; ngx_array_t *caches; ngx_http_file_cache_t *cache, **ce; @@ -2230,10 +2265,15 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) use_temp_path = 1; inactive = 600; + loader_files = 100; loader_sleep = 50; loader_threshold = 200; + manager_files = 100; + manager_sleep = 50; + manager_threshold = 200; + name.len = 0; size = 0; max_size = NGX_MAX_OFF_T_VALUE; @@ -2405,6 +2445,48 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) continue; } + if (ngx_strncmp(value[i].data, "manager_files=", 14) == 0) { + + manager_files = ngx_atoi(value[i].data + 14, value[i].len - 14); + if (manager_files == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid manager_files value \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "manager_sleep=", 14) == 0) { + + s.len = value[i].len - 14; + s.data = value[i].data + 14; + + manager_sleep = ngx_parse_time(&s, 0); + if (manager_sleep == (ngx_msec_t) NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid manager_sleep value \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "manager_threshold=", 18) == 0) { + + s.len = value[i].len - 18; + s.data = value[i].data + 18; + + manager_threshold = ngx_parse_time(&s, 0); + if (manager_threshold == (ngx_msec_t) NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid manager_threshold value \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", &value[i]); return NGX_CONF_ERROR; @@ -2425,6 +2507,9 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) cache->loader_files = loader_files; cache->loader_sleep = loader_sleep; cache->loader_threshold = loader_threshold; + cache->manager_files = manager_files; + cache->manager_sleep = manager_sleep; + cache->manager_threshold = manager_threshold; if (ngx_add_path(cf, &cache->path) != NGX_OK) { return NGX_CONF_ERROR; diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c index bd6c9c9..9f99473 100644 --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -149,7 +149,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) break; } - if ((ch < 'A' || ch > 'Z') && ch != '_') { + if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') { return NGX_HTTP_PARSE_INVALID_METHOD; } @@ -270,7 +270,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) break; } - if ((ch < 'A' || ch > 'Z') && ch != '_') { + if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') { return NGX_HTTP_PARSE_INVALID_METHOD; } diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index 499c1ef..cf9ee3c 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -286,9 +286,7 @@ typedef struct { ngx_chain_t *bufs; ngx_buf_t *buf; off_t rest; -#if (NGX_HTTP_V2) off_t received; -#endif ngx_chain_t *free; ngx_chain_t *busy; ngx_http_chunked_t *chunked; @@ -302,7 +300,7 @@ typedef struct { ngx_http_addr_conf_t *addr_conf; ngx_http_conf_ctx_t *conf_ctx; -#if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME) +#if (NGX_HTTP_SSL || NGX_COMPAT) ngx_str_t *ssl_servername; #if (NGX_PCRE) ngx_http_regex_t *ssl_servername_regex; @@ -315,9 +313,7 @@ typedef struct { ngx_buf_t **free; ngx_int_t nfree; -#if (NGX_HTTP_SSL) unsigned ssl:1; -#endif unsigned proxy_protocol:1; } ngx_http_connection_t; @@ -438,9 +434,7 @@ struct ngx_http_request_s { ngx_uint_t err_status; ngx_http_connection_t *http_connection; -#if (NGX_HTTP_V2) ngx_http_v2_stream_t *stream; -#endif ngx_http_log_handler_pt log_handler; @@ -539,11 +533,11 @@ struct ngx_http_request_s { unsigned subrequest_ranges:1; unsigned single_range:1; unsigned disable_not_modified:1; - -#if (NGX_STAT_STUB) unsigned stat_reading:1; unsigned stat_writing:1; -#endif + unsigned stat_processing:1; + + unsigned health_check:1; /* used to parse HTTP headers */ diff --git a/src/http/ngx_http_special_response.c b/src/http/ngx_http_special_response.c index 64e5acd..7692f80 100644 --- a/src/http/ngx_http_special_response.c +++ b/src/http/ngx_http_special_response.c @@ -792,7 +792,7 @@ ngx_http_send_refresh(ngx_http_request_t *r) b->last = ngx_cpymem(p, ngx_http_msie_refresh_tail, sizeof(ngx_http_msie_refresh_tail) - 1); - b->last_buf = 1; + b->last_buf = (r == r->main) ? 1 : 0; b->last_in_chain = 1; out.buf = b; diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index 7e4b3c5..ceb798f 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -748,6 +748,8 @@ found: return; } + u->upstream = uscf; + #if (NGX_HTTP_SSL) u->ssl_name = uscf->host; #endif @@ -5442,6 +5444,7 @@ ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) uscf = ngx_http_upstream_add(cf, &u, NGX_HTTP_UPSTREAM_CREATE |NGX_HTTP_UPSTREAM_WEIGHT + |NGX_HTTP_UPSTREAM_MAX_CONNS |NGX_HTTP_UPSTREAM_MAX_FAILS |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT |NGX_HTTP_UPSTREAM_DOWN @@ -5543,7 +5546,7 @@ ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) time_t fail_timeout; ngx_str_t *value, s; ngx_url_t u; - ngx_int_t weight, max_fails; + ngx_int_t weight, max_conns, max_fails; ngx_uint_t i; ngx_http_upstream_server_t *us; @@ -5557,6 +5560,7 @@ ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) value = cf->args->elts; weight = 1; + max_conns = 0; max_fails = 1; fail_timeout = 10; @@ -5577,6 +5581,21 @@ ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) continue; } + if (ngx_strncmp(value[i].data, "max_conns=", 10) == 0) { + + if (!(uscf->flags & NGX_HTTP_UPSTREAM_MAX_CONNS)) { + goto not_supported; + } + + max_conns = ngx_atoi(&value[i].data[10], value[i].len - 10); + + if (max_conns == NGX_ERROR) { + goto invalid; + } + + continue; + } + if (ngx_strncmp(value[i].data, "max_fails=", 10) == 0) { if (!(uscf->flags & NGX_HTTP_UPSTREAM_MAX_FAILS)) { @@ -5653,6 +5672,7 @@ ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) us->addrs = u.addrs; us->naddrs = u.naddrs; us->weight = weight; + us->max_conns = max_conns; us->max_fails = max_fails; us->fail_timeout = fail_timeout; @@ -5717,14 +5737,14 @@ ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags) } if ((uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE) && !u->no_port) { - ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "upstream \"%V\" may not have port %d", &u->host, u->port); return NULL; } if ((flags & NGX_HTTP_UPSTREAM_CREATE) && !uscfp[i]->no_port) { - ngx_log_error(NGX_LOG_WARN, cf->log, 0, + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "upstream \"%V\" may not have port %d in %s:%ui", &u->host, uscfp[i]->port, uscfp[i]->file_name, uscfp[i]->line); diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h index ef861f4..3d521f2 100644 --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -95,11 +95,16 @@ typedef struct { ngx_addr_t *addrs; ngx_uint_t naddrs; ngx_uint_t weight; + ngx_uint_t max_conns; ngx_uint_t max_fails; time_t fail_timeout; + ngx_msec_t slow_start; unsigned down:1; unsigned backup:1; + + NGX_COMPAT_BEGIN(6) + NGX_COMPAT_END } ngx_http_upstream_server_t; @@ -109,6 +114,7 @@ typedef struct { #define NGX_HTTP_UPSTREAM_FAIL_TIMEOUT 0x0008 #define NGX_HTTP_UPSTREAM_DOWN 0x0010 #define NGX_HTTP_UPSTREAM_BACKUP 0x0020 +#define NGX_HTTP_UPSTREAM_MAX_CONNS 0x0100 struct ngx_http_upstream_srv_conf_s { @@ -202,6 +208,7 @@ typedef struct { ngx_array_t *cache_valid; ngx_array_t *cache_bypass; + ngx_array_t *cache_purge; ngx_array_t *no_cache; #endif @@ -215,7 +222,7 @@ typedef struct { unsigned intercept_404:1; unsigned change_buffering:1; -#if (NGX_HTTP_SSL) +#if (NGX_HTTP_SSL || NGX_COMPAT) ngx_ssl_t *ssl; ngx_flag_t ssl_session_reuse; @@ -225,6 +232,9 @@ typedef struct { #endif ngx_str_t module; + + NGX_COMPAT_BEGIN(2) + NGX_COMPAT_END } ngx_http_upstream_conf_t; @@ -313,6 +323,7 @@ struct ngx_http_upstream_s { ngx_chain_writer_ctx_t writer; ngx_http_upstream_conf_t *conf; + ngx_http_upstream_srv_conf_t *upstream; #if (NGX_HTTP_CACHE) ngx_array_t *caches; #endif @@ -356,7 +367,7 @@ struct ngx_http_upstream_s { ngx_str_t schema; ngx_str_t uri; -#if (NGX_HTTP_SSL) +#if (NGX_HTTP_SSL || NGX_COMPAT) ngx_str_t ssl_name; #endif @@ -377,6 +388,9 @@ struct ngx_http_upstream_s { unsigned request_sent:1; unsigned request_body_sent:1; unsigned header_sent:1; + + NGX_COMPAT_BEGIN(1) + NGX_COMPAT_END }; diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c index 8479c42..0137bf6 100644 --- a/src/http/ngx_http_upstream_round_robin.c +++ b/src/http/ngx_http_upstream_round_robin.c @@ -92,6 +92,7 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf, peer[n].weight = server[i].weight; peer[n].effective_weight = server[i].weight; peer[n].current_weight = 0; + peer[n].max_conns = server[i].max_conns; peer[n].max_fails = server[i].max_fails; peer[n].fail_timeout = server[i].fail_timeout; peer[n].down = server[i].down; @@ -155,6 +156,7 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf, peer[n].weight = server[i].weight; peer[n].effective_weight = server[i].weight; peer[n].current_weight = 0; + peer[n].max_conns = server[i].max_conns; peer[n].max_fails = server[i].max_fails; peer[n].fail_timeout = server[i].fail_timeout; peer[n].down = server[i].down; @@ -223,6 +225,7 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf, peer[i].weight = 1; peer[i].effective_weight = 1; peer[i].current_weight = 0; + peer[i].max_conns = 0; peer[i].max_fails = 1; peer[i].fail_timeout = 10; *peerp = &peer[i]; @@ -257,6 +260,7 @@ ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r, rrp->peers = us->peer.data; rrp->current = NULL; + rrp->config = 0; n = rrp->peers->number; @@ -337,6 +341,7 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r, peer[0].weight = 1; peer[0].effective_weight = 1; peer[0].current_weight = 0; + peer[0].max_conns = 0; peer[0].max_fails = 1; peer[0].fail_timeout = 10; peers->peer = peer; @@ -370,6 +375,7 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r, peer[i].weight = 1; peer[i].effective_weight = 1; peer[i].current_weight = 0; + peer[i].max_conns = 0; peer[i].max_fails = 1; peer[i].fail_timeout = 10; *peerp = &peer[i]; @@ -379,6 +385,7 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r, rrp->peers = peers; rrp->current = NULL; + rrp->config = 0; if (rrp->peers->number <= 8 * sizeof(uintptr_t)) { rrp->tried = &rrp->data; @@ -432,6 +439,10 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data) goto failed; } + if (peer->max_conns && peer->conns >= peer->max_conns) { + goto failed; + } + rrp->current = peer; } else { @@ -485,12 +496,6 @@ failed: ngx_http_upstream_rr_peers_wlock(peers); } - /* all peers failed, mark them as live for quick recovery */ - - for (peer = peers->peer; peer; peer = peer->next) { - peer->fails = 0; - } - ngx_http_upstream_rr_peers_unlock(peers); pc->name = peers->name; @@ -521,7 +526,6 @@ ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp) peer; peer = peer->next, i++) { - n = i / (8 * sizeof(uintptr_t)); m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); @@ -540,6 +544,10 @@ ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp) continue; } + if (peer->max_conns && peer->conns >= peer->max_conns) { + continue; + } + peer->current_weight += peer->effective_weight; total += peer->effective_weight; diff --git a/src/http/ngx_http_upstream_round_robin.h b/src/http/ngx_http_upstream_round_robin.h index f2c573f..45f258d 100644 --- a/src/http/ngx_http_upstream_round_robin.h +++ b/src/http/ngx_http_upstream_round_robin.h @@ -27,6 +27,7 @@ struct ngx_http_upstream_rr_peer_s { ngx_int_t weight; ngx_uint_t conns; + ngx_uint_t max_conns; ngx_uint_t fails; time_t accessed; @@ -34,19 +35,24 @@ struct ngx_http_upstream_rr_peer_s { ngx_uint_t max_fails; time_t fail_timeout; + ngx_msec_t slow_start; + ngx_msec_t start_time; - ngx_uint_t down; /* unsigned down:1; */ + ngx_uint_t down; -#if (NGX_HTTP_SSL) +#if (NGX_HTTP_SSL || NGX_COMPAT) void *ssl_session; int ssl_session_len; #endif - ngx_http_upstream_rr_peer_t *next; - #if (NGX_HTTP_UPSTREAM_ZONE) ngx_atomic_t lock; #endif + + ngx_http_upstream_rr_peer_t *next; + + NGX_COMPAT_BEGIN(32) + NGX_COMPAT_END }; @@ -119,6 +125,7 @@ struct ngx_http_upstream_rr_peers_s { typedef struct { + ngx_uint_t config; ngx_http_upstream_rr_peers_t *peers; ngx_http_upstream_rr_peer_t *current; uintptr_t *tried; diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index d0cd2ab..235092b 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -3178,7 +3178,7 @@ ngx_http_v2_parse_method(ngx_http_request_t *r, ngx_http_v2_header_t *header) p = r->method_name.data; do { - if ((*p < 'A' || *p > 'Z') && *p != '_') { + if ((*p < 'A' || *p > 'Z') && *p != '_' && *p != '-') { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "client sent invalid method: \"%V\"", &r->method_name); diff --git a/src/mail/ngx_mail.c b/src/mail/ngx_mail.c index e5a77b0..9e560bb 100644 --- a/src/mail/ngx_mail.c +++ b/src/mail/ngx_mail.c @@ -341,7 +341,7 @@ ngx_mail_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports) ls->keepcnt = addr[i].opt.tcp_keepcnt; #endif -#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) +#if (NGX_HAVE_INET6) ls->ipv6only = addr[i].opt.ipv6only; #endif diff --git a/src/mail/ngx_mail.h b/src/mail/ngx_mail.h index 1068bb3..c30af35 100644 --- a/src/mail/ngx_mail.h +++ b/src/mail/ngx_mail.h @@ -35,10 +35,8 @@ typedef struct { unsigned bind:1; unsigned wildcard:1; -#if (NGX_MAIL_SSL) unsigned ssl:1; -#endif -#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) +#if (NGX_HAVE_INET6) unsigned ipv6only:1; #endif unsigned so_keepalive:2; @@ -54,9 +52,7 @@ typedef struct { typedef struct { ngx_mail_conf_ctx_t *ctx; ngx_str_t addr_text; -#if (NGX_MAIL_SSL) ngx_uint_t ssl; /* unsigned ssl:1; */ -#endif } ngx_mail_addr_conf_t; typedef struct { diff --git a/src/mail/ngx_mail_core_module.c b/src/mail/ngx_mail_core_module.c index 48eacfa..b974d90 100644 --- a/src/mail/ngx_mail_core_module.c +++ b/src/mail/ngx_mail_core_module.c @@ -353,7 +353,7 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ls->wildcard = u.wildcard; ls->ctx = cf->ctx; -#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) +#if (NGX_HAVE_INET6) ls->ipv6only = 1; #endif diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c index 11e428c..fbc9bc7 100644 --- a/src/mail/ngx_mail_ssl_module.c +++ b/src/mail/ngx_mail_ssl_module.c @@ -488,7 +488,7 @@ ngx_mail_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } if (scf->enable && (ngx_int_t) scf->starttls > NGX_MAIL_STARTTLS_OFF) { - ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"starttls\" directive conflicts with \"ssl on\""); return NGX_CONF_ERROR; } @@ -514,7 +514,7 @@ ngx_mail_ssl_starttls(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } if (scf->enable == 1 && (ngx_int_t) scf->starttls > NGX_MAIL_STARTTLS_OFF) { - ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"ssl\" directive conflicts with \"starttls\""); return NGX_CONF_ERROR; } diff --git a/src/os/unix/ngx_darwin_init.c b/src/os/unix/ngx_darwin_init.c index a9d12a8..aabe02f 100644 --- a/src/os/unix/ngx_darwin_init.c +++ b/src/os/unix/ngx_darwin_init.c @@ -24,6 +24,7 @@ static ngx_os_io_t ngx_darwin_io = { ngx_udp_unix_recv, ngx_unix_send, ngx_udp_unix_send, + ngx_udp_unix_sendmsg_chain, #if (NGX_HAVE_SENDFILE) ngx_darwin_sendfile_chain, NGX_IO_SENDFILE diff --git a/src/os/unix/ngx_freebsd_init.c b/src/os/unix/ngx_freebsd_init.c index 71672c7..1823f02 100644 --- a/src/os/unix/ngx_freebsd_init.c +++ b/src/os/unix/ngx_freebsd_init.c @@ -33,6 +33,7 @@ static ngx_os_io_t ngx_freebsd_io = { ngx_udp_unix_recv, ngx_unix_send, ngx_udp_unix_send, + ngx_udp_unix_sendmsg_chain, #if (NGX_HAVE_SENDFILE) ngx_freebsd_sendfile_chain, NGX_IO_SENDFILE diff --git a/src/os/unix/ngx_linux_init.c b/src/os/unix/ngx_linux_init.c index a1372e9..a8cf6a0 100644 --- a/src/os/unix/ngx_linux_init.c +++ b/src/os/unix/ngx_linux_init.c @@ -19,6 +19,7 @@ static ngx_os_io_t ngx_linux_io = { ngx_udp_unix_recv, ngx_unix_send, ngx_udp_unix_send, + ngx_udp_unix_sendmsg_chain, #if (NGX_HAVE_SENDFILE) ngx_linux_sendfile_chain, NGX_IO_SENDFILE diff --git a/src/os/unix/ngx_os.h b/src/os/unix/ngx_os.h index e22f07c..3b32819 100644 --- a/src/os/unix/ngx_os.h +++ b/src/os/unix/ngx_os.h @@ -29,6 +29,7 @@ typedef struct { ngx_recv_pt udp_recv; ngx_send_pt send; ngx_send_pt udp_send; + ngx_send_chain_pt udp_send_chain; ngx_send_chain_pt send_chain; ngx_uint_t flags; } ngx_os_io_t; @@ -49,6 +50,8 @@ ssize_t ngx_unix_send(ngx_connection_t *c, u_char *buf, size_t size); ngx_chain_t *ngx_writev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit); ssize_t ngx_udp_unix_send(ngx_connection_t *c, u_char *buf, size_t size); +ngx_chain_t *ngx_udp_unix_sendmsg_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); #if (IOV_MAX > 64) diff --git a/src/os/unix/ngx_posix_init.c b/src/os/unix/ngx_posix_init.c index 7e6e79d..583ea4f 100644 --- a/src/os/unix/ngx_posix_init.c +++ b/src/os/unix/ngx_posix_init.c @@ -25,6 +25,7 @@ ngx_os_io_t ngx_os_io = { ngx_udp_unix_recv, ngx_unix_send, ngx_udp_unix_send, + ngx_udp_unix_sendmsg_chain, ngx_writev_chain, 0 }; diff --git a/src/os/unix/ngx_process_cycle.c b/src/os/unix/ngx_process_cycle.c index 83b04ee..5c4e21d 100644 --- a/src/os/unix/ngx_process_cycle.c +++ b/src/os/unix/ngx_process_cycle.c @@ -1148,11 +1148,11 @@ ngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data) static void ngx_cache_manager_process_handler(ngx_event_t *ev) { - time_t next, n; ngx_uint_t i; + ngx_msec_t next, n; ngx_path_t **path; - next = 60 * 60; + next = 60 * 60 * 1000; path = ngx_cycle->paths.elts; for (i = 0; i < ngx_cycle->paths.nelts; i++) { @@ -1170,7 +1170,7 @@ ngx_cache_manager_process_handler(ngx_event_t *ev) next = 1; } - ngx_add_timer(ev, next * 1000); + ngx_add_timer(ev, next); } diff --git a/src/os/unix/ngx_solaris_init.c b/src/os/unix/ngx_solaris_init.c index 83acae1..65d7875 100644 --- a/src/os/unix/ngx_solaris_init.c +++ b/src/os/unix/ngx_solaris_init.c @@ -20,6 +20,7 @@ static ngx_os_io_t ngx_solaris_io = { ngx_udp_unix_recv, ngx_unix_send, ngx_udp_unix_send, + ngx_udp_unix_sendmsg_chain, #if (NGX_HAVE_SENDFILE) ngx_solaris_sendfilev_chain, NGX_IO_SENDFILE diff --git a/src/os/unix/ngx_udp_sendmsg_chain.c b/src/os/unix/ngx_udp_sendmsg_chain.c new file mode 100644 index 0000000..65bde6f --- /dev/null +++ b/src/os/unix/ngx_udp_sendmsg_chain.c @@ -0,0 +1,245 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +static ngx_chain_t *ngx_udp_output_chain_to_iovec(ngx_iovec_t *vec, + ngx_chain_t *in, ngx_log_t *log); +static ssize_t ngx_sendmsg(ngx_connection_t *c, ngx_iovec_t *vec); + + +ngx_chain_t * +ngx_udp_unix_sendmsg_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) +{ + ssize_t n; + off_t send; + ngx_chain_t *cl; + ngx_event_t *wev; + ngx_iovec_t vec; + struct iovec iovs[NGX_IOVS_PREALLOCATE]; + + wev = c->write; + + if (!wev->ready) { + return in; + } + +#if (NGX_HAVE_KQUEUE) + + if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) { + (void) ngx_connection_error(c, wev->kq_errno, + "kevent() reported about an closed connection"); + wev->error = 1; + return NGX_CHAIN_ERROR; + } + +#endif + + /* the maximum limit size is the maximum size_t value - the page size */ + + if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) { + limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize; + } + + send = 0; + + vec.iovs = iovs; + vec.nalloc = NGX_IOVS_PREALLOCATE; + + for ( ;; ) { + + /* create the iovec and coalesce the neighbouring bufs */ + + cl = ngx_udp_output_chain_to_iovec(&vec, in, c->log); + + if (cl == NGX_CHAIN_ERROR) { + return NGX_CHAIN_ERROR; + } + + if (cl && cl->buf->in_file) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "file buf in sendmsg " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + cl->buf->temporary, + cl->buf->recycled, + cl->buf->in_file, + cl->buf->start, + cl->buf->pos, + cl->buf->last, + cl->buf->file, + cl->buf->file_pos, + cl->buf->file_last); + + ngx_debug_point(); + + return NGX_CHAIN_ERROR; + } + + if (cl == in) { + return in; + } + + send += vec.size; + + n = ngx_sendmsg(c, &vec); + + if (n == NGX_ERROR) { + return NGX_CHAIN_ERROR; + } + + if (n == NGX_AGAIN) { + wev->ready = 0; + return in; + } + + c->sent += n; + + in = ngx_chain_update_sent(in, n); + + if (send >= limit || in == NULL) { + return in; + } + } +} + + +static ngx_chain_t * +ngx_udp_output_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *in, ngx_log_t *log) +{ + size_t total, size; + u_char *prev; + ngx_uint_t n, flush; + ngx_chain_t *cl; + struct iovec *iov; + + cl = in; + iov = NULL; + prev = NULL; + total = 0; + n = 0; + flush = 0; + + for ( /* void */ ; in && !flush; in = in->next) { + + if (in->buf->flush || in->buf->last_buf) { + flush = 1; + } + + if (ngx_buf_special(in->buf)) { + continue; + } + + if (in->buf->in_file) { + break; + } + + if (!ngx_buf_in_memory(in->buf)) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "bad buf in output chain " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + in->buf->temporary, + in->buf->recycled, + in->buf->in_file, + in->buf->start, + in->buf->pos, + in->buf->last, + in->buf->file, + in->buf->file_pos, + in->buf->file_last); + + ngx_debug_point(); + + return NGX_CHAIN_ERROR; + } + + size = in->buf->last - in->buf->pos; + + if (prev == in->buf->pos) { + iov->iov_len += size; + + } else { + if (n == vec->nalloc) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "too many parts in a datagram"); + return NGX_CHAIN_ERROR; + } + + iov = &vec->iovs[n++]; + + iov->iov_base = (void *) in->buf->pos; + iov->iov_len = size; + } + + prev = in->buf->pos + size; + total += size; + } + + if (!flush) { +#if (NGX_SUPPRESS_WARN) + vec->size = 0; + vec->count = 0; +#endif + return cl; + } + + vec->count = n; + vec->size = total; + + return in; +} + + +static ssize_t +ngx_sendmsg(ngx_connection_t *c, ngx_iovec_t *vec) +{ + ssize_t n; + ngx_err_t err; + struct msghdr msg; + + ngx_memzero(&msg, sizeof(struct msghdr)); + + if (c->socklen) { + msg.msg_name = c->sockaddr; + msg.msg_namelen = c->socklen; + } + + msg.msg_iov = vec->iovs; + msg.msg_iovlen = vec->count; + +eintr: + + n = sendmsg(c->fd, &msg, 0); + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sendmsg: %z of %uz", n, vec->size); + + if (n == -1) { + err = ngx_errno; + + switch (err) { + case NGX_EAGAIN: + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "sendmsg() not ready"); + return NGX_AGAIN; + + case NGX_EINTR: + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "sendmsg() was interrupted"); + goto eintr; + + default: + c->write->error = 1; + ngx_connection_error(c, err, "sendmsg() failed"); + return NGX_ERROR; + } + } + + return n; +} diff --git a/src/stream/ngx_stream.c b/src/stream/ngx_stream.c index 873e102..4a394d7 100644 --- a/src/stream/ngx_stream.c +++ b/src/stream/ngx_stream.c @@ -12,6 +12,10 @@ static char *ngx_stream_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static ngx_int_t ngx_stream_init_phases(ngx_conf_t *cf, + ngx_stream_core_main_conf_t *cmcf); +static ngx_int_t ngx_stream_init_phase_handlers(ngx_conf_t *cf, + ngx_stream_core_main_conf_t *cmcf); static ngx_int_t ngx_stream_add_ports(ngx_conf_t *cf, ngx_array_t *ports, ngx_stream_listen_t *listen); static char *ngx_stream_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports); @@ -27,6 +31,9 @@ static ngx_int_t ngx_stream_cmp_conf_addrs(const void *one, const void *two); ngx_uint_t ngx_stream_max_module; +ngx_stream_filter_pt ngx_stream_top_filter; + + static ngx_command_t ngx_stream_commands[] = { { ngx_string("stream"), @@ -216,6 +223,10 @@ ngx_stream_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } } + if (ngx_stream_init_phases(cf, cmcf) != NGX_OK) { + return NGX_CONF_ERROR; + } + for (m = 0; cf->cycle->modules[m]; m++) { if (cf->cycle->modules[m]->type != NGX_STREAM_MODULE) { continue; @@ -236,6 +247,9 @@ ngx_stream_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) *cf = pcf; + if (ngx_stream_init_phase_handlers(cf, cmcf) != NGX_OK) { + return NGX_CONF_ERROR; + } if (ngx_array_init(&ports, cf->temp_pool, 4, sizeof(ngx_stream_conf_port_t)) != NGX_OK) @@ -255,6 +269,114 @@ ngx_stream_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } +static ngx_int_t +ngx_stream_init_phases(ngx_conf_t *cf, ngx_stream_core_main_conf_t *cmcf) +{ + if (ngx_array_init(&cmcf->phases[NGX_STREAM_POST_ACCEPT_PHASE].handlers, + cf->pool, 1, sizeof(ngx_stream_handler_pt)) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_array_init(&cmcf->phases[NGX_STREAM_PREACCESS_PHASE].handlers, + cf->pool, 1, sizeof(ngx_stream_handler_pt)) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_array_init(&cmcf->phases[NGX_STREAM_ACCESS_PHASE].handlers, + cf->pool, 1, sizeof(ngx_stream_handler_pt)) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_array_init(&cmcf->phases[NGX_STREAM_SSL_PHASE].handlers, + cf->pool, 1, sizeof(ngx_stream_handler_pt)) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_array_init(&cmcf->phases[NGX_STREAM_PREREAD_PHASE].handlers, + cf->pool, 1, sizeof(ngx_stream_handler_pt)) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_array_init(&cmcf->phases[NGX_STREAM_LOG_PHASE].handlers, + cf->pool, 1, sizeof(ngx_stream_handler_pt)) + != NGX_OK) + { + return NGX_ERROR; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_init_phase_handlers(ngx_conf_t *cf, + ngx_stream_core_main_conf_t *cmcf) +{ + ngx_int_t j; + ngx_uint_t i, n; + ngx_stream_handler_pt *h; + ngx_stream_phase_handler_t *ph; + ngx_stream_phase_handler_pt checker; + + n = 1 /* content phase */; + + for (i = 0; i < NGX_STREAM_LOG_PHASE; i++) { + n += cmcf->phases[i].handlers.nelts; + } + + ph = ngx_pcalloc(cf->pool, + n * sizeof(ngx_stream_phase_handler_t) + sizeof(void *)); + if (ph == NULL) { + return NGX_ERROR; + } + + cmcf->phase_engine.handlers = ph; + n = 0; + + for (i = 0; i < NGX_STREAM_LOG_PHASE; i++) { + h = cmcf->phases[i].handlers.elts; + + switch (i) { + + case NGX_STREAM_PREREAD_PHASE: + checker = ngx_stream_core_preread_phase; + break; + + case NGX_STREAM_CONTENT_PHASE: + ph->checker = ngx_stream_core_content_phase; + n++; + ph++; + + continue; + + default: + checker = ngx_stream_core_generic_phase; + } + + n += cmcf->phases[i].handlers.nelts; + + for (j = cmcf->phases[i].handlers.nelts - 1; j >= 0; j--) { + ph->checker = checker; + ph->handler = h[j]; + ph->next = n; + ph++; + } + } + + return NGX_OK; +} + + static ngx_int_t ngx_stream_add_ports(ngx_conf_t *cf, ngx_array_t *ports, ngx_stream_listen_t *listen) @@ -382,7 +504,7 @@ ngx_stream_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports) ls->keepcnt = addr[i].opt.tcp_keepcnt; #endif -#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) +#if (NGX_HAVE_INET6) ls->ipv6only = addr[i].opt.ipv6only; #endif diff --git a/src/stream/ngx_stream.h b/src/stream/ngx_stream.h index 6251cc7..9e4169c 100644 --- a/src/stream/ngx_stream.h +++ b/src/stream/ngx_stream.h @@ -49,15 +49,11 @@ typedef struct { unsigned bind:1; unsigned wildcard:1; -#if (NGX_STREAM_SSL) unsigned ssl:1; -#endif -#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) +#if (NGX_HAVE_INET6) unsigned ipv6only:1; #endif -#if (NGX_HAVE_REUSEPORT) unsigned reuseport:1; -#endif unsigned so_keepalive:2; unsigned proxy_protocol:1; #if (NGX_HAVE_KEEPALIVE_TUNABLE) @@ -73,9 +69,7 @@ typedef struct { typedef struct { ngx_stream_conf_ctx_t *ctx; ngx_str_t addr_text; -#if (NGX_STREAM_SSL) unsigned ssl:1; -#endif unsigned proxy_protocol:1; } ngx_stream_addr_conf_t; @@ -115,17 +109,47 @@ typedef struct { } ngx_stream_conf_addr_t; -typedef ngx_int_t (*ngx_stream_access_pt)(ngx_stream_session_t *s); +typedef enum { + NGX_STREAM_POST_ACCEPT_PHASE = 0, + NGX_STREAM_PREACCESS_PHASE, + NGX_STREAM_ACCESS_PHASE, + NGX_STREAM_SSL_PHASE, + NGX_STREAM_PREREAD_PHASE, + NGX_STREAM_CONTENT_PHASE, + NGX_STREAM_LOG_PHASE +} ngx_stream_phases; + + +typedef struct ngx_stream_phase_handler_s ngx_stream_phase_handler_t; + +typedef ngx_int_t (*ngx_stream_phase_handler_pt)(ngx_stream_session_t *s, + ngx_stream_phase_handler_t *ph); +typedef ngx_int_t (*ngx_stream_handler_pt)(ngx_stream_session_t *s); +typedef void (*ngx_stream_content_handler_pt)(ngx_stream_session_t *s); + + +struct ngx_stream_phase_handler_s { + ngx_stream_phase_handler_pt checker; + ngx_stream_handler_pt handler; + ngx_uint_t next; +}; + + +typedef struct { + ngx_stream_phase_handler_t *handlers; +} ngx_stream_phase_engine_t; + + +typedef struct { + ngx_array_t handlers; +} ngx_stream_phase_t; typedef struct { ngx_array_t servers; /* ngx_stream_core_srv_conf_t */ ngx_array_t listen; /* ngx_stream_listen_t */ - ngx_stream_access_pt realip_handler; - ngx_stream_access_pt limit_conn_handler; - ngx_stream_access_pt access_handler; - ngx_stream_access_pt access_log_handler; + ngx_stream_phase_engine_t phase_engine; ngx_hash_t variables_hash; @@ -136,14 +160,13 @@ typedef struct { ngx_uint_t variables_hash_bucket_size; ngx_hash_keys_arrays_t *variables_keys; + + ngx_stream_phase_t phases[NGX_STREAM_LOG_PHASE + 1]; } ngx_stream_core_main_conf_t; -typedef void (*ngx_stream_handler_pt)(ngx_stream_session_t *s); - - typedef struct { - ngx_stream_handler_pt handler; + ngx_stream_content_handler_pt handler; ngx_stream_conf_ctx_t *ctx; @@ -151,6 +174,8 @@ typedef struct { ngx_uint_t line; ngx_flag_t tcp_nodelay; + size_t preread_buffer_size; + ngx_msec_t preread_timeout; ngx_log_t *error_log; @@ -189,11 +214,14 @@ struct ngx_stream_session_s { u_char *captures_data; #endif + ngx_int_t phase_handler; ngx_uint_t status; -#if (NGX_STREAM_SSL) - ngx_uint_t ssl; /* unsigned ssl:1; */ -#endif + unsigned ssl:1; + + unsigned stat_processing:1; + + unsigned health_check:1; }; @@ -243,7 +271,20 @@ typedef struct { NULL) +#define NGX_STREAM_WRITE_BUFFERED 0x10 + + +void ngx_stream_core_run_phases(ngx_stream_session_t *s); +ngx_int_t ngx_stream_core_generic_phase(ngx_stream_session_t *s, + ngx_stream_phase_handler_t *ph); +ngx_int_t ngx_stream_core_preread_phase(ngx_stream_session_t *s, + ngx_stream_phase_handler_t *ph); +ngx_int_t ngx_stream_core_content_phase(ngx_stream_session_t *s, + ngx_stream_phase_handler_t *ph); + + void ngx_stream_init_connection(ngx_connection_t *c); +void ngx_stream_session_handler(ngx_event_t *rev); void ngx_stream_finalize_session(ngx_stream_session_t *s, ngx_uint_t rc); @@ -252,4 +293,11 @@ extern ngx_uint_t ngx_stream_max_module; extern ngx_module_t ngx_stream_core_module; +typedef ngx_int_t (*ngx_stream_filter_pt)(ngx_stream_session_t *s, + ngx_chain_t *chain, ngx_uint_t from_upstream); + + +extern ngx_stream_filter_pt ngx_stream_top_filter; + + #endif /* _NGX_STREAM_H_INCLUDED_ */ diff --git a/src/stream/ngx_stream_access_module.c b/src/stream/ngx_stream_access_module.c index 6985d36..1745cdf 100644 --- a/src/stream/ngx_stream_access_module.c +++ b/src/stream/ngx_stream_access_module.c @@ -275,7 +275,7 @@ ngx_stream_access_found(ngx_stream_session_t *s, ngx_uint_t deny) if (deny) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "access forbidden by rule"); - return NGX_ABORT; + return NGX_STREAM_FORBIDDEN; } return NGX_OK; @@ -443,10 +443,17 @@ ngx_stream_access_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) static ngx_int_t ngx_stream_access_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); - cmcf->access_handler = ngx_stream_access_handler; + + h = ngx_array_push(&cmcf->phases[NGX_STREAM_ACCESS_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_stream_access_handler; return NGX_OK; } diff --git a/src/stream/ngx_stream_core_module.c b/src/stream/ngx_stream_core_module.c index 1c808ad..f7870ee 100644 --- a/src/stream/ngx_stream_core_module.c +++ b/src/stream/ngx_stream_core_module.c @@ -91,6 +91,20 @@ static ngx_command_t ngx_stream_core_commands[] = { offsetof(ngx_stream_core_srv_conf_t, tcp_nodelay), NULL }, + { ngx_string("preread_buffer_size"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_core_srv_conf_t, preread_buffer_size), + NULL }, + + { ngx_string("preread_timeout"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_core_srv_conf_t, preread_timeout), + NULL }, + ngx_null_command }; @@ -123,6 +137,214 @@ ngx_module_t ngx_stream_core_module = { }; +void +ngx_stream_core_run_phases(ngx_stream_session_t *s) +{ + ngx_int_t rc; + ngx_stream_phase_handler_t *ph; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); + + ph = cmcf->phase_engine.handlers; + + while (ph[s->phase_handler].checker) { + + rc = ph[s->phase_handler].checker(s, &ph[s->phase_handler]); + + if (rc == NGX_OK) { + return; + } + } +} + + +ngx_int_t +ngx_stream_core_generic_phase(ngx_stream_session_t *s, + ngx_stream_phase_handler_t *ph) +{ + ngx_int_t rc; + + /* + * generic phase checker, + * used by all phases, except for preread and content + */ + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "generic phase: %ui", s->phase_handler); + + rc = ph->handler(s); + + if (rc == NGX_OK) { + s->phase_handler = ph->next; + return NGX_AGAIN; + } + + if (rc == NGX_DECLINED) { + s->phase_handler++; + return NGX_AGAIN; + } + + if (rc == NGX_AGAIN || rc == NGX_DONE) { + return NGX_OK; + } + + if (rc == NGX_ERROR) { + rc = NGX_STREAM_INTERNAL_SERVER_ERROR; + } + + ngx_stream_finalize_session(s, rc); + + return NGX_OK; +} + + +ngx_int_t +ngx_stream_core_preread_phase(ngx_stream_session_t *s, + ngx_stream_phase_handler_t *ph) +{ + size_t size; + ssize_t n; + ngx_int_t rc; + ngx_connection_t *c; + ngx_stream_core_srv_conf_t *cscf; + + c = s->connection; + + c->log->action = "prereading client data"; + + cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); + + if (c->read->timedout) { + rc = NGX_STREAM_OK; + + } else if (c->read->timer_set) { + rc = NGX_AGAIN; + + } else { + rc = ph->handler(s); + } + + while (rc == NGX_AGAIN) { + + if (c->buffer == NULL) { + c->buffer = ngx_create_temp_buf(c->pool, cscf->preread_buffer_size); + if (c->buffer == NULL) { + rc = NGX_ERROR; + break; + } + } + + size = c->buffer->end - c->buffer->last; + + if (size == 0) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, "preread buffer full"); + rc = NGX_STREAM_BAD_REQUEST; + break; + } + + if (c->read->eof) { + rc = NGX_STREAM_OK; + break; + } + + if (!c->read->ready) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + rc = NGX_ERROR; + break; + } + + if (!c->read->timer_set) { + ngx_add_timer(c->read, cscf->preread_timeout); + } + + c->read->handler = ngx_stream_session_handler; + + return NGX_OK; + } + + n = c->recv(c, c->buffer->last, size); + + if (n == NGX_ERROR) { + rc = NGX_STREAM_OK; + break; + } + + if (n > 0) { + c->buffer->last += n; + } + + rc = ph->handler(s); + } + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (rc == NGX_OK) { + s->phase_handler = ph->next; + return NGX_AGAIN; + } + + if (rc == NGX_DECLINED) { + s->phase_handler++; + return NGX_AGAIN; + } + + if (rc == NGX_DONE) { + return NGX_OK; + } + + if (rc == NGX_ERROR) { + rc = NGX_STREAM_INTERNAL_SERVER_ERROR; + } + + ngx_stream_finalize_session(s, rc); + + return NGX_OK; +} + + +ngx_int_t +ngx_stream_core_content_phase(ngx_stream_session_t *s, + ngx_stream_phase_handler_t *ph) +{ + int tcp_nodelay; + ngx_connection_t *c; + ngx_stream_core_srv_conf_t *cscf; + + c = s->connection; + + c->log->action = NULL; + + cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); + + if (c->type == SOCK_STREAM + && cscf->tcp_nodelay + && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) + { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, "tcp_nodelay"); + + tcp_nodelay = 1; + + if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, + (const void *) &tcp_nodelay, sizeof(int)) == -1) + { + ngx_connection_error(c, ngx_socket_errno, + "setsockopt(TCP_NODELAY) failed"); + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return NGX_OK; + } + + c->tcp_nodelay = NGX_TCP_NODELAY_SET; + } + + cscf->handler(s); + + return NGX_OK; +} + + static ngx_int_t ngx_stream_core_preconfiguration(ngx_conf_t *cf) { @@ -201,6 +423,8 @@ ngx_stream_core_create_srv_conf(ngx_conf_t *cf) cscf->resolver_timeout = NGX_CONF_UNSET_MSEC; cscf->proxy_protocol_timeout = NGX_CONF_UNSET_MSEC; cscf->tcp_nodelay = NGX_CONF_UNSET; + cscf->preread_buffer_size = NGX_CONF_UNSET_SIZE; + cscf->preread_timeout = NGX_CONF_UNSET_MSEC; return cscf; } @@ -253,6 +477,12 @@ ngx_stream_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->tcp_nodelay, prev->tcp_nodelay, 1); + ngx_conf_merge_size_value(conf->preread_buffer_size, + prev->preread_buffer_size, 16384); + + ngx_conf_merge_msec_value(conf->preread_timeout, + prev->preread_timeout, 30000); + return NGX_CONF_OK; } @@ -394,7 +624,7 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ls->wildcard = u.wildcard; ls->ctx = cf->ctx; -#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) +#if (NGX_HAVE_INET6) ls->ipv6only = 1; #endif diff --git a/src/stream/ngx_stream_handler.c b/src/stream/ngx_stream_handler.c index 6e2ed82..669b6a1 100644 --- a/src/stream/ngx_stream_handler.c +++ b/src/stream/ngx_stream_handler.c @@ -11,15 +11,10 @@ #include +static void ngx_stream_log_session(ngx_stream_session_t *s); static void ngx_stream_close_connection(ngx_connection_t *c); static u_char *ngx_stream_log_error(ngx_log_t *log, u_char *buf, size_t len); static void ngx_stream_proxy_protocol_handler(ngx_event_t *rev); -static void ngx_stream_init_session_handler(ngx_event_t *rev); - -#if (NGX_STREAM_SSL) -static void ngx_stream_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c); -static void ngx_stream_ssl_handshake_handler(ngx_connection_t *c); -#endif void @@ -134,6 +129,10 @@ ngx_stream_init_connection(ngx_connection_t *c) s->ssl = addr_conf->ssl; #endif + if (c->buffer) { + s->received += c->buffer->last - c->buffer->pos; + } + s->connection = c; c->data = s; @@ -150,7 +149,7 @@ ngx_stream_init_connection(ngx_connection_t *c) c->log->connection = c->number; c->log->handler = ngx_stream_log_error; c->log->data = s; - c->log->action = "initializing connection"; + c->log->action = "initializing session"; c->log_error = NGX_ERROR_INFO; s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_stream_max_module); @@ -175,7 +174,7 @@ ngx_stream_init_connection(ngx_connection_t *c) s->start_msec = tp->msec; rev = c->read; - rev->handler = ngx_stream_init_session_handler; + rev->handler = ngx_stream_session_handler; if (addr_conf->proxy_protocol) { c->log->action = "reading PROXY protocol"; @@ -275,192 +274,57 @@ ngx_stream_proxy_protocol_handler(ngx_event_t *rev) return; } - ngx_stream_init_session_handler(rev); + c->log->action = "initializing session"; + + ngx_stream_session_handler(rev); } -static void -ngx_stream_init_session_handler(ngx_event_t *rev) +void +ngx_stream_session_handler(ngx_event_t *rev) { - int tcp_nodelay; - ngx_int_t rc; - ngx_connection_t *c; - ngx_stream_session_t *s; - ngx_stream_core_srv_conf_t *cscf; - ngx_stream_core_main_conf_t *cmcf; + ngx_connection_t *c; + ngx_stream_session_t *s; c = rev->data; s = c->data; - c->log->action = "initializing session"; - - cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); - - if (cmcf->realip_handler) { - rc = cmcf->realip_handler(s); - - if (rc == NGX_ERROR) { - ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); - return; - } - } - - if (cmcf->limit_conn_handler) { - rc = cmcf->limit_conn_handler(s); - - if (rc == NGX_ERROR) { - ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); - return; - } - - if (rc == NGX_ABORT) { - ngx_stream_finalize_session(s, NGX_STREAM_SERVICE_UNAVAILABLE); - return; - } - } - - if (cmcf->access_handler) { - rc = cmcf->access_handler(s); - - if (rc == NGX_ERROR) { - ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); - return; - } - - if (rc == NGX_ABORT) { - ngx_stream_finalize_session(s, NGX_STREAM_FORBIDDEN); - return; - } - } - - cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); - - if (c->type == SOCK_STREAM - && cscf->tcp_nodelay - && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) - { - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, "tcp_nodelay"); - - tcp_nodelay = 1; - - if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, - (const void *) &tcp_nodelay, sizeof(int)) == -1) - { - ngx_connection_error(c, ngx_socket_errno, - "setsockopt(TCP_NODELAY) failed"); - ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); - return; - } - - c->tcp_nodelay = NGX_TCP_NODELAY_SET; - } - - -#if (NGX_STREAM_SSL) - { - ngx_stream_ssl_conf_t *sslcf; - - sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module); - - if (s->ssl) { - c->log->action = "SSL handshaking"; - - if (sslcf->ssl.ctx == NULL) { - ngx_log_error(NGX_LOG_ERR, c->log, 0, - "no \"ssl_certificate\" is defined " - "in server listening on SSL port"); - ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); - return; - } - - ngx_stream_ssl_init_connection(&sslcf->ssl, c); - return; - } - } -#endif - - c->log->action = "handling client connection"; - - cscf->handler(s); + ngx_stream_core_run_phases(s); } -#if (NGX_STREAM_SSL) - -static void -ngx_stream_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c) -{ - ngx_stream_session_t *s; - ngx_stream_ssl_conf_t *sslcf; - - s = c->data; - - if (ngx_ssl_create_connection(ssl, c, 0) == NGX_ERROR) { - ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); - return; - } - - if (ngx_ssl_handshake(c) == NGX_AGAIN) { - sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module); - - ngx_add_timer(c->read, sslcf->handshake_timeout); - - c->ssl->handler = ngx_stream_ssl_handshake_handler; - - return; - } - - ngx_stream_ssl_handshake_handler(c); -} - - -static void -ngx_stream_ssl_handshake_handler(ngx_connection_t *c) -{ - ngx_stream_session_t *s; - ngx_stream_core_srv_conf_t *cscf; - - if (!c->ssl->handshaked) { - ngx_stream_finalize_session(c->data, NGX_STREAM_INTERNAL_SERVER_ERROR); - return; - } - - if (c->read->timer_set) { - ngx_del_timer(c->read); - } - - c->log->action = "handling client connection"; - - s = c->data; - - cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); - - cscf->handler(s); -} - -#endif - - void ngx_stream_finalize_session(ngx_stream_session_t *s, ngx_uint_t rc) { - ngx_stream_core_main_conf_t *cmcf; - ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "finalize stream session: %i", rc); s->status = rc; - cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); - - if (cmcf->access_log_handler) { - (void) cmcf->access_log_handler(s); - } + ngx_stream_log_session(s); ngx_stream_close_connection(s->connection); } +static void +ngx_stream_log_session(ngx_stream_session_t *s) +{ + ngx_uint_t i, n; + ngx_stream_handler_pt *log_handler; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); + + log_handler = cmcf->phases[NGX_STREAM_LOG_PHASE].handlers.elts; + n = cmcf->phases[NGX_STREAM_LOG_PHASE].handlers.nelts; + + for (i = 0; i < n; i++) { + log_handler[i](s); + } +} + + static void ngx_stream_close_connection(ngx_connection_t *c) { diff --git a/src/stream/ngx_stream_limit_conn_module.c b/src/stream/ngx_stream_limit_conn_module.c index 40eca94..b64a426 100644 --- a/src/stream/ngx_stream_limit_conn_module.c +++ b/src/stream/ngx_stream_limit_conn_module.c @@ -178,7 +178,7 @@ ngx_stream_limit_conn_handler(ngx_stream_session_t *s) if (node == NULL) { ngx_shmtx_unlock(&shpool->mutex); ngx_stream_limit_conn_cleanup_all(s->connection->pool); - return NGX_ABORT; + return NGX_STREAM_SERVICE_UNAVAILABLE; } lc = (ngx_stream_limit_conn_node_t *) &node->color; @@ -203,7 +203,7 @@ ngx_stream_limit_conn_handler(ngx_stream_session_t *s) &limits[i].shm_zone->shm.name); ngx_stream_limit_conn_cleanup_all(s->connection->pool); - return NGX_ABORT; + return NGX_STREAM_SERVICE_UNAVAILABLE; } lc->conn++; @@ -630,11 +630,17 @@ ngx_stream_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) static ngx_int_t ngx_stream_limit_conn_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); - cmcf->limit_conn_handler = ngx_stream_limit_conn_handler; + h = ngx_array_push(&cmcf->phases[NGX_STREAM_PREACCESS_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_stream_limit_conn_handler; return NGX_OK; } diff --git a/src/stream/ngx_stream_log_module.c b/src/stream/ngx_stream_log_module.c index 4affbdf..26e6d22 100644 --- a/src/stream/ngx_stream_log_module.c +++ b/src/stream/ngx_stream_log_module.c @@ -1464,11 +1464,17 @@ ngx_stream_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) static ngx_int_t ngx_stream_log_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); - cmcf->access_log_handler = ngx_stream_log_handler; + h = ngx_array_push(&cmcf->phases[NGX_STREAM_LOG_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_stream_log_handler; return NGX_OK; } diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c index ed802e7..4231f97 100644 --- a/src/stream/ngx_stream_proxy_module.c +++ b/src/stream/ngx_stream_proxy_module.c @@ -84,10 +84,10 @@ static char *ngx_stream_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_stream_proxy_bind(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static ngx_int_t ngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s); #if (NGX_STREAM_SSL) +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 void ngx_stream_proxy_ssl_init_connection(ngx_stream_session_t *s); @@ -385,8 +385,6 @@ ngx_stream_proxy_handler(ngx_stream_session_t *s) } u->peer.type = c->type; - - u->proxy_protocol = pscf->proxy_protocol; u->start_sec = ngx_time(); c->write->handler = ngx_stream_proxy_downstream_handler; @@ -411,28 +409,6 @@ ngx_stream_proxy_handler(ngx_stream_session_t *s) u->downstream_buf.pos = p; u->downstream_buf.last = p; - if (u->proxy_protocol -#if (NGX_STREAM_SSL) - && pscf->ssl == NULL -#endif - && pscf->buffer_size >= NGX_PROXY_PROTOCOL_MAX_HEADER) - { - /* optimization for a typical case */ - - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, - "stream proxy send PROXY protocol header"); - - p = ngx_proxy_protocol_write(c, u->downstream_buf.last, - u->downstream_buf.end); - if (p == NULL) { - ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); - return; - } - - u->downstream_buf.last = p; - u->proxy_protocol = 0; - } - if (c->read->ready) { ngx_post_event(c->read, &ngx_posted_events); } @@ -545,6 +521,8 @@ found: return; } + u->upstream = uscf; + #if (NGX_STREAM_SSL) u->ssl_name = uscf->host; #endif @@ -682,8 +660,13 @@ ngx_stream_proxy_connect(ngx_stream_session_t *s) c->log->action = "connecting to upstream"; + pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); + u = s->upstream; + u->connected = 0; + u->proxy_protocol = pscf->proxy_protocol; + if (u->state) { u->state->response_time = ngx_current_msec - u->state->response_time; } @@ -740,8 +723,6 @@ ngx_stream_proxy_connect(ngx_stream_session_t *s) pc->read->handler = ngx_stream_proxy_connect_handler; pc->write->handler = ngx_stream_proxy_connect_handler; - pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); - ngx_add_timer(pc->write, pscf->connect_timeout); } @@ -751,6 +732,7 @@ ngx_stream_proxy_init_upstream(ngx_stream_session_t *s) { int tcp_nodelay; u_char *p; + ngx_chain_t *cl; ngx_connection_t *c, *pc; ngx_log_handler_pt handler; ngx_stream_upstream_t *u; @@ -782,21 +764,26 @@ ngx_stream_proxy_init_upstream(ngx_stream_session_t *s) pc->tcp_nodelay = NGX_TCP_NODELAY_SET; } - if (u->proxy_protocol) { - if (ngx_stream_proxy_send_proxy_protocol(s) != NGX_OK) { - return; - } - - u->proxy_protocol = 0; - } - pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); #if (NGX_STREAM_SSL) - if (pc->type == SOCK_STREAM && pscf->ssl && pc->ssl == NULL) { - ngx_stream_proxy_ssl_init_connection(s); - return; + + if (pc->type == SOCK_STREAM && pscf->ssl) { + + if (u->proxy_protocol) { + if (ngx_stream_proxy_send_proxy_protocol(s) != NGX_OK) { + return; + } + + u->proxy_protocol = 0; + } + + if (pc->ssl == NULL) { + ngx_stream_proxy_ssl_init_connection(s); + return; + } } + #endif c = s->connection; @@ -838,14 +825,66 @@ ngx_stream_proxy_init_upstream(ngx_stream_session_t *s) u->upstream_buf.last = p; } - if (c->type == SOCK_DGRAM) { - s->received = c->buffer->last - c->buffer->pos; - u->downstream_buf = *c->buffer; + 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); - if (pscf->responses == 0) { - pc->read->ready = 0; - pc->read->eof = 1; + cl = ngx_chain_get_free_buf(c->pool, &u->free); + if (cl == NULL) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; } + + *cl->buf = *c->buffer; + + cl->buf->tag = (ngx_buf_tag_t) &ngx_stream_proxy_module; + cl->buf->flush = 1; + cl->buf->last_buf = (c->type == SOCK_DGRAM); + + cl->next = u->upstream_out; + u->upstream_out = cl; + } + + if (u->proxy_protocol) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream proxy add PROXY protocol header"); + + cl = ngx_chain_get_free_buf(c->pool, &u->free); + if (cl == NULL) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + p = ngx_pnalloc(c->pool, NGX_PROXY_PROTOCOL_MAX_HEADER); + if (p == NULL) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + cl->buf->pos = p; + + p = ngx_proxy_protocol_write(c, p, p + NGX_PROXY_PROTOCOL_MAX_HEADER); + if (p == NULL) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + cl->buf->last = p; + cl->buf->temporary = 1; + cl->buf->flush = 0; + cl->buf->last_buf = 0; + cl->buf->tag = (ngx_buf_tag_t) &ngx_stream_proxy_module; + + cl->next = u->upstream_out; + u->upstream_out = cl; + + u->proxy_protocol = 0; + } + + if (c->type == SOCK_DGRAM && pscf->responses == 0) { + pc->read->ready = 0; + pc->read->eof = 1; } u->connected = 1; @@ -861,6 +900,8 @@ ngx_stream_proxy_init_upstream(ngx_stream_session_t *s) } +#if (NGX_STREAM_SSL) + static ngx_int_t ngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s) { @@ -931,8 +972,6 @@ ngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s) } -#if (NGX_STREAM_SSL) - static char * ngx_stream_proxy_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) @@ -1412,8 +1451,10 @@ ngx_stream_proxy_process(ngx_stream_session_t *s, ngx_uint_t from_upstream, size_t size, limit_rate; ssize_t n; ngx_buf_t *b; + ngx_int_t rc; ngx_uint_t flags; ngx_msec_t delay; + ngx_chain_t *cl, **ll, **out, **busy; ngx_connection_t *c, *pc, *src, *dst; ngx_log_handler_pt handler; ngx_stream_upstream_t *u; @@ -1447,6 +1488,8 @@ ngx_stream_proxy_process(ngx_stream_session_t *s, ngx_uint_t from_upstream, b = &u->upstream_buf; limit_rate = pscf->download_rate; received = &u->received; + out = &u->downstream_out; + busy = &u->downstream_busy; } else { src = c; @@ -1454,24 +1497,18 @@ ngx_stream_proxy_process(ngx_stream_session_t *s, ngx_uint_t from_upstream, b = &u->downstream_buf; limit_rate = pscf->upload_rate; received = &s->received; + out = &u->upstream_out; + busy = &u->upstream_busy; } for ( ;; ) { - if (do_write) { + if (do_write && dst) { - size = b->last - b->pos; + if (*out || *busy || dst->buffered) { + rc = ngx_stream_top_filter(s, *out, from_upstream); - if (size && dst && dst->write->ready) { - - n = dst->send(dst, b->pos, size); - - if (n == NGX_AGAIN && dst->shared) { - /* cannot wait on a shared socket */ - n = NGX_ERROR; - } - - if (n == NGX_ERROR) { + if (rc == NGX_ERROR) { if (c->type == SOCK_DGRAM && !from_upstream) { ngx_stream_proxy_next_upstream(s); return; @@ -1481,13 +1518,12 @@ ngx_stream_proxy_process(ngx_stream_session_t *s, ngx_uint_t from_upstream, return; } - if (n > 0) { - b->pos += n; + ngx_chain_update_chains(c->pool, &u->free, busy, out, + (ngx_buf_tag_t) &ngx_stream_proxy_module); - if (b->pos == b->last) { - b->pos = b->start; - b->last = b->start; - } + if (*busy == NULL) { + b->pos = b->start; + b->last = b->start; } } } @@ -1514,11 +1550,21 @@ ngx_stream_proxy_process(ngx_stream_session_t *s, ngx_uint_t from_upstream, n = src->recv(src, b->last, size); - if (n == NGX_AGAIN || n == 0) { + if (n == NGX_AGAIN) { break; } - if (n > 0) { + if (n == NGX_ERROR) { + if (c->type == SOCK_DGRAM && u->received == 0) { + ngx_stream_proxy_next_upstream(s); + return; + } + + src->read->eof = 1; + n = 0; + } + + if (n >= 0) { if (limit_rate) { delay = (ngx_msec_t) (n * 1000 / limit_rate); @@ -1541,27 +1587,37 @@ ngx_stream_proxy_process(ngx_stream_session_t *s, ngx_uint_t from_upstream, src->read->eof = 1; } + for (ll = out; *ll; ll = &(*ll)->next) { /* void */ } + + cl = ngx_chain_get_free_buf(c->pool, &u->free); + if (cl == NULL) { + ngx_stream_proxy_finalize(s, + NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + *ll = cl; + + cl->buf->pos = b->last; + cl->buf->last = b->last + n; + cl->buf->tag = (ngx_buf_tag_t) &ngx_stream_proxy_module; + + cl->buf->temporary = (n ? 1 : 0); + cl->buf->last_buf = src->read->eof; + cl->buf->flush = 1; + *received += n; b->last += n; do_write = 1; continue; } - - if (n == NGX_ERROR) { - if (c->type == SOCK_DGRAM && u->received == 0) { - ngx_stream_proxy_next_upstream(s); - return; - } - - src->read->eof = 1; - } } break; } - if (src->read->eof && (b->pos == b->last || (dst && dst->read->eof))) { + if (src->read->eof && dst && (dst->read->eof || !dst->buffered)) { handler = c->log->handler; c->log->handler = NULL; @@ -1614,6 +1670,14 @@ ngx_stream_proxy_next_upstream(ngx_stream_session_t *s) "stream proxy next upstream"); u = s->upstream; + pc = u->peer.connection; + + if (u->upstream_out || u->upstream_busy || (pc && pc->buffered)) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "pending buffers on next upstream"); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } if (u->peer.sockaddr) { u->peer.free(&u->peer, u->peer.data, NGX_PEER_FAILED); @@ -1632,8 +1696,6 @@ ngx_stream_proxy_next_upstream(ngx_stream_session_t *s) return; } - pc = u->peer.connection; - if (pc) { ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "close proxy upstream connection: %d", pc->fd); diff --git a/src/stream/ngx_stream_realip_module.c b/src/stream/ngx_stream_realip_module.c index 8ce05a0..0740431 100644 --- a/src/stream/ngx_stream_realip_module.c +++ b/src/stream/ngx_stream_realip_module.c @@ -279,11 +279,17 @@ ngx_stream_realip_add_variables(ngx_conf_t *cf) static ngx_int_t ngx_stream_realip_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); - cmcf->realip_handler = ngx_stream_realip_handler; + h = ngx_array_push(&cmcf->phases[NGX_STREAM_POST_ACCEPT_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_stream_realip_handler; return NGX_OK; } diff --git a/src/stream/ngx_stream_return_module.c b/src/stream/ngx_stream_return_module.c index c22087f..9301b02 100644 --- a/src/stream/ngx_stream_return_module.c +++ b/src/stream/ngx_stream_return_module.c @@ -11,12 +11,12 @@ typedef struct { - ngx_stream_complex_value_t text; + ngx_stream_complex_value_t text; } ngx_stream_return_srv_conf_t; typedef struct { - ngx_buf_t buf; + ngx_chain_t *out; } ngx_stream_return_ctx_t; @@ -72,6 +72,7 @@ static void ngx_stream_return_handler(ngx_stream_session_t *s) { ngx_str_t text; + ngx_buf_t *b; ngx_connection_t *c; ngx_stream_return_ctx_t *ctx; ngx_stream_return_srv_conf_t *rscf; @@ -103,8 +104,25 @@ ngx_stream_return_handler(ngx_stream_session_t *s) ngx_stream_set_ctx(s, ctx, ngx_stream_return_module); - ctx->buf.pos = text.data; - ctx->buf.last = text.data + text.len; + b = ngx_calloc_buf(c->pool); + if (b == NULL) { + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + b->memory = 1; + b->pos = text.data; + b->last = text.data + text.len; + b->last_buf = 1; + + ctx->out = ngx_alloc_chain_link(c->pool); + if (ctx->out == NULL) { + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + ctx->out->buf = b; + ctx->out->next = NULL; c->write->handler = ngx_stream_return_write_handler; @@ -115,8 +133,6 @@ ngx_stream_return_handler(ngx_stream_session_t *s) static void ngx_stream_return_write_handler(ngx_event_t *ev) { - ssize_t n; - ngx_buf_t *b; ngx_connection_t *c; ngx_stream_session_t *s; ngx_stream_return_ctx_t *ctx; @@ -130,25 +146,20 @@ ngx_stream_return_write_handler(ngx_event_t *ev) return; } - if (ev->ready) { - ctx = ngx_stream_get_module_ctx(s, ngx_stream_return_module); + ctx = ngx_stream_get_module_ctx(s, ngx_stream_return_module); - b = &ctx->buf; + if (ngx_stream_top_filter(s, ctx->out, 1) == NGX_ERROR) { + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } - n = c->send(c, b->pos, b->last - b->pos); - if (n == NGX_ERROR) { - ngx_stream_finalize_session(s, NGX_STREAM_OK); - return; - } + ctx->out = NULL; - if (n > 0) { - b->pos += n; - - if (b->pos == b->last) { - ngx_stream_finalize_session(s, NGX_STREAM_OK); - return; - } - } + if (!c->buffered) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream return done sending"); + ngx_stream_finalize_session(s, NGX_STREAM_OK); + return; } if (ngx_handle_write_event(ev, 0) != NGX_OK) { diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c index 2661220..d00718b 100644 --- a/src/stream/ngx_stream_ssl_module.c +++ b/src/stream/ngx_stream_ssl_module.c @@ -18,6 +18,10 @@ typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c, #define NGX_DEFAULT_ECDH_CURVE "auto" +static ngx_int_t ngx_stream_ssl_handler(ngx_stream_session_t *s); +static ngx_int_t ngx_stream_ssl_init_connection(ngx_ssl_t *ssl, + ngx_connection_t *c); +static void ngx_stream_ssl_handshake_handler(ngx_connection_t *c); static ngx_int_t ngx_stream_ssl_static_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_ssl_variable(ngx_stream_session_t *s, @@ -32,6 +36,7 @@ static char *ngx_stream_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_stream_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static ngx_int_t ngx_stream_ssl_init(ngx_conf_t *cf); static ngx_conf_bitmask_t ngx_stream_ssl_protocols[] = { @@ -143,7 +148,7 @@ static ngx_command_t ngx_stream_ssl_commands[] = { static ngx_stream_module_t ngx_stream_ssl_module_ctx = { ngx_stream_ssl_add_variables, /* preconfiguration */ - NULL, /* postconfiguration */ + ngx_stream_ssl_init, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ @@ -193,6 +198,88 @@ static ngx_stream_variable_t ngx_stream_ssl_vars[] = { static ngx_str_t ngx_stream_ssl_sess_id_ctx = ngx_string("STREAM"); +static ngx_int_t +ngx_stream_ssl_handler(ngx_stream_session_t *s) +{ + ngx_connection_t *c; + ngx_stream_ssl_conf_t *sslcf; + + c = s->connection; + + sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module); + + if (s->ssl && c->ssl == NULL) { + c->log->action = "SSL handshaking"; + + if (sslcf->ssl.ctx == NULL) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "no \"ssl_certificate\" is defined " + "in server listening on SSL port"); + return NGX_ERROR; + } + + return ngx_stream_ssl_init_connection(&sslcf->ssl, c); + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c) +{ + ngx_int_t rc; + ngx_stream_session_t *s; + ngx_stream_ssl_conf_t *sslcf; + + s = c->data; + + if (ngx_ssl_create_connection(ssl, c, 0) == NGX_ERROR) { + return NGX_ERROR; + } + + rc = ngx_ssl_handshake(c); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_AGAIN) { + sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module); + + ngx_add_timer(c->read, sslcf->handshake_timeout); + + c->ssl->handler = ngx_stream_ssl_handshake_handler; + + return NGX_AGAIN; + } + + /* rc == NGX_OK */ + + return NGX_OK; +} + + +static void +ngx_stream_ssl_handshake_handler(ngx_connection_t *c) +{ + ngx_stream_session_t *s; + + s = c->data; + + if (!c->ssl->handshaked) { + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + ngx_stream_core_run_phases(s); +} + + static ngx_int_t ngx_stream_ssl_static_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data) @@ -565,3 +652,22 @@ invalid: return NGX_CONF_ERROR; } + + +static ngx_int_t +ngx_stream_ssl_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_SSL_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_stream_ssl_handler; + + return NGX_OK; +} diff --git a/src/stream/ngx_stream_ssl_preread_module.c b/src/stream/ngx_stream_ssl_preread_module.c new file mode 100644 index 0000000..e26c518 --- /dev/null +++ b/src/stream/ngx_stream_ssl_preread_module.c @@ -0,0 +1,449 @@ + +/* + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + ngx_flag_t enabled; +} ngx_stream_ssl_preread_srv_conf_t; + + +typedef struct { + size_t left; + size_t size; + u_char *pos; + u_char *dst; + u_char buf[4]; + ngx_str_t host; + ngx_log_t *log; + ngx_pool_t *pool; + ngx_uint_t state; +} ngx_stream_ssl_preread_ctx_t; + + +static ngx_int_t ngx_stream_ssl_preread_handler(ngx_stream_session_t *s); +static ngx_int_t ngx_stream_ssl_preread_parse_record( + ngx_stream_ssl_preread_ctx_t *ctx, u_char *pos, u_char *last); +static ngx_int_t ngx_stream_ssl_preread_server_name_variable( + ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_ssl_preread_add_variables(ngx_conf_t *cf); +static void *ngx_stream_ssl_preread_create_srv_conf(ngx_conf_t *cf); +static char *ngx_stream_ssl_preread_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); +static ngx_int_t ngx_stream_ssl_preread_init(ngx_conf_t *cf); + + +static ngx_command_t ngx_stream_ssl_preread_commands[] = { + + { ngx_string("ssl_preread"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_preread_srv_conf_t, enabled), + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_ssl_preread_module_ctx = { + ngx_stream_ssl_preread_add_variables, /* preconfiguration */ + ngx_stream_ssl_preread_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_stream_ssl_preread_create_srv_conf, /* create server configuration */ + ngx_stream_ssl_preread_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_ssl_preread_module = { + NGX_MODULE_V1, + &ngx_stream_ssl_preread_module_ctx, /* module context */ + ngx_stream_ssl_preread_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_stream_variable_t ngx_stream_ssl_preread_vars[] = { + + { ngx_string("ssl_preread_server_name"), NULL, + ngx_stream_ssl_preread_server_name_variable, 0, 0, 0 }, + + { ngx_null_string, NULL, NULL, 0, 0, 0 } +}; + + +static ngx_int_t +ngx_stream_ssl_preread_handler(ngx_stream_session_t *s) +{ + u_char *last, *p; + size_t len; + ngx_int_t rc; + ngx_connection_t *c; + ngx_stream_ssl_preread_ctx_t *ctx; + ngx_stream_ssl_preread_srv_conf_t *sscf; + + c = s->connection; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, "ssl preread handler"); + + sscf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_preread_module); + + if (!sscf->enabled) { + return NGX_DECLINED; + } + + if (c->type != SOCK_STREAM) { + return NGX_DECLINED; + } + + if (c->buffer == NULL) { + return NGX_AGAIN; + } + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_ssl_preread_module); + if (ctx == NULL) { + ctx = ngx_pcalloc(c->pool, sizeof(ngx_stream_ssl_preread_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_stream_set_ctx(s, ctx, ngx_stream_ssl_preread_module); + + ctx->pool = c->pool; + ctx->log = c->log; + ctx->pos = c->buffer->pos; + } + + p = ctx->pos; + last = c->buffer->last; + + while (last - p >= 5) { + + if (p[0] != 0x16) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: not a handshake"); + return NGX_DECLINED; + } + + if (p[1] != 3 || p[2] == 0) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: unsupported SSL version"); + return NGX_DECLINED; + } + + len = (p[3] << 8) + p[4]; + + /* read the whole record before parsing */ + if ((size_t) (last - p) < len + 5) { + break; + } + + p += 5; + + rc = ngx_stream_ssl_preread_parse_record(ctx, p, p + len); + if (rc != NGX_AGAIN) { + return rc; + } + + p += len; + } + + ctx->pos = p; + + return NGX_AGAIN; +} + + +static ngx_int_t +ngx_stream_ssl_preread_parse_record(ngx_stream_ssl_preread_ctx_t *ctx, + u_char *pos, u_char *last) +{ + size_t left, n, size; + u_char *dst, *p; + + enum { + sw_start = 0, + sw_header, /* handshake msg_type, length */ + sw_head_tail, /* version, random */ + sw_sid_len, /* session_id length */ + sw_sid, /* session_id */ + sw_cs_len, /* cipher_suites length */ + sw_cs, /* cipher_suites */ + sw_cm_len, /* compression_methods length */ + sw_cm, /* compression_methods */ + sw_ext, /* extension */ + sw_ext_header, /* extension_type, extension_data length */ + sw_sni_len, /* SNI length */ + sw_sni_host_head, /* SNI name_type, host_name length */ + sw_sni_host /* SNI host_name */ + } state; + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: state %ui left %z", ctx->state, ctx->left); + + state = ctx->state; + size = ctx->size; + left = ctx->left; + dst = ctx->dst; + p = ctx->buf; + + for ( ;; ) { + n = ngx_min((size_t) (last - pos), size); + + if (dst) { + dst = ngx_cpymem(dst, pos, n); + } + + pos += n; + size -= n; + left -= n; + + if (size != 0) { + break; + } + + switch (state) { + + case sw_start: + state = sw_header; + dst = p; + size = 4; + left = size; + break; + + case sw_header: + if (p[0] != 1) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: not a client hello"); + return NGX_DECLINED; + } + + state = sw_head_tail; + dst = NULL; + size = 34; + left = (p[1] << 16) + (p[2] << 8) + p[3]; + break; + + case sw_head_tail: + state = sw_sid_len; + dst = p; + size = 1; + break; + + case sw_sid_len: + state = sw_sid; + dst = NULL; + size = p[0]; + break; + + case sw_sid: + state = sw_cs_len; + dst = p; + size = 2; + break; + + case sw_cs_len: + state = sw_cs; + dst = NULL; + size = (p[0] << 8) + p[1]; + break; + + case sw_cs: + state = sw_cm_len; + dst = p; + size = 1; + break; + + case sw_cm_len: + state = sw_cm; + dst = NULL; + size = p[0]; + break; + + case sw_cm: + if (left == 0) { + /* no extensions */ + return NGX_OK; + } + + state = sw_ext; + dst = p; + size = 2; + break; + + case sw_ext: + if (left == 0) { + return NGX_OK; + } + + state = sw_ext_header; + dst = p; + size = 4; + break; + + case sw_ext_header: + if (p[0] == 0 && p[1] == 0) { + /* SNI extension */ + state = sw_sni_len; + dst = NULL; + size = 2; + break; + } + + state = sw_ext; + dst = NULL; + size = (p[2] << 8) + p[3]; + break; + + case sw_sni_len: + state = sw_sni_host_head; + dst = p; + size = 3; + break; + + case sw_sni_host_head: + if (p[0] != 0) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: SNI hostname type is not DNS"); + return NGX_DECLINED; + } + + state = sw_sni_host; + size = (p[1] << 8) + p[2]; + + ctx->host.data = ngx_pnalloc(ctx->pool, size); + if (ctx->host.data == NULL) { + return NGX_ERROR; + } + + dst = ctx->host.data; + break; + + case sw_sni_host: + ctx->host.len = (p[1] << 8) + p[2]; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: SNI hostname \"%V\"", &ctx->host); + return NGX_OK; + } + + if (left < size) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: failed to parse handshake"); + return NGX_DECLINED; + } + } + + ctx->state = state; + ctx->size = size; + ctx->left = left; + ctx->dst = dst; + + return NGX_AGAIN; +} + + +static ngx_int_t +ngx_stream_ssl_preread_server_name_variable(ngx_stream_session_t *s, + ngx_variable_value_t *v, uintptr_t data) +{ + ngx_stream_ssl_preread_ctx_t *ctx; + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_ssl_preread_module); + + if (ctx == NULL) { + v->not_found = 1; + return NGX_OK; + } + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->len = ctx->host.len; + v->data = ctx->host.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_ssl_preread_add_variables(ngx_conf_t *cf) +{ + ngx_stream_variable_t *var, *v; + + for (v = ngx_stream_ssl_preread_vars; v->name.len; v++) { + var = ngx_stream_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + +static void * +ngx_stream_ssl_preread_create_srv_conf(ngx_conf_t *cf) +{ + ngx_stream_ssl_preread_srv_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_ssl_preread_srv_conf_t)); + if (conf == NULL) { + return NULL; + } + + conf->enabled = NGX_CONF_UNSET; + + return conf; +} + + +static char * +ngx_stream_ssl_preread_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_stream_ssl_preread_srv_conf_t *prev = parent; + ngx_stream_ssl_preread_srv_conf_t *conf = child; + + ngx_conf_merge_value(conf->enabled, prev->enabled, 0); + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_stream_ssl_preread_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_PREREAD_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_stream_ssl_preread_handler; + + return NGX_OK; +} diff --git a/src/stream/ngx_stream_upstream.c b/src/stream/ngx_stream_upstream.c index 0c59780..c9e1784 100644 --- a/src/stream/ngx_stream_upstream.c +++ b/src/stream/ngx_stream_upstream.c @@ -322,6 +322,7 @@ ngx_stream_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) uscf = ngx_stream_upstream_add(cf, &u, NGX_STREAM_UPSTREAM_CREATE |NGX_STREAM_UPSTREAM_WEIGHT + |NGX_STREAM_UPSTREAM_MAX_CONNS |NGX_STREAM_UPSTREAM_MAX_FAILS |NGX_STREAM_UPSTREAM_FAIL_TIMEOUT |NGX_STREAM_UPSTREAM_DOWN @@ -407,7 +408,7 @@ ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) time_t fail_timeout; ngx_str_t *value, s; ngx_url_t u; - ngx_int_t weight, max_fails; + ngx_int_t weight, max_conns, max_fails; ngx_uint_t i; ngx_stream_upstream_server_t *us; @@ -421,6 +422,7 @@ ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) value = cf->args->elts; weight = 1; + max_conns = 0; max_fails = 1; fail_timeout = 10; @@ -441,6 +443,21 @@ ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) continue; } + if (ngx_strncmp(value[i].data, "max_conns=", 10) == 0) { + + if (!(uscf->flags & NGX_STREAM_UPSTREAM_MAX_CONNS)) { + goto not_supported; + } + + max_conns = ngx_atoi(&value[i].data[10], value[i].len - 10); + + if (max_conns == NGX_ERROR) { + goto invalid; + } + + continue; + } + if (ngx_strncmp(value[i].data, "max_fails=", 10) == 0) { if (!(uscf->flags & NGX_STREAM_UPSTREAM_MAX_FAILS)) { @@ -522,6 +539,7 @@ ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) us->addrs = u.addrs; us->naddrs = u.naddrs; us->weight = weight; + us->max_conns = max_conns; us->max_fails = max_fails; us->fail_timeout = fail_timeout; @@ -586,14 +604,14 @@ ngx_stream_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags) } if ((uscfp[i]->flags & NGX_STREAM_UPSTREAM_CREATE) && !u->no_port) { - ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "upstream \"%V\" may not have port %d", &u->host, u->port); return NULL; } if ((flags & NGX_STREAM_UPSTREAM_CREATE) && !uscfp[i]->no_port) { - ngx_log_error(NGX_LOG_WARN, cf->log, 0, + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "upstream \"%V\" may not have port %d in %s:%ui", &u->host, uscfp[i]->port, uscfp[i]->file_name, uscfp[i]->line); diff --git a/src/stream/ngx_stream_upstream.h b/src/stream/ngx_stream_upstream.h index f83b5ba..764a340 100644 --- a/src/stream/ngx_stream_upstream.h +++ b/src/stream/ngx_stream_upstream.h @@ -21,6 +21,7 @@ #define NGX_STREAM_UPSTREAM_FAIL_TIMEOUT 0x0008 #define NGX_STREAM_UPSTREAM_DOWN 0x0010 #define NGX_STREAM_UPSTREAM_BACKUP 0x0020 +#define NGX_STREAM_UPSTREAM_MAX_CONNS 0x0100 typedef struct { @@ -50,11 +51,16 @@ typedef struct { ngx_addr_t *addrs; ngx_uint_t naddrs; ngx_uint_t weight; + ngx_uint_t max_conns; ngx_uint_t max_fails; time_t fail_timeout; + ngx_msec_t slow_start; unsigned down:1; unsigned backup:1; + + NGX_COMPAT_BEGIN(4) + NGX_COMPAT_END } ngx_stream_upstream_server_t; @@ -106,14 +112,23 @@ typedef struct { typedef struct { ngx_peer_connection_t peer; + ngx_buf_t downstream_buf; ngx_buf_t upstream_buf; + + ngx_chain_t *free; + ngx_chain_t *upstream_out; + ngx_chain_t *upstream_busy; + ngx_chain_t *downstream_out; + ngx_chain_t *downstream_busy; + off_t received; time_t start_sec; ngx_uint_t responses; -#if (NGX_STREAM_SSL) + ngx_str_t ssl_name; -#endif + + ngx_stream_upstream_srv_conf_t *upstream; ngx_stream_upstream_resolved_t *resolved; ngx_stream_upstream_state_t *state; unsigned connected:1; diff --git a/src/stream/ngx_stream_upstream_hash_module.c b/src/stream/ngx_stream_upstream_hash_module.c index 88185eb..cb44fcd 100644 --- a/src/stream/ngx_stream_upstream_hash_module.c +++ b/src/stream/ngx_stream_upstream_hash_module.c @@ -241,6 +241,10 @@ ngx_stream_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data) goto next; } + if (peer->max_conns && peer->conns >= peer->max_conns) { + goto next; + } + break; next: @@ -524,7 +528,6 @@ ngx_stream_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data) peer; peer = peer->next, i++) { - n = i / (8 * sizeof(uintptr_t)); m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); @@ -550,6 +553,10 @@ ngx_stream_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data) continue; } + if (peer->max_conns && peer->conns >= peer->max_conns) { + continue; + } + peer->current_weight += peer->effective_weight; total += peer->effective_weight; @@ -572,6 +579,7 @@ ngx_stream_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data) hp->tries++; if (hp->tries >= points->number) { + pc->name = hp->rrp.peers->name; ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); return NGX_BUSY; } @@ -646,6 +654,7 @@ ngx_stream_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) uscf->flags = NGX_STREAM_UPSTREAM_CREATE |NGX_STREAM_UPSTREAM_WEIGHT + |NGX_STREAM_UPSTREAM_MAX_CONNS |NGX_STREAM_UPSTREAM_MAX_FAILS |NGX_STREAM_UPSTREAM_FAIL_TIMEOUT |NGX_STREAM_UPSTREAM_DOWN; diff --git a/src/stream/ngx_stream_upstream_least_conn_module.c b/src/stream/ngx_stream_upstream_least_conn_module.c index e884975..739b20a 100644 --- a/src/stream/ngx_stream_upstream_least_conn_module.c +++ b/src/stream/ngx_stream_upstream_least_conn_module.c @@ -132,7 +132,6 @@ ngx_stream_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data) peer; peer = peer->next, i++) { - n = i / (8 * sizeof(uintptr_t)); m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); @@ -151,6 +150,10 @@ ngx_stream_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data) continue; } + if (peer->max_conns && peer->conns >= peer->max_conns) { + continue; + } + /* * select peer with least number of connections; if there are * multiple peers with the same number of connections, select @@ -206,6 +209,10 @@ ngx_stream_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data) continue; } + if (peer->max_conns && peer->conns >= peer->max_conns) { + continue; + } + peer->current_weight += peer->effective_weight; total += peer->effective_weight; @@ -269,12 +276,6 @@ failed: ngx_stream_upstream_rr_peers_wlock(peers); } - /* all peers failed, mark them as live for quick recovery */ - - for (peer = peers->peer; peer; peer = peer->next) { - peer->fails = 0; - } - ngx_stream_upstream_rr_peers_unlock(peers); pc->name = peers->name; @@ -299,6 +300,7 @@ ngx_stream_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) uscf->flags = NGX_STREAM_UPSTREAM_CREATE |NGX_STREAM_UPSTREAM_WEIGHT + |NGX_STREAM_UPSTREAM_MAX_CONNS |NGX_STREAM_UPSTREAM_MAX_FAILS |NGX_STREAM_UPSTREAM_FAIL_TIMEOUT |NGX_STREAM_UPSTREAM_DOWN diff --git a/src/stream/ngx_stream_upstream_round_robin.c b/src/stream/ngx_stream_upstream_round_robin.c index 7aced0f..3a62501 100644 --- a/src/stream/ngx_stream_upstream_round_robin.c +++ b/src/stream/ngx_stream_upstream_round_robin.c @@ -96,6 +96,7 @@ ngx_stream_upstream_init_round_robin(ngx_conf_t *cf, peer[n].weight = server[i].weight; peer[n].effective_weight = server[i].weight; peer[n].current_weight = 0; + peer[n].max_conns = server[i].max_conns; peer[n].max_fails = server[i].max_fails; peer[n].fail_timeout = server[i].fail_timeout; peer[n].down = server[i].down; @@ -159,6 +160,7 @@ ngx_stream_upstream_init_round_robin(ngx_conf_t *cf, peer[n].weight = server[i].weight; peer[n].effective_weight = server[i].weight; peer[n].current_weight = 0; + peer[n].max_conns = server[i].max_conns; peer[n].max_fails = server[i].max_fails; peer[n].fail_timeout = server[i].fail_timeout; peer[n].down = server[i].down; @@ -227,6 +229,7 @@ ngx_stream_upstream_init_round_robin(ngx_conf_t *cf, peer[i].weight = 1; peer[i].effective_weight = 1; peer[i].current_weight = 0; + peer[i].max_conns = 0; peer[i].max_fails = 1; peer[i].fail_timeout = 10; *peerp = &peer[i]; @@ -262,6 +265,7 @@ ngx_stream_upstream_init_round_robin_peer(ngx_stream_session_t *s, rrp->peers = us->peer.data; rrp->current = NULL; + rrp->config = 0; n = rrp->peers->number; @@ -344,6 +348,7 @@ ngx_stream_upstream_create_round_robin_peer(ngx_stream_session_t *s, peer[0].weight = 1; peer[0].effective_weight = 1; peer[0].current_weight = 0; + peer[0].max_conns = 0; peer[0].max_fails = 1; peer[0].fail_timeout = 10; peers->peer = peer; @@ -377,6 +382,7 @@ ngx_stream_upstream_create_round_robin_peer(ngx_stream_session_t *s, peer[i].weight = 1; peer[i].effective_weight = 1; peer[i].current_weight = 0; + peer[i].max_conns = 0; peer[i].max_fails = 1; peer[i].fail_timeout = 10; *peerp = &peer[i]; @@ -386,6 +392,7 @@ ngx_stream_upstream_create_round_robin_peer(ngx_stream_session_t *s, rrp->peers = peers; rrp->current = NULL; + rrp->config = 0; if (rrp->peers->number <= 8 * sizeof(uintptr_t)) { rrp->tried = &rrp->data; @@ -438,6 +445,10 @@ ngx_stream_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data) goto failed; } + if (peer->max_conns && peer->conns >= peer->max_conns) { + goto failed; + } + rrp->current = peer; } else { @@ -491,12 +502,6 @@ failed: ngx_stream_upstream_rr_peers_wlock(peers); } - /* all peers failed, mark them as live for quick recovery */ - - for (peer = peers->peer; peer; peer = peer->next) { - peer->fails = 0; - } - ngx_stream_upstream_rr_peers_unlock(peers); pc->name = peers->name; @@ -527,7 +532,6 @@ ngx_stream_upstream_get_peer(ngx_stream_upstream_rr_peer_data_t *rrp) peer; peer = peer->next, i++) { - n = i / (8 * sizeof(uintptr_t)); m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); @@ -546,6 +550,10 @@ ngx_stream_upstream_get_peer(ngx_stream_upstream_rr_peer_data_t *rrp) continue; } + if (peer->max_conns && peer->conns >= peer->max_conns) { + continue; + } + peer->current_weight += peer->effective_weight; total += peer->effective_weight; diff --git a/src/stream/ngx_stream_upstream_round_robin.h b/src/stream/ngx_stream_upstream_round_robin.h index 452c2e9..35d9fce 100644 --- a/src/stream/ngx_stream_upstream_round_robin.h +++ b/src/stream/ngx_stream_upstream_round_robin.h @@ -27,6 +27,7 @@ struct ngx_stream_upstream_rr_peer_s { ngx_int_t weight; ngx_uint_t conns; + ngx_uint_t max_conns; ngx_uint_t fails; time_t accessed; @@ -34,19 +35,22 @@ struct ngx_stream_upstream_rr_peer_s { ngx_uint_t max_fails; time_t fail_timeout; + ngx_msec_t slow_start; + ngx_msec_t start_time; - ngx_uint_t down; /* unsigned down:1; */ + ngx_uint_t down; -#if (NGX_STREAM_SSL) void *ssl_session; int ssl_session_len; -#endif - - ngx_stream_upstream_rr_peer_t *next; #if (NGX_STREAM_UPSTREAM_ZONE) ngx_atomic_t lock; #endif + + ngx_stream_upstream_rr_peer_t *next; + + NGX_COMPAT_BEGIN(25) + NGX_COMPAT_END }; @@ -119,6 +123,7 @@ struct ngx_stream_upstream_rr_peers_s { typedef struct { + ngx_uint_t config; ngx_stream_upstream_rr_peers_t *peers; ngx_stream_upstream_rr_peer_t *current; uintptr_t *tried; diff --git a/src/stream/ngx_stream_write_filter_module.c b/src/stream/ngx_stream_write_filter_module.c new file mode 100644 index 0000000..8fdcd37 --- /dev/null +++ b/src/stream/ngx_stream_write_filter_module.c @@ -0,0 +1,273 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + ngx_chain_t *from_upstream; + ngx_chain_t *from_downstream; +} ngx_stream_write_filter_ctx_t; + + +static ngx_int_t ngx_stream_write_filter(ngx_stream_session_t *s, + ngx_chain_t *in, ngx_uint_t from_upstream); +static ngx_int_t ngx_stream_write_filter_init(ngx_conf_t *cf); + + +static ngx_stream_module_t ngx_stream_write_filter_module_ctx = { + NULL, /* preconfiguration */ + ngx_stream_write_filter_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_write_filter_module = { + NGX_MODULE_V1, + &ngx_stream_write_filter_module_ctx, /* module context */ + NULL, /* 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_write_filter(ngx_stream_session_t *s, ngx_chain_t *in, + ngx_uint_t from_upstream) +{ + off_t size; + ngx_uint_t last, flush, sync; + ngx_chain_t *cl, *ln, **ll, **out, *chain; + ngx_connection_t *c; + ngx_stream_write_filter_ctx_t *ctx; + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_write_filter_module); + + if (ctx == NULL) { + ctx = ngx_pcalloc(s->connection->pool, + sizeof(ngx_stream_write_filter_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_stream_set_ctx(s, ctx, ngx_stream_write_filter_module); + } + + if (from_upstream) { + c = s->connection; + out = &ctx->from_upstream; + + } else { + c = s->upstream->peer.connection; + out = &ctx->from_downstream; + } + + if (c->error) { + return NGX_ERROR; + } + + size = 0; + flush = 0; + sync = 0; + last = 0; + ll = out; + + /* find the size, the flush point and the last link of the saved chain */ + + for (cl = *out; cl; cl = cl->next) { + ll = &cl->next; + + ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, + "write old buf t:%d f:%d %p, pos %p, size: %z " + "file: %O, size: %O", + cl->buf->temporary, cl->buf->in_file, + cl->buf->start, cl->buf->pos, + cl->buf->last - cl->buf->pos, + cl->buf->file_pos, + cl->buf->file_last - cl->buf->file_pos); + +#if 1 + if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "zero size buf in writer " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + cl->buf->temporary, + cl->buf->recycled, + cl->buf->in_file, + cl->buf->start, + cl->buf->pos, + cl->buf->last, + cl->buf->file, + cl->buf->file_pos, + cl->buf->file_last); + + ngx_debug_point(); + return NGX_ERROR; + } +#endif + + size += ngx_buf_size(cl->buf); + + if (cl->buf->flush || cl->buf->recycled) { + flush = 1; + } + + if (cl->buf->sync) { + sync = 1; + } + + if (cl->buf->last_buf) { + last = 1; + } + } + + /* add the new chain to the existent one */ + + for (ln = in; ln; ln = ln->next) { + cl = ngx_alloc_chain_link(c->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = ln->buf; + *ll = cl; + ll = &cl->next; + + ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, + "write new buf t:%d f:%d %p, pos %p, size: %z " + "file: %O, size: %O", + cl->buf->temporary, cl->buf->in_file, + cl->buf->start, cl->buf->pos, + cl->buf->last - cl->buf->pos, + cl->buf->file_pos, + cl->buf->file_last - cl->buf->file_pos); + +#if 1 + if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "zero size buf in writer " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + cl->buf->temporary, + cl->buf->recycled, + cl->buf->in_file, + cl->buf->start, + cl->buf->pos, + cl->buf->last, + cl->buf->file, + cl->buf->file_pos, + cl->buf->file_last); + + ngx_debug_point(); + return NGX_ERROR; + } +#endif + + size += ngx_buf_size(cl->buf); + + if (cl->buf->flush || cl->buf->recycled) { + flush = 1; + } + + if (cl->buf->sync) { + sync = 1; + } + + if (cl->buf->last_buf) { + last = 1; + } + } + + *ll = NULL; + + ngx_log_debug3(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream write filter: l:%ui f:%ui s:%O", last, flush, size); + + if (size == 0 + && !(c->buffered & NGX_LOWLEVEL_BUFFERED) + && !(last && c->need_last_buf)) + { + if (last || flush || sync) { + for (cl = *out; cl; /* void */) { + ln = cl; + cl = cl->next; + ngx_free_chain(c->pool, ln); + } + + *out = NULL; + c->buffered &= ~NGX_STREAM_WRITE_BUFFERED; + + return NGX_OK; + } + + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "the stream output chain is empty"); + + ngx_debug_point(); + + return NGX_ERROR; + } + + chain = c->send_chain(c, *out, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream write filter %p", chain); + + if (chain == NGX_CHAIN_ERROR) { + c->error = 1; + return NGX_ERROR; + } + + for (cl = *out; cl && cl != chain; /* void */) { + ln = cl; + cl = cl->next; + ngx_free_chain(c->pool, ln); + } + + *out = chain; + + if (chain) { + if (c->shared) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "shared connection is busy"); + return NGX_ERROR; + } + + c->buffered |= NGX_STREAM_WRITE_BUFFERED; + return NGX_AGAIN; + } + + c->buffered &= ~NGX_STREAM_WRITE_BUFFERED; + + if (c->buffered & NGX_LOWLEVEL_BUFFERED) { + return NGX_AGAIN; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_write_filter_init(ngx_conf_t *cf) +{ + ngx_stream_top_filter = ngx_stream_write_filter; + + return NGX_OK; +} From 6c9b75da5f2af954c9a393f4e30d21f051e6048f Mon Sep 17 00:00:00 2001 From: Christos Trochalakis Date: Sat, 12 Nov 2016 09:18:26 +0200 Subject: [PATCH 004/475] Release 1.10.2-2 --- debian/changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 470a04a..89b8f64 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -nginx (1.10.2-2) UNRELEASED; urgency=medium +nginx (1.10.2-2) unstable; urgency=medium * Build against OpenSSL 1.1.0. (Closes: #828453) + Patch for nginx-lua by Alessandro Ghedini. @@ -8,7 +8,7 @@ nginx (1.10.2-2) UNRELEASED; urgency=medium + Convert dav-ext to a dynamic module package: o libnginx-mod-http-dav-ext - -- Christos Trochalakis Sat, 29 Oct 2016 17:26:04 +0300 + -- Christos Trochalakis Sat, 12 Nov 2016 09:18:12 +0200 nginx (1.10.2-1) unstable; urgency=high From 14707687a2e53588102a3a4d33e7e37eeb52c602 Mon Sep 17 00:00:00 2001 From: Christos Trochalakis Date: Wed, 16 Nov 2016 10:29:39 +0200 Subject: [PATCH 005/475] New upstream version 1.11.6 --- CHANGES | 39 +++++ CHANGES.ru | 42 ++++- auto/lib/libgd/conf | 5 + src/core/nginx.h | 4 +- src/core/ngx_buf.c | 16 +- src/core/ngx_conf_file.c | 83 ++++++--- src/core/ngx_cycle.c | 3 + src/core/ngx_cycle.h | 4 + src/core/ngx_file.c | 32 +++- src/core/ngx_hash.c | 1 - src/event/ngx_event_openssl.c | 110 +++++++++++- src/event/ngx_event_openssl.h | 4 + src/http/modules/ngx_http_fastcgi_module.c | 34 +++- .../modules/ngx_http_image_filter_module.c | 164 +++++++++++++++++- src/http/modules/ngx_http_mp4_module.c | 74 ++++++-- src/http/modules/ngx_http_proxy_module.c | 75 +++++--- .../modules/ngx_http_range_filter_module.c | 12 +- src/http/modules/ngx_http_scgi_module.c | 34 +++- src/http/modules/ngx_http_ssl_module.c | 6 + src/http/modules/ngx_http_uwsgi_module.c | 34 +++- src/http/modules/perl/ngx_http_perl_module.c | 2 +- src/http/ngx_http_cache.h | 4 +- src/http/ngx_http_core_module.c | 2 - src/http/ngx_http_file_cache.c | 77 ++++---- src/http/ngx_http_special_response.c | 1 - src/http/ngx_http_upstream.c | 156 +++++++++++++---- src/http/ngx_http_upstream.h | 4 +- src/http/ngx_http_upstream_round_robin.c | 2 +- src/http/v2/ngx_http_v2.c | 68 ++++++-- src/http/v2/ngx_http_v2.h | 1 + src/http/v2/ngx_http_v2_filter_module.c | 4 + src/http/v2/ngx_http_v2_module.c | 9 + src/http/v2/ngx_http_v2_module.h | 1 + src/mail/ngx_mail.h | 13 +- src/mail/ngx_mail_auth_http_module.c | 1 + src/mail/ngx_mail_handler.c | 34 ++++ src/mail/ngx_mail_imap_handler.c | 11 ++ src/mail/ngx_mail_imap_module.c | 6 +- src/mail/ngx_mail_parse.c | 22 ++- src/mail/ngx_mail_pop3_handler.c | 11 ++ src/mail/ngx_mail_pop3_module.c | 96 +++++++--- src/mail/ngx_mail_smtp_handler.c | 11 ++ src/mail/ngx_mail_smtp_module.c | 6 +- src/stream/ngx_stream_proxy_module.c | 45 +++-- src/stream/ngx_stream_upstream.h | 1 + src/stream/ngx_stream_upstream_round_robin.c | 2 +- 46 files changed, 1101 insertions(+), 265 deletions(-) diff --git a/CHANGES b/CHANGES index 6a88ce4..1882976 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,43 @@ +Changes with nginx 1.11.6 15 Nov 2016 + + *) Change: format of the $ssl_client_s_dn and $ssl_client_i_dn variables + has been changed to follow RFC 2253 (RFC 4514); values in the old + format are available in the $ssl_client_s_dn_legacy and + $ssl_client_i_dn_legacy variables. + + *) Change: when storing temporary files in a cache directory they will + be stored in the same subdirectories as corresponding cache files + instead of a separate subdirectory for temporary files. + + *) Feature: EXTERNAL authentication mechanism support in mail proxy. + Thanks to Robert Norris. + + *) Feature: WebP support in the ngx_http_image_filter_module. + + *) Feature: variables support in the "proxy_method" directive. + Thanks to Dmitry Lazurkin. + + *) Feature: the "http2_max_requests" directive in the + ngx_http_v2_module. + + *) Feature: the "proxy_cache_max_range_offset", + "fastcgi_cache_max_range_offset", "scgi_cache_max_range_offset", and + "uwsgi_cache_max_range_offset" directives. + + *) Bugfix: graceful shutdown of old worker processes might require + infinite time when using HTTP/2. + + *) Bugfix: in the ngx_http_mp4_module. + + *) Bugfix: "ignore long locked inactive cache entry" alerts might appear + in logs when proxying WebSocket connections with caching enabled. + + *) Bugfix: nginx did not write anything to log and returned a response + with code 502 instead of 504 when a timeout occurred during an SSL + handshake to a backend. + + Changes with nginx 1.11.5 11 Oct 2016 *) Change: the --with-ipv6 configure option was removed, now IPv6 diff --git a/CHANGES.ru b/CHANGES.ru index f0bdaf5..3b66b94 100644 --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,44 @@ +Изменения в nginx 1.11.6 15.11.2016 + + *) Изменение: формат переменных $ssl_client_s_dn и $ssl_client_i_dn + изменён на соответствующий RFC 2253 (RFC 4514); значения в старом + формате доступны через переменные $ssl_client_s_dn_legacy и + $ssl_client_i_dn_legacy. + + *) Изменение: при сохранении временных файлов в каталоге кэша они теперь + располагаются не в отдельном подкаталоге для временных файлов, а в + том же подкаталоге, что и соответствующие файлы в кэше. + + *) Добавление: поддержка метода аутентификации EXTERNAL в почтовом + прокси-сервере. + Спасибо Robert Norris. + + *) Добавление: поддержка WebP в модуле ngx_http_image_filter_module. + + *) Добавление: директива proxy_method поддерживает переменные. + Спасибо Дмитрию Лазуркину. + + *) Добавление: директива http2_max_requests в модуле ngx_http_v2_module. + + *) Добавление: директивы proxy_cache_max_range_offset, + fastcgi_cache_max_range_offset, scgi_cache_max_range_offset и + uwsgi_cache_max_range_offset. + + *) Исправление: плавное завершение старых рабочих процессов могло + занимать бесконечное время при использовании HTTP/2. + + *) Исправление: в модуле ngx_http_mp4_module. + + *) Исправление: при проксировании WebSocket-соединений и включённом + кэшировании в логах могли появляться сообщения "ignore long locked + inactive cache entry". + + *) Исправление: если во время SSL handshake с бэкендом происходил + таймаут, nginx ничего не писал в лог и возвращал ответ с кодом 502 + вместо 504. + + Изменения в nginx 1.11.5 11.10.2016 *) Изменение: параметр configure --with-ipv6 упразднён, поддержка IPv6 @@ -5589,7 +5629,7 @@ Изменения в nginx 0.4.11 25.10.2006 - *) Добавление: POP3 прокси поддерживает AUTH LOIGN PLAIN и CRAM-MD5. + *) Добавление: POP3 прокси поддерживает AUTH LOGIN PLAIN и CRAM-MD5. *) Добавление: модуль ngx_http_perl_module поддерживает метод $r->allow_ranges. diff --git a/auto/lib/libgd/conf b/auto/lib/libgd/conf index 6e4e91c..87761f1 100644 --- a/auto/lib/libgd/conf +++ b/auto/lib/libgd/conf @@ -74,6 +74,11 @@ if [ $ngx_found = yes ]; then NGX_LIB_LIBGD=$ngx_feature_libs + ngx_feature="GD WebP support" + ngx_feature_name="NGX_HAVE_GD_WEBP" + ngx_feature_test="gdImagePtr img = gdImageCreateFromWebpPtr(1, NULL);" + . auto/feature + else cat << END diff --git a/src/core/nginx.h b/src/core/nginx.h index 1446251..a75ef04 100644 --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1011005 -#define NGINX_VERSION "1.11.5" +#define nginx_version 1011006 +#define NGINX_VERSION "1.11.6" #define NGINX_VER "nginx/" NGINX_VERSION #ifdef NGX_BUILD diff --git a/src/core/ngx_buf.c b/src/core/ngx_buf.c index 00b6644..d30a0a4 100644 --- a/src/core/ngx_buf.c +++ b/src/core/ngx_buf.c @@ -186,17 +186,19 @@ ngx_chain_update_chains(ngx_pool_t *p, ngx_chain_t **free, ngx_chain_t **busy, { ngx_chain_t *cl; - if (*busy == NULL) { - *busy = *out; + if (*out) { + if (*busy == NULL) { + *busy = *out; - } else { - for (cl = *busy; cl->next; cl = cl->next) { /* void */ } + } else { + for (cl = *busy; cl->next; cl = cl->next) { /* void */ } - cl->next = *out; + cl->next = *out; + } + + *out = NULL; } - *out = NULL; - while (*busy) { cl = *busy; diff --git a/src/core/ngx_conf_file.c b/src/core/ngx_conf_file.c index ea1dceb..ce8c602 100644 --- a/src/core/ngx_conf_file.c +++ b/src/core/ngx_conf_file.c @@ -10,6 +10,7 @@ #define NGX_CONF_BUFFER 4096 +static ngx_int_t ngx_conf_add_dump(ngx_conf_t *cf, ngx_str_t *filename); static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last); static ngx_int_t ngx_conf_read_token(ngx_conf_t *cf); static void ngx_conf_flush_files(ngx_cycle_t *cycle); @@ -97,17 +98,70 @@ ngx_conf_param(ngx_conf_t *cf) } +static ngx_int_t +ngx_conf_add_dump(ngx_conf_t *cf, ngx_str_t *filename) +{ + off_t size; + u_char *p; + uint32_t hash; + ngx_buf_t *buf; + ngx_str_node_t *sn; + ngx_conf_dump_t *cd; + + hash = ngx_crc32_long(filename->data, filename->len); + + sn = ngx_str_rbtree_lookup(&cf->cycle->config_dump_rbtree, filename, hash); + + if (sn) { + cf->conf_file->dump = NULL; + return NGX_OK; + } + + p = ngx_pstrdup(cf->cycle->pool, filename); + if (p == NULL) { + return NGX_ERROR; + } + + cd = ngx_array_push(&cf->cycle->config_dump); + if (cd == NULL) { + return NGX_ERROR; + } + + size = ngx_file_size(&cf->conf_file->file.info); + + buf = ngx_create_temp_buf(cf->cycle->pool, (size_t) size); + if (buf == NULL) { + return NGX_ERROR; + } + + cd->name.data = p; + cd->name.len = filename->len; + cd->buffer = buf; + + cf->conf_file->dump = buf; + + sn = ngx_palloc(cf->temp_pool, sizeof(ngx_str_node_t)); + if (sn == NULL) { + return NGX_ERROR; + } + + sn->node.key = hash; + sn->str = cd->name; + + ngx_rbtree_insert(&cf->cycle->config_dump_rbtree, &sn->node); + + return NGX_OK; +} + + char * ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename) { char *rv; - u_char *p; - off_t size; ngx_fd_t fd; ngx_int_t rc; - ngx_buf_t buf, *tbuf; + ngx_buf_t buf; ngx_conf_file_t *prev, conf_file; - ngx_conf_dump_t *cd; enum { parse_file = 0, parse_block, @@ -167,29 +221,10 @@ ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename) #endif ) { - p = ngx_pstrdup(cf->cycle->pool, filename); - if (p == NULL) { + if (ngx_conf_add_dump(cf, filename) != NGX_OK) { goto failed; } - size = ngx_file_size(&cf->conf_file->file.info); - - tbuf = ngx_create_temp_buf(cf->cycle->pool, (size_t) size); - if (tbuf == NULL) { - goto failed; - } - - cd = ngx_array_push(&cf->cycle->config_dump); - if (cd == NULL) { - goto failed; - } - - cd->name.len = filename->len; - cd->name.data = p; - cd->buffer = tbuf; - - cf->conf_file->dump = tbuf; - } else { cf->conf_file->dump = NULL; } diff --git a/src/core/ngx_cycle.c b/src/core/ngx_cycle.c index 98599f3..a57991c 100644 --- a/src/core/ngx_cycle.c +++ b/src/core/ngx_cycle.c @@ -132,6 +132,9 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) return NULL; } + ngx_rbtree_init(&cycle->config_dump_rbtree, &cycle->config_dump_sentinel, + ngx_str_rbtree_insert_value); + if (old_cycle->open_files.part.nelts) { n = old_cycle->open_files.part.nelts; for (part = old_cycle->open_files.part.next; part; part = part->next) { diff --git a/src/core/ngx_cycle.h b/src/core/ngx_cycle.h index c51b7ff..5cdacf1 100644 --- a/src/core/ngx_cycle.h +++ b/src/core/ngx_cycle.h @@ -56,7 +56,11 @@ struct ngx_cycle_s { ngx_array_t listening; ngx_array_t paths; + ngx_array_t config_dump; + ngx_rbtree_t config_dump_rbtree; + ngx_rbtree_node_t config_dump_sentinel; + ngx_list_t open_files; ngx_list_t shared_memory; diff --git a/src/core/ngx_file.c b/src/core/ngx_file.c index 8359c0f..b7dd4bc 100644 --- a/src/core/ngx_file.c +++ b/src/core/ngx_file.c @@ -141,12 +141,27 @@ ngx_int_t ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path, ngx_pool_t *pool, ngx_uint_t persistent, ngx_uint_t clean, ngx_uint_t access) { + size_t levels; + u_char *p; uint32_t n; ngx_err_t err; + ngx_str_t name; + ngx_uint_t prefix; ngx_pool_cleanup_t *cln; ngx_pool_cleanup_file_t *clnf; - file->name.len = path->name.len + 1 + path->len + 10; + if (file->name.len) { + name = file->name; + levels = 0; + prefix = 1; + + } else { + name = path->name; + levels = path->len; + prefix = 0; + } + + file->name.len = name.len + 1 + levels + 10; file->name.data = ngx_pnalloc(pool, file->name.len + 1); if (file->name.data == NULL) { @@ -159,7 +174,13 @@ ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path, ngx_pool_t *pool, } #endif - ngx_memcpy(file->name.data, path->name.data, path->name.len); + p = ngx_cpymem(file->name.data, name.data, name.len); + + if (prefix) { + *p = '.'; + } + + p += 1 + levels; n = (uint32_t) ngx_next_temp_number(0); @@ -169,10 +190,11 @@ ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path, ngx_pool_t *pool, } for ( ;; ) { - (void) ngx_sprintf(file->name.data + path->name.len + 1 + path->len, - "%010uD%Z", n); + (void) ngx_sprintf(p, "%010uD%Z", n); - ngx_create_hashed_filename(path, file->name.data, file->name.len); + if (!prefix) { + ngx_create_hashed_filename(path, file->name.data, file->name.len); + } ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0, "hashed path: %s", file->name.data); diff --git a/src/core/ngx_hash.c b/src/core/ngx_hash.c index 151e643..1944c7a 100644 --- a/src/core/ngx_hash.c +++ b/src/core/ngx_hash.c @@ -390,7 +390,6 @@ found: buckets[i] = (ngx_hash_elt_t *) elts; elts += test[i]; - } for (i = 0; i < size; i++) { diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 68d02bf..5c7734d 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -2137,7 +2137,9 @@ ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...) break; } - if (p >= last) { + /* ERR_error_string_n() requires at least one byte */ + + if (p >= last - 1) { goto next; } @@ -3435,6 +3437,109 @@ ngx_ssl_get_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) ngx_int_t ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) +{ + BIO *bio; + X509 *cert; + X509_NAME *name; + + s->len = 0; + + cert = SSL_get_peer_certificate(c->ssl->connection); + if (cert == NULL) { + return NGX_OK; + } + + name = X509_get_subject_name(cert); + if (name == NULL) { + return NGX_ERROR; + } + + bio = BIO_new(BIO_s_mem()); + if (bio == NULL) { + X509_free(cert); + return NGX_ERROR; + } + + if (X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253) < 0) { + goto failed; + } + + s->len = BIO_pending(bio); + s->data = ngx_pnalloc(pool, s->len); + if (s->data == NULL) { + goto failed; + } + + BIO_read(bio, s->data, s->len); + + BIO_free(bio); + X509_free(cert); + + return NGX_OK; + +failed: + + BIO_free(bio); + X509_free(cert); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_ssl_get_issuer_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) +{ + BIO *bio; + X509 *cert; + X509_NAME *name; + + s->len = 0; + + cert = SSL_get_peer_certificate(c->ssl->connection); + if (cert == NULL) { + return NGX_OK; + } + + name = X509_get_issuer_name(cert); + if (name == NULL) { + return NGX_ERROR; + } + + bio = BIO_new(BIO_s_mem()); + if (bio == NULL) { + X509_free(cert); + return NGX_ERROR; + } + + if (X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253) < 0) { + goto failed; + } + + s->len = BIO_pending(bio); + s->data = ngx_pnalloc(pool, s->len); + if (s->data == NULL) { + goto failed; + } + + BIO_read(bio, s->data, s->len); + + BIO_free(bio); + X509_free(cert); + + return NGX_OK; + +failed: + + BIO_free(bio); + X509_free(cert); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_ssl_get_subject_dn_legacy(ngx_connection_t *c, ngx_pool_t *pool, + ngx_str_t *s) { char *p; size_t len; @@ -3476,7 +3581,8 @@ ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) ngx_int_t -ngx_ssl_get_issuer_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) +ngx_ssl_get_issuer_dn_legacy(ngx_connection_t *c, ngx_pool_t *pool, + ngx_str_t *s) { char *p; size_t len; diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index 24b812f..d233c02 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -205,6 +205,10 @@ ngx_int_t ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); ngx_int_t ngx_ssl_get_issuer_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); +ngx_int_t ngx_ssl_get_subject_dn_legacy(ngx_connection_t *c, ngx_pool_t *pool, + ngx_str_t *s); +ngx_int_t ngx_ssl_get_issuer_dn_legacy(ngx_connection_t *c, ngx_pool_t *pool, + ngx_str_t *s); ngx_int_t ngx_ssl_get_serial_number(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); ngx_int_t ngx_ssl_get_fingerprint(ngx_connection_t *c, ngx_pool_t *pool, diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c index 62502b0..c7c417b 100644 --- a/src/http/modules/ngx_http_fastcgi_module.c +++ b/src/http/modules/ngx_http_fastcgi_module.c @@ -420,6 +420,13 @@ static ngx_command_t ngx_http_fastcgi_commands[] = { offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_min_uses), NULL }, + { ngx_string("fastcgi_cache_max_range_offset"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_off_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_max_range_offset), + NULL }, + { ngx_string("fastcgi_cache_use_stale"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, ngx_conf_set_bitmask_slot, @@ -766,16 +773,14 @@ ngx_http_fastcgi_eval(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf) return NGX_ERROR; } - if (url.addrs && url.addrs[0].sockaddr) { + if (url.addrs) { u->resolved->sockaddr = url.addrs[0].sockaddr; u->resolved->socklen = url.addrs[0].socklen; + u->resolved->name = url.addrs[0].name; u->resolved->naddrs = 1; - u->resolved->host = url.addrs[0].name; - - } else { - u->resolved->host = url.host; } + u->resolved->host = url.host; u->resolved->port = url.port; u->resolved->no_port = url.no_port; @@ -2756,6 +2761,7 @@ ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf) #if (NGX_HTTP_CACHE) conf->upstream.cache = NGX_CONF_UNSET; conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT; + conf->upstream.cache_max_range_offset = NGX_CONF_UNSET; conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR; conf->upstream.no_cache = NGX_CONF_UNSET_PTR; conf->upstream.cache_valid = NGX_CONF_UNSET_PTR; @@ -3001,6 +3007,10 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_uint_value(conf->upstream.cache_min_uses, prev->upstream.cache_min_uses, 1); + ngx_conf_merge_off_value(conf->upstream.cache_max_range_offset, + prev->upstream.cache_max_range_offset, + NGX_MAX_OFF_T_VALUE); + ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale, prev->upstream.cache_use_stale, (NGX_CONF_BITMASK_SET @@ -3127,6 +3137,20 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) #endif + /* + * special handling to preserve conf->params in the "http" section + * to inherit it to all servers + */ + + if (prev->params.hash.buckets == NULL + && conf->params_source == prev->params_source) + { + prev->params = conf->params; +#if (NGX_HTTP_CACHE) + prev->params_cache = conf->params_cache; +#endif + } + return NGX_CONF_OK; } diff --git a/src/http/modules/ngx_http_image_filter_module.c b/src/http/modules/ngx_http_image_filter_module.c index b608de1..dbec5d8 100644 --- a/src/http/modules/ngx_http_image_filter_module.c +++ b/src/http/modules/ngx_http_image_filter_module.c @@ -31,6 +31,7 @@ #define NGX_HTTP_IMAGE_JPEG 1 #define NGX_HTTP_IMAGE_GIF 2 #define NGX_HTTP_IMAGE_PNG 3 +#define NGX_HTTP_IMAGE_WEBP 4 #define NGX_HTTP_IMAGE_BUFFERED 0x08 @@ -42,6 +43,7 @@ typedef struct { ngx_uint_t height; ngx_uint_t angle; ngx_uint_t jpeg_quality; + ngx_uint_t webp_quality; ngx_uint_t sharpen; ngx_flag_t transparency; @@ -51,6 +53,7 @@ typedef struct { ngx_http_complex_value_t *hcv; ngx_http_complex_value_t *acv; ngx_http_complex_value_t *jqcv; + ngx_http_complex_value_t *wqcv; ngx_http_complex_value_t *shcv; size_t buffer_size; @@ -109,6 +112,8 @@ static char *ngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_image_filter_jpeg_quality(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_image_filter_webp_quality(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); static char *ngx_http_image_filter_sharpen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_int_t ngx_http_image_filter_init(ngx_conf_t *cf); @@ -130,6 +135,13 @@ static ngx_command_t ngx_http_image_filter_commands[] = { 0, NULL }, + { ngx_string("image_filter_webp_quality"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_image_filter_webp_quality, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + { ngx_string("image_filter_sharpen"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_http_image_filter_sharpen, @@ -200,7 +212,8 @@ static ngx_http_output_body_filter_pt ngx_http_next_body_filter; static ngx_str_t ngx_http_image_types[] = { ngx_string("image/jpeg"), ngx_string("image/gif"), - ngx_string("image/png") + ngx_string("image/png"), + ngx_string("image/webp") }; @@ -441,6 +454,13 @@ ngx_http_image_test(ngx_http_request_t *r, ngx_chain_t *in) /* PNG */ return NGX_HTTP_IMAGE_PNG; + + } else if (p[0] == 'R' && p[1] == 'I' && p[2] == 'F' && p[3] == 'F' + && p[8] == 'W' && p[9] == 'E' && p[10] == 'B' && p[11] == 'P') + { + /* WebP */ + + return NGX_HTTP_IMAGE_WEBP; } return NGX_HTTP_IMAGE_NONE; @@ -731,6 +751,56 @@ ngx_http_image_size(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx) break; + case NGX_HTTP_IMAGE_WEBP: + + if (ctx->length < 30) { + return NGX_DECLINED; + } + + if (p[12] != 'V' || p[13] != 'P' || p[14] != '8') { + return NGX_DECLINED; + } + + switch (p[15]) { + + case ' ': + if (p[20] & 1) { + /* not a key frame */ + return NGX_DECLINED; + } + + if (p[23] != 0x9d || p[24] != 0x01 || p[25] != 0x2a) { + /* invalid start code */ + return NGX_DECLINED; + } + + width = (p[26] | p[27] << 8) & 0x3fff; + height = (p[28] | p[29] << 8) & 0x3fff; + + break; + + case 'L': + if (p[20] != 0x2f) { + /* invalid signature */ + return NGX_DECLINED; + } + + width = ((p[21] | p[22] << 8) & 0x3fff) + 1; + height = ((p[22] >> 6 | p[23] << 2 | p[24] << 10) & 0x3fff) + 1; + + break; + + case 'X': + width = (p[24] | p[25] << 8 | p[26] << 16) + 1; + height = (p[27] | p[28] << 8 | p[29] << 16) + 1; + break; + + default: + return NGX_DECLINED; + } + + break; + default: return NGX_DECLINED; @@ -1043,6 +1113,15 @@ ngx_http_image_source(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx) failed = "gdImageCreateFromPngPtr() failed"; break; + case NGX_HTTP_IMAGE_WEBP: +#if (NGX_HAVE_GD_WEBP) + img = gdImageCreateFromWebpPtr(ctx->length, ctx->image); + failed = "gdImageCreateFromWebpPtr() failed"; +#else + failed = "nginx was built without GD WebP support"; +#endif + break; + default: failed = "unknown image type"; break; @@ -1090,7 +1169,7 @@ ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type, gdImagePtr img, { char *failed; u_char *out; - ngx_int_t jq; + ngx_int_t q; ngx_http_image_filter_conf_t *conf; out = NULL; @@ -1100,12 +1179,12 @@ ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type, gdImagePtr img, case NGX_HTTP_IMAGE_JPEG: conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module); - jq = ngx_http_image_filter_get_value(r, conf->jqcv, conf->jpeg_quality); - if (jq <= 0) { + q = ngx_http_image_filter_get_value(r, conf->jqcv, conf->jpeg_quality); + if (q <= 0) { return NULL; } - out = gdImageJpegPtr(img, size, jq); + out = gdImageJpegPtr(img, size, q); failed = "gdImageJpegPtr() failed"; break; @@ -1119,6 +1198,22 @@ ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type, gdImagePtr img, failed = "gdImagePngPtr() failed"; break; + case NGX_HTTP_IMAGE_WEBP: +#if (NGX_HAVE_GD_WEBP) + conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module); + + q = ngx_http_image_filter_get_value(r, conf->wqcv, conf->webp_quality); + if (q <= 0) { + return NULL; + } + + out = gdImageWebpPtrEx(img, size, q); + failed = "gdImageWebpPtrEx() failed"; +#else + failed = "nginx was built without GD WebP support"; +#endif + break; + default: failed = "unknown image type"; break; @@ -1196,11 +1291,13 @@ ngx_http_image_filter_create_conf(ngx_conf_t *cf) * conf->hcv = NULL; * conf->acv = NULL; * conf->jqcv = NULL; + * conf->wqcv = NULL; * conf->shcv = NULL; */ conf->filter = NGX_CONF_UNSET_UINT; conf->jpeg_quality = NGX_CONF_UNSET_UINT; + conf->webp_quality = NGX_CONF_UNSET_UINT; conf->sharpen = NGX_CONF_UNSET_UINT; conf->transparency = NGX_CONF_UNSET; conf->interlace = NGX_CONF_UNSET; @@ -1242,6 +1339,16 @@ ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child) } } + if (conf->webp_quality == NGX_CONF_UNSET_UINT) { + + /* 80 is libwebp default quality */ + ngx_conf_merge_uint_value(conf->webp_quality, prev->webp_quality, 80); + + if (conf->wqcv == NULL) { + conf->wqcv = prev->wqcv; + } + } + if (conf->sharpen == NGX_CONF_UNSET_UINT) { ngx_conf_merge_uint_value(conf->sharpen, prev->sharpen, 0); @@ -1461,6 +1568,53 @@ ngx_http_image_filter_jpeg_quality(ngx_conf_t *cf, ngx_command_t *cmd, } +static char * +ngx_http_image_filter_webp_quality(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + ngx_http_image_filter_conf_t *imcf = conf; + + ngx_str_t *value; + ngx_int_t n; + ngx_http_complex_value_t cv; + ngx_http_compile_complex_value_t ccv; + + value = cf->args->elts; + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &cv; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (cv.lengths == NULL) { + n = ngx_http_image_filter_value(&value[1]); + + if (n <= 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%V\"", &value[1]); + return NGX_CONF_ERROR; + } + + imcf->webp_quality = (ngx_uint_t) n; + + } else { + imcf->wqcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t)); + if (imcf->wqcv == NULL) { + return NGX_CONF_ERROR; + } + + *imcf->wqcv = cv; + } + + return NGX_CONF_OK; +} + + static char * ngx_http_image_filter_sharpen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) diff --git a/src/http/modules/ngx_http_mp4_module.c b/src/http/modules/ngx_http_mp4_module.c index 2a68bae..8f2920f 100644 --- a/src/http/modules/ngx_http_mp4_module.c +++ b/src/http/modules/ngx_http_mp4_module.c @@ -216,6 +216,7 @@ typedef struct { static ngx_int_t ngx_http_mp4_handler(ngx_http_request_t *r); +static ngx_int_t ngx_http_mp4_atofp(u_char *line, size_t n, size_t point); static ngx_int_t ngx_http_mp4_process(ngx_http_mp4_file_t *mp4); static ngx_int_t ngx_http_mp4_read_atom(ngx_http_mp4_file_t *mp4, @@ -537,26 +538,15 @@ ngx_http_mp4_handler(ngx_http_request_t *r) /* * A Flash player may send start value with a lot of digits - * after dot so strtod() is used instead of atofp(). NaNs and - * infinities become negative numbers after (int) conversion. + * after dot so a custom function is used instead of ngx_atofp(). */ - ngx_set_errno(0); - start = (int) (strtod((char *) value.data, NULL) * 1000); - - if (ngx_errno != 0) { - start = -1; - } + start = ngx_http_mp4_atofp(value.data, value.len, 3); } if (ngx_http_arg(r, (u_char *) "end", 3, &value) == NGX_OK) { - ngx_set_errno(0); - end = (int) (strtod((char *) value.data, NULL) * 1000); - - if (ngx_errno != 0) { - end = -1; - } + end = ngx_http_mp4_atofp(value.data, value.len, 3); if (end > 0) { if (start < 0) { @@ -686,6 +676,62 @@ ngx_http_mp4_handler(ngx_http_request_t *r) } +static ngx_int_t +ngx_http_mp4_atofp(u_char *line, size_t n, size_t point) +{ + ngx_int_t value, cutoff, cutlim; + ngx_uint_t dot; + + /* same as ngx_atofp(), but allows additional digits */ + + if (n == 0) { + return NGX_ERROR; + } + + cutoff = NGX_MAX_INT_T_VALUE / 10; + cutlim = NGX_MAX_INT_T_VALUE % 10; + + dot = 0; + + for (value = 0; n--; line++) { + + if (*line == '.') { + if (dot) { + return NGX_ERROR; + } + + dot = 1; + continue; + } + + if (*line < '0' || *line > '9') { + return NGX_ERROR; + } + + if (point == 0) { + continue; + } + + if (value >= cutoff && (value > cutoff || *line - '0' > cutlim)) { + return NGX_ERROR; + } + + value = value * 10 + (*line - '0'); + point -= dot; + } + + while (point--) { + if (value > cutoff) { + return NGX_ERROR; + } + + value = value * 10; + } + + return value; +} + + static ngx_int_t ngx_http_mp4_process(ngx_http_mp4_file_t *mp4) { diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c index 4f49a52..42b6afc 100644 --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -73,7 +73,7 @@ typedef struct { ngx_array_t *cookie_domains; ngx_array_t *cookie_paths; - ngx_str_t method; + ngx_http_complex_value_t *method; ngx_str_t location; ngx_str_t url; @@ -380,7 +380,7 @@ static ngx_command_t ngx_http_proxy_commands[] = { { ngx_string("proxy_method"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_conf_set_str_slot, + ngx_http_set_complex_value_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_proxy_loc_conf_t, method), NULL }, @@ -492,6 +492,13 @@ static ngx_command_t ngx_http_proxy_commands[] = { offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_min_uses), NULL }, + { ngx_string("proxy_cache_max_range_offset"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_off_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_max_range_offset), + NULL }, + { ngx_string("proxy_cache_use_stale"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, ngx_conf_set_bitmask_slot, @@ -1008,16 +1015,14 @@ ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx, return NGX_ERROR; } - if (url.addrs && url.addrs[0].sockaddr) { + if (url.addrs) { u->resolved->sockaddr = url.addrs[0].sockaddr; u->resolved->socklen = url.addrs[0].socklen; + u->resolved->name = url.addrs[0].name; u->resolved->naddrs = 1; - u->resolved->host = url.addrs[0].name; - - } else { - u->resolved->host = url.host; } + u->resolved->host = url.host; u->resolved->port = (in_port_t) (url.no_port ? port : url.port); u->resolved->no_port = url.no_port; @@ -1159,8 +1164,10 @@ ngx_http_proxy_create_request(ngx_http_request_t *r) /* HEAD was changed to GET to cache response */ method = u->method; - } else if (plcf->method.len) { - method = plcf->method; + } else if (plcf->method) { + if (ngx_http_complex_value(r, plcf->method, &method) != NGX_OK) { + return NGX_ERROR; + } } else { method = r->method_name; @@ -2797,7 +2804,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf) * conf->upstream.store_values = NULL; * conf->upstream.ssl_name = NULL; * - * conf->method = { 0, NULL }; + * conf->method = NULL; * conf->headers_source = NULL; * conf->headers.lengths = NULL; * conf->headers.values = NULL; @@ -2847,6 +2854,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf) #if (NGX_HTTP_CACHE) conf->upstream.cache = NGX_CONF_UNSET; conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT; + conf->upstream.cache_max_range_offset = NGX_CONF_UNSET; conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR; conf->upstream.no_cache = NGX_CONF_UNSET_PTR; conf->upstream.cache_valid = NGX_CONF_UNSET_PTR; @@ -3108,6 +3116,10 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_uint_value(conf->upstream.cache_min_uses, prev->upstream.cache_min_uses, 1); + ngx_conf_merge_off_value(conf->upstream.cache_max_range_offset, + prev->upstream.cache_max_range_offset, + NGX_MAX_OFF_T_VALUE); + ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale, prev->upstream.cache_use_stale, (NGX_CONF_BITMASK_SET @@ -3158,7 +3170,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) #endif - ngx_conf_merge_str_value(conf->method, prev->method, ""); + if (conf->method == NULL) { + conf->method = prev->method; + } ngx_conf_merge_value(conf->upstream.pass_request_headers, prev->upstream.pass_request_headers, 1); @@ -3357,6 +3371,20 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) #endif + /* + * special handling to preserve conf->headers in the "http" section + * to inherit it to all servers + */ + + if (prev->headers.hash.buckets == NULL + && conf->headers_source == prev->headers_source) + { + prev->headers = conf->headers; +#if (NGX_HTTP_CACHE) + prev->headers_cache = conf->headers_cache; +#endif + } + return NGX_CONF_OK; } @@ -3392,14 +3420,6 @@ ngx_http_proxy_init_headers(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *conf, return NGX_ERROR; } - if (conf->headers_source == NULL) { - conf->headers_source = ngx_array_create(cf->pool, 4, - sizeof(ngx_keyval_t)); - if (conf->headers_source == NULL) { - return NGX_ERROR; - } - } - headers->lengths = ngx_array_create(cf->pool, 64, 1); if (headers->lengths == NULL) { return NGX_ERROR; @@ -3410,15 +3430,18 @@ ngx_http_proxy_init_headers(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *conf, return NGX_ERROR; } - src = conf->headers_source->elts; - for (i = 0; i < conf->headers_source->nelts; i++) { + if (conf->headers_source) { - s = ngx_array_push(&headers_merged); - if (s == NULL) { - return NGX_ERROR; + src = conf->headers_source->elts; + for (i = 0; i < conf->headers_source->nelts; i++) { + + s = ngx_array_push(&headers_merged); + if (s == NULL) { + return NGX_ERROR; + } + + *s = src[i]; } - - *s = src[i]; } h = default_headers; diff --git a/src/http/modules/ngx_http_range_filter_module.c b/src/http/modules/ngx_http_range_filter_module.c index 095ef06..951a00d 100644 --- a/src/http/modules/ngx_http_range_filter_module.c +++ b/src/http/modules/ngx_http_range_filter_module.c @@ -224,12 +224,6 @@ parse: ctx->offset = r->headers_out.content_offset; - if (ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_range_t)) - != NGX_OK) - { - return NGX_ERROR; - } - ranges = r->single_range ? 1 : clcf->max_ranges; switch (ngx_http_range_parse(r, ctx, ranges)) { @@ -291,6 +285,12 @@ ngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx, } } + if (ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_range_t)) + != NGX_OK) + { + return NGX_ERROR; + } + p = r->headers_in.range->value.data + 6; size = 0; content_length = r->headers_out.content_length_n; diff --git a/src/http/modules/ngx_http_scgi_module.c b/src/http/modules/ngx_http_scgi_module.c index 36656ec..aa84a3d 100644 --- a/src/http/modules/ngx_http_scgi_module.c +++ b/src/http/modules/ngx_http_scgi_module.c @@ -270,6 +270,13 @@ static ngx_command_t ngx_http_scgi_commands[] = { offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_min_uses), NULL }, + { ngx_string("scgi_cache_max_range_offset"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_off_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_max_range_offset), + NULL }, + { ngx_string("scgi_cache_use_stale"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, ngx_conf_set_bitmask_slot, @@ -562,16 +569,14 @@ ngx_http_scgi_eval(ngx_http_request_t *r, ngx_http_scgi_loc_conf_t * scf) return NGX_ERROR; } - if (url.addrs && url.addrs[0].sockaddr) { + if (url.addrs) { u->resolved->sockaddr = url.addrs[0].sockaddr; u->resolved->socklen = url.addrs[0].socklen; + u->resolved->name = url.addrs[0].name; u->resolved->naddrs = 1; - u->resolved->host = url.addrs[0].name; - - } else { - u->resolved->host = url.host; } + u->resolved->host = url.host; u->resolved->port = url.port; u->resolved->no_port = url.no_port; @@ -1206,6 +1211,7 @@ ngx_http_scgi_create_loc_conf(ngx_conf_t *cf) #if (NGX_HTTP_CACHE) conf->upstream.cache = NGX_CONF_UNSET; conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT; + conf->upstream.cache_max_range_offset = NGX_CONF_UNSET; conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR; conf->upstream.no_cache = NGX_CONF_UNSET_PTR; conf->upstream.cache_valid = NGX_CONF_UNSET_PTR; @@ -1446,6 +1452,10 @@ ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_uint_value(conf->upstream.cache_min_uses, prev->upstream.cache_min_uses, 1); + ngx_conf_merge_off_value(conf->upstream.cache_max_range_offset, + prev->upstream.cache_max_range_offset, + NGX_MAX_OFF_T_VALUE); + ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale, prev->upstream.cache_use_stale, (NGX_CONF_BITMASK_SET @@ -1558,6 +1568,20 @@ ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) #endif + /* + * special handling to preserve conf->params in the "http" section + * to inherit it to all servers + */ + + if (prev->params.hash.buckets == NULL + && conf->params_source == prev->params_source) + { + prev->params = conf->params; +#if (NGX_HTTP_CACHE) + prev->params_cache = conf->params_cache; +#endif + } + return NGX_CONF_OK; } diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index d685ae9..e75f5d8 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -298,6 +298,12 @@ static ngx_http_variable_t ngx_http_ssl_vars[] = { { ngx_string("ssl_client_i_dn"), NULL, ngx_http_ssl_variable, (uintptr_t) ngx_ssl_get_issuer_dn, NGX_HTTP_VAR_CHANGEABLE, 0 }, + { ngx_string("ssl_client_s_dn_legacy"), NULL, ngx_http_ssl_variable, + (uintptr_t) ngx_ssl_get_subject_dn_legacy, NGX_HTTP_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_client_i_dn_legacy"), NULL, ngx_http_ssl_variable, + (uintptr_t) ngx_ssl_get_issuer_dn_legacy, NGX_HTTP_VAR_CHANGEABLE, 0 }, + { ngx_string("ssl_client_serial"), NULL, ngx_http_ssl_variable, (uintptr_t) ngx_ssl_get_serial_number, NGX_HTTP_VAR_CHANGEABLE, 0 }, diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c index 7f916e8..b9c8dba 100644 --- a/src/http/modules/ngx_http_uwsgi_module.c +++ b/src/http/modules/ngx_http_uwsgi_module.c @@ -330,6 +330,13 @@ static ngx_command_t ngx_http_uwsgi_commands[] = { offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_min_uses), NULL }, + { ngx_string("uwsgi_cache_max_range_offset"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_off_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_max_range_offset), + NULL }, + { ngx_string("uwsgi_cache_use_stale"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, ngx_conf_set_bitmask_slot, @@ -764,16 +771,14 @@ ngx_http_uwsgi_eval(ngx_http_request_t *r, ngx_http_uwsgi_loc_conf_t * uwcf) return NGX_ERROR; } - if (url.addrs && url.addrs[0].sockaddr) { + if (url.addrs) { u->resolved->sockaddr = url.addrs[0].sockaddr; u->resolved->socklen = url.addrs[0].socklen; + u->resolved->name = url.addrs[0].name; u->resolved->naddrs = 1; - u->resolved->host = url.addrs[0].name; - - } else { - u->resolved->host = url.host; } + u->resolved->host = url.host; u->resolved->port = url.port; u->resolved->no_port = url.no_port; @@ -1412,6 +1417,7 @@ ngx_http_uwsgi_create_loc_conf(ngx_conf_t *cf) #if (NGX_HTTP_CACHE) conf->upstream.cache = NGX_CONF_UNSET; conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT; + conf->upstream.cache_max_range_offset = NGX_CONF_UNSET; conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR; conf->upstream.no_cache = NGX_CONF_UNSET_PTR; conf->upstream.cache_valid = NGX_CONF_UNSET_PTR; @@ -1660,6 +1666,10 @@ ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_uint_value(conf->upstream.cache_min_uses, prev->upstream.cache_min_uses, 1); + ngx_conf_merge_off_value(conf->upstream.cache_max_range_offset, + prev->upstream.cache_max_range_offset, + NGX_MAX_OFF_T_VALUE); + ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale, prev->upstream.cache_use_stale, (NGX_CONF_BITMASK_SET @@ -1820,6 +1830,20 @@ ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) #endif + /* + * special handling to preserve conf->params in the "http" section + * to inherit it to all servers + */ + + if (prev->params.hash.buckets == NULL + && conf->params_source == prev->params_source) + { + prev->params = conf->params; +#if (NGX_HTTP_CACHE) + prev->params_cache = conf->params_cache; +#endif + } + return NGX_CONF_OK; } diff --git a/src/http/modules/perl/ngx_http_perl_module.c b/src/http/modules/perl/ngx_http_perl_module.c index 6a8894c..f9a9a84 100644 --- a/src/http/modules/perl/ngx_http_perl_module.c +++ b/src/http/modules/perl/ngx_http_perl_module.c @@ -410,7 +410,7 @@ ngx_http_perl_ssi(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ssi_ctx, args = ¶ms[NGX_HTTP_PERL_SSI_ARG]; - if (args) { + if (args[0]) { for (i = 0; args[i]; i++) { /* void */ } diff --git a/src/http/ngx_http_cache.h b/src/http/ngx_http_cache.h index 70342d0..4075f3d 100644 --- a/src/http/ngx_http_cache.h +++ b/src/http/ngx_http_cache.h @@ -151,7 +151,6 @@ struct ngx_http_file_cache_s { ngx_slab_pool_t *shpool; ngx_path_t *path; - ngx_path_t *temp_path; off_t max_size; size_t bsize; @@ -171,6 +170,9 @@ struct ngx_http_file_cache_s { ngx_msec_t manager_threshold; ngx_shm_zone_t *shm_zone; + + ngx_uint_t use_temp_path; + /* unsigned use_temp_path:1 */ }; diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index 9da5d10..4e37cec 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -4827,8 +4827,6 @@ ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) case NGX_HTTPS_CERT_ERROR: case NGX_HTTPS_NO_CERT: err->overwrite = NGX_HTTP_BAD_REQUEST; - default: - break; } } diff --git a/src/http/ngx_http_file_cache.c b/src/http/ngx_http_file_cache.c index 199a901..3c8ad7d 100644 --- a/src/http/ngx_http_file_cache.c +++ b/src/http/ngx_http_file_cache.c @@ -1920,17 +1920,18 @@ ngx_http_file_cache_manager(void *data) ngx_http_file_cache_t *cache = data; off_t size; - time_t next, wait; - ngx_msec_t elapsed; + time_t wait; + ngx_msec_t elapsed, next; ngx_uint_t count, watermark; cache->last = ngx_current_msec; cache->files = 0; - next = ngx_http_file_cache_expire(cache); + next = (ngx_msec_t) ngx_http_file_cache_expire(cache) * 1000; if (next == 0) { - return cache->manager_sleep; + next = cache->manager_sleep; + goto done; } for ( ;; ) { @@ -1947,21 +1948,23 @@ ngx_http_file_cache_manager(void *data) size, count, (ngx_int_t) watermark); if (size < cache->max_size && count < watermark) { - return (ngx_msec_t) next * 1000; + break; } wait = ngx_http_file_cache_forced_expire(cache); if (wait > 0) { - return (ngx_msec_t) wait * 1000; + next = (ngx_msec_t) wait * 1000; + break; } if (ngx_quit || ngx_terminate) { - return (ngx_msec_t) next * 1000; + break; } if (++cache->files >= cache->manager_files) { - return cache->manager_sleep; + next = cache->manager_sleep; + break; } ngx_time_update(); @@ -1969,9 +1972,20 @@ ngx_http_file_cache_manager(void *data) elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last)); if (elapsed >= cache->manager_threshold) { - return cache->manager_sleep; + next = cache->manager_sleep; + break; } } + +done: + + elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last)); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "http file cache manager: %ui e:%M n:%M", + cache->files, elapsed, next); + + return next; } @@ -2098,6 +2112,17 @@ ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx, ngx_str_t *name) return NGX_ERROR; } + /* + * Temporary files in cache have a suffix consisting of a dot + * followed by 10 digits. + */ + + if (name->len >= 2 * NGX_HTTP_CACHE_KEY_LEN + 1 + 10 + && name->data[name->len - 10 - 1] == '.') + { + return NGX_OK; + } + if (ctx->size < (off_t) sizeof(ngx_http_file_cache_header_t)) { ngx_log_error(NGX_LOG_CRIT, ctx->log, 0, "cache file \"%s\" is too small", name->data); @@ -2242,7 +2267,6 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) off_t max_size; u_char *last, *p; time_t inactive; - size_t len; ssize_t size; ngx_str_t s, name, *value; ngx_int_t loader_files, manager_files; @@ -2515,37 +2539,6 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_ERROR; } - if (!use_temp_path) { - cache->temp_path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t)); - if (cache->temp_path == NULL) { - return NGX_CONF_ERROR; - } - - len = cache->path->name.len + sizeof("/temp") - 1; - - p = ngx_pnalloc(cf->pool, len + 1); - if (p == NULL) { - return NGX_CONF_ERROR; - } - - cache->temp_path->name.len = len; - cache->temp_path->name.data = p; - - p = ngx_cpymem(p, cache->path->name.data, cache->path->name.len); - ngx_memcpy(p, "/temp", sizeof("/temp")); - - ngx_memcpy(&cache->temp_path->level, &cache->path->level, - NGX_MAX_PATH_LEVEL * sizeof(size_t)); - - cache->temp_path->len = cache->path->len; - cache->temp_path->conf_file = cf->conf_file->file.name.data; - cache->temp_path->line = cf->conf_file->line; - - if (ngx_add_path(cf, &cache->temp_path) != NGX_OK) { - return NGX_CONF_ERROR; - } - } - cache->shm_zone = ngx_shared_memory_add(cf, &name, size, cmd->post); if (cache->shm_zone == NULL) { return NGX_CONF_ERROR; @@ -2561,6 +2554,8 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) cache->shm_zone->init = ngx_http_file_cache_init; cache->shm_zone->data = cache; + cache->use_temp_path = use_temp_path; + cache->inactive = inactive; cache->max_size = max_size; diff --git a/src/http/ngx_http_special_response.c b/src/http/ngx_http_special_response.c index 7692f80..d4c39ff 100644 --- a/src/http/ngx_http_special_response.c +++ b/src/http/ngx_http_special_response.c @@ -473,7 +473,6 @@ ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error) case NGX_HTTPS_NO_CERT: case NGX_HTTP_REQUEST_HEADER_TOO_LARGE: r->err_status = NGX_HTTP_BAD_REQUEST; - break; } } else { diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index ceb798f..ed25f43 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -17,6 +17,8 @@ static ngx_int_t ngx_http_upstream_cache_get(ngx_http_request_t *r, ngx_http_upstream_t *u, ngx_http_file_cache_t **cache); static ngx_int_t ngx_http_upstream_cache_send(ngx_http_request_t *r, ngx_http_upstream_t *u); +static ngx_int_t ngx_http_upstream_cache_check_range(ngx_http_request_t *r, + ngx_http_upstream_t *u); static ngx_int_t ngx_http_upstream_cache_status(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_upstream_cache_last_modified(ngx_http_request_t *r, @@ -654,6 +656,23 @@ ngx_http_upstream_init_request(ngx_http_request_t *r) host = &u->resolved->host; + umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); + + uscfp = umcf->upstreams.elts; + + for (i = 0; i < umcf->upstreams.nelts; i++) { + + uscf = uscfp[i]; + + if (uscf->host.len == host->len + && ((uscf->port == 0 && u->resolved->no_port) + || uscf->port == u->resolved->port) + && ngx_strncasecmp(uscf->host.data, host->data, host->len) == 0) + { + goto found; + } + } + if (u->resolved->sockaddr) { if (u->resolved->port == 0 @@ -679,23 +698,6 @@ ngx_http_upstream_init_request(ngx_http_request_t *r) return; } - umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); - - uscfp = umcf->upstreams.elts; - - for (i = 0; i < umcf->upstreams.nelts; i++) { - - uscf = uscfp[i]; - - if (uscf->host.len == host->len - && ((uscf->port == 0 && u->resolved->no_port) - || uscf->port == u->resolved->port) - && ngx_strncasecmp(uscf->host.data, host->data, host->len) == 0) - { - goto found; - } - } - if (u->resolved->port == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no port in upstream \"%V\"", host); @@ -922,6 +924,10 @@ ngx_http_upstream_cache(ngx_http_request_t *r, ngx_http_upstream_t *u) return rc; } + if (ngx_http_upstream_cache_check_range(r, u) == NGX_DECLINED) { + u->cacheable = 0; + } + r->cached = 0; return NGX_DECLINED; @@ -1023,6 +1029,55 @@ ngx_http_upstream_cache_send(ngx_http_request_t *r, ngx_http_upstream_t *u) return rc; } + +static ngx_int_t +ngx_http_upstream_cache_check_range(ngx_http_request_t *r, + ngx_http_upstream_t *u) +{ + off_t offset; + u_char *p, *start; + ngx_table_elt_t *h; + + h = r->headers_in.range; + + if (h == NULL + || !u->cacheable + || u->conf->cache_max_range_offset == NGX_MAX_OFF_T_VALUE) + { + return NGX_OK; + } + + if (u->conf->cache_max_range_offset == 0) { + return NGX_DECLINED; + } + + if (h->value.len < 7 + || ngx_strncasecmp(h->value.data, (u_char *) "bytes=", 6) != 0) + { + return NGX_OK; + } + + p = h->value.data + 6; + + while (*p == ' ') { p++; } + + if (*p == '-') { + return NGX_DECLINED; + } + + start = p; + + while (*p >= '0' && *p <= '9') { p++; } + + offset = ngx_atoof(start, p - start); + + if (offset >= u->conf->cache_max_range_offset) { + return NGX_DECLINED; + } + + return NGX_OK; +} + #endif @@ -1611,6 +1666,13 @@ ngx_http_upstream_ssl_handshake(ngx_connection_t *c) return; } + if (c->write->timedout) { + c = r->connection; + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT); + ngx_http_run_posted_requests(c); + return; + } + failed: c = r->connection; @@ -1696,7 +1758,10 @@ ngx_http_upstream_ssl_name(ngx_http_request_t *r, ngx_http_upstream_t *u, ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "upstream SSL server name: \"%s\"", name.data); - if (SSL_set_tlsext_host_name(c->ssl->connection, name.data) == 0) { + if (SSL_set_tlsext_host_name(c->ssl->connection, + (char *) name.data) + == 0) + { ngx_ssl_error(NGX_LOG_ERR, r->connection->log, 0, "SSL_set_tlsext_host_name(\"%s\") failed", name.data); return NGX_ERROR; @@ -2702,6 +2767,15 @@ ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u) u->header_sent = 1; if (u->upgrade) { + +#if (NGX_HTTP_CACHE) + + if (r->cache) { + ngx_http_file_cache_free(r->cache, u->pipe->temp_file); + } + +#endif + ngx_http_upstream_upgrade(r, u); return; } @@ -2732,6 +2806,14 @@ ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u) if (!u->buffering) { +#if (NGX_HTTP_CACHE) + + if (r->cache) { + ngx_http_file_cache_free(r->cache, u->pipe->temp_file); + } + +#endif + if (u->input_filter == NULL) { u->input_filter_init = ngx_http_upstream_non_buffered_filter_init; u->input_filter = ngx_http_upstream_non_buffered_filter; @@ -2922,8 +3004,9 @@ ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u) p->temp_file->persistent = 1; #if (NGX_HTTP_CACHE) - if (r->cache && r->cache->file_cache->temp_path) { - p->temp_file->path = r->cache->file_cache->temp_path; + if (r->cache && !r->cache->file_cache->use_temp_path) { + p->temp_file->path = r->cache->file_cache->path; + p->temp_file->file.name = r->cache->file.name; } #endif @@ -5757,14 +5840,9 @@ ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags) continue; } - if (uscfp[i]->default_port && u->default_port - && uscfp[i]->default_port != u->default_port) - { - continue; - } - if (flags & NGX_HTTP_UPSTREAM_CREATE) { uscfp[i]->flags = flags; + uscfp[i]->port = 0; } return uscfp[i]; @@ -5780,7 +5858,6 @@ ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags) uscf->file_name = cf->conf_file->file.name.data; uscf->line = cf->conf_file->line; uscf->port = u->port; - uscf->default_port = u->default_port; uscf->no_port = u->no_port; if (u->naddrs == 1 && (u->port || u->family == AF_UNIX)) { @@ -6021,12 +6098,7 @@ ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf, conf->hide_headers_hash = prev->hide_headers_hash; - if (conf->hide_headers_hash.buckets -#if (NGX_HTTP_CACHE) - && ((conf->cache == 0) == (prev->cache == 0)) -#endif - ) - { + if (conf->hide_headers_hash.buckets) { return NGX_OK; } @@ -6111,7 +6183,23 @@ ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf, hash->pool = cf->pool; hash->temp_pool = NULL; - return ngx_hash_init(hash, hide_headers.elts, hide_headers.nelts); + if (ngx_hash_init(hash, hide_headers.elts, hide_headers.nelts) != NGX_OK) { + return NGX_ERROR; + } + + /* + * special handling to preserve conf->hide_headers_hash + * in the "http" section to inherit it to all servers + */ + + if (prev->hide_headers_hash.buckets == NULL + && conf->hide_headers == prev->hide_headers + && conf->pass_headers == prev->pass_headers) + { + prev->hide_headers_hash = conf->hide_headers_hash; + } + + return NGX_OK; } diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h index 3d521f2..7390f2e 100644 --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -128,7 +128,6 @@ struct ngx_http_upstream_srv_conf_s { u_char *file_name; ngx_uint_t line; in_port_t port; - in_port_t default_port; ngx_uint_t no_port; /* unsigned no_port:1 */ #if (NGX_HTTP_UPSTREAM_ZONE) @@ -199,6 +198,8 @@ typedef struct { ngx_uint_t cache_use_stale; ngx_uint_t cache_methods; + off_t cache_max_range_offset; + ngx_flag_t cache_lock; ngx_msec_t cache_lock_timeout; ngx_msec_t cache_lock_age; @@ -300,6 +301,7 @@ typedef struct { struct sockaddr *sockaddr; socklen_t socklen; + ngx_str_t name; ngx_resolver_ctx_t *ctx; } ngx_http_upstream_resolved_t; diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c index 0137bf6..f6051ae 100644 --- a/src/http/ngx_http_upstream_round_robin.c +++ b/src/http/ngx_http_upstream_round_robin.c @@ -337,7 +337,7 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r, if (ur->sockaddr) { peer[0].sockaddr = ur->sockaddr; peer[0].socklen = ur->socklen; - peer[0].name = ur->host; + peer[0].name = ur->name.data ? ur->name : ur->host; peer[0].weight = 1; peer[0].effective_weight = 1; peer[0].current_weight = 0; diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index 235092b..8301630 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -136,6 +136,8 @@ static ngx_int_t ngx_http_v2_send_window_update(ngx_http_v2_connection_t *h2c, ngx_uint_t sid, size_t window); static ngx_int_t ngx_http_v2_send_rst_stream(ngx_http_v2_connection_t *h2c, ngx_uint_t sid, ngx_uint_t status); +static ngx_int_t ngx_http_v2_send_goaway(ngx_http_v2_connection_t *h2c, + ngx_uint_t status); static ngx_http_v2_out_frame_t *ngx_http_v2_get_frame( ngx_http_v2_connection_t *h2c, size_t length, ngx_uint_t type, @@ -293,6 +295,8 @@ ngx_http_v2_init(ngx_event_t *rev) rev->handler = ngx_http_v2_read_handler; c->write->handler = ngx_http_v2_write_handler; + c->idle = 1; + ngx_http_v2_read_handler(rev); } @@ -320,6 +324,30 @@ ngx_http_v2_read_handler(ngx_event_t *rev) h2c->blocked = 1; + if (c->close) { + c->close = 0; + + if (!h2c->goaway) { + h2c->goaway = 1; + + if (ngx_http_v2_send_goaway(h2c, NGX_HTTP_V2_NO_ERROR) + == NGX_ERROR) + { + ngx_http_v2_finalize_connection(h2c, 0); + return; + } + + if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) { + ngx_http_v2_finalize_connection(h2c, 0); + return; + } + } + + h2c->blocked = 0; + + return; + } + h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx, ngx_http_v2_module); @@ -633,6 +661,11 @@ ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c) /* rc == NGX_OK */ } + if (h2c->goaway) { + ngx_http_close_connection(c); + return; + } + h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, ngx_http_v2_module); if (h2c->state.incomplete) { @@ -640,11 +673,6 @@ ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c) return; } - if (ngx_terminate || ngx_exiting) { - ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_NO_ERROR); - return; - } - ngx_destroy_pool(h2c->pool); h2c->pool = NULL; @@ -658,7 +686,6 @@ ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c) #endif c->destroyed = 1; - c->idle = 1; ngx_reusable_connection(c, 1); c->write->handler = ngx_http_empty_handler; @@ -1027,6 +1054,12 @@ ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos, return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); } + if (h2c->goaway) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "skipping http2 HEADERS frame"); + return ngx_http_v2_state_skip(h2c, pos, end); + } + if ((size_t) (end - pos) < size) { return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_headers); @@ -1149,6 +1182,15 @@ ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos, ngx_http_v2_set_dependency(h2c, node, depend, excl); } + if (h2c->connection->requests >= h2scf->max_requests) { + h2c->goaway = 1; + + if (ngx_http_v2_send_goaway(h2c, NGX_HTTP_V2_NO_ERROR) == NGX_ERROR) { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + } + return ngx_http_v2_state_header_block(h2c, pos, end); rst_stream: @@ -2550,7 +2592,7 @@ ngx_http_v2_send_rst_stream(ngx_http_v2_connection_t *h2c, ngx_uint_t sid, ngx_http_v2_out_frame_t *frame; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2 send RST_STREAM frame sid:%ui, status:%uz", + "http2 send RST_STREAM frame sid:%ui, status:%ui", sid, status); frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_RST_STREAM_SIZE, @@ -2576,8 +2618,9 @@ ngx_http_v2_send_goaway(ngx_http_v2_connection_t *h2c, ngx_uint_t status) ngx_buf_t *buf; ngx_http_v2_out_frame_t *frame; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2 send GOAWAY frame, status:%uz", status); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send GOAWAY frame: last sid %ui, error %ui", + h2c->last_sid, status); frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_GOAWAY_SIZE, NGX_HTTP_V2_GOAWAY_FRAME, @@ -4162,7 +4205,6 @@ ngx_http_v2_idle_handler(ngx_event_t *rev) #endif c->destroyed = 0; - c->idle = 0; ngx_reusable_connection(c, 0); h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, @@ -4197,8 +4239,10 @@ ngx_http_v2_finalize_connection(ngx_http_v2_connection_t *h2c, h2c->blocked = 1; - if (!c->error && ngx_http_v2_send_goaway(h2c, status) != NGX_ERROR) { - (void) ngx_http_v2_send_output_queue(h2c); + if (!c->error && !h2c->goaway) { + if (ngx_http_v2_send_goaway(h2c, status) != NGX_ERROR) { + (void) ngx_http_v2_send_output_queue(h2c); + } } c->error = 1; diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h index d712d38..63bbdad 100644 --- a/src/http/v2/ngx_http_v2.h +++ b/src/http/v2/ngx_http_v2.h @@ -146,6 +146,7 @@ struct ngx_http_v2_connection_s { unsigned closed_nodes:8; unsigned settings_ack:1; unsigned blocked:1; + unsigned goaway:1; }; diff --git a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c index 4ab7791..878020e 100644 --- a/src/http/v2/ngx_http_v2_filter_module.c +++ b/src/http/v2/ngx_http_v2_filter_module.c @@ -1079,6 +1079,10 @@ static ngx_inline ngx_int_t ngx_http_v2_flow_control(ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream) { + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2:%ui available windows: conn:%uz stream:%z", + stream->node->id, h2c->send_window, stream->send_window); + if (stream->send_window <= 0) { stream->exhausted = 1; return NGX_DECLINED; diff --git a/src/http/v2/ngx_http_v2_module.c b/src/http/v2/ngx_http_v2_module.c index b7d99e0..032abcb 100644 --- a/src/http/v2/ngx_http_v2_module.c +++ b/src/http/v2/ngx_http_v2_module.c @@ -73,6 +73,13 @@ static ngx_command_t ngx_http_v2_commands[] = { offsetof(ngx_http_v2_srv_conf_t, concurrent_streams), NULL }, + { 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_string("http2_max_field_size"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, @@ -322,6 +329,7 @@ ngx_http_v2_create_srv_conf(ngx_conf_t *cf) h2scf->pool_size = NGX_CONF_UNSET_SIZE; h2scf->concurrent_streams = 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; @@ -347,6 +355,7 @@ ngx_http_v2_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_uint_value(conf->concurrent_streams, prev->concurrent_streams, 128); + 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); diff --git a/src/http/v2/ngx_http_v2_module.h b/src/http/v2/ngx_http_v2_module.h index 91f97c2..540f826 100644 --- a/src/http/v2/ngx_http_v2_module.h +++ b/src/http/v2/ngx_http_v2_module.h @@ -23,6 +23,7 @@ typedef struct { typedef struct { size_t pool_size; ngx_uint_t concurrent_streams; + ngx_uint_t max_requests; size_t max_field_size; size_t max_header_size; size_t preread_size; diff --git a/src/mail/ngx_mail.h b/src/mail/ngx_mail.h index c30af35..6002508 100644 --- a/src/mail/ngx_mail.h +++ b/src/mail/ngx_mail.h @@ -132,7 +132,8 @@ typedef enum { ngx_pop3_auth_login_username, ngx_pop3_auth_login_password, ngx_pop3_auth_plain, - ngx_pop3_auth_cram_md5 + ngx_pop3_auth_cram_md5, + ngx_pop3_auth_external } ngx_pop3_state_e; @@ -142,6 +143,7 @@ typedef enum { ngx_imap_auth_login_password, ngx_imap_auth_plain, ngx_imap_auth_cram_md5, + ngx_imap_auth_external, ngx_imap_login, ngx_imap_user, ngx_imap_passwd @@ -154,6 +156,7 @@ typedef enum { ngx_smtp_auth_login_password, ngx_smtp_auth_plain, ngx_smtp_auth_cram_md5, + ngx_smtp_auth_external, ngx_smtp_helo, ngx_smtp_helo_xclient, ngx_smtp_helo_from, @@ -285,14 +288,16 @@ typedef struct { #define NGX_MAIL_AUTH_LOGIN_USERNAME 2 #define NGX_MAIL_AUTH_APOP 3 #define NGX_MAIL_AUTH_CRAM_MD5 4 -#define NGX_MAIL_AUTH_NONE 5 +#define NGX_MAIL_AUTH_EXTERNAL 5 +#define NGX_MAIL_AUTH_NONE 6 #define NGX_MAIL_AUTH_PLAIN_ENABLED 0x0002 #define NGX_MAIL_AUTH_LOGIN_ENABLED 0x0004 #define NGX_MAIL_AUTH_APOP_ENABLED 0x0008 #define NGX_MAIL_AUTH_CRAM_MD5_ENABLED 0x0010 -#define NGX_MAIL_AUTH_NONE_ENABLED 0x0020 +#define NGX_MAIL_AUTH_EXTERNAL_ENABLED 0x0020 +#define NGX_MAIL_AUTH_NONE_ENABLED 0x0040 #define NGX_MAIL_PARSE_INVALID_COMMAND 20 @@ -377,6 +382,8 @@ ngx_int_t ngx_mail_auth_login_password(ngx_mail_session_t *s, ngx_int_t ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s, ngx_connection_t *c, char *prefix, size_t len); ngx_int_t ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c); +ngx_int_t ngx_mail_auth_external(ngx_mail_session_t *s, ngx_connection_t *c, + ngx_uint_t n); ngx_int_t ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c); void ngx_mail_send(ngx_event_t *wev); diff --git a/src/mail/ngx_mail_auth_http_module.c b/src/mail/ngx_mail_auth_http_module.c index a94434a..6b57358 100644 --- a/src/mail/ngx_mail_auth_http_module.c +++ b/src/mail/ngx_mail_auth_http_module.c @@ -151,6 +151,7 @@ static ngx_str_t ngx_mail_auth_http_method[] = { ngx_string("plain"), ngx_string("apop"), ngx_string("cram-md5"), + ngx_string("external"), ngx_string("none") }; diff --git a/src/mail/ngx_mail_handler.c b/src/mail/ngx_mail_handler.c index 901bb8f..9d4ef56 100644 --- a/src/mail/ngx_mail_handler.c +++ b/src/mail/ngx_mail_handler.c @@ -612,6 +612,40 @@ ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c) } +ngx_int_t +ngx_mail_auth_external(ngx_mail_session_t *s, ngx_connection_t *c, + ngx_uint_t n) +{ + ngx_str_t *arg, external; + + arg = s->args.elts; + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth external: \"%V\"", &arg[n]); + + external.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len)); + if (external.data == NULL) { + return NGX_ERROR; + } + + if (ngx_decode_base64(&external, &arg[n]) != NGX_OK) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid base64 encoding in AUTH EXTERNAL command"); + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + s->login.len = external.len; + s->login.data = external.data; + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth external: \"%V\"", &s->login); + + s->auth_method = NGX_MAIL_AUTH_EXTERNAL; + + return NGX_DONE; +} + + void ngx_mail_send(ngx_event_t *wev) { diff --git a/src/mail/ngx_mail_imap_handler.c b/src/mail/ngx_mail_imap_handler.c index 57e2fb7..1c54457 100644 --- a/src/mail/ngx_mail_imap_handler.c +++ b/src/mail/ngx_mail_imap_handler.c @@ -222,6 +222,10 @@ ngx_mail_imap_auth_state(ngx_event_t *rev) case ngx_imap_auth_cram_md5: rc = ngx_mail_auth_cram_md5(s, c); break; + + case ngx_imap_auth_external: + rc = ngx_mail_auth_external(s, c, 0); + break; } } else if (rc == NGX_IMAP_NEXT) { @@ -399,6 +403,13 @@ ngx_mail_imap_authenticate(ngx_mail_session_t *s, ngx_connection_t *c) } return NGX_ERROR; + + case NGX_MAIL_AUTH_EXTERNAL: + + ngx_str_set(&s->out, imap_username); + s->mail_state = ngx_imap_auth_external; + + return NGX_OK; } return rc; diff --git a/src/mail/ngx_mail_imap_module.c b/src/mail/ngx_mail_imap_module.c index d281070..1f187fd 100644 --- a/src/mail/ngx_mail_imap_module.c +++ b/src/mail/ngx_mail_imap_module.c @@ -29,6 +29,7 @@ static ngx_conf_bitmask_t ngx_mail_imap_auth_methods[] = { { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED }, { ngx_string("login"), NGX_MAIL_AUTH_LOGIN_ENABLED }, { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED }, + { ngx_string("external"), NGX_MAIL_AUTH_EXTERNAL_ENABLED }, { ngx_null_string, 0 } }; @@ -38,6 +39,7 @@ static ngx_str_t ngx_mail_imap_auth_methods_names[] = { ngx_string("AUTH=LOGIN"), ngx_null_string, /* APOP */ ngx_string("AUTH=CRAM-MD5"), + ngx_string("AUTH=EXTERNAL"), ngx_null_string /* NONE */ }; @@ -179,7 +181,7 @@ ngx_mail_imap_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) } for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; - m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED; + m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED; m <<= 1, i++) { if (m & conf->auth_methods) { @@ -205,7 +207,7 @@ ngx_mail_imap_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) auth = p; for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; - m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED; + m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED; m <<= 1, i++) { if (m & conf->auth_methods) { diff --git a/src/mail/ngx_mail_parse.c b/src/mail/ngx_mail_parse.c index b158f5a..2c2cdff 100644 --- a/src/mail/ngx_mail_parse.c +++ b/src/mail/ngx_mail_parse.c @@ -905,13 +905,27 @@ ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c) if (arg[0].len == 8) { - if (s->args.nelts != 1) { - return NGX_MAIL_PARSE_INVALID_COMMAND; - } - if (ngx_strncasecmp(arg[0].data, (u_char *) "CRAM-MD5", 8) == 0) { + + if (s->args.nelts != 1) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + return NGX_MAIL_AUTH_CRAM_MD5; } + + if (ngx_strncasecmp(arg[0].data, (u_char *) "EXTERNAL", 8) == 0) { + + if (s->args.nelts == 1) { + return NGX_MAIL_AUTH_EXTERNAL; + } + + if (s->args.nelts == 2) { + return ngx_mail_auth_external(s, c, 1); + } + } + + return NGX_MAIL_PARSE_INVALID_COMMAND; } return NGX_MAIL_PARSE_INVALID_COMMAND; diff --git a/src/mail/ngx_mail_pop3_handler.c b/src/mail/ngx_mail_pop3_handler.c index 51bc257..a2d5658 100644 --- a/src/mail/ngx_mail_pop3_handler.c +++ b/src/mail/ngx_mail_pop3_handler.c @@ -240,6 +240,10 @@ ngx_mail_pop3_auth_state(ngx_event_t *rev) case ngx_pop3_auth_cram_md5: rc = ngx_mail_auth_cram_md5(s, c); break; + + case ngx_pop3_auth_external: + rc = ngx_mail_auth_external(s, c, 0); + break; } } @@ -494,6 +498,13 @@ ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c) } return NGX_ERROR; + + case NGX_MAIL_AUTH_EXTERNAL: + + ngx_str_set(&s->out, pop3_username); + s->mail_state = ngx_pop3_auth_external; + + return NGX_OK; } return rc; diff --git a/src/mail/ngx_mail_pop3_module.c b/src/mail/ngx_mail_pop3_module.c index 73f8531..bd60e0a 100644 --- a/src/mail/ngx_mail_pop3_module.c +++ b/src/mail/ngx_mail_pop3_module.c @@ -29,23 +29,19 @@ static ngx_conf_bitmask_t ngx_mail_pop3_auth_methods[] = { { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED }, { ngx_string("apop"), NGX_MAIL_AUTH_APOP_ENABLED }, { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED }, + { ngx_string("external"), NGX_MAIL_AUTH_EXTERNAL_ENABLED }, { ngx_null_string, 0 } }; -static ngx_str_t ngx_mail_pop3_auth_plain_capability = - ngx_string("+OK methods supported:" CRLF - "LOGIN" CRLF - "PLAIN" CRLF - "." CRLF); - - -static ngx_str_t ngx_mail_pop3_auth_cram_md5_capability = - ngx_string("+OK methods supported:" CRLF - "LOGIN" CRLF - "PLAIN" CRLF - "CRAM-MD5" CRLF - "." CRLF); +static ngx_str_t ngx_mail_pop3_auth_methods_names[] = { + ngx_string("PLAIN"), + ngx_string("LOGIN"), + ngx_null_string, /* APOP */ + ngx_string("CRAM-MD5"), + ngx_string("EXTERNAL"), + ngx_null_string /* NONE */ +}; static ngx_mail_protocol_t ngx_mail_pop3_protocol = { @@ -140,13 +136,17 @@ ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) u_char *p; size_t size, stls_only_size; ngx_str_t *c, *d; - ngx_uint_t i; + ngx_uint_t i, m; ngx_conf_merge_bitmask_value(conf->auth_methods, prev->auth_methods, (NGX_CONF_BITMASK_SET |NGX_MAIL_AUTH_PLAIN_ENABLED)); + if (conf->auth_methods & NGX_MAIL_AUTH_PLAIN_ENABLED) { + conf->auth_methods |= NGX_MAIL_AUTH_LOGIN_ENABLED; + } + if (conf->capabilities.nelts == 0) { conf->capabilities = prev->capabilities; } @@ -179,11 +179,15 @@ ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) stls_only_size += c[i].len + sizeof(CRLF) - 1; } - if (conf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED) { - size += sizeof("SASL LOGIN PLAIN CRAM-MD5" CRLF) - 1; + size += sizeof("SASL") - 1 + sizeof(CRLF) - 1; - } else { - size += sizeof("SASL LOGIN PLAIN" CRLF) - 1; + for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; + m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED; + m <<= 1, i++) + { + if (m & conf->auth_methods) { + size += 1 + ngx_mail_pop3_auth_methods_names[i].len; + } } p = ngx_pnalloc(cf->pool, size); @@ -202,15 +206,21 @@ ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) *p++ = CR; *p++ = LF; } - if (conf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED) { - p = ngx_cpymem(p, "SASL LOGIN PLAIN CRAM-MD5" CRLF, - sizeof("SASL LOGIN PLAIN CRAM-MD5" CRLF) - 1); + p = ngx_cpymem(p, "SASL", sizeof("SASL") - 1); - } else { - p = ngx_cpymem(p, "SASL LOGIN PLAIN" CRLF, - sizeof("SASL LOGIN PLAIN" CRLF) - 1); + for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; + m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED; + m <<= 1, i++) + { + if (m & conf->auth_methods) { + *p++ = ' '; + p = ngx_cpymem(p, ngx_mail_pop3_auth_methods_names[i].data, + ngx_mail_pop3_auth_methods_names[i].len); + } } + *p++ = CR; *p++ = LF; + *p++ = '.'; *p++ = CR; *p = LF; @@ -231,13 +241,43 @@ ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) *p++ = '.'; *p++ = CR; *p = LF; - if (conf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED) { - conf->auth_capability = ngx_mail_pop3_auth_cram_md5_capability; + size = sizeof("+OK methods supported:" CRLF) - 1 + + sizeof("." CRLF) - 1; - } else { - conf->auth_capability = ngx_mail_pop3_auth_plain_capability; + for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; + m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED; + m <<= 1, i++) + { + if (m & conf->auth_methods) { + size += ngx_mail_pop3_auth_methods_names[i].len + + sizeof(CRLF) - 1; + } } + p = ngx_pnalloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->auth_capability.data = p; + conf->auth_capability.len = size; + + p = ngx_cpymem(p, "+OK methods supported:" CRLF, + sizeof("+OK methods supported:" CRLF) - 1); + + for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; + m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED; + m <<= 1, i++) + { + if (m & conf->auth_methods) { + p = ngx_cpymem(p, ngx_mail_pop3_auth_methods_names[i].data, + ngx_mail_pop3_auth_methods_names[i].len); + *p++ = CR; *p++ = LF; + } + } + + *p++ = '.'; *p++ = CR; *p = LF; + p = ngx_pnalloc(cf->pool, stls_only_size); if (p == NULL) { diff --git a/src/mail/ngx_mail_smtp_handler.c b/src/mail/ngx_mail_smtp_handler.c index 81cc75f..47756c3 100644 --- a/src/mail/ngx_mail_smtp_handler.c +++ b/src/mail/ngx_mail_smtp_handler.c @@ -485,6 +485,10 @@ ngx_mail_smtp_auth_state(ngx_event_t *rev) case ngx_smtp_auth_cram_md5: rc = ngx_mail_auth_cram_md5(s, c); break; + + case ngx_smtp_auth_external: + rc = ngx_mail_auth_external(s, c, 0); + break; } } @@ -652,6 +656,13 @@ ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c) } return NGX_ERROR; + + case NGX_MAIL_AUTH_EXTERNAL: + + ngx_str_set(&s->out, smtp_username); + s->mail_state = ngx_smtp_auth_external; + + return NGX_OK; } return rc; diff --git a/src/mail/ngx_mail_smtp_module.c b/src/mail/ngx_mail_smtp_module.c index d5bb51c..f03bd90 100644 --- a/src/mail/ngx_mail_smtp_module.c +++ b/src/mail/ngx_mail_smtp_module.c @@ -21,6 +21,7 @@ static ngx_conf_bitmask_t ngx_mail_smtp_auth_methods[] = { { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED }, { ngx_string("login"), NGX_MAIL_AUTH_LOGIN_ENABLED }, { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED }, + { ngx_string("external"), NGX_MAIL_AUTH_EXTERNAL_ENABLED }, { ngx_string("none"), NGX_MAIL_AUTH_NONE_ENABLED }, { ngx_null_string, 0 } }; @@ -31,6 +32,7 @@ static ngx_str_t ngx_mail_smtp_auth_methods_names[] = { ngx_string("LOGIN"), ngx_null_string, /* APOP */ ngx_string("CRAM-MD5"), + ngx_string("EXTERNAL"), ngx_null_string /* NONE */ }; @@ -207,7 +209,7 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) auth_enabled = 0; for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; - m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED; + m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED; m <<= 1, i++) { if (m & conf->auth_methods) { @@ -250,7 +252,7 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) *p++ = 'A'; *p++ = 'U'; *p++ = 'T'; *p++ = 'H'; for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; - m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED; + m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED; m <<= 1, i++) { if (m & conf->auth_methods) { diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c index 4231f97..c03b515 100644 --- a/src/stream/ngx_stream_proxy_module.c +++ b/src/stream/ngx_stream_proxy_module.c @@ -433,6 +433,23 @@ ngx_stream_proxy_handler(ngx_stream_session_t *s) host = &u->resolved->host; + umcf = ngx_stream_get_module_main_conf(s, ngx_stream_upstream_module); + + uscfp = umcf->upstreams.elts; + + for (i = 0; i < umcf->upstreams.nelts; i++) { + + uscf = uscfp[i]; + + if (uscf->host.len == host->len + && ((uscf->port == 0 && u->resolved->no_port) + || uscf->port == u->resolved->port) + && ngx_strncasecmp(uscf->host.data, host->data, host->len) == 0) + { + goto found; + } + } + if (u->resolved->sockaddr) { if (u->resolved->port == 0 @@ -456,23 +473,6 @@ ngx_stream_proxy_handler(ngx_stream_session_t *s) return; } - umcf = ngx_stream_get_module_main_conf(s, ngx_stream_upstream_module); - - uscfp = umcf->upstreams.elts; - - for (i = 0; i < umcf->upstreams.nelts; i++) { - - uscf = uscfp[i]; - - if (uscf->host.len == host->len - && ((uscf->port == 0 && u->resolved->no_port) - || uscf->port == u->resolved->port) - && ngx_strncasecmp(uscf->host.data, host->data, host->len) == 0) - { - goto found; - } - } - if (u->resolved->port == 0) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "no port in upstream \"%V\"", host); @@ -578,16 +578,14 @@ ngx_stream_proxy_eval(ngx_stream_session_t *s, return NGX_ERROR; } - if (url.addrs && url.addrs[0].sockaddr) { + if (url.addrs) { u->resolved->sockaddr = url.addrs[0].sockaddr; u->resolved->socklen = url.addrs[0].socklen; + u->resolved->name = url.addrs[0].name; u->resolved->naddrs = 1; - u->resolved->host = url.addrs[0].name; - - } else { - u->resolved->host = url.host; } + u->resolved->host = url.host; u->resolved->port = url.port; u->resolved->no_port = url.no_port; @@ -1183,7 +1181,8 @@ ngx_stream_proxy_ssl_name(ngx_stream_session_t *s) ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "upstream SSL server name: \"%s\"", name.data); - if (SSL_set_tlsext_host_name(u->peer.connection->ssl->connection, name.data) + if (SSL_set_tlsext_host_name(u->peer.connection->ssl->connection, + (char *) name.data) == 0) { ngx_ssl_error(NGX_LOG_ERR, s->connection->log, 0, diff --git a/src/stream/ngx_stream_upstream.h b/src/stream/ngx_stream_upstream.h index 764a340..ec75768 100644 --- a/src/stream/ngx_stream_upstream.h +++ b/src/stream/ngx_stream_upstream.h @@ -105,6 +105,7 @@ typedef struct { struct sockaddr *sockaddr; socklen_t socklen; + ngx_str_t name; ngx_resolver_ctx_t *ctx; } ngx_stream_upstream_resolved_t; diff --git a/src/stream/ngx_stream_upstream_round_robin.c b/src/stream/ngx_stream_upstream_round_robin.c index 3a62501..db620ef 100644 --- a/src/stream/ngx_stream_upstream_round_robin.c +++ b/src/stream/ngx_stream_upstream_round_robin.c @@ -344,7 +344,7 @@ ngx_stream_upstream_create_round_robin_peer(ngx_stream_session_t *s, if (ur->sockaddr) { peer[0].sockaddr = ur->sockaddr; peer[0].socklen = ur->socklen; - peer[0].name = ur->host; + peer[0].name = ur->name; peer[0].weight = 1; peer[0].effective_weight = 1; peer[0].current_weight = 0; From 7c03e6673ba20bd34ae0795219eb2baf90825702 Mon Sep 17 00:00:00 2001 From: Christos Trochalakis Date: Fri, 18 Nov 2016 17:17:57 +0200 Subject: [PATCH 006/475] Adjust experimental flow We now dummy merge (-s ours) upstream into upstream-1.11 instead of force pushing a clean branch on every release. That way we can easier track the branch's history. --- debian/README.Packaging | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/debian/README.Packaging b/debian/README.Packaging index bbb7875..47a582f 100644 --- a/debian/README.Packaging +++ b/debian/README.Packaging @@ -58,11 +58,11 @@ them. are commited (changelog entry etc) and the build is made. * upstream-1.11 - Force-pushed on every 1.11.x release. + Pushed on every 1.11.x release. - Before a new 1.11.x release the branch is reset to origin/upstream. - This is a technicallity so we can avoid resolving conflicts when a new 1.10.x - release happens between two experimental releases. + Before a new 1.11.x release origin/upstream is dummy merged (-s ours) into + ustream-1.11. This is a technicallity so we can avoid resolving conflicts + when a new 1.10.x release happens between two experimental releases. Older 1.11.x releases are not referenced by any branch, but they can be found by the relevant debian/* tag. From e7164cf7acc2ef081cbedc8f5c9801f53e64c1ba Mon Sep 17 00:00:00 2001 From: Christos Trochalakis Date: Mon, 21 Nov 2016 16:20:29 +0200 Subject: [PATCH 007/475] Change my email --- debian/control | 2 +- debian/copyright | 2 +- debian/dh_nginx | 2 +- ...2-Make-sure-signature-stays-the-same-in-all-nginx-buil.patch | 2 +- debian/patches/perl-use-dpkg-buildflags.patch | 2 +- debian/po/templates.pot | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/debian/control b/debian/control index e553a8a..ff6aace 100644 --- a/debian/control +++ b/debian/control @@ -4,7 +4,7 @@ Priority: optional Maintainer: Debian Nginx Maintainers Uploaders: Kartik Mistry , Michael Lustfield , - Christos Trochalakis + Christos Trochalakis Build-Depends: autotools-dev, debhelper (>= 9), po-debconf, diff --git a/debian/copyright b/debian/copyright index 019dc9d..40218cb 100644 --- a/debian/copyright +++ b/debian/copyright @@ -34,7 +34,7 @@ Copyright: 2007-2009, Fabio Tranchitella 2010-2014, Michael Lustfield 2011 Dmitry E. Oboukhov 2011-2013, Cyril Lavier - 2013-2014, Christos Trochalakis + 2013-2016, Christos Trochalakis License: BSD-2-clause Files: debian/modules/headers-more-nginx-module/* diff --git a/debian/dh_nginx b/debian/dh_nginx index 3be792d..8e6ce3b 100755 --- a/debian/dh_nginx +++ b/debian/dh_nginx @@ -1,7 +1,7 @@ #! /usr/bin/perl # dh_nginx - Nginx configuration helper -# Copyright (C) 2016 Christos Trochalakis +# Copyright (C) 2016 Christos Trochalakis # # This program is licensed under the terms of the GNU General # Public License veserion 2+ diff --git a/debian/patches/0002-Make-sure-signature-stays-the-same-in-all-nginx-buil.patch b/debian/patches/0002-Make-sure-signature-stays-the-same-in-all-nginx-buil.patch index 1bea8b2..9cc3962 100644 --- a/debian/patches/0002-Make-sure-signature-stays-the-same-in-all-nginx-buil.patch +++ b/debian/patches/0002-Make-sure-signature-stays-the-same-in-all-nginx-buil.patch @@ -1,4 +1,4 @@ -From: Christos Trochalakis +From: Christos Trochalakis Date: Wed, 30 Mar 2016 09:47:11 +0300 Subject: Make sure signature stays the same in all nginx builds diff --git a/debian/patches/perl-use-dpkg-buildflags.patch b/debian/patches/perl-use-dpkg-buildflags.patch index d6b6f96..5379313 100644 --- a/debian/patches/perl-use-dpkg-buildflags.patch +++ b/debian/patches/perl-use-dpkg-buildflags.patch @@ -1,6 +1,6 @@ Description: Use linker flags from environment for perl (dpkg-buildflags). Necessary for hardening flags. -Author: Christos Trochalakis +Author: Christos Trochalakis --- a/src/http/modules/perl/Makefile.PL +++ b/src/http/modules/perl/Makefile.PL @@ -3,6 +3,7 @@ diff --git a/debian/po/templates.pot b/debian/po/templates.pot index bb72383..9260ff1 100644 --- a/debian/po/templates.pot +++ b/debian/po/templates.pot @@ -1,7 +1,7 @@ # Nginx debconf translations # Copyright (C) 2016 Christos Trochalakis # This file is distributed under the same license as the nginx package. -# Chrirtos Trochalakis , 2016. +# Christos Trochalakis , 2016. # #, fuzzy msgid "" From bf78540e31b57a79b79b54d019a2faa2f988c89c Mon Sep 17 00:00:00 2001 From: Christos Trochalakis Date: Fri, 18 Nov 2016 16:14:24 +0200 Subject: [PATCH 008/475] debian/rules: Correctly clean patched modules Closes: #844506 Thanks: Sven-Haegar Koch for the initial patch. --- debian/rules | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/debian/rules b/debian/rules index f1806b1..4a8d986 100755 --- a/debian/rules +++ b/debian/rules @@ -145,7 +145,7 @@ extras_configure_flags := \ override_dh_auto_configure: config_patch_modules $(foreach flavour,$(FLAVOURS),config.arch.$(flavour)) override_dh_auto_build: $(foreach flavour,$(FLAVOURS),build.arch.$(flavour)) override_dh_strip: $(foreach flavour,$(FLAVOURS),strip.arch.$(flavour)) $(foreach mod,$(DYN_MODS),strip.mods.$(mod)) -override_dh_clean: $(foreach flavour,$(FLAVOURS),clean.$(flavour)) +override_dh_clean: clean_patch_modules $(foreach flavour,$(FLAVOURS),clean.$(flavour)) dh_clean override_dh_install: @@ -174,6 +174,13 @@ config_patch_modules: $(foreach mod,$(modules_with_patches),config.patch.$(mod)) config.patch.%: cd $(MODULESDIR)/$* && QUILT_PATCHES=$(MODULESPATCHDIR)/$* quilt push -a +clean_patch_modules: $(foreach mod,$(modules_with_patches),clean.patch.$(mod)) +clean.patch.%: + if [ -s $(MODULESDIR)/$*/.pc/applied-patches ]; then \ + cd $(MODULESDIR)/$* && QUILT_PATCHES=$(MODULESPATCHDIR)/$* quilt pop -q -a; \ + rm -rf $(MODULESDIR)/$*/.pc; \ + fi + config.arch.%: dh_testdir mkdir -p $(BUILDDIR_$*) @@ -188,4 +195,4 @@ config.arch.%: clean.%: rm -rf $(BUILDDIR_$*) -.PHONY: config_patch_modules +.PHONY: config_patch_modules clean_patch_modules From 4deb3d7f527d86f4d2072bb96b8090214d0bdc8a Mon Sep 17 00:00:00 2001 From: Michael Lustfield Date: Sat, 3 Dec 2016 07:58:19 +0000 Subject: [PATCH 009/475] Correcting location of default php-fpm socket. --- debian/changelog | 7 +++++++ debian/conf/sites-available/default | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 89b8f64..af0ea9f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +nginx (1.10.2-3) UNRELEASED; urgency=medium + + * debian/conf/sites-enabled/default: + + Correcting location of default php-fpm socket. (Closes #846145) + + -- Michael Lustfield Sat, 03 Dec 2016 07:57:39 +0000 + nginx (1.10.2-2) unstable; urgency=medium * Build against OpenSSL 1.1.0. (Closes: #828453) diff --git a/debian/conf/sites-available/default b/debian/conf/sites-available/default index 890f572..c841ceb 100644 --- a/debian/conf/sites-available/default +++ b/debian/conf/sites-available/default @@ -57,7 +57,7 @@ server { # include snippets/fastcgi-php.conf; # # # With php-fpm (or other unix sockets): - # fastcgi_pass unix:/var/run/php7.0-fpm.sock; + # fastcgi_pass unix:/var/run/php/php7.0-fpm.sock; # # With php-cgi (or other tcp sockets): # fastcgi_pass 127.0.0.1:9000; #} From 0b60e49e0895dcdbac7a8773359eec09d4a5f746 Mon Sep 17 00:00:00 2001 From: Christos Trochalakis Date: Tue, 13 Dec 2016 18:47:26 +0200 Subject: [PATCH 010/475] New upstream version 1.11.7 --- CHANGES | 27 ++ CHANGES.ru | 27 ++ auto/lib/perl/conf | 6 +- auto/make | 17 +- auto/module | 4 + auto/modules | 4 +- src/core/nginx.c | 48 ++- src/core/nginx.h | 4 +- src/core/ngx_cycle.c | 13 +- src/core/ngx_output_chain.c | 2 +- src/core/ngx_slab.c | 338 +++++++++-------- src/core/ngx_slab.h | 12 + src/event/modules/ngx_devpoll_module.c | 10 +- src/event/modules/ngx_epoll_module.c | 19 +- src/event/modules/ngx_eventport_module.c | 10 +- src/event/modules/ngx_poll_module.c | 10 +- src/event/ngx_event_openssl.c | 380 ++++++++++++++++++- src/event/ngx_event_openssl.h | 11 + src/event/ngx_event_openssl_stapling.c | 79 ++-- src/http/modules/ngx_http_map_module.c | 18 +- src/http/modules/ngx_http_mp4_module.c | 4 +- src/http/modules/ngx_http_ssl_module.c | 15 + src/http/modules/perl/ngx_http_perl_module.c | 8 + src/http/v2/ngx_http_v2.c | 32 +- src/http/v2/ngx_http_v2.h | 3 +- src/http/v2/ngx_http_v2_filter_module.c | 50 ++- src/stream/ngx_stream_map_module.c | 18 +- src/stream/ngx_stream_ssl_module.c | 6 + 28 files changed, 866 insertions(+), 309 deletions(-) diff --git a/CHANGES b/CHANGES index 1882976..99e360e 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,31 @@ +Changes with nginx 1.11.7 13 Dec 2016 + + *) Change: now in case of a client certificate verification error the + $ssl_client_verify variable contains a string with the failure + reason, for example, "FAILED:certificate has expired". + + *) Feature: the $ssl_ciphers, $ssl_curves, $ssl_client_v_start, + $ssl_client_v_end, and $ssl_client_v_remain variables. + + *) Feature: the "volatile" parameter of the "map" directive. + + *) Bugfix: dependencies specified for a module were ignored while + building dynamic modules. + + *) Bugfix: when using HTTP/2 and the "limit_req" or "auth_request" + directives client request body might be corrupted; the bug had + appeared in 1.11.0. + + *) Bugfix: a segmentation fault might occur in a worker process when + using HTTP/2; the bug had appeared in 1.11.3. + + *) Bugfix: in the ngx_http_mp4_module. + Thanks to Congcong Hu. + + *) Bugfix: in the ngx_http_perl_module. + + Changes with nginx 1.11.6 15 Nov 2016 *) Change: format of the $ssl_client_s_dn and $ssl_client_i_dn variables diff --git a/CHANGES.ru b/CHANGES.ru index 3b66b94..695547b 100644 --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,31 @@ +Изменения в nginx 1.11.7 13.12.2016 + + *) Изменение: переменная $ssl_client_verify теперь в случае ошибки + проверки клиентского сертификата содержит строку с описанием ошибки, + например, "FAILED:certificate has expired". + + *) Добавление: переменные $ssl_ciphers, $ssl_curves, + $ssl_client_v_start, $ssl_client_v_end и $ssl_client_v_remain. + + *) Добавление: параметр volatile директивы map. + + *) Исправление: при сборке динамических модулей не учитывались заданные + для модуля зависимости. + + *) Исправление: при использовании HTTP/2 и директив limit_req или + auth_request тело запроса могло быть повреждено; ошибка появилась в + 1.11.0. + + *) Исправление: при использовании HTTP/2 в рабочем процессе мог + произойти segmentation fault; ошибка появилась в 1.11.3. + + *) Исправление: в модуле ngx_http_mp4_module. + Спасибо Congcong Hu. + + *) Исправление: в модуле ngx_http_perl_module. + + Изменения в nginx 1.11.6 15.11.2016 *) Изменение: формат переменных $ssl_client_s_dn и $ssl_client_i_dn diff --git a/auto/lib/perl/conf b/auto/lib/perl/conf index d891d82..e16a1bc 100644 --- a/auto/lib/perl/conf +++ b/auto/lib/perl/conf @@ -12,9 +12,9 @@ NGX_PERL_VER=`$NGX_PERL -v 2>&1 | grep '^This is perl' 2>&1 \ if test -n "$NGX_PERL_VER"; then echo " + perl version: $NGX_PERL_VER" - if [ "`$NGX_PERL -e 'use 5.006001; print "OK"'`" != "OK" ]; then + if [ "`$NGX_PERL -e 'use 5.008006; print "OK"'`" != "OK" ]; then echo - echo "$0: error: perl 5.6.1 or higher is required" + echo "$0: error: perl 5.8.6 or higher is required" echo exit 1; @@ -76,7 +76,7 @@ if test -n "$NGX_PERL_VER"; then else echo - echo "$0: error: perl 5.6.1 or higher is required" + echo "$0: error: perl 5.8.6 or higher is required" echo exit 1; diff --git a/auto/make b/auto/make index 84d2668..7ddd100 100644 --- a/auto/make +++ b/auto/make @@ -156,7 +156,7 @@ fi ngx_all_srcs="$ngx_all_srcs $MISC_SRCS" -if test -n "$NGX_ADDON_SRCS"; then +if test -n "$NGX_ADDON_SRCS$DYNAMIC_MODULES"; then cat << END >> $NGX_MAKEFILE @@ -499,17 +499,6 @@ else ngx_perl_cc="$ngx_perl_cc \$(ALL_INCS)" fi -ngx_obj_deps="\$(CORE_DEPS)" -if [ $HTTP != NO ]; then - ngx_obj_deps="$ngx_obj_deps \$(HTTP_DEPS)" -fi -if [ $MAIL != NO ]; then - ngx_obj_deps="$ngx_obj_deps \$(MAIL_DEPS)" -fi -if [ $STREAM != NO ]; then - ngx_obj_deps="$ngx_obj_deps \$(STREAM_DEPS)" -fi - for ngx_module in $DYNAMIC_MODULES do eval ngx_module_srcs="\$${ngx_module}_SRCS" @@ -665,7 +654,7 @@ END cat << END >> $NGX_MAKEFILE -$ngx_obj: $ngx_obj_deps$ngx_cont$ngx_src +$ngx_obj: \$(ADDON_DEPS)$ngx_cont$ngx_src $ngx_perl_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX END @@ -673,7 +662,7 @@ END cat << END >> $NGX_MAKEFILE -$ngx_obj: $ngx_obj_deps$ngx_cont$ngx_src +$ngx_obj: \$(ADDON_DEPS)$ngx_cont$ngx_src $ngx_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX END diff --git a/auto/module b/auto/module index 3b00a07..a2b578d 100644 --- a/auto/module +++ b/auto/module @@ -35,6 +35,10 @@ if [ "$ngx_module_link" = DYNAMIC ]; then CORE_INCS="$CORE_INCS $ngx_module_incs" fi + if test -n "$ngx_module_deps"; then + NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_module_deps" + fi + libs= for lib in $ngx_module_libs do diff --git a/auto/modules b/auto/modules index 89377bf..c664fe3 100644 --- a/auto/modules +++ b/auto/modules @@ -1252,7 +1252,7 @@ if [ $MAIL != NO ]; then elif [ $MAIL = DYNAMIC ]; then ngx_module_name=$MAIL_MODULES ngx_module_incs= - ngx_module_deps=$MAIL_DEPS + ngx_module_deps= ngx_module_srcs=$MAIL_SRCS ngx_module_libs= ngx_module_link=DYNAMIC @@ -1272,7 +1272,7 @@ if [ $STREAM != NO ]; then elif [ $STREAM = DYNAMIC ]; then ngx_module_name=$STREAM_MODULES ngx_module_incs= - ngx_module_deps=$STREAM_DEPS + ngx_module_deps= ngx_module_srcs=$STREAM_SRCS ngx_module_libs= ngx_module_link=DYNAMIC diff --git a/src/core/nginx.c b/src/core/nginx.c index 60f8fe7..c5f09a5 100644 --- a/src/core/nginx.c +++ b/src/core/nginx.c @@ -12,6 +12,7 @@ static void ngx_show_version_info(void); static ngx_int_t ngx_add_inherited_sockets(ngx_cycle_t *cycle); +static void ngx_cleanup_environment(void *data); static ngx_int_t ngx_get_options(int argc, char *const *argv); static ngx_int_t ngx_process_options(ngx_cycle_t *cycle); static ngx_int_t ngx_save_argv(ngx_cycle_t *cycle, int argc, char *const *argv); @@ -495,10 +496,11 @@ ngx_add_inherited_sockets(ngx_cycle_t *cycle) char ** ngx_set_environment(ngx_cycle_t *cycle, ngx_uint_t *last) { - char **p, **env; - ngx_str_t *var; - ngx_uint_t i, n; - ngx_core_conf_t *ccf; + char **p, **env; + ngx_str_t *var; + ngx_uint_t i, n; + ngx_core_conf_t *ccf; + ngx_pool_cleanup_t *cln; ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); @@ -550,14 +552,25 @@ tz_found: if (last) { env = ngx_alloc((*last + n + 1) * sizeof(char *), cycle->log); + if (env == NULL) { + return NULL; + } + *last = n; } else { - env = ngx_palloc(cycle->pool, (n + 1) * sizeof(char *)); - } + cln = ngx_pool_cleanup_add(cycle->pool, 0); + if (cln == NULL) { + return NULL; + } - if (env == NULL) { - return NULL; + env = ngx_alloc((n + 1) * sizeof(char *), cycle->log); + if (env == NULL) { + return NULL; + } + + cln->handler = ngx_cleanup_environment; + cln->data = env; } n = 0; @@ -591,6 +604,25 @@ tz_found: } +static void +ngx_cleanup_environment(void *data) +{ + char **env = data; + + if (environ == env) { + + /* + * if the environment is still used, as it happens on exit, + * the only option is to leak it + */ + + return; + } + + ngx_free(env); +} + + ngx_pid_t ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv) { diff --git a/src/core/nginx.h b/src/core/nginx.h index a75ef04..7733313 100644 --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1011006 -#define NGINX_VERSION "1.11.6" +#define nginx_version 1011007 +#define NGINX_VERSION "1.11.7" #define NGINX_VER "nginx/" NGINX_VERSION #ifdef NGX_BUILD diff --git a/src/core/ngx_cycle.c b/src/core/ngx_cycle.c index a57991c..5e95628 100644 --- a/src/core/ngx_cycle.c +++ b/src/core/ngx_cycle.c @@ -37,7 +37,7 @@ ngx_cycle_t * ngx_init_cycle(ngx_cycle_t *old_cycle) { void *rv; - char **senv, **env; + char **senv; ngx_uint_t i, n; ngx_log_t *log; ngx_time_t *tp; @@ -750,20 +750,9 @@ old_shm_zone_done: if (ngx_process == NGX_PROCESS_MASTER || ngx_is_init_cycle(old_cycle)) { - /* - * perl_destruct() frees environ, if it is not the same as it was at - * perl_construct() time, therefore we save the previous cycle - * environment before ngx_conf_parse() where it will be changed. - */ - - env = environ; - environ = senv; - ngx_destroy_pool(old_cycle->pool); cycle->old_cycle = NULL; - environ = env; - return cycle; } diff --git a/src/core/ngx_output_chain.c b/src/core/ngx_output_chain.c index f784578..7f5dc78 100644 --- a/src/core/ngx_output_chain.c +++ b/src/core/ngx_output_chain.c @@ -512,7 +512,7 @@ ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx) size = ngx_buf_size(src); size = ngx_min(size, dst->end - dst->pos); - sendfile = ctx->sendfile & !ctx->directio; + sendfile = ctx->sendfile && !ctx->directio; #if (NGX_SENDFILE_LIMIT) diff --git a/src/core/ngx_slab.c b/src/core/ngx_slab.c index 56e7765..66faecc 100644 --- a/src/core/ngx_slab.c +++ b/src/core/ngx_slab.c @@ -41,6 +41,19 @@ #endif +#define ngx_slab_slots(pool) \ + (ngx_slab_page_t *) ((u_char *) (pool) + sizeof(ngx_slab_pool_t)) + +#define ngx_slab_page_type(page) ((page)->prev & NGX_SLAB_PAGE_MASK) + +#define ngx_slab_page_prev(page) \ + (ngx_slab_page_t *) ((page)->prev & ~NGX_SLAB_PAGE_MASK) + +#define ngx_slab_page_addr(pool, page) \ + ((((page) - (pool)->pages) << ngx_pagesize_shift) \ + + (uintptr_t) (pool)->start) + + #if (NGX_DEBUG_MALLOC) #define ngx_slab_junk(p, size) ngx_memset(p, 0xA5, size) @@ -76,7 +89,7 @@ ngx_slab_init(ngx_slab_pool_t *pool) size_t size; ngx_int_t m; ngx_uint_t i, n, pages; - ngx_slab_page_t *slots; + ngx_slab_page_t *slots, *page; /* STUB */ if (ngx_slab_max_size == 0) { @@ -90,15 +103,17 @@ ngx_slab_init(ngx_slab_pool_t *pool) pool->min_size = 1 << pool->min_shift; - p = (u_char *) pool + sizeof(ngx_slab_pool_t); + slots = ngx_slab_slots(pool); + + p = (u_char *) slots; size = pool->end - p; ngx_slab_junk(p, size); - slots = (ngx_slab_page_t *) p; n = ngx_pagesize_shift - pool->min_shift; for (i = 0; i < n; i++) { + /* only "next" is used in list head */ slots[i].slab = 0; slots[i].next = &slots[i]; slots[i].prev = 0; @@ -106,30 +121,40 @@ ngx_slab_init(ngx_slab_pool_t *pool) p += n * sizeof(ngx_slab_page_t); + pool->stats = (ngx_slab_stat_t *) p; + ngx_memzero(pool->stats, n * sizeof(ngx_slab_stat_t)); + + p += n * sizeof(ngx_slab_stat_t); + + size -= n * (sizeof(ngx_slab_page_t) + sizeof(ngx_slab_stat_t)); + pages = (ngx_uint_t) (size / (ngx_pagesize + sizeof(ngx_slab_page_t))); - ngx_memzero(p, pages * sizeof(ngx_slab_page_t)); - pool->pages = (ngx_slab_page_t *) p; + ngx_memzero(pool->pages, pages * sizeof(ngx_slab_page_t)); + page = pool->pages; + + /* only "next" is used in list head */ + pool->free.slab = 0; + pool->free.next = page; pool->free.prev = 0; - pool->free.next = (ngx_slab_page_t *) p; - pool->pages->slab = pages; - pool->pages->next = &pool->free; - pool->pages->prev = (uintptr_t) &pool->free; + page->slab = pages; + page->next = &pool->free; + page->prev = (uintptr_t) &pool->free; - pool->start = (u_char *) - ngx_align_ptr((uintptr_t) p + pages * sizeof(ngx_slab_page_t), - ngx_pagesize); + pool->start = ngx_align_ptr(p + pages * sizeof(ngx_slab_page_t), + ngx_pagesize); m = pages - (pool->end - pool->start) / ngx_pagesize; if (m > 0) { pages -= m; - pool->pages->slab = pages; + page->slab = pages; } pool->last = pool->pages + pages; + pool->pfree = pages; pool->log_nomem = 1; pool->log_ctx = &pool->zero; @@ -168,8 +193,7 @@ ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size) page = ngx_slab_alloc_pages(pool, (size >> ngx_pagesize_shift) + ((size % ngx_pagesize) ? 1 : 0)); if (page) { - p = (page - pool->pages) << ngx_pagesize_shift; - p += (uintptr_t) pool->start; + p = ngx_slab_page_addr(pool, page); } else { p = 0; @@ -184,166 +208,140 @@ ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size) slot = shift - pool->min_shift; } else { - size = pool->min_size; shift = pool->min_shift; slot = 0; } + pool->stats[slot].reqs++; + ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, "slab alloc: %uz slot: %ui", size, slot); - slots = (ngx_slab_page_t *) ((u_char *) pool + sizeof(ngx_slab_pool_t)); + slots = ngx_slab_slots(pool); page = slots[slot].next; if (page->next != page) { if (shift < ngx_slab_exact_shift) { - do { - p = (page - pool->pages) << ngx_pagesize_shift; - bitmap = (uintptr_t *) (pool->start + p); + bitmap = (uintptr_t *) ngx_slab_page_addr(pool, page); - map = (1 << (ngx_pagesize_shift - shift)) - / (sizeof(uintptr_t) * 8); + map = (ngx_pagesize >> shift) / (sizeof(uintptr_t) * 8); - for (n = 0; n < map; n++) { + for (n = 0; n < map; n++) { - if (bitmap[n] != NGX_SLAB_BUSY) { + if (bitmap[n] != NGX_SLAB_BUSY) { - for (m = 1, i = 0; m; m <<= 1, i++) { - if ((bitmap[n] & m)) { - continue; - } - - bitmap[n] |= m; - - i = ((n * sizeof(uintptr_t) * 8) << shift) - + (i << shift); - - if (bitmap[n] == NGX_SLAB_BUSY) { - for (n = n + 1; n < map; n++) { - if (bitmap[n] != NGX_SLAB_BUSY) { - p = (uintptr_t) bitmap + i; - - goto done; - } - } - - prev = (ngx_slab_page_t *) - (page->prev & ~NGX_SLAB_PAGE_MASK); - prev->next = page->next; - page->next->prev = page->prev; - - page->next = NULL; - page->prev = NGX_SLAB_SMALL; - } - - p = (uintptr_t) bitmap + i; - - goto done; + for (m = 1, i = 0; m; m <<= 1, i++) { + if (bitmap[n] & m) { + continue; } + + bitmap[n] |= m; + + i = (n * sizeof(uintptr_t) * 8 + i) << shift; + + p = (uintptr_t) bitmap + i; + + pool->stats[slot].used++; + + if (bitmap[n] == NGX_SLAB_BUSY) { + for (n = n + 1; n < map; n++) { + if (bitmap[n] != NGX_SLAB_BUSY) { + goto done; + } + } + + prev = ngx_slab_page_prev(page); + prev->next = page->next; + page->next->prev = page->prev; + + page->next = NULL; + page->prev = NGX_SLAB_SMALL; + } + + goto done; } } - - page = page->next; - - } while (page); + } } else if (shift == ngx_slab_exact_shift) { - do { - if (page->slab != NGX_SLAB_BUSY) { - - for (m = 1, i = 0; m; m <<= 1, i++) { - if ((page->slab & m)) { - continue; - } - - page->slab |= m; - - if (page->slab == NGX_SLAB_BUSY) { - prev = (ngx_slab_page_t *) - (page->prev & ~NGX_SLAB_PAGE_MASK); - prev->next = page->next; - page->next->prev = page->prev; - - page->next = NULL; - page->prev = NGX_SLAB_EXACT; - } - - p = (page - pool->pages) << ngx_pagesize_shift; - p += i << shift; - p += (uintptr_t) pool->start; - - goto done; - } + for (m = 1, i = 0; m; m <<= 1, i++) { + if (page->slab & m) { + continue; } - page = page->next; + page->slab |= m; - } while (page); + if (page->slab == NGX_SLAB_BUSY) { + prev = ngx_slab_page_prev(page); + prev->next = page->next; + page->next->prev = page->prev; + + page->next = NULL; + page->prev = NGX_SLAB_EXACT; + } + + p = ngx_slab_page_addr(pool, page) + (i << shift); + + pool->stats[slot].used++; + + goto done; + } } else { /* shift > ngx_slab_exact_shift */ - n = ngx_pagesize_shift - (page->slab & NGX_SLAB_SHIFT_MASK); - n = 1 << n; - n = ((uintptr_t) 1 << n) - 1; - mask = n << NGX_SLAB_MAP_SHIFT; + mask = ((uintptr_t) 1 << (ngx_pagesize >> shift)) - 1; + mask <<= NGX_SLAB_MAP_SHIFT; - do { - if ((page->slab & NGX_SLAB_MAP_MASK) != mask) { - - for (m = (uintptr_t) 1 << NGX_SLAB_MAP_SHIFT, i = 0; - m & mask; - m <<= 1, i++) - { - if ((page->slab & m)) { - continue; - } - - page->slab |= m; - - if ((page->slab & NGX_SLAB_MAP_MASK) == mask) { - prev = (ngx_slab_page_t *) - (page->prev & ~NGX_SLAB_PAGE_MASK); - prev->next = page->next; - page->next->prev = page->prev; - - page->next = NULL; - page->prev = NGX_SLAB_BIG; - } - - p = (page - pool->pages) << ngx_pagesize_shift; - p += i << shift; - p += (uintptr_t) pool->start; - - goto done; - } + for (m = (uintptr_t) 1 << NGX_SLAB_MAP_SHIFT, i = 0; + m & mask; + m <<= 1, i++) + { + if (page->slab & m) { + continue; } - page = page->next; + page->slab |= m; - } while (page); + if ((page->slab & NGX_SLAB_MAP_MASK) == mask) { + prev = ngx_slab_page_prev(page); + prev->next = page->next; + page->next->prev = page->prev; + + page->next = NULL; + page->prev = NGX_SLAB_BIG; + } + + p = ngx_slab_page_addr(pool, page) + (i << shift); + + pool->stats[slot].used++; + + goto done; + } } + + ngx_slab_error(pool, NGX_LOG_ALERT, "ngx_slab_alloc(): page is busy"); + ngx_debug_point(); } page = ngx_slab_alloc_pages(pool, 1); if (page) { if (shift < ngx_slab_exact_shift) { - p = (page - pool->pages) << ngx_pagesize_shift; - bitmap = (uintptr_t *) (pool->start + p); + bitmap = (uintptr_t *) ngx_slab_page_addr(pool, page); - s = 1 << shift; - n = (1 << (ngx_pagesize_shift - shift)) / 8 / s; + n = (ngx_pagesize >> shift) / ((1 << shift) * 8); if (n == 0) { n = 1; } - bitmap[0] = (2 << n) - 1; + /* "n" elements for bitmap, plus one requested */ + bitmap[0] = ((uintptr_t) 2 << n) - 1; - map = (1 << (ngx_pagesize_shift - shift)) / (sizeof(uintptr_t) * 8); + map = (ngx_pagesize >> shift) / (sizeof(uintptr_t) * 8); for (i = 1; i < map; i++) { bitmap[i] = 0; @@ -355,8 +353,11 @@ ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size) slots[slot].next = page; - p = ((page - pool->pages) << ngx_pagesize_shift) + s * n; - p += (uintptr_t) pool->start; + pool->stats[slot].total += (ngx_pagesize >> shift) - n; + + p = ngx_slab_page_addr(pool, page) + (n << shift); + + pool->stats[slot].used++; goto done; @@ -368,8 +369,11 @@ ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size) slots[slot].next = page; - p = (page - pool->pages) << ngx_pagesize_shift; - p += (uintptr_t) pool->start; + pool->stats[slot].total += sizeof(uintptr_t) * 8; + + p = ngx_slab_page_addr(pool, page); + + pool->stats[slot].used++; goto done; @@ -381,8 +385,11 @@ ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size) slots[slot].next = page; - p = (page - pool->pages) << ngx_pagesize_shift; - p += (uintptr_t) pool->start; + pool->stats[slot].total += ngx_pagesize >> shift; + + p = ngx_slab_page_addr(pool, page); + + pool->stats[slot].used++; goto done; } @@ -390,6 +397,8 @@ ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size) p = 0; + pool->stats[slot].fails++; + done: ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, @@ -444,7 +453,7 @@ ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p) { size_t size; uintptr_t slab, m, *bitmap; - ngx_uint_t n, type, slot, shift, map; + ngx_uint_t i, n, type, slot, shift, map; ngx_slab_page_t *slots, *page; ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, "slab free: %p", p); @@ -457,7 +466,7 @@ ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p) n = ((u_char *) p - pool->start) >> ngx_pagesize_shift; page = &pool->pages[n]; slab = page->slab; - type = page->prev & NGX_SLAB_PAGE_MASK; + type = ngx_slab_page_type(page); switch (type) { @@ -471,17 +480,16 @@ ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p) } n = ((uintptr_t) p & (ngx_pagesize - 1)) >> shift; - m = (uintptr_t) 1 << (n & (sizeof(uintptr_t) * 8 - 1)); - n /= (sizeof(uintptr_t) * 8); + m = (uintptr_t) 1 << (n % (sizeof(uintptr_t) * 8)); + n /= sizeof(uintptr_t) * 8; bitmap = (uintptr_t *) ((uintptr_t) p & ~((uintptr_t) ngx_pagesize - 1)); if (bitmap[n] & m) { + slot = shift - pool->min_shift; if (page->next == NULL) { - slots = (ngx_slab_page_t *) - ((u_char *) pool + sizeof(ngx_slab_pool_t)); - slot = shift - pool->min_shift; + slots = ngx_slab_slots(pool); page->next = slots[slot].next; slots[slot].next = page; @@ -492,7 +500,7 @@ ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p) bitmap[n] &= ~m; - n = (1 << (ngx_pagesize_shift - shift)) / 8 / (1 << shift); + n = (ngx_pagesize >> shift) / ((1 << shift) * 8); if (n == 0) { n = 1; @@ -502,16 +510,18 @@ ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p) goto done; } - map = (1 << (ngx_pagesize_shift - shift)) / (sizeof(uintptr_t) * 8); + map = (ngx_pagesize >> shift) / (sizeof(uintptr_t) * 8); - for (n = 1; n < map; n++) { - if (bitmap[n]) { + for (i = 1; i < map; i++) { + if (bitmap[i]) { goto done; } } ngx_slab_free_pages(pool, page, 1); + pool->stats[slot].total -= (ngx_pagesize >> shift) - n; + goto done; } @@ -528,10 +538,10 @@ ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p) } if (slab & m) { + slot = ngx_slab_exact_shift - pool->min_shift; + if (slab == NGX_SLAB_BUSY) { - slots = (ngx_slab_page_t *) - ((u_char *) pool + sizeof(ngx_slab_pool_t)); - slot = ngx_slab_exact_shift - pool->min_shift; + slots = ngx_slab_slots(pool); page->next = slots[slot].next; slots[slot].next = page; @@ -548,6 +558,8 @@ ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p) ngx_slab_free_pages(pool, page, 1); + pool->stats[slot].total -= sizeof(uintptr_t) * 8; + goto done; } @@ -566,11 +578,10 @@ ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p) + NGX_SLAB_MAP_SHIFT); if (slab & m) { + slot = shift - pool->min_shift; if (page->next == NULL) { - slots = (ngx_slab_page_t *) - ((u_char *) pool + sizeof(ngx_slab_pool_t)); - slot = shift - pool->min_shift; + slots = ngx_slab_slots(pool); page->next = slots[slot].next; slots[slot].next = page; @@ -587,6 +598,8 @@ ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p) ngx_slab_free_pages(pool, page, 1); + pool->stats[slot].total -= ngx_pagesize >> shift; + goto done; } @@ -598,7 +611,7 @@ ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p) goto wrong_chunk; } - if (slab == NGX_SLAB_PAGE_FREE) { + if (!(slab & NGX_SLAB_PAGE_START)) { ngx_slab_error(pool, NGX_LOG_ALERT, "ngx_slab_free(): page is already free"); goto fail; @@ -626,6 +639,8 @@ ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p) done: + pool->stats[slot].used--; + ngx_slab_junk(p, size); return; @@ -678,6 +693,8 @@ ngx_slab_alloc_pages(ngx_slab_pool_t *pool, ngx_uint_t pages) page->next = NULL; page->prev = NGX_SLAB_PAGE; + pool->pfree -= pages; + if (--pages == 0) { return page; } @@ -706,9 +723,10 @@ static void ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page, ngx_uint_t pages) { - ngx_uint_t type; ngx_slab_page_t *prev, *join; + pool->pfree += pages; + page->slab = pages--; if (pages) { @@ -716,7 +734,7 @@ ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page, } if (page->next) { - prev = (ngx_slab_page_t *) (page->prev & ~NGX_SLAB_PAGE_MASK); + prev = ngx_slab_page_prev(page); prev->next = page->next; page->next->prev = page->prev; } @@ -724,15 +742,14 @@ ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page, join = page + page->slab; if (join < pool->last) { - type = join->prev & NGX_SLAB_PAGE_MASK; - if (type == NGX_SLAB_PAGE) { + if (ngx_slab_page_type(join) == NGX_SLAB_PAGE) { if (join->next != NULL) { pages += join->slab; page->slab += join->slab; - prev = (ngx_slab_page_t *) (join->prev & ~NGX_SLAB_PAGE_MASK); + prev = ngx_slab_page_prev(join); prev->next = join->next; join->next->prev = join->prev; @@ -745,19 +762,18 @@ ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page, if (page > pool->pages) { join = page - 1; - type = join->prev & NGX_SLAB_PAGE_MASK; - if (type == NGX_SLAB_PAGE) { + if (ngx_slab_page_type(join) == NGX_SLAB_PAGE) { if (join->slab == NGX_SLAB_PAGE_FREE) { - join = (ngx_slab_page_t *) (join->prev & ~NGX_SLAB_PAGE_MASK); + join = ngx_slab_page_prev(join); } if (join->next != NULL) { pages += join->slab; join->slab += page->slab; - prev = (ngx_slab_page_t *) (join->prev & ~NGX_SLAB_PAGE_MASK); + prev = ngx_slab_page_prev(join); prev->next = join->next; join->next->prev = join->prev; diff --git a/src/core/ngx_slab.h b/src/core/ngx_slab.h index 2922a80..eff893c 100644 --- a/src/core/ngx_slab.h +++ b/src/core/ngx_slab.h @@ -22,6 +22,15 @@ struct ngx_slab_page_s { }; +typedef struct { + ngx_uint_t total; + ngx_uint_t used; + + ngx_uint_t reqs; + ngx_uint_t fails; +} ngx_slab_stat_t; + + typedef struct { ngx_shmtx_sh_t lock; @@ -32,6 +41,9 @@ typedef struct { ngx_slab_page_t *last; ngx_slab_page_t free; + ngx_slab_stat_t *stats; + ngx_uint_t pfree; + u_char *start; u_char *end; diff --git a/src/event/modules/ngx_devpoll_module.c b/src/event/modules/ngx_devpoll_module.c index f985fbd..39e480e 100644 --- a/src/event/modules/ngx_devpoll_module.c +++ b/src/event/modules/ngx_devpoll_module.c @@ -481,13 +481,11 @@ ngx_devpoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, fd, event_list[i].events, revents); } - if ((revents & (POLLERR|POLLHUP|POLLNVAL)) - && (revents & (POLLIN|POLLOUT)) == 0) - { + if (revents & (POLLERR|POLLHUP|POLLNVAL)) { + /* - * if the error events were returned without POLLIN or POLLOUT, - * then add these flags to handle the events at least in one - * active handler + * if the error events were returned, add POLLIN and POLLOUT + * to handle the events at least in one active handler */ revents |= POLLIN|POLLOUT; diff --git a/src/event/modules/ngx_epoll_module.c b/src/event/modules/ngx_epoll_module.c index c267fd6..760c69b 100644 --- a/src/event/modules/ngx_epoll_module.c +++ b/src/event/modules/ngx_epoll_module.c @@ -863,6 +863,13 @@ ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "epoll_wait() error on fd:%d ev:%04XD", c->fd, revents); + + /* + * if the error events were returned, add EPOLLIN and EPOLLOUT + * to handle the events at least in one active handler + */ + + revents |= EPOLLIN|EPOLLOUT; } #if 0 @@ -873,18 +880,6 @@ ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) } #endif - if ((revents & (EPOLLERR|EPOLLHUP)) - && (revents & (EPOLLIN|EPOLLOUT)) == 0) - { - /* - * if the error events were returned without EPOLLIN or EPOLLOUT, - * then add these flags to handle the events at least in one - * active handler - */ - - revents |= EPOLLIN|EPOLLOUT; - } - if ((revents & EPOLLIN) && rev->active) { #if (NGX_HAVE_EPOLLRDHUP) diff --git a/src/event/modules/ngx_eventport_module.c b/src/event/modules/ngx_eventport_module.c index dafa27f..0413599 100644 --- a/src/event/modules/ngx_eventport_module.c +++ b/src/event/modules/ngx_eventport_module.c @@ -540,13 +540,11 @@ ngx_eventport_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, (int) event_list[i].portev_object, revents); } - if ((revents & (POLLERR|POLLHUP|POLLNVAL)) - && (revents & (POLLIN|POLLOUT)) == 0) - { + if (revents & (POLLERR|POLLHUP|POLLNVAL)) { + /* - * if the error events were returned without POLLIN or POLLOUT, - * then add these flags to handle the events at least in one - * active handler + * if the error events were returned, add POLLIN and POLLOUT + * to handle the events at least in one active handler */ revents |= POLLIN|POLLOUT; diff --git a/src/event/modules/ngx_poll_module.c b/src/event/modules/ngx_poll_module.c index 4370950..a2a7079 100644 --- a/src/event/modules/ngx_poll_module.c +++ b/src/event/modules/ngx_poll_module.c @@ -353,13 +353,11 @@ ngx_poll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) continue; } - if ((revents & (POLLERR|POLLHUP|POLLNVAL)) - && (revents & (POLLIN|POLLOUT)) == 0) - { + if (revents & (POLLERR|POLLHUP|POLLNVAL)) { + /* - * if the error events were returned without POLLIN or POLLOUT, - * then add these flags to handle the events at least in one - * active handler + * if the error events were returned, add POLLIN and POLLOUT + * to handle the events at least in one active handler */ revents |= POLLIN|POLLOUT; diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 5c7734d..1b39f33 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -59,6 +59,12 @@ static int ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn, static ngx_int_t ngx_ssl_check_name(ngx_str_t *name, ASN1_STRING *str); #endif +static time_t ngx_ssl_parse_time( +#if OPENSSL_VERSION_NUMBER > 0x10100000L + const +#endif + ASN1_TIME *asn1time); + static void *ngx_openssl_create_conf(ngx_cycle_t *cycle); static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static void ngx_openssl_exit(ngx_cycle_t *cycle); @@ -106,6 +112,7 @@ int ngx_ssl_session_cache_index; int ngx_ssl_session_ticket_keys_index; int ngx_ssl_certificate_index; int ngx_ssl_next_certificate_index; +int ngx_ssl_certificate_name_index; int ngx_ssl_stapling_index; @@ -193,6 +200,14 @@ ngx_ssl_init(ngx_log_t *log) return NGX_ERROR; } + ngx_ssl_certificate_name_index = X509_get_ex_new_index(0, NULL, NULL, NULL, + NULL); + + if (ngx_ssl_certificate_name_index == -1) { + ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed"); + return NGX_ERROR; + } + ngx_ssl_stapling_index = X509_get_ex_new_index(0, NULL, NULL, NULL, NULL); if (ngx_ssl_stapling_index == -1) { @@ -385,6 +400,15 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, return NGX_ERROR; } + if (X509_set_ex_data(x509, ngx_ssl_certificate_name_index, cert->data) + == 0) + { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed"); + X509_free(x509); + BIO_free(bio); + return NGX_ERROR; + } + if (X509_set_ex_data(x509, ngx_ssl_next_certificate_index, SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index)) == 0) @@ -3269,6 +3293,158 @@ ngx_ssl_get_cipher_name(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) } +ngx_int_t +ngx_ssl_get_ciphers(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) +{ +#ifdef SSL_CTRL_GET_RAW_CIPHERLIST + + int n, i, bytes; + size_t len; + u_char *ciphers, *p; + const SSL_CIPHER *cipher; + + bytes = SSL_get0_raw_cipherlist(c->ssl->connection, NULL); + n = SSL_get0_raw_cipherlist(c->ssl->connection, &ciphers); + + if (n <= 0) { + s->len = 0; + return NGX_OK; + } + + len = 0; + n /= bytes; + + for (i = 0; i < n; i++) { + cipher = SSL_CIPHER_find(c->ssl->connection, ciphers + i * bytes); + + if (cipher) { + len += ngx_strlen(SSL_CIPHER_get_name(cipher)); + + } else { + len += sizeof("0x") - 1 + bytes * (sizeof("00") - 1); + } + + len += sizeof(":") - 1; + } + + s->data = ngx_pnalloc(pool, len); + if (s->data == NULL) { + return NGX_ERROR; + } + + p = s->data; + + for (i = 0; i < n; i++) { + cipher = SSL_CIPHER_find(c->ssl->connection, ciphers + i * bytes); + + if (cipher) { + p = ngx_sprintf(p, "%s", SSL_CIPHER_get_name(cipher)); + + } else { + p = ngx_sprintf(p, "0x"); + p = ngx_hex_dump(p, ciphers + i * bytes, bytes); + } + + *p++ = ':'; + } + + p--; + + s->len = p - s->data; + +#else + + u_char buf[4096]; + + if (SSL_get_shared_ciphers(c->ssl->connection, (char *) buf, 4096) + == NULL) + { + s->len = 0; + return NGX_OK; + } + + s->len = ngx_strlen(buf); + s->data = ngx_pnalloc(pool, s->len); + if (s->data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->data, buf, s->len); + +#endif + + return NGX_OK; +} + + +ngx_int_t +ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) +{ +#ifdef SSL_CTRL_GET_CURVES + + int *curves, n, i, nid; + u_char *p; + size_t len; + + n = SSL_get1_curves(c->ssl->connection, NULL); + + if (n <= 0) { + s->len = 0; + return NGX_OK; + } + + curves = ngx_palloc(pool, n * sizeof(int)); + + n = SSL_get1_curves(c->ssl->connection, curves); + len = 0; + + for (i = 0; i < n; i++) { + nid = curves[i]; + + if (nid & TLSEXT_nid_unknown) { + len += sizeof("0x0000") - 1; + + } else { + len += ngx_strlen(OBJ_nid2sn(nid)); + } + + len += sizeof(":") - 1; + } + + s->data = ngx_pnalloc(pool, len); + if (s->data == NULL) { + return NGX_ERROR; + } + + p = s->data; + + for (i = 0; i < n; i++) { + nid = curves[i]; + + if (nid & TLSEXT_nid_unknown) { + p = ngx_sprintf(p, "0x%04xd", nid & 0xffff); + + } else { + p = ngx_sprintf(p, "%s", OBJ_nid2sn(nid)); + } + + *p++ = ':'; + } + + p--; + + s->len = p - s->data; + +#else + + s->len = 0; + +#endif + + return NGX_OK; +} + + ngx_int_t ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) { @@ -3699,28 +3875,210 @@ ngx_ssl_get_fingerprint(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) ngx_int_t ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) { - X509 *cert; - - if (SSL_get_verify_result(c->ssl->connection) != X509_V_OK) { - ngx_str_set(s, "FAILED"); - return NGX_OK; - } + X509 *cert; + long rc; + const char *str; cert = SSL_get_peer_certificate(c->ssl->connection); - - if (cert) { - ngx_str_set(s, "SUCCESS"); - - } else { + if (cert == NULL) { ngx_str_set(s, "NONE"); + return NGX_OK; } X509_free(cert); + rc = SSL_get_verify_result(c->ssl->connection); + + if (rc == X509_V_OK) { + ngx_str_set(s, "SUCCESS"); + return NGX_OK; + } + + str = X509_verify_cert_error_string(rc); + + s->data = ngx_pnalloc(pool, sizeof("FAILED:") - 1 + ngx_strlen(str)); + if (s->data == NULL) { + return NGX_ERROR; + } + + s->len = ngx_sprintf(s->data, "FAILED:%s", str) - s->data; + return NGX_OK; } +ngx_int_t +ngx_ssl_get_client_v_start(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) +{ + BIO *bio; + X509 *cert; + size_t len; + + s->len = 0; + + cert = SSL_get_peer_certificate(c->ssl->connection); + if (cert == NULL) { + return NGX_OK; + } + + bio = BIO_new(BIO_s_mem()); + if (bio == NULL) { + X509_free(cert); + return NGX_ERROR; + } + +#if OPENSSL_VERSION_NUMBER > 0x10100000L + ASN1_TIME_print(bio, X509_get0_notBefore(cert)); +#else + ASN1_TIME_print(bio, X509_get_notBefore(cert)); +#endif + + len = BIO_pending(bio); + + s->len = len; + s->data = ngx_pnalloc(pool, len); + if (s->data == NULL) { + BIO_free(bio); + X509_free(cert); + return NGX_ERROR; + } + + BIO_read(bio, s->data, len); + BIO_free(bio); + X509_free(cert); + + return NGX_OK; +} + + +ngx_int_t +ngx_ssl_get_client_v_end(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) +{ + BIO *bio; + X509 *cert; + size_t len; + + s->len = 0; + + cert = SSL_get_peer_certificate(c->ssl->connection); + if (cert == NULL) { + return NGX_OK; + } + + bio = BIO_new(BIO_s_mem()); + if (bio == NULL) { + X509_free(cert); + return NGX_ERROR; + } + +#if OPENSSL_VERSION_NUMBER > 0x10100000L + ASN1_TIME_print(bio, X509_get0_notAfter(cert)); +#else + ASN1_TIME_print(bio, X509_get_notAfter(cert)); +#endif + + len = BIO_pending(bio); + + s->len = len; + s->data = ngx_pnalloc(pool, len); + if (s->data == NULL) { + BIO_free(bio); + X509_free(cert); + return NGX_ERROR; + } + + BIO_read(bio, s->data, len); + BIO_free(bio); + X509_free(cert); + + return NGX_OK; +} + + +ngx_int_t +ngx_ssl_get_client_v_remain(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) +{ + X509 *cert; + time_t now, end; + + s->len = 0; + + cert = SSL_get_peer_certificate(c->ssl->connection); + if (cert == NULL) { + return NGX_OK; + } + +#if OPENSSL_VERSION_NUMBER > 0x10100000L + end = ngx_ssl_parse_time(X509_get0_notAfter(cert)); +#else + end = ngx_ssl_parse_time(X509_get_notAfter(cert)); +#endif + + if (end == (time_t) NGX_ERROR) { + X509_free(cert); + return NGX_OK; + } + + now = ngx_time(); + + if (end < now + 86400) { + ngx_str_set(s, "0"); + X509_free(cert); + return NGX_OK; + } + + s->data = ngx_pnalloc(pool, NGX_TIME_T_LEN); + if (s->data == NULL) { + X509_free(cert); + return NGX_ERROR; + } + + s->len = ngx_sprintf(s->data, "%T", (end - now) / 86400) - s->data; + + X509_free(cert); + + return NGX_OK; +} + + +static time_t +ngx_ssl_parse_time( +#if OPENSSL_VERSION_NUMBER > 0x10100000L + const +#endif + ASN1_TIME *asn1time) +{ + BIO *bio; + u_char *value; + size_t len; + time_t time; + + /* + * OpenSSL doesn't provide a way to convert ASN1_TIME + * into time_t. To do this, we use ASN1_TIME_print(), + * which uses the "MMM DD HH:MM:SS YYYY [GMT]" format (e.g., + * "Feb 3 00:55:52 2015 GMT"), and parse the result. + */ + + bio = BIO_new(BIO_s_mem()); + if (bio == NULL) { + return NGX_ERROR; + } + + /* fake weekday prepended to match C asctime() format */ + + BIO_write(bio, "Tue ", sizeof("Tue ") - 1); + ASN1_TIME_print(bio, asn1time); + len = BIO_get_mem_data(bio, &value); + + time = ngx_parse_http_time(value, len); + + BIO_free(bio); + + return time; +} + + static void * ngx_openssl_create_conf(ngx_cycle_t *cycle) { diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index d233c02..c022307 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -191,6 +191,10 @@ ngx_int_t ngx_ssl_get_protocol(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); ngx_int_t ngx_ssl_get_cipher_name(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); +ngx_int_t ngx_ssl_get_ciphers(ngx_connection_t *c, ngx_pool_t *pool, + ngx_str_t *s); +ngx_int_t ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, + ngx_str_t *s); ngx_int_t ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); ngx_int_t ngx_ssl_get_session_reused(ngx_connection_t *c, ngx_pool_t *pool, @@ -215,6 +219,12 @@ ngx_int_t ngx_ssl_get_fingerprint(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); ngx_int_t ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); +ngx_int_t ngx_ssl_get_client_v_start(ngx_connection_t *c, ngx_pool_t *pool, + ngx_str_t *s); +ngx_int_t ngx_ssl_get_client_v_end(ngx_connection_t *c, ngx_pool_t *pool, + ngx_str_t *s); +ngx_int_t ngx_ssl_get_client_v_remain(ngx_connection_t *c, ngx_pool_t *pool, + ngx_str_t *s); ngx_int_t ngx_ssl_handshake(ngx_connection_t *c); @@ -236,6 +246,7 @@ extern int ngx_ssl_session_cache_index; extern int ngx_ssl_session_ticket_keys_index; extern int ngx_ssl_certificate_index; extern int ngx_ssl_next_certificate_index; +extern int ngx_ssl_certificate_name_index; extern int ngx_ssl_stapling_index; diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c index 09fab76..2100516 100644 --- a/src/event/ngx_event_openssl_stapling.c +++ b/src/event/ngx_event_openssl_stapling.c @@ -31,6 +31,8 @@ typedef struct { X509 *cert; X509 *issuer; + u_char *name; + time_t valid; time_t refresh; @@ -45,6 +47,8 @@ struct ngx_ssl_ocsp_ctx_s { X509 *cert; X509 *issuer; + u_char *name; + ngx_uint_t naddrs; ngx_addr_t *addrs; @@ -57,14 +61,14 @@ struct ngx_ssl_ocsp_ctx_s { ngx_msec_t timeout; - void (*handler)(ngx_ssl_ocsp_ctx_t *r); + void (*handler)(ngx_ssl_ocsp_ctx_t *ctx); void *data; ngx_buf_t *request; ngx_buf_t *response; ngx_peer_connection_t peer; - ngx_int_t (*process)(ngx_ssl_ocsp_ctx_t *r); + ngx_int_t (*process)(ngx_ssl_ocsp_ctx_t *ctx); ngx_uint_t state; @@ -173,6 +177,8 @@ ngx_ssl_stapling_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, X509 *cert, staple->timeout = 60000; staple->verify = verify; staple->cert = cert; + staple->name = X509_get_ex_data(staple->cert, + ngx_ssl_certificate_name_index); if (file->len) { /* use OCSP response from the file */ @@ -354,7 +360,9 @@ ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl, if (rc == 0) { ngx_log_error(NGX_LOG_WARN, ssl->log, 0, - "\"ssl_stapling\" ignored, issuer certificate not found"); + "\"ssl_stapling\" ignored, " + "issuer certificate not found for certificate \"%s\"", + staple->name); X509_STORE_CTX_free(store_ctx); return NGX_DECLINED; } @@ -374,9 +382,9 @@ static ngx_int_t ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_ssl_stapling_t *staple, ngx_str_t *responder) { - ngx_url_t u; char *s; ngx_str_t rsp; + ngx_url_t u; STACK_OF(OPENSSL_STRING) *aia; if (responder->len == 0) { @@ -387,7 +395,8 @@ ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl, if (aia == NULL) { ngx_log_error(NGX_LOG_WARN, ssl->log, 0, "\"ssl_stapling\" ignored, " - "no OCSP responder URL in the certificate"); + "no OCSP responder URL in the certificate \"%s\"", + staple->name); return NGX_DECLINED; } @@ -399,7 +408,8 @@ ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl, if (s == NULL) { ngx_log_error(NGX_LOG_WARN, ssl->log, 0, "\"ssl_stapling\" ignored, " - "no OCSP responder URL in the certificate"); + "no OCSP responder URL in the certificate \"%s\"", + staple->name); X509_email_free(aia); return NGX_DECLINED; } @@ -432,7 +442,9 @@ ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl, } else { ngx_log_error(NGX_LOG_WARN, ssl->log, 0, "\"ssl_stapling\" ignored, " - "invalid URL prefix in OCSP responder \"%V\"", &u.url); + "invalid URL prefix in OCSP responder \"%V\" " + "in the certificate \"%s\"", + &u.url, staple->name); return NGX_DECLINED; } @@ -440,7 +452,9 @@ ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl, if (u.err) { ngx_log_error(NGX_LOG_WARN, ssl->log, 0, "\"ssl_stapling\" ignored, " - "%s in OCSP responder \"%V\"", u.err, &u.url); + "%s in OCSP responder \"%V\" " + "in the certificate \"%s\"", + u.err, &u.url, staple->name); return NGX_DECLINED; } @@ -547,6 +561,7 @@ ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple) ctx->cert = staple->cert; ctx->issuer = staple->issuer; + ctx->name = staple->name; ctx->addrs = staple->addrs; ctx->host = staple->host; @@ -757,10 +772,10 @@ error: static time_t ngx_ssl_stapling_time(ASN1_GENERALIZEDTIME *asn1time) { + BIO *bio; u_char *value; size_t len; time_t time; - BIO *bio; /* * OpenSSL doesn't provide a way to convert ASN1_GENERALIZEDTIME @@ -1005,7 +1020,7 @@ failed: static void ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx) { - ngx_int_t rc; + ngx_int_t rc; ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, "ssl ocsp connect"); @@ -1103,10 +1118,10 @@ ngx_ssl_ocsp_write_handler(ngx_event_t *wev) static void ngx_ssl_ocsp_read_handler(ngx_event_t *rev) { - ssize_t n, size; - ngx_int_t rc; - ngx_ssl_ocsp_ctx_t *ctx; - ngx_connection_t *c; + ssize_t n, size; + ngx_int_t rc; + ngx_connection_t *c; + ngx_ssl_ocsp_ctx_t *ctx; c = rev->data; ctx = c->data; @@ -1310,12 +1325,11 @@ ngx_ssl_ocsp_process_status_line(ngx_ssl_ocsp_ctx_t *ctx) rc = ngx_ssl_ocsp_parse_status_line(ctx); if (rc == NGX_OK) { -#if 0 - ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0, - "ssl ocsp status line \"%*s\"", - ctx->response->pos - ctx->response->start, - ctx->response->start); -#endif + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ctx->log, 0, + "ssl ocsp status %ui \"%*s\"", + ctx->code, + ctx->header_end - ctx->header_start, + ctx->header_start); ctx->process = ngx_ssl_ocsp_process_headers; return ctx->process(ctx); @@ -1476,6 +1490,7 @@ ngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx) if (++ctx->count == 3) { state = sw_space_after_status; + ctx->header_start = p - 2; } break; @@ -1493,6 +1508,7 @@ ngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx) state = sw_almost_done; break; case LF: + ctx->header_end = p; goto done; default: return NGX_ERROR; @@ -1506,6 +1522,7 @@ ngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx) state = sw_almost_done; break; case LF: + ctx->header_end = p; goto done; } break; @@ -1514,6 +1531,7 @@ ngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx) case sw_almost_done: switch (ch) { case LF: + ctx->header_end = p - 1; goto done; default: return NGX_ERROR; @@ -1608,10 +1626,11 @@ ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx) 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; + u_char c, ch, *p; enum { sw_start = 0, sw_name, @@ -1821,12 +1840,27 @@ ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len) if (log->action) { p = ngx_snprintf(buf, len, " while %s", log->action); len -= p - buf; + buf = p; } ctx = log->data; if (ctx) { - p = ngx_snprintf(p, len, ", responder: %V", &ctx->host); + p = ngx_snprintf(buf, len, ", responder: %V", &ctx->host); + len -= p - buf; + buf = p; + } + + if (ctx && ctx->peer.name) { + p = ngx_snprintf(buf, len, ", peer: %V", ctx->peer.name); + len -= p - buf; + buf = p; + } + + if (ctx && ctx->name) { + p = ngx_snprintf(buf, len, ", certificate: \"%s\"", ctx->name); + len -= p - buf; + buf = p; } return p; @@ -1846,6 +1880,7 @@ ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file, return NGX_OK; } + ngx_int_t ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_resolver_t *resolver, ngx_msec_t resolver_timeout) diff --git a/src/http/modules/ngx_http_map_module.c b/src/http/modules/ngx_http_map_module.c index 732aa5d..2fc9be9 100644 --- a/src/http/modules/ngx_http_map_module.c +++ b/src/http/modules/ngx_http_map_module.c @@ -26,7 +26,8 @@ typedef struct { ngx_http_variable_value_t *default_value; ngx_conf_t *cf; - ngx_uint_t hostnames; /* unsigned hostnames:1 */ + unsigned hostnames:1; + unsigned no_cacheable:1; } ngx_http_map_conf_ctx_t; @@ -265,6 +266,7 @@ ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ctx.default_value = NULL; ctx.cf = &save; ctx.hostnames = 0; + ctx.no_cacheable = 0; save = *cf; cf->pool = pool; @@ -281,6 +283,10 @@ ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return rv; } + if (ctx.no_cacheable) { + var->flags |= NGX_HTTP_VAR_NOCACHEABLE; + } + map->default_value = ctx.default_value ? ctx.default_value: &ngx_http_variable_null_value; @@ -393,8 +399,16 @@ ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) { ctx->hostnames = 1; return NGX_CONF_OK; + } - } else if (cf->args->nelts != 2) { + if (cf->args->nelts == 1 + && ngx_strcmp(value[0].data, "volatile") == 0) + { + ctx->no_cacheable = 1; + return NGX_CONF_OK; + } + + if (cf->args->nelts != 2) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid number of the map parameters"); return NGX_CONF_ERROR; diff --git a/src/http/modules/ngx_http_mp4_module.c b/src/http/modules/ngx_http_mp4_module.c index 8f2920f..f3c0fdd 100644 --- a/src/http/modules/ngx_http_mp4_module.c +++ b/src/http/modules/ngx_http_mp4_module.c @@ -1229,7 +1229,9 @@ ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4, off_t start_offset, atom_header = mp4->mdat_atom_header; - if ((uint64_t) atom_data_size > (uint64_t) 0xffffffff) { + if ((uint64_t) atom_data_size + > (uint64_t) 0xffffffff - sizeof(ngx_mp4_atom_header_t)) + { atom_size = 1; atom_header_size = sizeof(ngx_mp4_atom_header64_t); ngx_mp4_set_64value(atom_header + sizeof(ngx_mp4_atom_header_t), diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index e75f5d8..2771ac1 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -276,6 +276,12 @@ static ngx_http_variable_t ngx_http_ssl_vars[] = { { ngx_string("ssl_cipher"), NULL, ngx_http_ssl_static_variable, (uintptr_t) ngx_ssl_get_cipher_name, NGX_HTTP_VAR_CHANGEABLE, 0 }, + { ngx_string("ssl_ciphers"), NULL, ngx_http_ssl_variable, + (uintptr_t) ngx_ssl_get_ciphers, NGX_HTTP_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_curves"), NULL, ngx_http_ssl_variable, + (uintptr_t) ngx_ssl_get_curves, NGX_HTTP_VAR_CHANGEABLE, 0 }, + { ngx_string("ssl_session_id"), NULL, ngx_http_ssl_variable, (uintptr_t) ngx_ssl_get_session_id, NGX_HTTP_VAR_CHANGEABLE, 0 }, @@ -313,6 +319,15 @@ static ngx_http_variable_t ngx_http_ssl_vars[] = { { ngx_string("ssl_client_verify"), NULL, ngx_http_ssl_variable, (uintptr_t) ngx_ssl_get_client_verify, NGX_HTTP_VAR_CHANGEABLE, 0 }, + { ngx_string("ssl_client_v_start"), NULL, ngx_http_ssl_variable, + (uintptr_t) ngx_ssl_get_client_v_start, NGX_HTTP_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_client_v_end"), NULL, ngx_http_ssl_variable, + (uintptr_t) ngx_ssl_get_client_v_end, NGX_HTTP_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_client_v_remain"), NULL, ngx_http_ssl_variable, + (uintptr_t) ngx_ssl_get_client_v_remain, NGX_HTTP_VAR_CHANGEABLE, 0 }, + { ngx_null_string, NULL, NULL, 0, 0, 0 } }; diff --git a/src/http/modules/perl/ngx_http_perl_module.c b/src/http/modules/perl/ngx_http_perl_module.c index f9a9a84..2796319 100644 --- a/src/http/modules/perl/ngx_http_perl_module.c +++ b/src/http/modules/perl/ngx_http_perl_module.c @@ -207,6 +207,7 @@ ngx_http_perl_handle_request(ngx_http_request_t *r) dTHXa(pmcf->perl); PERL_SET_CONTEXT(pmcf->perl); + PERL_SET_INTERP(pmcf->perl); if (ctx->next == NULL) { plcf = ngx_http_get_module_loc_conf(r, ngx_http_perl_module); @@ -322,6 +323,7 @@ ngx_http_perl_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, dTHXa(pmcf->perl); PERL_SET_CONTEXT(pmcf->perl); + PERL_SET_INTERP(pmcf->perl); rc = ngx_http_perl_call_handler(aTHX_ r, pmcf->nginx, pv->sub, NULL, &pv->handler, &value); @@ -387,6 +389,7 @@ ngx_http_perl_ssi(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ssi_ctx, dTHXa(pmcf->perl); PERL_SET_CONTEXT(pmcf->perl); + PERL_SET_INTERP(pmcf->perl); #if 0 @@ -568,6 +571,7 @@ ngx_http_perl_create_interpreter(ngx_conf_t *cf, dTHXa(perl); PERL_SET_CONTEXT(perl); + PERL_SET_INTERP(perl); perl_construct(perl); @@ -828,6 +832,7 @@ ngx_http_perl_cleanup_perl(void *data) PerlInterpreter *perl = data; PERL_SET_CONTEXT(perl); + PERL_SET_INTERP(perl); (void) perl_destruct(perl); @@ -936,6 +941,7 @@ ngx_http_perl(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) dTHXa(pmcf->perl); PERL_SET_CONTEXT(pmcf->perl); + PERL_SET_INTERP(pmcf->perl); ngx_http_perl_eval_anon_sub(aTHX_ &value[1], &plcf->sub); @@ -1007,6 +1013,7 @@ ngx_http_perl_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) dTHXa(pmcf->perl); PERL_SET_CONTEXT(pmcf->perl); + PERL_SET_INTERP(pmcf->perl); ngx_http_perl_eval_anon_sub(aTHX_ &value[2], &pv->sub); @@ -1039,6 +1046,7 @@ ngx_http_perl_init_worker(ngx_cycle_t *cycle) if (pmcf) { dTHXa(pmcf->perl); PERL_SET_CONTEXT(pmcf->perl); + PERL_SET_INTERP(pmcf->perl); /* set worker's $$ */ diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index 8301630..f3050f1 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -286,7 +286,6 @@ ngx_http_v2_init(ngx_event_t *rev) : ngx_http_v2_state_preface; ngx_queue_init(&h2c->waiting); - ngx_queue_init(&h2c->posted); ngx_queue_init(&h2c->dependencies); ngx_queue_init(&h2c->closed); @@ -420,9 +419,7 @@ static void ngx_http_v2_write_handler(ngx_event_t *wev) { ngx_int_t rc; - ngx_queue_t *q; ngx_connection_t *c; - ngx_http_v2_stream_t *stream; ngx_http_v2_connection_t *h2c; c = wev->data; @@ -457,26 +454,6 @@ ngx_http_v2_write_handler(ngx_event_t *wev) return; } - while (!ngx_queue_empty(&h2c->posted)) { - q = ngx_queue_head(&h2c->posted); - - ngx_queue_remove(q); - - stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue); - - stream->handled = 0; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "run http2 stream %ui", stream->node->id); - - wev = stream->request->connection->write; - - wev->active = 0; - wev->ready = 1; - - wev->handler(wev); - } - h2c->blocked = 0; if (rc == NGX_AGAIN) { @@ -2254,7 +2231,7 @@ ngx_http_v2_state_window_update(ngx_http_v2_connection_t *h2c, u_char *pos, stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue); - stream->handled = 0; + stream->waiting = 0; wev = stream->request->connection->write; @@ -3575,6 +3552,11 @@ ngx_http_v2_read_request_body(ngx_http_request_t *r, rb->buf = ngx_create_temp_buf(r->pool, (size_t) len); } else { + if (stream->preread) { + /* enforce writing preread buffer to file */ + r->request_body_in_file_only = 1; + } + rb->buf = ngx_calloc_buf(r->pool); if (rb->buf != NULL) { @@ -4271,7 +4253,7 @@ ngx_http_v2_finalize_connection(ngx_http_v2_connection_t *h2c, continue; } - stream->handled = 0; + stream->waiting = 0; r = stream->request; fc = r->connection; diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h index 63bbdad..cddfccd 100644 --- a/src/http/v2/ngx_http_v2.h +++ b/src/http/v2/ngx_http_v2.h @@ -137,7 +137,6 @@ struct ngx_http_v2_connection_s { ngx_http_v2_out_frame_t *last_out; - ngx_queue_t posted; ngx_queue_t dependencies; ngx_queue_t closed; @@ -192,7 +191,7 @@ struct ngx_http_v2_stream_s { ngx_pool_t *pool; - unsigned handled:1; + unsigned waiting:1; unsigned blocked:1; unsigned exhausted:1; unsigned in_closed:1; diff --git a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c index 878020e..8abca4d 100644 --- a/src/http/v2/ngx_http_v2_filter_module.c +++ b/src/http/v2/ngx_http_v2_filter_module.c @@ -1104,11 +1104,11 @@ ngx_http_v2_waiting_queue(ngx_http_v2_connection_t *h2c, ngx_queue_t *q; ngx_http_v2_stream_t *s; - if (stream->handled) { + if (stream->waiting) { return; } - stream->handled = 1; + stream->waiting = 1; for (q = ngx_queue_last(&h2c->waiting); q != ngx_queue_sentinel(&h2c->waiting); @@ -1298,20 +1298,29 @@ static ngx_inline void ngx_http_v2_handle_stream(ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream) { + ngx_event_t *wev; ngx_connection_t *fc; - if (stream->handled || stream->blocked) { + if (stream->waiting || stream->blocked) { return; } fc = stream->request->connection; - if (!fc->error && (stream->exhausted || fc->write->delayed)) { + if (!fc->error && stream->exhausted) { return; } - stream->handled = 1; - ngx_queue_insert_tail(&h2c->posted, &stream->queue); + wev = fc->write; + + wev->active = 0; + wev->ready = 1; + + if (!fc->error && wev->delayed) { + return; + } + + ngx_post_event(wev, &ngx_posted_events); } @@ -1321,11 +1330,13 @@ ngx_http_v2_filter_cleanup(void *data) ngx_http_v2_stream_t *stream = data; size_t window; + ngx_event_t *wev; + ngx_queue_t *q; ngx_http_v2_out_frame_t *frame, **fn; ngx_http_v2_connection_t *h2c; - if (stream->handled) { - stream->handled = 0; + if (stream->waiting) { + stream->waiting = 0; ngx_queue_remove(&stream->queue); } @@ -1359,9 +1370,26 @@ ngx_http_v2_filter_cleanup(void *data) fn = &frame->next; } - if (h2c->send_window == 0 && window && !ngx_queue_empty(&h2c->waiting)) { - ngx_queue_add(&h2c->posted, &h2c->waiting); - ngx_queue_init(&h2c->waiting); + if (h2c->send_window == 0 && window) { + + while (!ngx_queue_empty(&h2c->waiting)) { + q = ngx_queue_head(&h2c->waiting); + + ngx_queue_remove(q); + + stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue); + + stream->waiting = 0; + + wev = stream->request->connection->write; + + wev->active = 0; + wev->ready = 1; + + if (!wev->delayed) { + ngx_post_event(wev, &ngx_posted_events); + } + } } h2c->send_window += window; diff --git a/src/stream/ngx_stream_map_module.c b/src/stream/ngx_stream_map_module.c index 47a15be..ef06b2d 100644 --- a/src/stream/ngx_stream_map_module.c +++ b/src/stream/ngx_stream_map_module.c @@ -26,7 +26,8 @@ typedef struct { ngx_stream_variable_value_t *default_value; ngx_conf_t *cf; - ngx_uint_t hostnames; /* unsigned hostnames:1 */ + unsigned hostnames:1; + unsigned no_cacheable:1; } ngx_stream_map_conf_ctx_t; @@ -264,6 +265,7 @@ ngx_stream_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ctx.default_value = NULL; ctx.cf = &save; ctx.hostnames = 0; + ctx.no_cacheable = 0; save = *cf; cf->pool = pool; @@ -280,6 +282,10 @@ ngx_stream_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return rv; } + if (ctx.no_cacheable) { + var->flags |= NGX_STREAM_VAR_NOCACHEABLE; + } + map->default_value = ctx.default_value ? ctx.default_value: &ngx_stream_variable_null_value; @@ -392,8 +398,16 @@ ngx_stream_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) { ctx->hostnames = 1; return NGX_CONF_OK; + } - } else if (cf->args->nelts != 2) { + if (cf->args->nelts == 1 + && ngx_strcmp(value[0].data, "volatile") == 0) + { + ctx->no_cacheable = 1; + return NGX_CONF_OK; + } + + if (cf->args->nelts != 2) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid number of the map parameters"); return NGX_CONF_ERROR; diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c index d00718b..9191641 100644 --- a/src/stream/ngx_stream_ssl_module.c +++ b/src/stream/ngx_stream_ssl_module.c @@ -182,6 +182,12 @@ static ngx_stream_variable_t ngx_stream_ssl_vars[] = { { ngx_string("ssl_cipher"), NULL, ngx_stream_ssl_static_variable, (uintptr_t) ngx_ssl_get_cipher_name, NGX_STREAM_VAR_CHANGEABLE, 0 }, + { ngx_string("ssl_ciphers"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_ciphers, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_curves"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_curves, NGX_STREAM_VAR_CHANGEABLE, 0 }, + { ngx_string("ssl_session_id"), NULL, ngx_stream_ssl_variable, (uintptr_t) ngx_ssl_get_session_id, NGX_STREAM_VAR_CHANGEABLE, 0 }, From a27ccda8362f40679d80e10bd1d8f0ab138bf677 Mon Sep 17 00:00:00 2001 From: Christos Trochalakis Date: Mon, 19 Dec 2016 17:02:11 +0200 Subject: [PATCH 011/475] Fix lintian error 'possible-missing-colon-in-closes' Gbp-Dch: Ignore --- debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index af0ea9f..8e4f587 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,7 +1,7 @@ nginx (1.10.2-3) UNRELEASED; urgency=medium * debian/conf/sites-enabled/default: - + Correcting location of default php-fpm socket. (Closes #846145) + + Correcting location of default php-fpm socket. (Closes: #846145) -- Michael Lustfield Sat, 03 Dec 2016 07:57:39 +0000 From 5140bc7eae4c595e8f2eb20ac3f1f8ec5d986df1 Mon Sep 17 00:00:00 2001 From: Christos Trochalakis Date: Tue, 20 Dec 2016 16:29:48 +0200 Subject: [PATCH 012/475] mod: Upgrade nchan to 1.0.8 Closes: #844473 --- debian/modules/README.Modules-versions | 2 +- debian/modules/nchan/README.md | 147 +- debian/modules/nchan/changelog.txt | 35 + debian/modules/nchan/config | 1 + debian/modules/nchan/dev/.gitignore | 6 - debian/modules/nchan/dev/Gemfile | 28 - debian/modules/nchan/dev/authserver.rb | 62 - debian/modules/nchan/dev/bench-parallel.sh | 27 - debian/modules/nchan/dev/bench.rb | 178 - debian/modules/nchan/dev/cert-key.pem | 28 - debian/modules/nchan/dev/cert.pem | 22 - debian/modules/nchan/dev/chattertest.rb | 43 - debian/modules/nchan/dev/debug.sh | 11 - debian/modules/nchan/dev/dev.conf | 63 - debian/modules/nchan/dev/examine_coredump.sh | 17 - .../modules/nchan/dev/gen_config_commands.rb | 129 - debian/modules/nchan/dev/longtest.rb | 184 - debian/modules/nchan/dev/memparse.lua | 269 - debian/modules/nchan/dev/nginx | 1 - .../modules/nchan/dev/nginx-cachemanager.conf | 67 - .../modules/nchan/dev/nginx-nchan/.gitignore | 6 - debian/modules/nchan/dev/nginx-nchan/PKGBUILD | 225 - debian/modules/nchan/dev/nginx-nchan/bl.txt | 6 - debian/modules/nchan/dev/nginx-nchan/install | 11 - debian/modules/nchan/dev/nginx-nchan/nchan | 1 - .../modules/nchan/dev/nginx-nchan/nginx.conf | 1 - .../nchan/dev/nginx-nchan/nginx.logrotate | 8 - .../nchan/dev/nginx-nchan/nginx.service | 14 - .../nchan/dev/nginx-nchan/ngx_slab.patch | 98 - debian/modules/nchan/dev/nginx-proxy.conf | 26 - debian/modules/nchan/dev/nginx.conf | 444 - debian/modules/nchan/dev/nginx.sh | 277 - debian/modules/nchan/dev/package/.gitignore | 1 - .../dev/package/nginx-nchan-static/.gitignore | 5 - .../dev/package/nginx-nchan-static/PKGBUILD | 165 - .../dev/package/nginx-nchan-static/nginx.conf | 1 - .../nginx-nchan-static/nginx.logrotate | 11 - .../package/nginx-nchan-static/nginx.service | 18 - debian/modules/nchan/dev/package/repackage.sh | 28 - debian/modules/nchan/dev/pub-multi.rb | 39 - debian/modules/nchan/dev/pub.rb | 90 - debian/modules/nchan/dev/pubsub.rb | 1483 - debian/modules/nchan/dev/rebuild.sh | 170 - debian/modules/nchan/dev/redis-trib.rb | 1696 - debian/modules/nchan/dev/redis.conf | 1018 - .../nchan/dev/redis_clusterconf/.gitignore | 1 - .../nchan/dev/redis_clusterconf/cluster.sh | 10 - .../nchan/dev/redis_clusterconf/failover.sh | 62 - .../dev/redis_clusterconf/redis-7000.conf | 1018 - .../dev/redis_clusterconf/redis-7001.conf | 1018 - .../dev/redis_clusterconf/redis-7002.conf | 1018 - .../dev/redis_clusterconf/redis-7003.conf | 1018 - .../dev/redis_clusterconf/redis-7004.conf | 1018 - .../dev/redis_clusterconf/redis-7005.conf | 1018 - .../dev/redis_clusterconf/redis-7006.conf | 1018 - .../redis-cluster-node-7000.conf | 7 - .../redis-cluster-node-7001.conf | 7 - .../redis-cluster-node-7002.conf | 7 - .../redis-cluster-node-7003.conf | 7 - .../redis-cluster-node-7004.conf | 7 - .../redis-cluster-node-7005.conf | 7 - .../redis-cluster-node-7006.conf | 2 - debian/modules/nchan/dev/redocument.rb | 295 - debian/modules/nchan/dev/rsck.sh | 67 - debian/modules/nchan/dev/sub.rb | 125 - debian/modules/nchan/dev/test-parallel.sh | 27 - debian/modules/nchan/dev/test.rb | 875 - debian/modules/nchan/dev/vg.supp | 26977 ---------------- debian/modules/nchan/src/nchan_commands.rb | 119 +- .../modules/nchan/src/nchan_config_commands.c | 30 +- debian/modules/nchan/src/nchan_defs.h | 2 +- debian/modules/nchan/src/nchan_module.c | 256 +- debian/modules/nchan/src/nchan_module.h | 5 +- debian/modules/nchan/src/nchan_setup.c | 74 +- debian/modules/nchan/src/nchan_types.h | 19 +- .../modules/nchan/src/store/memory/memstore.c | 239 +- .../nchan/src/store/memory/store-private.h | 4 +- debian/modules/nchan/src/store/memory/store.h | 2 + .../modules/nchan/src/store/redis/rdsstore.c | 21 +- .../src/store/redis/redis_lua_commands.h | 49 +- .../src/store/redis/scripts/find_channel.lua | 47 +- debian/modules/nchan/src/store/spool.c | 41 +- debian/modules/nchan/src/store/spool.h | 6 + debian/modules/nchan/src/subscribers/common.c | 418 +- debian/modules/nchan/src/subscribers/common.h | 9 +- .../nchan/src/subscribers/eventsource.c | 8 +- .../src/subscribers/http-multipart-mixed.c | 8 +- .../modules/nchan/src/subscribers/internal.c | 10 +- .../modules/nchan/src/subscribers/longpoll.c | 38 +- .../nchan/src/subscribers/memstore_ipc.c | 8 +- .../nchan/src/subscribers/memstore_multi.c | 2 +- .../nchan/src/subscribers/memstore_redis.c | 6 +- .../modules/nchan/src/subscribers/websocket.c | 615 +- .../nchan/src/util/nchan_channel_info.c | 13 +- .../nchan/src/util/nchan_fake_request.c | 277 + .../nchan/src/util/nchan_fake_request.h | 6 + debian/modules/nchan/src/util/nchan_output.c | 120 +- debian/modules/nchan/src/util/nchan_output.h | 2 + debian/modules/nchan/src/util/nchan_reaper.c | 2 +- debian/modules/nchan/src/util/nchan_reaper.h | 2 +- .../modules/nchan/src/util/nchan_subrequest.c | 17 + .../modules/nchan/src/util/nchan_subrequest.h | 1 + debian/modules/nchan/src/util/nchan_util.c | 14 + debian/modules/nchan/src/util/nchan_util.h | 1 + .../nchan/src/util/ngx_nchan_hacked_slab.c | 11 +- 105 files changed, 1990 insertions(+), 43283 deletions(-) delete mode 100644 debian/modules/nchan/dev/.gitignore delete mode 100644 debian/modules/nchan/dev/Gemfile delete mode 100755 debian/modules/nchan/dev/authserver.rb delete mode 100755 debian/modules/nchan/dev/bench-parallel.sh delete mode 100755 debian/modules/nchan/dev/bench.rb delete mode 100644 debian/modules/nchan/dev/cert-key.pem delete mode 100644 debian/modules/nchan/dev/cert.pem delete mode 100755 debian/modules/nchan/dev/chattertest.rb delete mode 100755 debian/modules/nchan/dev/debug.sh delete mode 100644 debian/modules/nchan/dev/dev.conf delete mode 100755 debian/modules/nchan/dev/examine_coredump.sh delete mode 100755 debian/modules/nchan/dev/gen_config_commands.rb delete mode 100755 debian/modules/nchan/dev/longtest.rb delete mode 100755 debian/modules/nchan/dev/memparse.lua delete mode 120000 debian/modules/nchan/dev/nginx delete mode 100644 debian/modules/nchan/dev/nginx-cachemanager.conf delete mode 100644 debian/modules/nchan/dev/nginx-nchan/.gitignore delete mode 100644 debian/modules/nchan/dev/nginx-nchan/PKGBUILD delete mode 100644 debian/modules/nchan/dev/nginx-nchan/bl.txt delete mode 100644 debian/modules/nchan/dev/nginx-nchan/install delete mode 120000 debian/modules/nchan/dev/nginx-nchan/nchan delete mode 100644 debian/modules/nchan/dev/nginx-nchan/nginx.conf delete mode 100644 debian/modules/nchan/dev/nginx-nchan/nginx.logrotate delete mode 100644 debian/modules/nchan/dev/nginx-nchan/nginx.service delete mode 100644 debian/modules/nchan/dev/nginx-nchan/ngx_slab.patch delete mode 100644 debian/modules/nchan/dev/nginx-proxy.conf delete mode 100644 debian/modules/nchan/dev/nginx.conf delete mode 100755 debian/modules/nchan/dev/nginx.sh delete mode 100644 debian/modules/nchan/dev/package/.gitignore delete mode 100644 debian/modules/nchan/dev/package/nginx-nchan-static/.gitignore delete mode 100644 debian/modules/nchan/dev/package/nginx-nchan-static/PKGBUILD delete mode 100644 debian/modules/nchan/dev/package/nginx-nchan-static/nginx.conf delete mode 100644 debian/modules/nchan/dev/package/nginx-nchan-static/nginx.logrotate delete mode 100644 debian/modules/nchan/dev/package/nginx-nchan-static/nginx.service delete mode 100755 debian/modules/nchan/dev/package/repackage.sh delete mode 100755 debian/modules/nchan/dev/pub-multi.rb delete mode 100755 debian/modules/nchan/dev/pub.rb delete mode 100644 debian/modules/nchan/dev/pubsub.rb delete mode 100755 debian/modules/nchan/dev/rebuild.sh delete mode 100755 debian/modules/nchan/dev/redis-trib.rb delete mode 100644 debian/modules/nchan/dev/redis.conf delete mode 100644 debian/modules/nchan/dev/redis_clusterconf/.gitignore delete mode 100755 debian/modules/nchan/dev/redis_clusterconf/cluster.sh delete mode 100755 debian/modules/nchan/dev/redis_clusterconf/failover.sh delete mode 100644 debian/modules/nchan/dev/redis_clusterconf/redis-7000.conf delete mode 100644 debian/modules/nchan/dev/redis_clusterconf/redis-7001.conf delete mode 100644 debian/modules/nchan/dev/redis_clusterconf/redis-7002.conf delete mode 100644 debian/modules/nchan/dev/redis_clusterconf/redis-7003.conf delete mode 100644 debian/modules/nchan/dev/redis_clusterconf/redis-7004.conf delete mode 100644 debian/modules/nchan/dev/redis_clusterconf/redis-7005.conf delete mode 100644 debian/modules/nchan/dev/redis_clusterconf/redis-7006.conf delete mode 100644 debian/modules/nchan/dev/redis_clusterconf/redis-cluster-node-7000.conf delete mode 100644 debian/modules/nchan/dev/redis_clusterconf/redis-cluster-node-7001.conf delete mode 100644 debian/modules/nchan/dev/redis_clusterconf/redis-cluster-node-7002.conf delete mode 100644 debian/modules/nchan/dev/redis_clusterconf/redis-cluster-node-7003.conf delete mode 100644 debian/modules/nchan/dev/redis_clusterconf/redis-cluster-node-7004.conf delete mode 100644 debian/modules/nchan/dev/redis_clusterconf/redis-cluster-node-7005.conf delete mode 100644 debian/modules/nchan/dev/redis_clusterconf/redis-cluster-node-7006.conf delete mode 100755 debian/modules/nchan/dev/redocument.rb delete mode 100755 debian/modules/nchan/dev/rsck.sh delete mode 100755 debian/modules/nchan/dev/sub.rb delete mode 100755 debian/modules/nchan/dev/test-parallel.sh delete mode 100755 debian/modules/nchan/dev/test.rb delete mode 100644 debian/modules/nchan/dev/vg.supp create mode 100644 debian/modules/nchan/src/util/nchan_fake_request.c create mode 100644 debian/modules/nchan/src/util/nchan_fake_request.h diff --git a/debian/modules/README.Modules-versions b/debian/modules/README.Modules-versions index a8b93ff..957ac00 100644 --- a/debian/modules/README.Modules-versions +++ b/debian/modules/README.Modules-versions @@ -33,7 +33,7 @@ README for Modules versions nchan Homepage: https://github.com/slact/nchan - Version: 1.0.2 + Version: 1.0.8 nginx-upload-progress Homepage: https://github.com/masterzen/nginx-upload-progress-module diff --git a/debian/modules/nchan/README.md b/debian/modules/nchan/README.md index fecd417..ee43ac9 100644 --- a/debian/modules/nchan/README.md +++ b/debian/modules/nchan/README.md @@ -2,20 +2,31 @@ https://nchan.slact.net -Nchan is a scalable, flexible pub/sub server for the modern web, built as a module for the [Nginx](http://nginx.org) web server. It can be configured as a standalone server, or as a shim between your application and tens, thousands, or millions of live subscribers. It can buffer messages in memory, on-disk, or via [Redis](http://redis.io). All connections are handled asynchronously and distributed among any number of worker processes. It can also scale to many nginx server instances with [Redis](http://redis.io). +Nchan is a scalable, flexible pub/sub server for the modern web, built as a module for the [Nginx](http://nginx.org) web server. It can be configured as a standalone server, or as a shim between your application and hundreds, thousands, or millions of live subscribers. It can buffer messages in memory, on-disk, or via [Redis](http://redis.io). All connections are handled asynchronously and distributed among any number of worker processes. It can also scale to many Nginx servers with [Redis](http://redis.io). -Messages are [published](#publisher-endpoints) to channels with HTTP `POST` requests or websockets, and [subscribed](#subscriber-endpoint) also through websockets, [long-polling](#long-polling), [EventSource](#eventsource) (SSE), old-fashioned [interval polling](#interval-polling), [and](#http-chunked-transfer) [more](#http-multipart-mixed). Each subscriber can listen to [up to 255 channels](#channel-multiplexing) per connection, and can be optionally [authenticated](https://nchan.slact.net/details#authenticate-with-nchan_authorize_request) via a custom application url. An [events](#nchan_channel_event_string) [meta channel](#nchan_channel_events_channel_id) is also available for debugging. +Messages are [published](#publisher-endpoints) to channels with HTTP `POST` requests or Websocket, and [subscribed](#subscriber-endpoint) also through [Websocket](#websocket), [long-polling](#long-polling), [EventSource](#eventsource) (SSE), old-fashioned [interval polling](#interval-polling), [and](#http-chunked-transfer) [more](#http-multipart-mixed). -For use in a web browser, you can try the [NchanSubscriber.js](https://github.com/slact/nchan/blob/master/NchanSubscriber.js) wrapper library. It supports Long-Polling, EventSource, and resumable Websockets -- or you can build your own. +In a web browser, you can use Websocket or EventSource directly, or the [NchanSubscriber.js](https://github.com/slact/nchan/blob/master/NchanSubscriber.js) wrapper library. It supports Long-Polling, EventSource, and resumable Websockets, and has a few other added convenience options. + +## Features + - RESTful, HTTP-native API. + - Supports [Websocket](https://nchan.slact.net/#websocket), [EventSource (Server-Sent Events)](https://nchan.slact.net/#eventsource), [Long-Polling](https://nchan.slact.net/#long-polling) and other HTTP-based subscribers. + - No-repeat, no-loss message delivery guarantees with per-channel configurable message buffers. + - Subscribe to [hundreds of channels](#channel-multiplexing) over a single subscriber connection. + - HTTP request [callbacks and hooks](https://nchan.slact.net/details#application-callbacks) for easy integration. + - Introspection with [channel events](https://nchan.slact.net/details#channel-events) and [url for monitoring performance statistics](https://nchan.slact.net/details#nchan_stub_status). + - Fast ephemeral local message storage and optional, slower, persistent storage with [Redis](https://nchan.slact.net/details#connecting-to-a-redis-server). + - Horizontally scalable (using [Redis](https://nchan.slact.net/details#connecting-to-a-redis-server)). + - Highly Available with no single point of failure (using [Redis Cluster](https://nchan.slact.net/details#redis-cluster)). + + ## Status and History -The latest Nchan release is v1.0.2 (August 29, 2016) ([changelog](https://nchan.slact.net/changelog)). +The latest Nchan release is v1.0.8 (November 28, 2016) ([changelog](https://nchan.slact.net/changelog)). The first iteration of Nchan was written in 2009-2010 as the [Nginx HTTP Push Module](https://pushmodule.slact.net), and was vastly refactored into its present state in 2014-2016. The present release is in the **testing** phase. The core features and old functionality are thoroughly tested and stable. Some of the new functionality, especially Redis Cluster may be a bit buggy. -Please help make the entire codebase ready for production use! Report any quirks, bugs, leaks, crashes, or larvae you find. - #### Upgrade from Nginx HTTP Push Module Although Nchan is backwards-compatible with all Push Module configuration directives, some of the more unusual and rarely used settings have been disabled and will be ignored (with a warning). See the [upgrade page](https://nchan.slact.net/upgrade) for a detailed list of changes and improvements, as well as a full list of incompatibilities. @@ -27,18 +38,19 @@ Although Nchan is backwards-compatible with all Push Module configuration direct Yes it does. Like Nginx, Nchan can easily handle as much traffic as you can throw at it. I've tried to benchmark it, but my benchmarking tools are much slower than Nchan. The data I've gathered is on how long Nchan itself takes to respond to every subscriber after publishing a message -- this excludes TCP handshake times and internal HTTP request parsing. Basically, it measures how Nchan scales assuming all other components are already tuned for scalability. The graphed data are averages of 5 runs with 50-byte messages. -With a well-tuned OS and network stack on commodity server hardware, expect to handle upwards of 300K concurrent subscribers per second at minimal CPU load. Nchan can also be scaled out to multiple Nginx instances using the [Redis storage engine](#nchan_use_redis), and that too can be scaled up beyond a single-point-of-failure by using Redis Cluster. +With a well-tuned OS and network stack on commodity server hardware, expect to handle upwards of 300K concurrent subscribers per second at minimal CPU load. Nchan can also be scaled out to multiple Nginx instances using the [Redis storage engine](#nchan_use_redis), and that too can be scaled up beyond a single-point-of-failure by using [Redis Cluster](https://nchan.slact.net/details#using-redis). -Currently, Nchan's performance is limited by available memory bandwidth. This can be improved significantly in future versions with fewer allocations and the use of contiguous memory pools. Please consider supporting Nchan to speed up the work of memory cache optimization. +Currently, Nchan's main bottleneck is not CPU load but memory bandwidth. This can be improved significantly in future versions with fewer allocations and better use of contiguous memory pools. Please consider supporting Nchan to speed up the work of memory cache optimization. ## Install #### Download Packages - [Arch Linux](https://archlinux.org): [nginx-nchan](https://aur.archlinux.org/packages/nginx-nchan/) and [nginx-nchan-git](https://aur.archlinux.org/packages/nginx-nchan-git/) are available in the Arch User Repository. - Mac OS X: a [homebrew](http://brew.sh) package is available. `brew tap homebrew/nginx; brew install nginx-full --with-nchan-module` - - [Debian](https://www.debian.org/): [nginx-common.deb](https://nchan.slact.net/download/nginx-common.deb) and [nginx-extras.deb](https://nchan.slact.net/download/nginx-extras.deb). Download both and install them with `dpkg -i`, followed by `sudo apt-get -f install`. These packages should soon be available directly from the Debian repository. + - [Debian](https://www.debian.org/): A dynamic module build for is available in the Debian package repository: [libnginx-mod-nchan](https://packages.debian.org/sid/libnginx-mod-nchan). + Additionally, you can use the pre-built static module packages [nginx-common.deb](https://nchan.slact.net/download/nginx-common.deb) and [nginx-extras.deb](https://nchan.slact.net/download/nginx-extras.deb). Download both and install them with `dpkg -i`, followed by `sudo apt-get -f install`. - [Ubuntu](http://www.ubuntu.com/): [nginx-common.ubuntu.deb](https://nchan.slact.net/download/nginx-common.ubuntu.deb) and [nginx-extras.ubuntu.deb](https://nchan.slact.net/download/nginx-extras.ubuntu.deb). Download both and install them with `dpkg -i`, followed by `sudo apt-get -f install`. Who knows when Ubuntu will add them to their repository?... - - [Fedora](https://fedoraproject.org): A 64-bit binary rpm and a source rpm are available: [nginx-nchan.x86_64.rpm](https://nchan.slact.net/download/nginx-nchan.x86-64.rpm), [ngx-nchan.src.rpm](https://nchan.slact.net/download/nginx-nchan.src.rpm). + - [Fedora](https://fedoraproject.org): Dynamic module builds for Nginx > 1.10.0 are available: [nginx-mod-nchan.x86_64.rpm](https://nchan.slact.net/download/nginx-mod-nchan.x86-64.rpm), [nginx-mod-nchan.src.rpm](https://nchan.slact.net/download/nginx-mod-nchan.src.rpm). - A statically compiled binary and associated linux nginx installation files are also [available as a tarball](https://nchan.slact.net/download/nginx-nchan-latest.tar.gz). @@ -52,6 +64,33 @@ If you're using Nginx > 1.9.11, you can build Nchan as a [dynamic module](https Run `make`, `make install`, and enjoy. (Caution, contents may be hot.) +## Getting Started + +Once you've built and installed Nchan, it's very easy to start using. Add two locations to your nginx config: + +```nginx +#... +http { + server { + #... + + location = /sub { + nchan_subscriber; + nchan_channel_id $arg_id; + } + + location = /pub { + nchan_publisher; + nchan_channel_id $arg_id; + } + } +} +``` + +You can now publish messages to channels by `POST`ing data to `/sub?id=channel_id` , and subscribe by pointing Websocket, EventSource, or [NchanSubscriber.js](https://github.com/slact/nchan/blob/master/NchanSubscriber.js) to `sub/?id=channel_id`. It's that simple. + +But Nchan is very flexible and highly configurable. So, of course, it can get a lot more complicated... + ## Conceptual Overview The basic unit of most pub/sub solutions is the messaging *channel*. Nchan is no different. Publishers send messages to channels with a certain *channel id*, and subscribers subscribed to those channels receive them. Some number of messages may be buffered for a time in a channel's message buffer before they are deleted. Pretty simple, right? @@ -88,6 +127,8 @@ Publisher endpoints are Nginx config *locations* with the [*`nchan_publisher`*]( Messages can be published to a channel by sending HTTP **POST** requests with the message contents to the *publisher endpoint* locations. You can also publish messages through a **Websocket** connection to the same location. + + ##### Publishing Messages Requests and websocket messages are responded to with information about the channel at time of message publication. Here's an example from publishing with `curl`: @@ -130,7 +171,9 @@ Metadata can be added to a message when using an HTTP POST request for publishin **HTTP `DELETE`** requests delete a channel and end all subscriber connections. Like the `GET` requests, this returns a `200` status response with channel info if the channel existed, and a `404` otherwise. -#### Subscriber Endpoint +For an in-depth explanation of how settings are applied to channels from publisher locations, see the [details page](https://nchan.slact.net/details#publisher-endpoint-configs). + +#### Subscriber Endpoints Subscriber endpoints are Nginx config *locations* with the [*`nchan_subscriber`*](#nchan_subscriber) directive. @@ -142,6 +185,7 @@ Nchan supports several different kinds of subscribers for receiving messages: [* The long-polling subscriber walks through a channel's message queue via the built-in cache mechanism of HTTP clients, namely with the "`Last-Modified`" and "`Etag`" headers. Explicitly, to receive the next message for given a long-poll subscriber response, send a request with the "`If-Modified-Since`" header set to the previous response's "`Last-Modified`" header, and "`If-None-Match`" likewise set to the previous response's "`Etag`" header. Sending a request without a "`If-Modified-Since`" or "`If-None-Match`" headers returns the oldest message in a channel's message queue, or waits until the next published message, depending on the value of the `nchan_subscriber_first_message` config directive. A message's associated content type, if present, will be sent to this subscriber with the `Content-Type` header. + - ##### Interval-Polling Works just like long-polling, except if the requested message is not yet available, immediately responds with a `304 Not Modified`. @@ -151,7 +195,7 @@ Nchan supports several different kinds of subscribers for receiving messages: [* Bidirectional communication for web browsers. Part of the [HTML5 spec](http://www.w3.org/TR/2014/REC-html5-20141028/single-page.html). Nchan supports the latest protocol version 13 ([RFC 6455](https://tools.ietf.org/html/rfc6455)). Initiated by sending a websocket handshake to the desired subscriber endpoint location. If the websocket connection is closed by the server, the `close` frame will contain the HTTP response code and status line describing the reason for closing the connection. Server-initiated keep-alive pings can be configured with the [`nchan_websocket_ping_interval`](#nchan_websocket_ping_interval) config directive. Websocket extensions are not yet supported. - Messages published through a websocket connection can be forwarded to an upstream application with the [`nchan_publisher_upstream_request`](nchan_publisher_upstream_request) config directive. + Messages published through a websocket connection can be forwarded to an upstream application with the [`nchan_publisher_upstream_request`](#nchan_publisher_upstream_request) config directive. Websocket subscribers can use the custom `ws+meta.nchan` subprotocol to receive message metadata with messages, making websocket connections resumable. Messages received with this subprotocol are of the form
   id: message_id
@@ -160,6 +204,8 @@ Nchan supports several different kinds of subscribers for receiving messages: [*
   message_data
   
The `content-type:` line may be omitted. + + - ##### EventSource Also known as [Server-Sent Events](https://en.wikipedia.org/wiki/Server-sent_events) or SSE, it predates Websockets in the [HTML5 spec](http://www.w3.org/TR/2014/REC-html5-20141028/single-page.html), and is a [very simple protocol](http://www.w3.org/TR/eventsource/#event-stream-interpretation). Initiated by sending an HTTP `GET` request to a channel subscriber endpoint with the "`Accept: text/event-stream`" header. @@ -167,7 +213,9 @@ Nchan supports several different kinds of subscribers for receiving messages: [* To resume a closed EventSource connection from the last-received message, one *should* start the connection with the "`Last-Event-ID`" header set to the last message's `id`. Unfortunately, browsers [don't support setting](http://www.w3.org/TR/2011/WD-eventsource-20111020/#concept-event-stream-last-event-id) this header for an `EventSource` object, so by default the last message id is set either from the "`Last-Event-Id`" header or the `last_event_id` url query string argument. This behavior can be configured via the [`nchan_subscriber_last_message_id`](#nchan_subscriber_last_message_id) config. - A message's associated `event` type, if present, will be sent to this subscriber with the `event:` line. + A message's `content-type` will not be received by an EventSource subscriber, as the protocol makes no provisions for this metadata. + A message's associated `event` type, if present, will be sent to this subscriber with the `event:` line. + - ##### HTTP [multipart/mixed](http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html#z0) The `multipart/mixed` MIMEtype was conceived for emails, but hey, why not use it for HTTP? It's easy to parse and includes metadata with each message. @@ -175,9 +223,11 @@ Nchan supports several different kinds of subscribers for receiving messages: [* The response headers and the unused "preamble" portion of the response body are sent right away, with the boundary string generated randomly for each subscriber. Each subsequent message will be sent as one part of the multipart message, and will include the message time and tag (`Last-Modified` and `Etag`) as well as the optional `Content-Type` headers. Each message is terminated with the next multipart message's boundary **without a trailing newline**. While this conforms to the multipart spec, it is unusual as multipart messages are defined as *starting*, rather than ending with a boundary. A message's associated content type, if present, will be sent to this subscriber with the `Content-Type` header. + - ##### HTTP Raw Stream A simple subscription method similar to the [streaming subscriber](https://github.com/wandenberg/nginx-push-stream-module/blob/master/docs/directives/subscribers.textile#push_stream_subscriber) of the [Nginx HTTP Push Stream Module](https://github.com/wandenberg/nginx-push-stream-module). Messages are appended to the response body, separated by a newline or configurable by `nchan_subscriber_http_raw_stream_separator`. + - ##### HTTP [Chunked Transfer](http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1) This subscription method uses the `chunked` `Transfer-Encoding` to receive messages. @@ -185,7 +235,10 @@ Nchan supports several different kinds of subscribers for receiving messages: [* `TE: chunked` (or `TE: chunked;q=??` where the qval > 0) The response headers are sent right away, and each message will be sent as an individual chunk. Note that because a zero-length chunk terminates the transfer, **zero-length messages will not be sent** to the subscriber. Unlike the other subscriber types, the `chunked` subscriber cannot be used with http/2 because it dissallows chunked encoding. - + + + + #### PubSub Endpoint PubSub endpoints are Nginx config *locations* with the [*`nchan_pubsub`*](#nchan_pubsub) directive. @@ -212,6 +265,8 @@ A more applicable setup may set different publisher and subscriber channel ids: Here, subscribers will listen for messages on channel `foo`, and publishers will publish messages to channel `bar`. This can be useful when setting up websocket proxying between web clients and your application. + + ### The Channel ID So far the examples have used static channel ids, which is not very useful in practice. It can be set to any nginx *variable*, such as a querystring argument, a header value, or a part of the location url: @@ -239,9 +294,11 @@ So far the examples have used static channel ids, which is not very useful in pr } ``` + + #### Channel Multiplexing -Any subscriber location can be an endpoint for up to 255 channels. Messages published to all the specified channels will be delivered in-order to the subscriber. There are two ways to enable multiplexing: +With channel multiplexing, subscribers can subscribe to up to 255 channels per connection. Messages published to all the specified channels will be delivered in-order to the subscriber. There are two ways to enable multiplexing: Up to 7 channel ids can be specified for the `nchan_channel_id` or `nchan_channel_subscriber_id` config directive: @@ -268,8 +325,6 @@ For more than 7 channels, `nchan_channel_id_split_delimiter` can be used to spli } ``` -`DELETE` requests on any channel are forwarded to relevant multi-channel subscribers, and their connections are terminated. - Publishing to multiple channels with a single request is also possible, with similar configuration: ```nginx @@ -278,14 +333,25 @@ Publishing to multiple channels with a single request is also possible, with sim nchan_channel_id "$1" "$2" "another_channel"; } ``` + +`DELETE` requests to a multiplexed channel broadcast the deletion to each of the channels it multiplexes, deletes all their messages and kicks out all clients subscribed to any of the channel ids. + +See the [details page](https://nchan.slact.net/details#securing-channels) for more information about using good IDs and keeping channels secure. + + + ## Storage Nchan can stores messages in memory, on disk, or via Redis. Memory storage is much faster, whereas Redis has additional overhead as is considerably slower for publishing messages, but offers near unlimited scalability for broadcast use cases with far more subscribers than publishers. + + ### Memory Storage This storage method uses a segment of shared memory to store messages and channel data. Large messages as determined by Nginx's caching layer are stored on-disk. The size of the memory segment is configured with `nchan_max_reserved_memory`. Data stored here is not persistent, and is lost if Nginx is restarted or reloaded. + + ### Redis Nchan can also store messages and channels on a Redis server, or in a Redis cluster. To use a Redis server, set `nchan_use_redis on;` and set the server url with `nchan_redis_url`. These two settings are inheritable by nested locations, so it is enough to set them within an `http { }` block to enable Redis for all Nchan locations in that block. Different locations can also use different Redis servers. @@ -317,6 +383,10 @@ Note that `nchan_redis_pass` implies `nchan_use_redis on;`, and that this settin When connecting several Nchan servers to the same Redis server (or cluster), the servers **must have their times synced up**. Failure to do so may result in missing and duplicated messages. +See the [details page](https://nchan.slact.net/details#using-redis) for more information on using Redis. + + + ## Variables Nchan makes several variables usabled in the config file: @@ -361,12 +431,14 @@ Additionally, `nchan_stub_status` data is also exposed as variables. These are a default: `(none)` context: server, location, if > Channel id for a publisher or subscriber location. Can have up to 4 values to subscribe to up to 4 channels. + [more details](#the-channel-id) - **nchan_channel_id_split_delimiter** arguments: 1 default: `(none)` context: server, location, if > Split the channel id into several ids for multiplexing using the delimiter string provided. + [more details](#channel-multiplexing) - **nchan_eventsource_event** arguments: 1 @@ -386,6 +458,7 @@ Additionally, `nchan_stub_status` data is also exposed as variables. These are a context: server, location, if legacy name: push_publisher > Defines a server or location as a publisher endpoint. Requests to a publisher location are treated as messages to be sent to subscribers. See the protocol documentation for a detailed description. + [more details](#publisher-endpoints) - **nchan_publisher_channel_id** arguments: 1 - 7 @@ -397,13 +470,15 @@ Additionally, `nchan_stub_status` data is also exposed as variables. These are a arguments: 1 context: server, location, if > Send POST request to internal location (which may proxy to an upstream server) with published message in the request body. Useful for bridging websocket publishers with HTTP applications, or for transforming message via upstream application before publishing to a channel. - > The upstream response code determine how publishing will proceed. A `200 OK` will publish the message from the upstream response's body. A `304 Not Modified` will publish the message as it was received from the publisher. A `204 No Content` will result in the message not being published. + > The upstream response code determines how publishing will proceed. A `200 OK` will publish the message from the upstream response's body. A `304 Not Modified` will publish the message as it was received from the publisher. A `204 No Content` will result in the message not being published. + [more details](https://nchan.slact.net/details#message-publishing-callbacks) - **nchan_pubsub** `[ http | websocket | eventsource | longpoll | intervalpoll | chunked | multipart-mixed | http-raw-stream ]` arguments: 0 - 6 default: `http websocket eventsource longpoll chunked multipart-mixed` context: server, location, if > Defines a server or location as a pubsub endpoint. For long-polling, GETs subscribe. and POSTs publish. For Websockets, publishing data on a connection does not yield a channel metadata response. Without additional configuration, this turns a location into an echo server. + [more details](#pubsub-endpoint) - **nchan_subscriber** `[ websocket | eventsource | longpoll | intervalpoll | chunked | multipart-mixed | http-raw-stream ]` arguments: 0 - 5 @@ -412,6 +487,7 @@ Additionally, `nchan_stub_status` data is also exposed as variables. These are a legacy name: push_subscriber > Defines a server or location as a channel subscriber endpoint. This location represents a subscriber's interface to a channel's message queue. The queue is traversed automatically, starting at the position defined by the `nchan_subscriber_first_message` setting. > The value is a list of permitted subscriber types. + [more details](#subscriber-endpoints) - **nchan_subscriber_channel_id** arguments: 1 - 7 @@ -426,12 +502,13 @@ Additionally, `nchan_stub_status` data is also exposed as variables. These are a > Override the default behavior of using both `Last-Modified` and `Etag` headers for the message id. > Enabling this option packs the entire message id into the `Etag` header, and discards > `Last-Modified` and `If-Modified-Since` headers. + [more details]() - **nchan_subscriber_first_message** `[ oldest | newest | ]` arguments: 1 default: `oldest` context: server, location, if - > Controls the first message received by a new subscriber. 'oldest' starts at the oldest available message in a channel's message queue, 'newest' waits until a message arrives. If a number `n` is specified, starts at `n`th message from the oldest. (`-n` start at `n`th from now). 0 is equivalent to 'newest'. + > Controls the first message received by a new subscriber. 'oldest' starts at the oldest available message in a channel's message queue, 'newest' waits until a message arrives. If a number `n` is specified, starts at `n`th message from the oldest. (`-n` starts at `n`th from now). 0 is equivalent to 'newest'. - **nchan_subscriber_http_raw_stream_separator** `` arguments: 1 @@ -468,6 +545,19 @@ Additionally, `nchan_stub_status` data is also exposed as variables. These are a arguments: 1 context: server, location, if > Send GET request to internal location (which may proxy to an upstream server) for authorization of a publisher or subscriber request. A 200 response authorizes the request, a 403 response forbids it. + [more details](https://nchan.slact.net/details#request-authorization) + +- **nchan_subscribe_request** `` + arguments: 1 + context: server, location, if + > Send GET request to internal location (which may proxy to an upstream server) after subscribing. Disabled for longpoll and interval-polling subscribers. + [more details](https://nchan.slact.net/details#subsribe-and-unsubscribe-callbacks) + +- **nchan_unsubscribe_request** `` + arguments: 1 + context: server, location, if + > Send GET request to internal location (which may proxy to an upstream server) after unsubscribing. Disabled for longpoll and interval-polling subscribers. + [more details](https://nchan.slact.net/details#subsribe-and-unsubscribe-callbacks) - **nchan_max_reserved_memory** `` arguments: 1 @@ -475,20 +565,21 @@ Additionally, `nchan_stub_status` data is also exposed as variables. These are a context: http legacy name: push_max_reserved_memory > The size of the shared memory chunk this module will use for message queuing and buffering. + [more details](#memory-storage) -- **nchan_message_buffer_length** `` +- **nchan_message_buffer_length** `[ | ]` arguments: 1 default: `10` context: http, server, location legacy names: push_max_message_buffer_length, push_message_buffer_length - > Publisher configuration setting the maximum number of messages to store per channel. A channel's message buffer will retain a maximum of this many most recent messages. + > Publisher configuration setting the maximum number of messages to store per channel. A channel's message buffer will retain a maximum of this many most recent messages. An Nginx variable can also be used to set the buffer length dynamically. -- **nchan_message_timeout** `