# HG changeset patch # User Igor Sysoev # Date 1194382800 -10800 # Node ID f70f2f565fe0cd3b32dd4cad3b0d010c595ba4cb # Parent 5bb1b28ddeaaf292d64f89e825049ecdae283d05 nginx 0.5.33 *) Change: now by default the "echo" SSI command uses entity encoding. *) Feature: the "encoding" parameter in the "echo" SSI command. *) Change: mail proxy was split on three modules: pop3, imap and smtp. *) Feature: the --without-mail_pop3_module, --without-mail_imap_module, and --without-mail_smtp_module configuration parameters. *) Feature: the "smtp_greeting_delay" and "smtp_client_buffer" directives of the ngx_mail_smtp_module. *) Feature: the "server_name" and "valid_referers" directives support regular expressions. *) Feature: the "server_name", "map", and "valid_referers" directives support the "www.example.*" wildcards. *) Bugfix: sub_filter did not work with empty substitution. *) Bugfix: in sub_filter parsing. *) Bugfix: a worker process may got caught in an endless loop, if the memcached was used. *) Bugfix: nginx supported low case only "close" and "keep-alive" values in the "Connection" request header line; bug appeared in 0.5.32. *) Bugfix: nginx could not start on Solaris if the shared PCRE library located in non-standard place was used. diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,39 @@ +Changes with nginx 0.5.33 07 Nov 2007 + + *) Change: now by default the "echo" SSI command uses entity encoding. + + *) Feature: the "encoding" parameter in the "echo" SSI command. + + *) Change: mail proxy was split on three modules: pop3, imap and smtp. + + *) Feature: the --without-mail_pop3_module, --without-mail_imap_module, + and --without-mail_smtp_module configuration parameters. + + *) Feature: the "smtp_greeting_delay" and "smtp_client_buffer" + directives of the ngx_mail_smtp_module. + + *) Feature: the "server_name" and "valid_referers" directives support + regular expressions. + + *) Feature: the "server_name", "map", and "valid_referers" directives + support the "www.example.*" wildcards. + + *) Bugfix: sub_filter did not work with empty substitution. + + *) Bugfix: in sub_filter parsing. + + *) Bugfix: a worker process may got caught in an endless loop, if the + memcached was used. + + *) Bugfix: nginx supported low case only "close" and "keep-alive" + values in the "Connection" request header line; bug appeared in + 0.5.32. + + *) Bugfix: nginx could not start on Solaris if the shared PCRE library + located in non-standard place was used. + + Changes with nginx 0.5.32 24 Sep 2007 *) Change: now nginx tries to set the "worker_priority", @@ -285,7 +320,7 @@ Changes with nginx 0.5.16 trailing ";"; bug appeared in 0.3.50. *) Bugfix: the "[alert] zero size buf" error when FastCGI server was - used and an request body written in a temporary file was multiple of + used and a request body written in a temporary file was multiple of 32K. *) Bugfix: nginx could not be built on Solaris without the --with-debug @@ -589,7 +624,7 @@ Changes with nginx 0.4.14 *) Bugfix: ngx_http_perl_module did not work if perl was called recursively. - *) Bugfix: nginx ignored a host name in an request line. + *) Bugfix: nginx ignored a host name in a request line. *) Bugfix: a worker process may got caught in an endless loop, if a FastCGI server sent too many data to the stderr. @@ -612,7 +647,7 @@ Changes with nginx 0.4.13 *) Feature: the "limit_except" directive supports all WebDAV methods. *) Bugfix: if the "add_before_body" directive was used without the - "add_after_body" directive, then an response did not transferred + "add_after_body" directive, then a response did not transferred complete. *) Bugfix: a large request body did not receive if the epoll method and @@ -681,8 +716,8 @@ Changes with nginx 0.4.9 Changes with nginx 0.4.8 11 Oct 2006 *) Bugfix: if an "include" SSI command were before another "include" - SSI command with an "wait" parameter, then the "wait" parameter - might not work. + SSI command with a "wait" parameter, then the "wait" parameter might + not work. *) Bugfix: the ngx_http_flv_module added the FLV header to the full responses. @@ -699,8 +734,8 @@ Changes with nginx 0.4.7 variables. *) Bugfix: if an "include" SSI command were before another "include" - SSI command with an "wait" parameter, then the "wait" parameter - might not work. + SSI command with a "wait" parameter, then the "wait" parameter might + not work. *) Bugfix: if the "proxy_buffering off" directive was used or while working with memcached the connections might not be closed on @@ -759,10 +794,10 @@ Changes with nginx 0.4.3 *) Feature: the ngx_http_browser_module. *) Bugfix: a segmentation fault may occur while redirecting the 400 - error to the proxied server using an "proxy_pass" directive. + error to the proxied server using a "proxy_pass" directive. *) Bugfix: a segmentation fault occurred if an unix domain socket was - used in an "proxy_pass" directive; bug appeared in 0.3.47. + used in a "proxy_pass" directive; bug appeared in 0.3.47. *) Bugfix: SSI did work with memcached and nonbuffered responses. @@ -877,7 +912,7 @@ Changes with nginx 0.3.56 *) Feature: the "if" directive supports the "-d", "!-d", "-e", "!-e", "-x", and "!-x" operators. - *) Bugfix: a segmentation fault occurred if an request returned an + *) Bugfix: a segmentation fault occurred if a request returned a redirect and some sent to client header lines were logged in the access log. diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,41 @@ +Изменения в nginx 0.5.33 07.11.2007 + + *) Изменение: теперь по умолчанию команда SSI echo использует + кодирование entity. + + *) Добавление: параметр encoding в команде SSI echo. + + *) Изменение: почтовый прокси-сервер разделён на три модуля: pop3, imap + и smtp. + + *) Добавление: параметры конфигурации --without-mail_pop3_module, + --without-mail_imap_module и --without-mail_smtp_module. + + *) Добавление: директивы smtp_greeting_delay и smtp_client_buffer + модуля ngx_mail_smtp_module. + + *) Добавление: директивы server_name и valid_referers поддерживают + регулярные выражения. + + *) Добавление: директивы "server_name", "map", and "valid_referers" + поддерживают маски вида "www.example.*". + + *) Исправление: sub_filter не работал с пустой строкой замены. + + *) Исправление: в парсинге sub_filter. + + *) Исправление: рабочий процесс мог зациклиться при использовании + memcached. + + *) Исправление: nginx распознавал параметры "close" и "keep-alive" в + строке "Connection" в заголовке запроса только, если они были в + нижнем регистре; ошибка появилась в 0.5.32. + + *) Исправление: при использовании разделяемой библиотеки PCRE, + расположенной в нестандартном месте, nginx не запускался на Solaris. + + Изменения в nginx 0.5.32 24.09.2007 *) Изменение: теперь nginx пытается установить директивы diff --git a/auto/lib/pcre/conf b/auto/lib/pcre/conf --- a/auto/lib/pcre/conf +++ b/auto/lib/pcre/conf @@ -77,7 +77,6 @@ if [ $PCRE != NONE ]; then CORE_DEPS="$CORE_DEPS $PCRE/pcre.h" LINK_DEPS="$LINK_DEPS $PCRE/.libs/libpcre.a" CORE_LIBS="$CORE_LIBS $PCRE/.libs/libpcre.a" - #CORE_LIBS="$CORE_LIBS -L $PCRE/.libs -lpcre" ;; esac @@ -111,7 +110,13 @@ else ngx_feature_run=no ngx_feature_incs="#include " ngx_feature_path="/usr/local/include" - ngx_feature_libs="-L /usr/local/lib -lpcre" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lpcre" + else + ngx_feature_libs="-L/usr/local/lib -lpcre" + fi + ngx_feature_test="pcre *re; re = pcre_compile(NULL, 0, NULL, 0, NULL)" . auto/feature @@ -160,7 +165,13 @@ else ngx_feature_run=no ngx_feature_incs="#include " ngx_feature_path="/usr/pkg/include" - ngx_feature_libs="-L /usr/pkg/lib -lpcre" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lpcre" + else + ngx_feature_libs="-L/usr/pkg/lib -lpcre" + fi + ngx_feature_test="pcre *re; re = pcre_compile(NULL, 0, NULL, 0, NULL)" . auto/feature @@ -185,7 +196,13 @@ else ngx_feature_run=no ngx_feature_incs="#include " ngx_feature_path="/opt/local/include" - ngx_feature_libs="-L/opt/local/lib -lpcre" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lpcre" + else + ngx_feature_libs="-L/opt/local/lib -lpcre" + fi + ngx_feature_test="pcre *re; re = pcre_compile(NULL, 0, NULL, 0, NULL)" . auto/feature diff --git a/auto/modules b/auto/modules --- a/auto/modules +++ b/auto/modules @@ -308,8 +308,6 @@ fi if [ $MAIL_SSL = YES ]; then - MAIL_DEPS="$MAIL_DEPS $MAIL_SSL_DEPS" - MAIL_SRCS="$MAIL_SRCS $MAIL_SSL_SRCS" have=NGX_MAIL_SSL . auto/have USE_OPENSSL=YES fi @@ -341,6 +339,26 @@ if [ $MAIL = YES ]; then if [ $MAIL_SSL = YES ]; then modules="$modules $MAIL_SSL_MODULE" + MAIL_DEPS="$MAIL_DEPS $MAIL_SSL_DEPS" + MAIL_SRCS="$MAIL_SRCS $MAIL_SSL_SRCS" + fi + + if [ $MAIL_POP3 = YES ]; then + modules="$modules $MAIL_POP3_MODULE" + MAIL_DEPS="$MAIL_DEPS $MAIL_POP3_DEPS" + MAIL_SRCS="$MAIL_SRCS $MAIL_POP3_SRCS" + fi + + if [ $MAIL_IMAP = YES ]; then + modules="$modules $MAIL_IMAP_MODULE" + MAIL_DEPS="$MAIL_DEPS $MAIL_IMAP_DEPS" + MAIL_SRCS="$MAIL_SRCS $MAIL_IMAP_SRCS" + fi + + if [ $MAIL_SMTP = YES ]; then + modules="$modules $MAIL_SMTP_MODULE" + MAIL_DEPS="$MAIL_DEPS $MAIL_SMTP_DEPS" + MAIL_SRCS="$MAIL_SRCS $MAIL_SMTP_SRCS" fi modules="$modules $MAIL_AUTH_HTTP_MODULE" diff --git a/auto/options b/auto/options --- a/auto/options +++ b/auto/options @@ -22,6 +22,8 @@ NGX_CC_OPT= NGX_LD_OPT= CPU=NO +NGX_RPATH=NO + NGX_TEST_BUILD_DEVPOLL=NO NGX_TEST_BUILD_EVENTPORT=NO NGX_TEST_BUILD_EPOLL=NO @@ -80,6 +82,9 @@ HTTP_STUB_STATUS=NO MAIL=NO MAIL_SSL=NO +MAIL_POP3=YES +MAIL_IMAP=YES +MAIL_SMTP=YES NGX_ADDONS= @@ -189,6 +194,9 @@ do # STUB --with-imap) MAIL=YES ;; --with-imap_ssl_module) MAIL_SSL=YES ;; + --without-mail_pop3_module) MAIL_POP3=NO ;; + --without-mail_imap_module) MAIL_IMAP=NO ;; + --without-mail_smtp_module) MAIL_SMTP=NO ;; --add-module=*) NGX_ADDONS="$NGX_ADDONS $value" ;; @@ -299,8 +307,11 @@ cat << END --without-http disable HTTP server - --with-mail enable IMAP4/POP3/SMTP proxy module + --with-mail enable POP3/IMAP4/SMTP proxy module --with-mail_ssl_module enable ngx_mail_ssl_module + --without-mail_pop3_module disable ngx_mail_pop3_module + --without-mail_imap_module disable ngx_mail_imap_module + --without-mail_smtp_module disable ngx_mail_smtp_module --add-module=PATH enable an external module diff --git a/auto/os/solaris b/auto/os/solaris --- a/auto/os/solaris +++ b/auto/os/solaris @@ -9,6 +9,8 @@ CORE_DEPS="$UNIX_DEPS $SOLARIS_DEPS" CORE_SRCS="$UNIX_SRCS $SOLARIS_SRCS " CORE_LIBS="$CORE_LIBS -lsocket -lnsl -lrt" +NGX_RPATH=YES + # Solaris's make does not support a blank line between target and rules ngx_spacer= diff --git a/auto/sources b/auto/sources --- a/auto/sources +++ b/auto/sources @@ -423,6 +423,21 @@ MAIL_SRCS="src/mail/ngx_mail.c \ src/mail/ngx_mail_handler.c \ src/mail/ngx_mail_parse.c" +MAIL_POP3_MODULE="ngx_mail_pop3_module" +MAIL_POP3_DEPS="src/mail/ngx_mail_pop3_module.h" +MAIL_POP3_SRCS="src/mail/ngx_mail_pop3_module.c \ + src/mail/ngx_mail_pop3_handler.c" + +MAIL_IMAP_MODULE="ngx_mail_imap_module" +MAIL_IMAP_DEPS="src/mail/ngx_mail_imap_module.h" +MAIL_IMAP_SRCS="src/mail/ngx_mail_imap_module.c \ + src/mail/ngx_mail_imap_handler.c" + +MAIL_SMTP_MODULE="ngx_mail_smtp_module" +MAIL_SMTP_DEPS="src/mail/ngx_mail_smtp_module.h" +MAIL_SMTP_SRCS="src/mail/ngx_mail_smtp_module.c \ + src/mail/ngx_mail_smtp_handler.c" + MAIL_SSL_MODULE="ngx_mail_ssl_module" MAIL_SSL_DEPS="src/mail/ngx_mail_ssl_module.h" MAIL_SSL_SRCS="src/mail/ngx_mail_ssl_module.c" diff --git a/auto/types/uintptr_t b/auto/types/uintptr_t --- a/auto/types/uintptr_t +++ b/auto/types/uintptr_t @@ -36,5 +36,6 @@ if [ $found = no ]; then found="uint`expr 8 \* $ngx_ptr_size`_t" echo ", $found used" - echo "typedef $found uintptr_t;" >> $NGX_AUTO_CONFIG_H + echo "typedef $found uintptr_t;" >> $NGX_AUTO_CONFIG_H + echo "typedef $found intptr_t;" | sed -e 's/u//g' >> $NGX_AUTO_CONFIG_H fi diff --git a/src/core/nginx.h b/src/core/nginx.h --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -8,7 +8,7 @@ #define _NGINX_H_INCLUDED_ -#define NGINX_VERSION "0.5.32" +#define NGINX_VERSION "0.5.33" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff --git a/src/core/ngx_config.h b/src/core/ngx_config.h --- a/src/core/ngx_config.h +++ b/src/core/ngx_config.h @@ -70,31 +70,20 @@ #endif - - -/* TODO: platform specific: array[NGX_INVALID_ARRAY_INDEX] must cause SIGSEGV */ -#define NGX_INVALID_ARRAY_INDEX 0x80000000 - +typedef intptr_t ngx_int_t; +typedef uintptr_t ngx_uint_t; +typedef intptr_t ngx_flag_t; -#if 1 -/* STUB: autoconf */ -typedef int ngx_int_t; -typedef u_int ngx_uint_t; -typedef int ngx_flag_t; -#define NGX_INT_T_LEN sizeof("-2147483648") - 1 - -#else - -typedef long ngx_int_t; -typedef u_long ngx_uint_t; -typedef long ngx_flag_t; -#define NGX_INT_T_LEN sizeof("-9223372036854775808") - 1 - -#endif #define NGX_INT32_LEN sizeof("-2147483648") - 1 #define NGX_INT64_LEN sizeof("-9223372036854775808") - 1 +#if (NGX_PTR_SIZE == 4) +#define NGX_INT_T_LEN NGX_INT32_LEN +#else +#define NGX_INT_T_LEN NGX_INT64_LEN +#endif + #ifndef NGX_ALIGNMENT #define NGX_ALIGNMENT sizeof(unsigned long) /* platform word */ @@ -108,6 +97,10 @@ typedef long ngx_flag_t; #define ngx_abort abort +/* TODO: platform specific: array[NGX_INVALID_ARRAY_INDEX] must cause SIGSEGV */ +#define NGX_INVALID_ARRAY_INDEX 0x80000000 + + /* TODO: auto_conf: ngx_inline inline __inline __inline__ */ #ifndef ngx_inline #define ngx_inline inline diff --git a/src/core/ngx_hash.c b/src/core/ngx_hash.c --- a/src/core/ngx_hash.c +++ b/src/core/ngx_hash.c @@ -53,7 +53,7 @@ ngx_hash_find(ngx_hash_t *hash, ngx_uint void * -ngx_hash_find_wildcard(ngx_hash_wildcard_t *hwc, u_char *name, size_t len) +ngx_hash_find_wc_head(ngx_hash_wildcard_t *hwc, u_char *name, size_t len) { void *value; ngx_uint_t i, n, key; @@ -63,7 +63,7 @@ ngx_hash_find_wildcard(ngx_hash_wildcard line.len = len; line.data = name; - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "wc:\"%V\"", &line); + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "wch:\"%V\"", &line); #endif n = len; @@ -112,7 +112,7 @@ ngx_hash_find_wildcard(ngx_hash_wildcard } } - value = ngx_hash_find_wildcard(hwc, name, n - 1); + value = ngx_hash_find_wc_head(hwc, name, n - 1); if (value) { return value; @@ -128,6 +128,104 @@ ngx_hash_find_wildcard(ngx_hash_wildcard } +void * +ngx_hash_find_wc_tail(ngx_hash_wildcard_t *hwc, u_char *name, size_t len) +{ + void *value; + ngx_uint_t i, key; + +#if 0 + ngx_str_t line; + + line.len = len; + line.data = name; + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "wct:\"%V\"", &line); +#endif + + key = 0; + + for (i = 0; i < len; i++) { + if (name[i] == '.') { + break; + } + + key = ngx_hash(key, name[i]); + } + + if (i == len) { + return NULL; + } + +#if 0 + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "key:\"%ui\"", key); +#endif + + value = ngx_hash_find(&hwc->hash, key, name, i); + + if (value) { + + /* + * the 2 low bits of value have the special meaning: + * 00 - value is data pointer, + * 01 - value is pointer to wildcard hash allowing "example.*". + */ + + if ((uintptr_t) value & 1) { + + i++; + + hwc = (ngx_hash_wildcard_t *) ((uintptr_t) value & (uintptr_t) ~3); + + value = ngx_hash_find_wc_tail(hwc, &name[i], len - i); + + if (value) { + return value; + } + + return hwc->value; + } + + return value; + } + + return hwc->value; +} + + +void * +ngx_hash_find_combined(ngx_hash_combined_t *hash, ngx_uint_t key, u_char *name, + size_t len) +{ + void *value; + + if (hash->hash.buckets) { + value = ngx_hash_find(&hash->hash, key, name, len); + + if (value) { + return value; + } + } + + if (hash->wc_head && hash->wc_head->hash.buckets) { + value = ngx_hash_find_wc_head(hash->wc_head, name, len); + + if (value) { + return value; + } + } + + if (hash->wc_tail && hash->wc_tail->hash.buckets) { + value = ngx_hash_find_wc_tail(hash->wc_tail, name, len); + + if (value) { + return value; + } + } + + return NULL; +} + + #define NGX_HASH_ELT_SIZE(name) \ (sizeof(void *) + ngx_align((name)->key.len + 1, sizeof(void *))) @@ -544,7 +642,14 @@ ngx_hash_keys_array_init(ngx_hash_keys_a return NGX_ERROR; } - if (ngx_array_init(&ha->dns_wildcards, ha->temp_pool, asize, + if (ngx_array_init(&ha->dns_wc_head, ha->temp_pool, asize, + sizeof(ngx_hash_key_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_array_init(&ha->dns_wc_tail, ha->temp_pool, asize, sizeof(ngx_hash_key_t)) != NGX_OK) { @@ -556,9 +661,15 @@ ngx_hash_keys_array_init(ngx_hash_keys_a return NGX_ERROR; } - ha->dns_wildcards_hash = ngx_pcalloc(ha->temp_pool, - sizeof(ngx_array_t) * ha->hsize); - if (ha->dns_wildcards_hash == NULL) { + ha->dns_wc_head_hash = ngx_pcalloc(ha->temp_pool, + sizeof(ngx_array_t) * ha->hsize); + if (ha->dns_wc_head_hash == NULL) { + return NGX_ERROR; + } + + ha->dns_wc_tail_hash = ngx_pcalloc(ha->temp_pool, + sizeof(ngx_array_t) * ha->hsize); + if (ha->dns_wc_tail_hash == NULL) { return NGX_ERROR; } @@ -571,37 +682,144 @@ ngx_hash_add_key(ngx_hash_keys_arrays_t ngx_uint_t flags) { size_t len; - u_char *reverse; + u_char *p; ngx_str_t *name; - ngx_uint_t i, k, n, skip; + ngx_uint_t i, k, n, skip, last; + ngx_array_t *keys, *hwc; ngx_hash_key_t *hk; - if (!(flags & NGX_HASH_WILDCARD_KEY)) { + last = key->len; + + if (flags & NGX_HASH_WILDCARD_KEY) { - /* exact hash */ + /* + * supported wildcards: + * "*.example.com", ".example.com", and "www.example.*" + */ - k = 0; + n = 0; for (i = 0; i < key->len; i++) { - if (!(flags & NGX_HASH_READONLY_KEY)) { - key->data[i] = ngx_tolower(key->data[i]); + + if (key->data[i] == '*') { + if (++n > 1) { + return NGX_DECLINED; + } + } + + if (key->data[i] == '.' && key->data[i + 1] == '.') { + return NGX_DECLINED; } - k = ngx_hash(k, key->data[i]); + } + + if (key->len > 1 && key->data[0] == '.') { + skip = 1; + goto wildcard; + } + + if (key->len > 2) { + + if (key->data[0] == '*' && key->data[1] == '.') { + skip = 2; + goto wildcard; + } + + if (key->data[i - 2] == '.' && key->data[i - 1] == '*') { + skip = 0; + last -= 2; + goto wildcard; + } } - k %= ha->hsize; + if (n) { + return NGX_DECLINED; + } + } + + /* exact hash */ + + k = 0; + + for (i = 0; i < last; i++) { + if (!(flags & NGX_HASH_READONLY_KEY)) { + key->data[i] = ngx_tolower(key->data[i]); + } + k = ngx_hash(k, key->data[i]); + } + + k %= ha->hsize; + + /* check conflicts in exact hash */ + + name = ha->keys_hash[k].elts; + + if (name) { + for (i = 0; i < ha->keys_hash[k].nelts; i++) { + if (last != name[i].len) { + continue; + } + + if (ngx_strncmp(key->data, name[i].data, last) == 0) { + return NGX_BUSY; + } + } - /* check conflicts in exact hash */ + } else { + if (ngx_array_init(&ha->keys_hash[k], ha->temp_pool, 4, + sizeof(ngx_str_t)) + != NGX_OK) + { + return NGX_ERROR; + } + } + + name = ngx_array_push(&ha->keys_hash[k]); + if (name == NULL) { + return NGX_ERROR; + } + + *name = *key; + + hk = ngx_array_push(&ha->keys); + if (hk == NULL) { + return NGX_ERROR; + } + + hk->key = *key; + hk->key_hash = ngx_hash_key(key->data, last); + hk->value = value; + + return NGX_OK; + + +wildcard: + + /* wildcard hash */ + + k = 0; + + for (i = skip; i < last; i++) { + key->data[i] = ngx_tolower(key->data[i]); + k = ngx_hash(k, key->data[i]); + } + + k %= ha->hsize; + + if (skip == 1) { + + /* check conflicts in exact hash for ".example.com" */ name = ha->keys_hash[k].elts; if (name) { + len = last - skip; + for (i = 0; i < ha->keys_hash[k].nelts; i++) { - if (key->len != name[i].len) { + if (len != name[i].len) { continue; } - if (ngx_strncmp(key->data, name[i].data, key->len) == 0) { + if (ngx_strncmp(&key->data[1], name[i].data, len) == 0) { return NGX_BUSY; } } @@ -620,92 +838,36 @@ ngx_hash_add_key(ngx_hash_keys_arrays_t return NGX_ERROR; } - *name = *key; - - hk = ngx_array_push(&ha->keys); - if (hk == NULL) { + name->len = last - 1; + name->data = ngx_palloc(ha->temp_pool, name->len); + if (name->data == NULL) { return NGX_ERROR; } - hk->key = *key; - hk->key_hash = ngx_hash_key(key->data, key->len); - hk->value = value; - - } else { - - /* wildcard hash */ - - skip = (key->data[0] == '*') ? 2 : 1; - k = 0; - - for (i = skip; i < key->len; i++) { - key->data[i] = ngx_tolower(key->data[i]); - k = ngx_hash(k, key->data[i]); - } - - k %= ha->hsize; - - if (skip == 1) { - - /* check conflicts in exact hash for ".example.com" */ - - name = ha->keys_hash[k].elts; - - if (name) { - len = key->len - skip; + ngx_memcpy(name->data, &key->data[1], name->len); + } - for (i = 0; i < ha->keys_hash[k].nelts; i++) { - if (len != name[i].len) { - continue; - } - if (ngx_strncmp(&key->data[1], name[i].data, len) == 0) { - return NGX_BUSY; - } - } - - } else { - if (ngx_array_init(&ha->keys_hash[k], ha->temp_pool, 4, - sizeof(ngx_str_t)) - != NGX_OK) - { - return NGX_ERROR; - } - } - - name = ngx_array_push(&ha->keys_hash[k]); - if (name == NULL) { - return NGX_ERROR; - } - - name->len = key->len - 1; - name->data = ngx_palloc(ha->temp_pool, name->len); - if (name->data == NULL) { - return NGX_ERROR; - } - - ngx_memcpy(name->data, &key->data[1], name->len); - } - + if (skip) { /* * convert "*.example.com" to "com.example.\0" * and ".example.com" to "com.example\0" */ - reverse = ngx_palloc(ha->temp_pool, key->len); - if (reverse == NULL) { + p = ngx_palloc(ha->temp_pool, last); + if (p == NULL) { return NGX_ERROR; } len = 0; n = 0; - for (i = key->len - 1; i; i--) { + for (i = last - 1; i; i--) { if (key->data[i] == '.') { - ngx_memcpy(&reverse[n], &key->data[i + 1], len); + ngx_memcpy(&p[n], &key->data[i + 1], len); n += len; - reverse[n++] = '.'; + p[n++] = '.'; len = 0; continue; } @@ -714,63 +876,80 @@ ngx_hash_add_key(ngx_hash_keys_arrays_t } if (len) { - ngx_memcpy(&reverse[n], &key->data[1], len); + ngx_memcpy(&p[n], &key->data[1], len); n += len; } - reverse[n] = '\0'; + p[n] = '\0'; + hwc = &ha->dns_wc_head; + keys = &ha->dns_wc_head_hash[k]; + + } else { - hk = ngx_array_push(&ha->dns_wildcards); - if (hk == NULL) { + /* convert "www.example.*" to "www.example\0" */ + + last++; + + p = ngx_palloc(ha->temp_pool, last); + if (p == NULL) { return NGX_ERROR; } - hk->key.len = key->len - 1; - hk->key.data = reverse; - hk->key_hash = 0; - hk->value = value; + ngx_cpystrn(p, key->data, last); + + hwc = &ha->dns_wc_tail; + keys = &ha->dns_wc_tail_hash[k]; + } - /* check conflicts in wildcard hash */ + hk = ngx_array_push(hwc); + if (hk == NULL) { + return NGX_ERROR; + } - name = ha->dns_wildcards_hash[k].elts; + hk->key.len = last - 1; + hk->key.data = p; + hk->key_hash = 0; + hk->value = value; - if (name) { - len = key->len - skip; - for (i = 0; i < ha->dns_wildcards_hash[k].nelts; i++) { - if (len != name[i].len) { - continue; - } + /* check conflicts in wildcard hash */ + + name = keys->elts; - if (ngx_strncmp(key->data + skip, name[i].data, len) == 0) { - return NGX_BUSY; - } + if (name) { + len = last - skip; + + for (i = 0; i < keys->nelts; i++) { + if (len != name[i].len) { + continue; } - } else { - if (ngx_array_init(&ha->dns_wildcards_hash[k], ha->temp_pool, 4, - sizeof(ngx_str_t)) - != NGX_OK) - { - return NGX_ERROR; + if (ngx_strncmp(key->data + skip, name[i].data, len) == 0) { + return NGX_BUSY; } } - name = ngx_array_push(&ha->dns_wildcards_hash[k]); - if (name == NULL) { + } else { + if (ngx_array_init(keys, ha->temp_pool, 4, sizeof(ngx_str_t)) != NGX_OK) + { return NGX_ERROR; } + } - name->len = key->len - skip; - name->data = ngx_palloc(ha->temp_pool, name->len); - if (name->data == NULL) { - return NGX_ERROR; - } + name = ngx_array_push(keys); + if (name == NULL) { + return NGX_ERROR; + } - ngx_memcpy(name->data, key->data + skip, name->len); + name->len = last - skip; + name->data = ngx_palloc(ha->temp_pool, name->len); + if (name->data == NULL) { + return NGX_ERROR; } + ngx_memcpy(name->data, key->data + skip, name->len); + return NGX_OK; } diff --git a/src/core/ngx_hash.h b/src/core/ngx_hash.h --- a/src/core/ngx_hash.h +++ b/src/core/ngx_hash.h @@ -42,6 +42,13 @@ typedef ngx_uint_t (*ngx_hash_key_pt) (u typedef struct { + ngx_hash_t hash; + ngx_hash_wildcard_t *wc_head; + ngx_hash_wildcard_t *wc_tail; +} ngx_hash_combined_t; + + +typedef struct { ngx_hash_t *hash; ngx_hash_key_pt key; @@ -73,8 +80,11 @@ typedef struct { ngx_array_t keys; ngx_array_t *keys_hash; - ngx_array_t dns_wildcards; - ngx_array_t *dns_wildcards_hash; + ngx_array_t dns_wc_head; + ngx_array_t *dns_wc_head_hash; + + ngx_array_t dns_wc_tail; + ngx_array_t *dns_wc_tail_hash; } ngx_hash_keys_arrays_t; @@ -87,8 +97,10 @@ typedef struct { void *ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len); -void *ngx_hash_find_wildcard(ngx_hash_wildcard_t *hwc, u_char *name, - size_t len); +void *ngx_hash_find_wc_head(ngx_hash_wildcard_t *hwc, u_char *name, size_t len); +void *ngx_hash_find_wc_tail(ngx_hash_wildcard_t *hwc, u_char *name, size_t len); +void *ngx_hash_find_combined(ngx_hash_combined_t *hash, ngx_uint_t key, + u_char *name, size_t len); ngx_int_t ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts); diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c --- a/src/core/ngx_string.c +++ b/src/core/ngx_string.c @@ -442,7 +442,7 @@ ngx_vsnprintf(u_char *buf, size_t max, c /* - * We use ngx_strcasecmp()/ngx_strncasecmp() for 7-bit ASCII string only, + * We use ngx_strcasecmp()/ngx_strncasecmp() for 7-bit ASCII strings only, * and implement our own ngx_strcasecmp()/ngx_strncasecmp() * to avoid libc locale overhead. Besides, we use the ngx_uint_t's * instead of the u_char's, because they are slightly faster. @@ -503,6 +503,95 @@ ngx_strncasecmp(u_char *s1, u_char *s2, } +u_char * +ngx_strnstr(u_char *s1, char *s2, size_t len) +{ + u_char c1, c2; + size_t n; + + c2 = *(u_char *) s2++; + + n = ngx_strlen(s2); + + do { + do { + if (len-- == 0) { + return NULL; + } + + c1 = *s1++; + + if (c1 == 0) { + return NULL; + } + + } while (c1 != c2); + + if (n > len) { + return NULL; + } + + } while (ngx_strncmp(s1, (u_char *) s2, n) != 0); + + return --s1; +} + + +/* + * ngx_strstrn() and ngx_strcasestrn() are intended to search for static + * substring with known length in null-terminated string. The argument n + * must be length of the second substring - 1. + */ + +u_char * +ngx_strstrn(u_char *s1, char *s2, size_t n) +{ + u_char c1, c2; + + c2 = *(u_char *) s2++; + + do { + do { + c1 = *s1++; + + if (c1 == 0) { + return NULL; + } + + } while (c1 != c2); + + } while (ngx_strncmp(s1, (u_char *) s2, n) != 0); + + return --s1; +} + + +u_char * +ngx_strcasestrn(u_char *s1, char *s2, size_t n) +{ + ngx_uint_t c1, c2; + + c2 = (ngx_uint_t) *s2++; + c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2; + + do { + do { + c1 = (ngx_uint_t) *s1++; + + if (c1 == 0) { + return NULL; + } + + c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1; + + } while (c1 != c2); + + } while (ngx_strncasecmp(s1, (u_char *) s2, n) != 0); + + return --s1; +} + + ngx_int_t ngx_rstrncmp(u_char *s1, u_char *s2, size_t n) { @@ -1250,6 +1339,67 @@ done: } +uintptr_t +ngx_escape_html(u_char *dst, u_char *src, size_t size) +{ + u_char ch; + ngx_uint_t i, len; + + if (dst == NULL) { + + len = 0; + + for (i = 0; i < size; i++) { + switch (*src++) { + + case '<': + len += sizeof("<") - 2; + break; + + case '>': + len += sizeof(">") - 2; + break; + + case '&': + len += sizeof("&") - 2; + break; + + default: + break; + } + } + + return (uintptr_t) len; + } + + for (i = 0; i < size; i++) { + ch = *src++; + + switch (ch) { + + case '<': + *dst++ = '&'; *dst++ = 'l'; *dst++ = 't'; *dst++ = ';'; + break; + + case '>': + *dst++ = '&'; *dst++ = 'g'; *dst++ = 't'; *dst++ = ';'; + break; + + case '&': + *dst++ = '&'; *dst++ = 'a'; *dst++ = 'm'; *dst++ = 'p'; + *dst++ = ';'; + break; + + default: + *dst++ = ch; + break; + } + } + + return (uintptr_t) dst; +} + + /* ngx_sort() is implemented as insertion sort because we need stable sort */ void diff --git a/src/core/ngx_string.h b/src/core/ngx_string.h --- a/src/core/ngx_string.h +++ b/src/core/ngx_string.h @@ -126,6 +126,11 @@ u_char *ngx_vsnprintf(u_char *buf, size_ ngx_int_t ngx_strcasecmp(u_char *s1, u_char *s2); ngx_int_t ngx_strncasecmp(u_char *s1, u_char *s2, size_t n); +u_char *ngx_strnstr(u_char *s1, char *s2, size_t n); + +u_char *ngx_strstrn(u_char *s1, char *s2, size_t n); +u_char *ngx_strcasestrn(u_char *s1, char *s2, size_t n); + ngx_int_t ngx_rstrncmp(u_char *s1, u_char *s2, size_t n); ngx_int_t ngx_rstrncasecmp(u_char *s1, u_char *s2, size_t n); ngx_int_t ngx_memn2cmp(u_char *s1, u_char *s2, size_t n1, size_t n2); @@ -162,6 +167,8 @@ u_char *ngx_utf_cpystrn(u_char *dst, u_c uintptr_t ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type); void ngx_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type); +uintptr_t ngx_escape_html(u_char *dst, u_char *src, size_t size); + void ngx_sort(void *base, size_t n, size_t size, diff --git a/src/event/modules/ngx_aio_module.c b/src/event/modules/ngx_aio_module.c --- a/src/event/modules/ngx_aio_module.c +++ b/src/event/modules/ngx_aio_module.c @@ -16,9 +16,11 @@ static ngx_int_t ngx_aio_init(ngx_cycle_t *cycle, ngx_msec_t timer); static void ngx_aio_done(ngx_cycle_t *cycle); -static ngx_int_t ngx_aio_add_event(ngx_event_t *ev, int event, u_int flags); -static ngx_int_t ngx_aio_del_event(ngx_event_t *ev, int event, u_int flags); -static ngx_int_t ngx_aio_del_connection(ngx_connection_t *c, u_int flags); +static ngx_int_t ngx_aio_add_event(ngx_event_t *ev, ngx_int_t event, + ngx_uint_t flags); +static ngx_int_t ngx_aio_del_event(ngx_event_t *ev, ngx_int_t event, + ngx_uint_t flags); +static ngx_int_t ngx_aio_del_connection(ngx_connection_t *c, ngx_uint_t flags); static ngx_int_t ngx_aio_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags); @@ -100,21 +102,21 @@ ngx_aio_done(ngx_cycle_t *cycle) /* the event adding and deleting are needed for the listening sockets */ static ngx_int_t -ngx_aio_add_event(ngx_event_t *ev, int event, u_int flags) +ngx_aio_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) { return ngx_kqueue_module_ctx.actions.add(ev, event, flags); } static ngx_int_t -ngx_aio_del_event(ngx_event_t *ev, int event, u_int flags) +ngx_aio_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) { return ngx_kqueue_module_ctx.actions.del(ev, event, flags); } static ngx_int_t -ngx_aio_del_connection(ngx_connection_t *c, u_int flags) +ngx_aio_del_connection(ngx_connection_t *c, ngx_uint_t flags) { int rc; diff --git a/src/event/modules/ngx_devpoll_module.c b/src/event/modules/ngx_devpoll_module.c --- a/src/event/modules/ngx_devpoll_module.c +++ b/src/event/modules/ngx_devpoll_module.c @@ -27,16 +27,19 @@ struct dvpoll { typedef struct { - u_int changes; - u_int events; + ngx_uint_t changes; + ngx_uint_t events; } ngx_devpoll_conf_t; static ngx_int_t ngx_devpoll_init(ngx_cycle_t *cycle, ngx_msec_t timer); static void ngx_devpoll_done(ngx_cycle_t *cycle); -static ngx_int_t ngx_devpoll_add_event(ngx_event_t *ev, int event, u_int flags); -static ngx_int_t ngx_devpoll_del_event(ngx_event_t *ev, int event, u_int flags); -static ngx_int_t ngx_devpoll_set_event(ngx_event_t *ev, int event, u_int flags); +static ngx_int_t ngx_devpoll_add_event(ngx_event_t *ev, ngx_int_t event, + ngx_uint_t flags); +static ngx_int_t ngx_devpoll_del_event(ngx_event_t *ev, ngx_int_t event, + ngx_uint_t flags); +static ngx_int_t ngx_devpoll_set_event(ngx_event_t *ev, ngx_int_t event, + ngx_uint_t flags); static ngx_int_t ngx_devpoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags); @@ -45,7 +48,7 @@ static char *ngx_devpoll_init_conf(ngx_c static int dp = -1; static struct pollfd *change_list, *event_list; -static u_int nchanges, max_changes, nevents; +static ngx_uint_t nchanges, max_changes, nevents; static ngx_event_t **change_index; @@ -209,7 +212,7 @@ ngx_devpoll_done(ngx_cycle_t *cycle) static ngx_int_t -ngx_devpoll_add_event(ngx_event_t *ev, int event, u_int flags) +ngx_devpoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) { #if (NGX_DEBUG) ngx_connection_t *c; @@ -222,7 +225,7 @@ ngx_devpoll_add_event(ngx_event_t *ev, i #if (NGX_DEBUG) c = ev->data; ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, - "devpoll add event: fd:%d ev:%04Xd", c->fd, event); + "devpoll add event: fd:%d ev:%04Xi", c->fd, event); #endif ev->active = 1; @@ -232,7 +235,7 @@ ngx_devpoll_add_event(ngx_event_t *ev, i static ngx_int_t -ngx_devpoll_del_event(ngx_event_t *ev, int event, u_int flags) +ngx_devpoll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) { ngx_event_t *e; ngx_connection_t *c; @@ -244,7 +247,7 @@ ngx_devpoll_del_event(ngx_event_t *ev, i #endif ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, - "devpoll del event: fd:%d ev:%04Xd", c->fd, event); + "devpoll del event: fd:%d ev:%04Xi", c->fd, event); if (ngx_devpoll_set_event(ev, POLLREMOVE, flags) == NGX_ERROR) { return NGX_ERROR; @@ -282,7 +285,7 @@ ngx_devpoll_del_event(ngx_event_t *ev, i static ngx_int_t -ngx_devpoll_set_event(ngx_event_t *ev, int event, u_int flags) +ngx_devpoll_set_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) { size_t n; ngx_connection_t *c; @@ -290,7 +293,7 @@ ngx_devpoll_set_event(ngx_event_t *ev, i c = ev->data; ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0, - "devpoll fd:%d ev:%04Xd fl:%04Xd", c->fd, event, flags); + "devpoll fd:%d ev:%04Xi fl:%04Xi", c->fd, event, flags); if (nchanges >= max_changes) { ngx_log_error(NGX_LOG_WARN, ev->log, 0, @@ -307,7 +310,7 @@ ngx_devpoll_set_event(ngx_event_t *ev, i } change_list[nchanges].fd = c->fd; - change_list[nchanges].events = event; + change_list[nchanges].events = (short) event; change_list[nchanges].revents = 0; change_index[nchanges] = ev; @@ -362,7 +365,7 @@ ngx_devpoll_process_events(ngx_cycle_t * } dvp.dp_fds = event_list; - dvp.dp_nfds = nevents; + dvp.dp_nfds = (int) nevents; dvp.dp_timeout = timer; events = ioctl(dp, DP_POLL, &dvp); diff --git a/src/event/modules/ngx_epoll_module.c b/src/event/modules/ngx_epoll_module.c --- a/src/event/modules/ngx_epoll_module.c +++ b/src/event/modules/ngx_epoll_module.c @@ -66,16 +66,19 @@ int epoll_wait(int epfd, struct epoll_ev typedef struct { - u_int events; + ngx_uint_t events; } ngx_epoll_conf_t; static ngx_int_t ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer); static void ngx_epoll_done(ngx_cycle_t *cycle); -static ngx_int_t ngx_epoll_add_event(ngx_event_t *ev, int event, u_int flags); -static ngx_int_t ngx_epoll_del_event(ngx_event_t *ev, int event, u_int flags); +static ngx_int_t ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, + ngx_uint_t flags); +static ngx_int_t ngx_epoll_del_event(ngx_event_t *ev, ngx_int_t event, + ngx_uint_t flags); static ngx_int_t ngx_epoll_add_connection(ngx_connection_t *c); -static ngx_int_t ngx_epoll_del_connection(ngx_connection_t *c, u_int flags); +static ngx_int_t ngx_epoll_del_connection(ngx_connection_t *c, + ngx_uint_t flags); static ngx_int_t ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags); @@ -84,7 +87,7 @@ static char *ngx_epoll_init_conf(ngx_cyc static int ep = -1; static struct epoll_event *event_list; -static u_int nevents; +static ngx_uint_t nevents; static ngx_str_t epoll_name = ngx_string("epoll"); @@ -205,7 +208,7 @@ ngx_epoll_done(ngx_cycle_t *cycle) static ngx_int_t -ngx_epoll_add_event(ngx_event_t *ev, int event, u_int flags) +ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) { int op; uint32_t events, prev; @@ -240,7 +243,7 @@ ngx_epoll_add_event(ngx_event_t *ev, int op = EPOLL_CTL_ADD; } - ee.events = events | flags; + ee.events = events | (uint32_t) flags; ee.data.ptr = (void *) ((uintptr_t) c | ev->instance); ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0, @@ -263,7 +266,7 @@ ngx_epoll_add_event(ngx_event_t *ev, int static ngx_int_t -ngx_epoll_del_event(ngx_event_t *ev, int event, u_int flags) +ngx_epoll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) { int op; uint32_t prev; @@ -295,7 +298,7 @@ ngx_epoll_del_event(ngx_event_t *ev, int if (e->active) { op = EPOLL_CTL_MOD; - ee.events = prev | flags; + ee.events = prev | (uint32_t) flags; ee.data.ptr = (void *) ((uintptr_t) c | ev->instance); } else { @@ -345,10 +348,10 @@ ngx_epoll_add_connection(ngx_connection_ static ngx_int_t -ngx_epoll_del_connection(ngx_connection_t *c, u_int flags) +ngx_epoll_del_connection(ngx_connection_t *c, ngx_uint_t flags) { - int op; - struct epoll_event ee; + int op; + struct epoll_event ee; /* * when the file descriptor is closed the epoll automatically deletes @@ -399,7 +402,7 @@ ngx_epoll_process_events(ngx_cycle_t *cy ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "epoll timer: %M", timer); - events = epoll_wait(ep, event_list, nevents, timer); + events = epoll_wait(ep, event_list, (int) nevents, timer); if (events == -1) { err = ngx_errno; diff --git a/src/event/modules/ngx_eventport_module.c b/src/event/modules/ngx_eventport_module.c --- a/src/event/modules/ngx_eventport_module.c +++ b/src/event/modules/ngx_eventport_module.c @@ -40,9 +40,9 @@ typedef struct port_notify { void *portnfy_user; /* user defined */ } port_notify_t; -typedef struct itimerspec { /* definition per POSIX.4 */ - struct timespec it_interval; /* timer period */ - struct timespec it_value; /* timer expiration */ +typedef struct itimerspec { /* definition per POSIX.4 */ + struct timespec it_interval;/* timer period */ + struct timespec it_value; /* timer expiration */ } itimerspec_t; int port_create(void) @@ -87,16 +87,16 @@ int timer_delete(timer_t timerid) typedef struct { - u_int events; + ngx_uint_t events; } ngx_eventport_conf_t; static ngx_int_t ngx_eventport_init(ngx_cycle_t *cycle, ngx_msec_t timer); static void ngx_eventport_done(ngx_cycle_t *cycle); -static ngx_int_t ngx_eventport_add_event(ngx_event_t *ev, int event, - u_int flags); -static ngx_int_t ngx_eventport_del_event(ngx_event_t *ev, int event, - u_int flags); +static ngx_int_t ngx_eventport_add_event(ngx_event_t *ev, ngx_int_t event, + ngx_uint_t flags); +static ngx_int_t ngx_eventport_del_event(ngx_event_t *ev, ngx_int_t event, + ngx_uint_t flags); static ngx_int_t ngx_eventport_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags); @@ -105,7 +105,7 @@ static char *ngx_eventport_init_conf(ngx static int ep = -1; static port_event_t *event_list; -static u_int nevents; +static ngx_uint_t nevents; static timer_t event_timer = -1; static ngx_str_t eventport_name = ngx_string("eventport"); @@ -261,9 +261,9 @@ ngx_eventport_done(ngx_cycle_t *cycle) static ngx_int_t -ngx_eventport_add_event(ngx_event_t *ev, int event, u_int flags) +ngx_eventport_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) { - int events, prev; + ngx_int_t events, prev; ngx_event_t *e; ngx_connection_t *c; @@ -291,7 +291,7 @@ ngx_eventport_add_event(ngx_event_t *ev, } ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, - "eventport add event: fd:%d ev:%04Xd", c->fd, events); + "eventport add event: fd:%d ev:%04Xi", c->fd, events); if (port_associate(ep, PORT_SOURCE_FD, c->fd, events, (void *) ((uintptr_t) ev | ev->instance)) @@ -310,7 +310,7 @@ ngx_eventport_add_event(ngx_event_t *ev, static ngx_int_t -ngx_eventport_del_event(ngx_event_t *ev, int event, u_int flags) +ngx_eventport_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) { ngx_event_t *e; ngx_connection_t *c; @@ -340,7 +340,7 @@ ngx_eventport_del_event(ngx_event_t *ev, if (e->oneshot) { ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, - "eventport change event: fd:%d ev:%04Xd", c->fd, event); + "eventport change event: fd:%d ev:%04Xi", c->fd, event); if (port_associate(ep, PORT_SOURCE_FD, c->fd, event, (void *) ((uintptr_t) ev | ev->instance)) @@ -396,7 +396,7 @@ ngx_eventport_process_events(ngx_cycle_t events = 1; - n = port_getn(ep, event_list, nevents, &events, tp); + n = port_getn(ep, event_list, (u_int) nevents, &events, tp); err = ngx_errno; diff --git a/src/event/modules/ngx_kqueue_module.c b/src/event/modules/ngx_kqueue_module.c --- a/src/event/modules/ngx_kqueue_module.c +++ b/src/event/modules/ngx_kqueue_module.c @@ -11,16 +11,19 @@ typedef struct { - int changes; - int events; + ngx_uint_t changes; + ngx_uint_t events; } ngx_kqueue_conf_t; static ngx_int_t ngx_kqueue_init(ngx_cycle_t *cycle, ngx_msec_t timer); static void ngx_kqueue_done(ngx_cycle_t *cycle); -static ngx_int_t ngx_kqueue_add_event(ngx_event_t *ev, int event, u_int flags); -static ngx_int_t ngx_kqueue_del_event(ngx_event_t *ev, int event, u_int flags); -static ngx_int_t ngx_kqueue_set_event(ngx_event_t *ev, int filter, u_int flags); +static ngx_int_t ngx_kqueue_add_event(ngx_event_t *ev, ngx_int_t event, + ngx_uint_t flags); +static ngx_int_t ngx_kqueue_del_event(ngx_event_t *ev, ngx_int_t event, + ngx_uint_t flags); +static ngx_int_t ngx_kqueue_set_event(ngx_event_t *ev, ngx_int_t filter, + ngx_uint_t flags); static ngx_int_t ngx_kqueue_process_changes(ngx_cycle_t *cycle, ngx_uint_t try); static ngx_int_t ngx_kqueue_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags); @@ -43,7 +46,7 @@ int ngx_kqueue = -1; static struct kevent *change_list, *change_list0, *change_list1; static struct kevent *event_list; -static int max_changes, nchanges, nevents; +static ngx_uint_t max_changes, nchanges, nevents; #if (NGX_THREADS) static ngx_mutex_t *list_mutex; @@ -151,7 +154,9 @@ ngx_kqueue_init(ngx_cycle_t *cycle, ngx_ ts.tv_sec = 0; ts.tv_nsec = 0; - if (kevent(ngx_kqueue, change_list, nchanges, NULL, 0, &ts) == -1) { + if (kevent(ngx_kqueue, change_list, (int) nchanges, NULL, 0, &ts) + == -1) + { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "kevent() failed"); return NGX_ERROR; @@ -273,7 +278,7 @@ ngx_kqueue_done(ngx_cycle_t *cycle) static ngx_int_t -ngx_kqueue_add_event(ngx_event_t *ev, int event, u_int flags) +ngx_kqueue_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) { ngx_int_t rc; #if 0 @@ -289,7 +294,7 @@ ngx_kqueue_add_event(ngx_event_t *ev, in #if 0 - if (ev->index < (u_int) nchanges + if (ev->index < nchanges && ((uintptr_t) change_list[ev->index].udata & (uintptr_t) ~1) == (uintptr_t) ev) { @@ -301,10 +306,10 @@ ngx_kqueue_add_event(ngx_event_t *ev, in */ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, - "kevent activated: %d: ft:%d", + "kevent activated: %d: ft:%i", ngx_event_ident(ev->data), event); - if (ev->index < (u_int) --nchanges) { + if (ev->index < --nchanges) { e = (ngx_event_t *) ((uintptr_t) change_list[nchanges].udata & (uintptr_t) ~1); change_list[ev->index] = change_list[nchanges]; @@ -337,7 +342,7 @@ ngx_kqueue_add_event(ngx_event_t *ev, in static ngx_int_t -ngx_kqueue_del_event(ngx_event_t *ev, int event, u_int flags) +ngx_kqueue_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) { ngx_int_t rc; ngx_event_t *e; @@ -347,19 +352,19 @@ ngx_kqueue_del_event(ngx_event_t *ev, in ngx_mutex_lock(list_mutex); - if (ev->index < (u_int) nchanges + if (ev->index < nchanges && ((uintptr_t) change_list[ev->index].udata & (uintptr_t) ~1) == (uintptr_t) ev) { ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, - "kevent deleted: %d: ft:%d", + "kevent deleted: %d: ft:%i", ngx_event_ident(ev->data), event); /* if the event is still not passed to a kernel we will not pass it */ nchanges--; - if (ev->index < (u_int) nchanges) { + if (ev->index < nchanges) { e = (ngx_event_t *) ((uintptr_t) change_list[nchanges].udata & (uintptr_t) ~1); change_list[ev->index] = change_list[nchanges]; @@ -396,7 +401,7 @@ ngx_kqueue_del_event(ngx_event_t *ev, in static ngx_int_t -ngx_kqueue_set_event(ngx_event_t *ev, int filter, u_int flags) +ngx_kqueue_set_event(ngx_event_t *ev, ngx_int_t filter, ngx_uint_t flags) { struct kevent *kev; struct timespec ts; @@ -405,7 +410,7 @@ ngx_kqueue_set_event(ngx_event_t *ev, in c = ev->data; ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0, - "kevent set event: %d: ft:%d fl:%04Xd", + "kevent set event: %d: ft:%i fl:%04Xi", c->fd, filter, flags); if (nchanges >= max_changes) { @@ -415,7 +420,9 @@ ngx_kqueue_set_event(ngx_event_t *ev, in ts.tv_sec = 0; ts.tv_nsec = 0; - if (kevent(ngx_kqueue, change_list, nchanges, NULL, 0, &ts) == -1) { + if (kevent(ngx_kqueue, change_list, (int) nchanges, NULL, 0, &ts) + == -1) + { ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, "kevent() failed"); return NGX_ERROR; } @@ -426,8 +433,8 @@ ngx_kqueue_set_event(ngx_event_t *ev, in kev = &change_list[nchanges]; kev->ident = c->fd; - kev->filter = filter; - kev->flags = flags; + kev->filter = (short) filter; + kev->flags = (u_short) flags; kev->udata = NGX_KQUEUE_UDATA_T ((uintptr_t) ev | ev->instance); if (filter == EVFILT_VNODE) { @@ -482,7 +489,7 @@ ngx_kqueue_process_events(ngx_cycle_t *c n = 0; } else { - n = nchanges; + n = (int) nchanges; nchanges = 0; } @@ -510,7 +517,7 @@ ngx_kqueue_process_events(ngx_cycle_t *c ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "kevent timer: %M, changes: %d", timer, n); - events = kevent(ngx_kqueue, change_list, n, event_list, nevents, tp); + events = kevent(ngx_kqueue, change_list, n, event_list, (int) nevents, tp); if (events == -1) { err = ngx_errno; @@ -696,7 +703,7 @@ ngx_kqueue_process_changes(ngx_cycle_t * change_list = change_list0; } - n = nchanges; + n = (int) nchanges; nchanges = 0; ngx_mutex_unlock(list_mutex); @@ -758,8 +765,8 @@ ngx_kqueue_init_conf(ngx_cycle_t *cycle, { ngx_kqueue_conf_t *kcf = conf; - ngx_conf_init_value(kcf->changes, 512); - ngx_conf_init_value(kcf->events, 512); + ngx_conf_init_uint_value(kcf->changes, 512); + ngx_conf_init_uint_value(kcf->events, 512); return NGX_CONF_OK; } diff --git a/src/event/modules/ngx_kqueue_module.h b/src/event/modules/ngx_kqueue_module.h --- a/src/event/modules/ngx_kqueue_module.h +++ b/src/event/modules/ngx_kqueue_module.h @@ -13,5 +13,4 @@ extern ngx_module_t ngx_kqueue_mo extern ngx_event_module_t ngx_kqueue_module_ctx; - #endif /* _NGX_KQUEUE_MODULE_H_INCLUDED_ */ diff --git a/src/event/modules/ngx_poll_module.c b/src/event/modules/ngx_poll_module.c --- a/src/event/modules/ngx_poll_module.c +++ b/src/event/modules/ngx_poll_module.c @@ -11,15 +11,17 @@ static ngx_int_t ngx_poll_init(ngx_cycle_t *cycle, ngx_msec_t timer); static void ngx_poll_done(ngx_cycle_t *cycle); -static ngx_int_t ngx_poll_add_event(ngx_event_t *ev, int event, u_int flags); -static ngx_int_t ngx_poll_del_event(ngx_event_t *ev, int event, u_int flags); +static ngx_int_t ngx_poll_add_event(ngx_event_t *ev, ngx_int_t event, + ngx_uint_t flags); +static ngx_int_t ngx_poll_del_event(ngx_event_t *ev, ngx_int_t event, + ngx_uint_t flags); static ngx_int_t ngx_poll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags); static char *ngx_poll_init_conf(ngx_cycle_t *cycle, void *conf); static struct pollfd *event_list; -static int nevents; +static ngx_int_t nevents; static ngx_str_t poll_name = ngx_string("poll"); @@ -108,7 +110,7 @@ ngx_poll_done(ngx_cycle_t *cycle) static ngx_int_t -ngx_poll_add_event(ngx_event_t *ev, int event, u_int flags) +ngx_poll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) { ngx_event_t *e; ngx_connection_t *c; @@ -119,7 +121,7 @@ ngx_poll_add_event(ngx_event_t *ev, int if (ev->index != NGX_INVALID_INDEX) { ngx_log_error(NGX_LOG_ALERT, ev->log, 0, - "poll event fd:%d ev:%d is already set", c->fd, event); + "poll event fd:%d ev:%i is already set", c->fd, event); return NGX_OK; } @@ -137,11 +139,11 @@ ngx_poll_add_event(ngx_event_t *ev, int } ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, - "poll add event: fd:%d ev:%d", c->fd, event); + "poll add event: fd:%d ev:%i", c->fd, event); if (e == NULL || e->index == NGX_INVALID_INDEX) { event_list[nevents].fd = c->fd; - event_list[nevents].events = event; + event_list[nevents].events = (short) event; event_list[nevents].revents = 0; ev->index = nevents; @@ -149,9 +151,9 @@ ngx_poll_add_event(ngx_event_t *ev, int } else { ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0, - "poll add index: %d", e->index); + "poll add index: %i", e->index); - event_list[e->index].events |= event; + event_list[e->index].events |= (short) event; ev->index = e->index; } @@ -160,7 +162,7 @@ ngx_poll_add_event(ngx_event_t *ev, int static ngx_int_t -ngx_poll_del_event(ngx_event_t *ev, int event, u_int flags) +ngx_poll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) { ngx_event_t *e; ngx_connection_t *c; @@ -171,7 +173,7 @@ ngx_poll_del_event(ngx_event_t *ev, int if (ev->index == NGX_INVALID_INDEX) { ngx_log_error(NGX_LOG_ALERT, ev->log, 0, - "poll event fd:%d ev:%d is already deleted", + "poll event fd:%d ev:%i is already deleted", c->fd, event); return NGX_OK; } @@ -190,15 +192,15 @@ ngx_poll_del_event(ngx_event_t *ev, int } ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, - "poll del event: fd:%d ev:%d", c->fd, event); + "poll del event: fd:%d ev:%i", c->fd, event); if (e == NULL || e->index == NGX_INVALID_INDEX) { nevents--; - if (ev->index < (u_int) nevents) { + if (ev->index < (ngx_uint_t) nevents) { ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, - "index: copy event %d to %d", nevents, ev->index); + "index: copy event %ui to %i", nevents, ev->index); event_list[ev->index] = event_list[nevents]; @@ -209,11 +211,11 @@ ngx_poll_del_event(ngx_event_t *ev, int "unexpected last event"); } else { - if (c->read->index == (u_int) nevents) { + if (c->read->index == (ngx_uint_t) nevents) { c->read->index = ev->index; } - if (c->write->index == (u_int) nevents) { + if (c->write->index == (ngx_uint_t) nevents) { c->write->index = ev->index; } } @@ -221,9 +223,9 @@ ngx_poll_del_event(ngx_event_t *ev, int } else { ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0, - "poll del index: %d", e->index); + "poll del index: %i", e->index); - event_list[e->index].events &= ~event; + event_list[e->index].events &= (short) ~event; } ev->index = NGX_INVALID_INDEX; diff --git a/src/event/modules/ngx_rtsig_module.c b/src/event/modules/ngx_rtsig_module.c --- a/src/event/modules/ngx_rtsig_module.c +++ b/src/event/modules/ngx_rtsig_module.c @@ -29,10 +29,10 @@ int ngx_linux_rtsig_max; typedef struct { - int signo; - ngx_int_t overflow_events; - ngx_int_t overflow_test; - ngx_int_t overflow_threshold; + ngx_uint_t signo; + ngx_uint_t overflow_events; + ngx_uint_t overflow_test; + ngx_uint_t overflow_threshold; } ngx_rtsig_conf_t; @@ -41,7 +41,8 @@ extern ngx_event_module_t ngx_poll_modu static ngx_int_t ngx_rtsig_init(ngx_cycle_t *cycle, ngx_msec_t timer); static void ngx_rtsig_done(ngx_cycle_t *cycle); static ngx_int_t ngx_rtsig_add_connection(ngx_connection_t *c); -static ngx_int_t ngx_rtsig_del_connection(ngx_connection_t *c, u_int flags); +static ngx_int_t ngx_rtsig_del_connection(ngx_connection_t *c, + ngx_uint_t flags); static ngx_int_t ngx_rtsig_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags); static ngx_int_t ngx_rtsig_process_overflow(ngx_cycle_t *cycle, @@ -143,8 +144,8 @@ ngx_rtsig_init(ngx_cycle_t *cycle, ngx_m rtscf = ngx_event_get_conf(cycle->conf_ctx, ngx_rtsig_module); sigemptyset(&set); - sigaddset(&set, rtscf->signo); - sigaddset(&set, rtscf->signo + 1); + sigaddset(&set, (int) rtscf->signo); + sigaddset(&set, (int) rtscf->signo + 1); sigaddset(&set, SIGIO); sigaddset(&set, SIGALRM); @@ -188,7 +189,7 @@ ngx_rtsig_done(ngx_cycle_t *cycle) static ngx_int_t ngx_rtsig_add_connection(ngx_connection_t *c) { - int signo; + ngx_uint_t signo; ngx_rtsig_conf_t *rtscf; if (c->read->accept && c->read->disabled) { @@ -211,7 +212,7 @@ ngx_rtsig_add_connection(ngx_connection_ signo = rtscf->signo + c->read->instance; ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, - "rtsig add connection: fd:%d signo:%d", c->fd, signo); + "rtsig add connection: fd:%d signo:%ui", c->fd, signo); if (fcntl(c->fd, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC) == -1) { ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, @@ -219,7 +220,7 @@ ngx_rtsig_add_connection(ngx_connection_ return NGX_ERROR; } - if (fcntl(c->fd, F_SETSIG, signo) == -1) { + if (fcntl(c->fd, F_SETSIG, (int) signo) == -1) { ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, "fcntl(F_SETSIG) failed"); return NGX_ERROR; @@ -247,7 +248,7 @@ ngx_rtsig_add_connection(ngx_connection_ static ngx_int_t -ngx_rtsig_del_connection(ngx_connection_t *c, u_int flags) +ngx_rtsig_del_connection(ngx_connection_t *c, ngx_uint_t flags) { ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "rtsig del connection: fd:%d", c->fd); @@ -348,7 +349,7 @@ ngx_rtsig_process_events(ngx_cycle_t *cy rtscf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_rtsig_module); - if (signo == rtscf->signo || signo == rtscf->signo + 1) { + if (signo == (int) rtscf->signo || signo == (int) rtscf->signo + 1) { if (overflow && (ngx_uint_t) si.si_fd > overflow_current) { return NGX_OK; @@ -363,7 +364,7 @@ ngx_rtsig_process_events(ngx_cycle_t *cy return NGX_OK; } - instance = signo - rtscf->signo; + instance = signo - (int) rtscf->signo; rev = c->read; @@ -459,8 +460,8 @@ ngx_rtsig_process_overflow(ngx_cycle_t * { int name[2], rtsig_max, rtsig_nr, events, ready; size_t len; - ngx_int_t tested, n, i; ngx_err_t err; + ngx_uint_t tested, n, i; ngx_event_t *rev, *wev, **queue; ngx_connection_t *c; ngx_rtsig_conf_t *rtscf; @@ -638,7 +639,7 @@ ngx_rtsig_process_overflow(ngx_cycle_t * * "/proc/sys/kernel/rtsig-max" / "rtsig_overflow_threshold" */ - if (rtsig_max / rtscf->overflow_threshold < rtsig_nr) { + if (rtsig_max / (int) rtscf->overflow_threshold < rtsig_nr) { ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "rtsig queue state: %d/%d", rtsig_nr, rtsig_max); @@ -703,19 +704,18 @@ ngx_rtsig_init_conf(ngx_cycle_t *cycle, ngx_rtsig_conf_t *rtscf = conf; /* LinuxThreads use the first 3 RT signals */ - ngx_conf_init_value(rtscf->signo, SIGRTMIN + 10); + ngx_conf_init_uint_value(rtscf->signo, SIGRTMIN + 10); - ngx_conf_init_value(rtscf->overflow_events, 16); - ngx_conf_init_value(rtscf->overflow_test, 32); - ngx_conf_init_value(rtscf->overflow_threshold, 10); + ngx_conf_init_uint_value(rtscf->overflow_events, 16); + ngx_conf_init_uint_value(rtscf->overflow_test, 32); + ngx_conf_init_uint_value(rtscf->overflow_threshold, 10); return NGX_CONF_OK; } static char * -ngx_check_ngx_overflow_threshold_bounds(ngx_conf_t *cf, - void *post, void *data) +ngx_check_ngx_overflow_threshold_bounds(ngx_conf_t *cf, void *post, void *data) { if (ngx_linux_rtsig_max) { return ngx_conf_check_num_bounds(cf, post, data); diff --git a/src/event/modules/ngx_select_module.c b/src/event/modules/ngx_select_module.c --- a/src/event/modules/ngx_select_module.c +++ b/src/event/modules/ngx_select_module.c @@ -12,8 +12,10 @@ static ngx_int_t ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer); static void ngx_select_done(ngx_cycle_t *cycle); -static ngx_int_t ngx_select_add_event(ngx_event_t *ev, int event, u_int flags); -static ngx_int_t ngx_select_del_event(ngx_event_t *ev, int event, u_int flags); +static ngx_int_t ngx_select_add_event(ngx_event_t *ev, ngx_int_t event, + ngx_uint_t flags); +static ngx_int_t ngx_select_del_event(ngx_event_t *ev, ngx_int_t event, + ngx_uint_t flags); static ngx_int_t ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags); static char *ngx_select_init_conf(ngx_cycle_t *cycle, void *conf); @@ -25,10 +27,10 @@ static fd_set work_read_fd_set; static fd_set work_write_fd_set; #if (NGX_WIN32) -static int max_read; -static int max_write; +static ngx_uint_t max_read; +static ngx_uint_t max_write; #else -static int max_fd; +static ngx_int_t max_fd; #endif static ngx_uint_t nevents; @@ -129,18 +131,18 @@ ngx_select_done(ngx_cycle_t *cycle) static ngx_int_t -ngx_select_add_event(ngx_event_t *ev, int event, u_int flags) +ngx_select_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) { ngx_connection_t *c; c = ev->data; ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, - "select add event fd:%d ev:%d", c->fd, event); + "select add event fd:%d ev:%i", c->fd, event); if (ev->index != NGX_INVALID_INDEX) { ngx_log_error(NGX_LOG_ALERT, ev->log, 0, - "select event fd:%d ev:%d is already set", c->fd, event); + "select event fd:%d ev:%i is already set", c->fd, event); return NGX_OK; } @@ -190,7 +192,7 @@ ngx_select_add_event(ngx_event_t *ev, in static ngx_int_t -ngx_select_del_event(ngx_event_t *ev, int event, u_int flags) +ngx_select_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) { ngx_connection_t *c; @@ -203,7 +205,7 @@ ngx_select_del_event(ngx_event_t *ev, in } ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, - "select del event fd:%d ev:%d", c->fd, event); + "select del event fd:%d ev:%i", c->fd, event); #if (NGX_WIN32) @@ -231,7 +233,7 @@ ngx_select_del_event(ngx_event_t *ev, in #endif - if (ev->index < (u_int) --nevents) { + if (ev->index < --nevents) { event_index[ev->index] = event_index[nevents]; event_index[ev->index]->index = ev->index; } diff --git a/src/event/ngx_event.c b/src/event/ngx_event.c --- a/src/event/ngx_event.c +++ b/src/event/ngx_event.c @@ -276,7 +276,7 @@ ngx_process_events_and_timers(ngx_cycle_ ngx_int_t -ngx_handle_read_event(ngx_event_t *rev, u_int flags) +ngx_handle_read_event(ngx_event_t *rev, ngx_uint_t flags) { if (ngx_event_flags & NGX_USE_CLEAR_EVENT) { diff --git a/src/event/ngx_event.h b/src/event/ngx_event.h --- a/src/event/ngx_event.h +++ b/src/event/ngx_event.h @@ -126,7 +126,7 @@ struct ngx_event_s { #endif - u_int index; + ngx_uint_t index; ngx_log_t *log; @@ -182,7 +182,7 @@ struct ngx_event_s { /* event should not cross cache line in SMP */ - int padding[NGX_EVENT_T_PADDING]; + uint32_t padding[NGX_EVENT_T_PADDING]; #endif #endif }; @@ -195,14 +195,14 @@ typedef struct { typedef struct { - ngx_int_t (*add)(ngx_event_t *ev, int event, u_int flags); - ngx_int_t (*del)(ngx_event_t *ev, int event, u_int flags); + ngx_int_t (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); + ngx_int_t (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); - ngx_int_t (*enable)(ngx_event_t *ev, int event, u_int flags); - ngx_int_t (*disable)(ngx_event_t *ev, int event, u_int flags); + ngx_int_t (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); + ngx_int_t (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); ngx_int_t (*add_conn)(ngx_connection_t *c); - ngx_int_t (*del_conn)(ngx_connection_t *c, u_int flags); + ngx_int_t (*del_conn)(ngx_connection_t *c, ngx_uint_t flags); ngx_int_t (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t nowait); ngx_int_t (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer, @@ -497,7 +497,7 @@ u_char *ngx_accept_log_error(ngx_log_t * void ngx_process_events_and_timers(ngx_cycle_t *cycle); -ngx_int_t ngx_handle_read_event(ngx_event_t *rev, u_int flags); +ngx_int_t ngx_handle_read_event(ngx_event_t *rev, ngx_uint_t flags); ngx_int_t ngx_handle_write_event(ngx_event_t *wev, size_t lowat); diff --git a/src/event/ngx_event_busy_lock.c b/src/event/ngx_event_busy_lock.c --- a/src/event/ngx_event_busy_lock.c +++ b/src/event/ngx_event_busy_lock.c @@ -9,7 +9,7 @@ #include -static int ngx_event_busy_lock_look_cachable(ngx_event_busy_lock_t *bl, +static ngx_int_t ngx_event_busy_lock_look_cachable(ngx_event_busy_lock_t *bl, ngx_event_busy_lock_ctx_t *ctx); static void ngx_event_busy_lock_handler(ngx_event_t *ev); static void ngx_event_busy_lock_posted_handler(ngx_event_t *ev); diff --git a/src/event/ngx_event_connect.c b/src/event/ngx_event_connect.c --- a/src/event/ngx_event_connect.c +++ b/src/event/ngx_event_connect.c @@ -14,7 +14,7 @@ ngx_int_t ngx_event_connect_peer(ngx_peer_connection_t *pc) { int rc; - u_int event; + ngx_int_t event; ngx_err_t err; ngx_uint_t level; ngx_socket_t s; diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -784,7 +784,7 @@ ngx_ssl_send_chain(ngx_connection_t *c, /* the maximum limit size is the maximum uint32_t value - the page size */ - if (limit == 0 || limit > NGX_MAX_UINT32_VALUE - ngx_pagesize) { + if (limit == 0 || limit > (off_t) (NGX_MAX_UINT32_VALUE - ngx_pagesize)) { limit = NGX_MAX_UINT32_VALUE - ngx_pagesize; } diff --git a/src/event/ngx_event_pipe.c b/src/event/ngx_event_pipe.c --- a/src/event/ngx_event_pipe.c +++ b/src/event/ngx_event_pipe.c @@ -21,7 +21,7 @@ static ngx_int_t ngx_event_pipe_drain_ch ngx_int_t -ngx_event_pipe(ngx_event_pipe_t *p, int do_write) +ngx_event_pipe(ngx_event_pipe_t *p, ngx_int_t do_write) { u_int flags; ngx_event_t *rev, *wev; diff --git a/src/event/ngx_event_pipe.h b/src/event/ngx_event_pipe.h --- a/src/event/ngx_event_pipe.h +++ b/src/event/ngx_event_pipe.h @@ -86,7 +86,7 @@ struct ngx_event_pipe_s { }; -ngx_int_t ngx_event_pipe(ngx_event_pipe_t *p, int do_write); +ngx_int_t ngx_event_pipe(ngx_event_pipe_t *p, ngx_int_t do_write); ngx_int_t ngx_event_pipe_copy_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf); ngx_int_t ngx_event_pipe_add_free_buf(ngx_event_pipe_t *p, ngx_buf_t *b); diff --git a/src/http/modules/ngx_http_empty_gif_module.c b/src/http/modules/ngx_http_empty_gif_module.c --- a/src/http/modules/ngx_http_empty_gif_module.c +++ b/src/http/modules/ngx_http_empty_gif_module.c @@ -127,6 +127,8 @@ ngx_http_empty_gif_handler(ngx_http_requ if (r->method == NGX_HTTP_HEAD) { r->headers_out.status = NGX_HTTP_OK; + r->headers_out.content_length_n = sizeof(ngx_empty_gif); + r->headers_out.last_modified_time = 23349600; return ngx_http_send_header(r); } diff --git a/src/http/modules/ngx_http_flv_module.c b/src/http/modules/ngx_http_flv_module.c --- a/src/http/modules/ngx_http_flv_module.c +++ b/src/http/modules/ngx_http_flv_module.c @@ -167,7 +167,7 @@ ngx_http_flv_handler(ngx_http_request_t i = 1; if (r->args.len) { - p = (u_char *) ngx_strstr(r->args.data, "start="); + p = (u_char *) ngx_strnstr(r->args.data, "start=", r->args.len); if (p) { p += 6; diff --git a/src/http/modules/ngx_http_gzip_filter_module.c b/src/http/modules/ngx_http_gzip_filter_module.c --- a/src/http/modules/ngx_http_gzip_filter_module.c +++ b/src/http/modules/ngx_http_gzip_filter_module.c @@ -279,7 +279,9 @@ ngx_http_gzip_header_filter(ngx_http_req || r->headers_in.accept_encoding == NULL || (r->headers_out.content_length_n != -1 && r->headers_out.content_length_n < conf->min_length) - || ngx_strstr(r->headers_in.accept_encoding->value.data, "gzip") == NULL + || ngx_strcasestrn(r->headers_in.accept_encoding->value.data, + "gzip", 4 - 1) + == NULL ) { return ngx_http_next_header_filter(r); diff --git a/src/http/modules/ngx_http_map_module.c b/src/http/modules/ngx_http_map_module.c --- a/src/http/modules/ngx_http_map_module.c +++ b/src/http/modules/ngx_http_map_module.c @@ -26,8 +26,7 @@ typedef struct { typedef struct { - ngx_hash_t hash; - ngx_hash_wildcard_t *dns_wildcards; + ngx_hash_combined_t hash; ngx_int_t index; ngx_http_variable_value_t *default_value; ngx_uint_t hostnames; /* unsigned hostnames:1 */ @@ -142,28 +141,13 @@ ngx_http_map_variable(ngx_http_request_t key = ngx_hash(key, name[i]); } - value = NULL; - - if (map->hash.buckets) { - value = ngx_hash_find(&map->hash, key, name, len); - } + value = ngx_hash_find_combined(&map->hash, key, name, len); if (value) { *v = *value; } else { - if (map->dns_wildcards && map->dns_wildcards->hash.buckets) { - value = ngx_hash_find_wildcard(map->dns_wildcards, name, len); - if (value) { - *v = *value; - - } else { - *v = *map->default_value; - } - - } else { - *v = *map->default_value; - } + *v = *map->default_value; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -282,6 +266,9 @@ ngx_http_map_block(ngx_conf_t *cf, ngx_c return rv; } + map->default_value = ctx.default_value ? ctx.default_value: + &ngx_http_variable_null_value; + hash.key = ngx_hash_key_lc; hash.max_size = mcf->hash_max_size; hash.bucket_size = mcf->hash_bucket_size; @@ -289,7 +276,7 @@ ngx_http_map_block(ngx_conf_t *cf, ngx_c hash.pool = cf->pool; if (ctx.keys.keys.nelts) { - hash.hash = &map->hash; + hash.hash = &map->hash.hash; hash.temp_pool = NULL; if (ngx_hash_init(&hash, ctx.keys.keys.elts, ctx.keys.keys.nelts) @@ -300,27 +287,44 @@ ngx_http_map_block(ngx_conf_t *cf, ngx_c } } - map->default_value = ctx.default_value ? ctx.default_value: - &ngx_http_variable_null_value; + if (ctx.keys.dns_wc_head.nelts) { - if (ctx.keys.dns_wildcards.nelts) { - - ngx_qsort(ctx.keys.dns_wildcards.elts, - (size_t) ctx.keys.dns_wildcards.nelts, + ngx_qsort(ctx.keys.dns_wc_head.elts, + (size_t) ctx.keys.dns_wc_head.nelts, sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards); hash.hash = NULL; hash.temp_pool = pool; - if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wildcards.elts, - ctx.keys.dns_wildcards.nelts) + 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->dns_wildcards = (ngx_hash_wildcard_t *) hash.hash; + 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_http_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->hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash; } ngx_destroy_pool(pool); @@ -344,10 +348,9 @@ ngx_http_map_cmp_dns_wildcards(const voi static char * ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) { - u_char ch; ngx_int_t rc; ngx_str_t *value, file; - ngx_uint_t i, key, flags; + ngx_uint_t i, key; ngx_http_map_conf_ctx_t *ctx; ngx_http_variable_value_t *var, **vp; @@ -439,50 +442,36 @@ ngx_http_map(ngx_conf_t *cf, ngx_command found: - ch = value[0].data[0]; - - if ((ch != '*' && ch != '.') || ctx->hostnames == 0) { - - 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; + if (ngx_strcmp(value[0].data, "default") == 0) { - return NGX_CONF_OK; - } - - if (value[0].len && ch == '!') { - value[0].len--; - value[0].data++; - } - - flags = 0; - - } else { - - if ((ch == '*' && (value[0].len < 3 || value[0].data[1] != '.')) - || (ch == '.' && value[0].len < 2)) - { + if (ctx->default_value) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid DNS wildcard \"%V\"", &value[0]); - + "duplicate default map parameter"); return NGX_CONF_ERROR; } - flags = NGX_HASH_WILDCARD_KEY; + ctx->default_value = var; + + return NGX_CONF_OK; } - rc = ngx_hash_add_key(&ctx->keys, &value[0], var, flags); + if (value[0].len && value[0].data[0] == '!') { + value[0].len--; + value[0].data++; + } + + rc = ngx_hash_add_key(&ctx->keys, &value[0], var, + (ctx->hostnames) ? NGX_HASH_WILDCARD_KEY : 0); if (rc == NGX_OK) { return NGX_CONF_OK; } + if (rc == NGX_DECLINED) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid hostname or wildcard \"%V\"", &value[0]); + } + if (rc == NGX_BUSY) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "conflicting parameter \"%V\"", &value[0]); diff --git a/src/http/modules/ngx_http_memcached_module.c b/src/http/modules/ngx_http_memcached_module.c --- a/src/http/modules/ngx_http_memcached_module.c +++ b/src/http/modules/ngx_http_memcached_module.c @@ -425,16 +425,16 @@ ngx_http_memcached_filter(void *data, ss if (u->length == ctx->rest) { if (ngx_strncmp(b->last, - ngx_http_memcached_end + NGX_HTTP_MEMCACHED_END - - ctx->rest, - bytes) != 0) + ngx_http_memcached_end + NGX_HTTP_MEMCACHED_END - ctx->rest, + ctx->rest) + != 0) { ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0, "memcached sent invalid trailer"); } - u->length -= bytes; - ctx->rest -= bytes; + u->length = 0; + ctx->rest = 0; return NGX_OK; } @@ -453,7 +453,8 @@ ngx_http_memcached_filter(void *data, ss *ll = cl; - cl->buf->pos = b->last; + last = b->last; + cl->buf->pos = last; b->last += bytes; cl->buf->last = b->last; @@ -461,20 +462,19 @@ ngx_http_memcached_filter(void *data, ss "memcached filter bytes:%z size:%z length:%z rest:%z", bytes, b->last - b->pos, u->length, ctx->rest); - if (b->last - b->pos <= (ssize_t) (u->length - NGX_HTTP_MEMCACHED_END)) { + if (bytes <= (ssize_t) (u->length - NGX_HTTP_MEMCACHED_END)) { u->length -= bytes; return NGX_OK; } - - last = b->pos + u->length - NGX_HTTP_MEMCACHED_END; + last += u->length - NGX_HTTP_MEMCACHED_END; if (ngx_strncmp(last, ngx_http_memcached_end, b->last - last) != 0) { ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0, "memcached sent invalid trailer"); } - ctx->rest = u->length - (b->last - b->pos); + ctx->rest -= b->last - last; b->last = last; cl->buf->last = last; u->length = ctx->rest; diff --git a/src/http/modules/ngx_http_referer_module.c b/src/http/modules/ngx_http_referer_module.c --- a/src/http/modules/ngx_http_referer_module.c +++ b/src/http/modules/ngx_http_referer_module.c @@ -11,9 +11,26 @@ #define NGX_HTTP_REFERER_NO_URI_PART ((void *) 4) +#if (NGX_PCRE) + typedef struct { - ngx_hash_t hash; - ngx_hash_wildcard_t *dns_wildcards; + ngx_regex_t *regex; + ngx_str_t name; +} ngx_http_referer_regex_t; + +#else + +#define ngx_regex_t void + +#endif + + +typedef struct { + ngx_hash_combined_t hash; + +#if (NGX_PCRE) + ngx_array_t *regex; +#endif ngx_flag_t no_referer; ngx_flag_t blocked_referer; @@ -29,6 +46,8 @@ static char *ngx_http_valid_referers(ngx void *conf); static char *ngx_http_add_referer(ngx_conf_t *cf, ngx_hash_keys_arrays_t *keys, ngx_str_t *value, ngx_str_t *uri); +static char *ngx_http_add_regex_referer(ngx_conf_t *cf, + ngx_http_referer_conf_t *rlcf, ngx_str_t *name, ngx_regex_t *regex); static int ngx_libc_cdecl ngx_http_cmp_referer_wildcards(const void *one, const void *two); @@ -81,16 +100,28 @@ static ngx_int_t ngx_http_referer_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - u_char *p, *ref, *last; - size_t len; - ngx_str_t *uri; - ngx_uint_t i, key; - ngx_http_referer_conf_t *rlcf; - u_char buf[256]; + u_char *p, *ref, *last; + size_t len; + ngx_str_t *uri; + ngx_uint_t i, key; + ngx_http_referer_conf_t *rlcf; + u_char buf[256]; +#if (NGX_PCRE) + ngx_int_t n; + ngx_str_t referer; + ngx_http_referer_regex_t *regex; +#endif rlcf = ngx_http_get_module_loc_conf(r, ngx_http_referer_module); - if (rlcf->hash.buckets == NULL && rlcf->dns_wildcards == NULL) { + if (rlcf->hash.hash.buckets == NULL + && rlcf->hash.wc_head == NULL + && rlcf->hash.wc_tail == NULL +#if (NGX_PCRE) + && rlcf->regex == NULL +#endif + ) + { goto valid; } @@ -133,21 +164,43 @@ ngx_http_referer_variable(ngx_http_reque } } - len = p - ref; + uri = ngx_hash_find_combined(&rlcf->hash, key, buf, p - ref); + + if (uri) { + goto uri; + } + +#if (NGX_PCRE) + + if (rlcf->regex) { + + referer.len = len - 7; + referer.data = ref; + + regex = rlcf->regex->elts; - if (rlcf->hash.buckets) { - uri = ngx_hash_find(&rlcf->hash, key, buf, len); - if (uri) { - goto uri; + for (i = 0; i < rlcf->regex->nelts; i++) { + n = ngx_regex_exec(regex[i].regex, &referer, NULL, 0); + + if (n == NGX_REGEX_NO_MATCHED) { + continue; + } + + if (n < 0) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + ngx_regex_exec_n + " failed: %d on \"%V\" using \"%V\"", + n, &referer, ®ex[i].name); + return NGX_ERROR; + } + + /* match */ + + goto valid; } } - if (rlcf->dns_wildcards) { - uri = ngx_hash_find_wildcard(rlcf->dns_wildcards, buf, len); - if (uri) { - goto uri; - } - } +#endif invalid: @@ -208,7 +261,6 @@ ngx_http_referer_merge_conf(ngx_conf_t * if (conf->keys == NULL) { conf->hash = prev->hash; - conf->dns_wildcards = prev->dns_wildcards; ngx_conf_merge_value(conf->no_referer, prev->no_referer, 0); ngx_conf_merge_value(conf->blocked_referer, prev->blocked_referer, 0); @@ -217,7 +269,9 @@ ngx_http_referer_merge_conf(ngx_conf_t * } if ((conf->no_referer == 1 || conf->blocked_referer == 1) - && conf->keys->keys.nelts == 0 && conf->keys->dns_wildcards.nelts == 0) + && conf->keys->keys.nelts == 0 + && conf->keys->dns_wc_head.nelts == 0 + && conf->keys->dns_wc_tail.nelts == 0) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "the \"none\" or \"blocked\" referers are specified " @@ -233,7 +287,7 @@ ngx_http_referer_merge_conf(ngx_conf_t * hash.pool = cf->pool; if (conf->keys->keys.nelts) { - hash.hash = &conf->hash; + hash.hash = &conf->hash.hash; hash.temp_pool = NULL; if (ngx_hash_init(&hash, conf->keys->keys.elts, conf->keys->keys.nelts) @@ -243,24 +297,44 @@ ngx_http_referer_merge_conf(ngx_conf_t * } } - if (conf->keys->dns_wildcards.nelts) { + if (conf->keys->dns_wc_head.nelts) { - ngx_qsort(conf->keys->dns_wildcards.elts, - (size_t) conf->keys->dns_wildcards.nelts, + ngx_qsort(conf->keys->dns_wc_head.elts, + (size_t) conf->keys->dns_wc_head.nelts, sizeof(ngx_hash_key_t), ngx_http_cmp_referer_wildcards); hash.hash = NULL; hash.temp_pool = cf->temp_pool; - if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wildcards.elts, - conf->keys->dns_wildcards.nelts) + if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wc_head.elts, + conf->keys->dns_wc_head.nelts) != NGX_OK) { return NGX_CONF_ERROR; } - conf->dns_wildcards = (ngx_hash_wildcard_t *) hash.hash; + conf->hash.wc_head = (ngx_hash_wildcard_t *) hash.hash; + } + + if (conf->keys->dns_wc_tail.nelts) { + + ngx_qsort(conf->keys->dns_wc_tail.elts, + (size_t) conf->keys->dns_wc_tail.nelts, + sizeof(ngx_hash_key_t), + ngx_http_cmp_referer_wildcards); + + hash.hash = NULL; + hash.temp_pool = cf->temp_pool; + + if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wc_tail.elts, + conf->keys->dns_wc_tail.nelts) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + conf->hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash; } if (conf->no_referer == NGX_CONF_UNSET) { @@ -342,6 +416,21 @@ ngx_http_valid_referers(ngx_conf_t *cf, sn = cscf->server_names.elts; for (n = 0; n < cscf->server_names.nelts; n++) { + +#if (NGX_PCRE) + if (sn[n].regex) { + + if (ngx_http_add_regex_referer(cf, rlcf, &sn[n].name, + sn[n].regex) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + continue; + } +#endif + if (ngx_http_add_referer(cf, rlcf->keys, &sn[n].name, &uri) != NGX_OK) { @@ -352,6 +441,15 @@ ngx_http_valid_referers(ngx_conf_t *cf, continue; } + if (value[i].data[0] == '~') { + if (ngx_http_add_regex_referer(cf, rlcf, &value[i], NULL) != NGX_OK) + { + return NGX_CONF_ERROR; + } + + continue; + } + p = (u_char *) ngx_strchr(value[i].data, '/'); if (p) { @@ -373,23 +471,8 @@ static char * ngx_http_add_referer(ngx_conf_t *cf, ngx_hash_keys_arrays_t *keys, ngx_str_t *value, ngx_str_t *uri) { - u_char ch; - ngx_int_t rc; - ngx_str_t *u; - ngx_uint_t flags; - - ch = value->data[0]; - - if ((ch == '*' && (value->len < 3 || value->data[1] != '.')) - || (ch == '.' && value->len < 2)) - { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid DNS wildcard \"%V\"", value); - - return NGX_CONF_ERROR; - } - - flags = (ch == '*' || ch == '.') ? NGX_HASH_WILDCARD_KEY : 0; + ngx_int_t rc; + ngx_str_t *u; if (uri->len == 0) { u = NGX_HTTP_REFERER_NO_URI_PART; @@ -403,12 +486,17 @@ ngx_http_add_referer(ngx_conf_t *cf, ngx *u = *uri; } - rc = ngx_hash_add_key(keys, value, u, flags); + rc = ngx_hash_add_key(keys, value, u, NGX_HASH_WILDCARD_KEY); if (rc == NGX_OK) { return NGX_CONF_OK; } + if (rc == NGX_DECLINED) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid hostname or wildcard \"%V\"", value); + } + if (rc == NGX_BUSY) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "conflicting parameter \"%V\"", value); @@ -418,6 +506,64 @@ ngx_http_add_referer(ngx_conf_t *cf, ngx } +static char * +ngx_http_add_regex_referer(ngx_conf_t *cf, ngx_http_referer_conf_t *rlcf, + ngx_str_t *name, ngx_regex_t *regex) +{ +#if (NGX_PCRE) + ngx_str_t err; + ngx_http_referer_regex_t *rr; + u_char errstr[NGX_MAX_CONF_ERRSTR]; + + if (rlcf->regex == NULL) { + rlcf->regex = ngx_array_create(cf->pool, 2, + sizeof(ngx_http_referer_regex_t)); + if (rlcf->regex == NULL) { + return NGX_CONF_ERROR; + } + } + + rr = ngx_array_push(rlcf->regex); + if (rr == NULL) { + return NGX_CONF_ERROR; + } + + if (regex) { + rr->regex = regex; + rr->name = *name; + + return NGX_CONF_OK; + } + + err.len = NGX_MAX_CONF_ERRSTR; + err.data = errstr; + + name->len--; + name->data++; + + rr->regex = ngx_regex_compile(name, NGX_REGEX_CASELESS, cf->pool, &err); + + if (rr->regex == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data); + return NGX_CONF_ERROR; + } + + rr->name = *name; + + return NGX_CONF_OK; + +#else + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the using of the regex \"%V\" requires PCRE library", + name); + + return NGX_CONF_ERROR; + +#endif +} + + static int ngx_libc_cdecl ngx_http_cmp_referer_wildcards(const void *one, const void *two) { diff --git a/src/http/modules/ngx_http_ssi_filter_module.c b/src/http/modules/ngx_http_ssi_filter_module.c --- a/src/http/modules/ngx_http_ssi_filter_module.c +++ b/src/http/modules/ngx_http_ssi_filter_module.c @@ -212,6 +212,7 @@ static ngx_str_t ngx_http_ssi_null_strin #define NGX_HTTP_SSI_ECHO_VAR 0 #define NGX_HTTP_SSI_ECHO_DEFAULT 1 +#define NGX_HTTP_SSI_ECHO_ENCODING 2 #define NGX_HTTP_SSI_CONFIG_ERRMSG 0 #define NGX_HTTP_SSI_CONFIG_TIMEFMT 1 @@ -237,6 +238,7 @@ static ngx_http_ssi_param_t ngx_http_ss static ngx_http_ssi_param_t ngx_http_ssi_echo_params[] = { { ngx_string("var"), NGX_HTTP_SSI_ECHO_VAR, 1, 0 }, { ngx_string("default"), NGX_HTTP_SSI_ECHO_DEFAULT, 0, 0 }, + { ngx_string("encoding"), NGX_HTTP_SSI_ECHO_ENCODING, 0, 0 }, { ngx_null_string, 0, 0, 0 } }; @@ -355,6 +357,7 @@ found: ctx->value_len = slcf->value_len; ctx->last_out = &ctx->out; + ctx->encoding = NGX_HTTP_SSI_ENTITY_ENCODING; ctx->output = 1; ctx->params.elts = ctx->params_array; @@ -2119,10 +2122,12 @@ static ngx_int_t ngx_http_ssi_echo(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, ngx_str_t **params) { + u_char *p; + uintptr_t len; ngx_int_t key; ngx_uint_t i; ngx_buf_t *b; - ngx_str_t *var, *value, text; + ngx_str_t *var, *value, *enc, text; ngx_chain_t *cl; ngx_http_variable_value_t *vv; @@ -2170,6 +2175,69 @@ ngx_http_ssi_echo(ngx_http_request_t *r, } } + enc = params[NGX_HTTP_SSI_ECHO_ENCODING]; + + if (enc) { + if (enc->len == 4 && ngx_strncmp(enc->data, "none", 4) == 0) { + + ctx->encoding = NGX_HTTP_SSI_NO_ENCODING; + + } else if (enc->len == 3 && ngx_strncmp(enc->data, "url", 3) == 0) { + + ctx->encoding = NGX_HTTP_SSI_URL_ENCODING; + + } else if (enc->len == 6 && ngx_strncmp(enc->data, "entity", 6) == 0) { + + ctx->encoding = NGX_HTTP_SSI_ENTITY_ENCODING; + + } else { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "unknown encoding \"%V\" in the \"echo\" command", + enc); + } + } + + switch (ctx->encoding) { + + case NGX_HTTP_SSI_NO_ENCODING: + break; + + case NGX_HTTP_SSI_URL_ENCODING: + len = 2 * ngx_escape_uri(NULL, value->data, value->len, + NGX_ESCAPE_HTML); + + if (len) { + p = ngx_palloc(r->pool, value->len + len); + if (p == NULL) { + return NGX_HTTP_SSI_ERROR; + } + + (void) ngx_escape_uri(p, value->data, value->len, NGX_ESCAPE_HTML); + + value->len += len; + value->data = p; + } + + break; + + case NGX_HTTP_SSI_ENTITY_ENCODING: + len = ngx_escape_html(NULL, value->data, value->len); + + if (len) { + p = ngx_palloc(r->pool, value->len + len); + if (p == NULL) { + return NGX_HTTP_SSI_ERROR; + } + + (void) ngx_escape_html(p, value->data, value->len); + + value->len += len; + value->data = p; + } + + break; + } + b = ngx_calloc_buf(r->pool); if (b == NULL) { return NGX_HTTP_SSI_ERROR; diff --git a/src/http/modules/ngx_http_ssi_filter_module.h b/src/http/modules/ngx_http_ssi_filter_module.h --- a/src/http/modules/ngx_http_ssi_filter_module.h +++ b/src/http/modules/ngx_http_ssi_filter_module.h @@ -13,15 +13,20 @@ #include -#define NGX_HTTP_SSI_MAX_PARAMS 16 +#define NGX_HTTP_SSI_MAX_PARAMS 16 -#define NGX_HTTP_SSI_COMMAND_LEN 32 -#define NGX_HTTP_SSI_PARAM_LEN 32 -#define NGX_HTTP_SSI_PARAMS_N 4 +#define NGX_HTTP_SSI_COMMAND_LEN 32 +#define NGX_HTTP_SSI_PARAM_LEN 32 +#define NGX_HTTP_SSI_PARAMS_N 4 -#define NGX_HTTP_SSI_COND_IF 1 -#define NGX_HTTP_SSI_COND_ELSE 2 +#define NGX_HTTP_SSI_COND_IF 1 +#define NGX_HTTP_SSI_COND_ELSE 2 + + +#define NGX_HTTP_SSI_NO_ENCODING 0 +#define NGX_HTTP_SSI_URL_ENCODING 1 +#define NGX_HTTP_SSI_ENTITY_ENCODING 2 typedef struct { @@ -60,6 +65,7 @@ typedef struct { ngx_array_t *blocks; unsigned conditional:2; + unsigned encoding:2; unsigned block:1; unsigned output:1; unsigned output_chosen:1; diff --git a/src/http/modules/ngx_http_sub_filter_module.c b/src/http/modules/ngx_http_sub_filter_module.c --- a/src/http/modules/ngx_http_sub_filter_module.c +++ b/src/http/modules/ngx_http_sub_filter_module.c @@ -369,9 +369,14 @@ ngx_http_sub_body_filter(ngx_http_reques } } - b->memory = 1; - b->pos = ctx->sub.data; - b->last = ctx->sub.data + ctx->sub.len; + if (ctx->sub.len) { + b->memory = 1; + b->pos = ctx->sub.data; + b->last = ctx->sub.data + ctx->sub.len; + + } else { + b->sync = 1; + } cl->buf = b; cl->next = NULL; @@ -557,6 +562,7 @@ ngx_http_sub_parse(ngx_http_request_t *r ch = ngx_tolower(ch); } + ctx->state = state; ctx->pos = p; ctx->looked = looked; ctx->copy_end = p; @@ -578,6 +584,10 @@ ngx_http_sub_parse(ngx_http_request_t *r looked++; if (looked == ctx->match.len) { + if ((size_t) (p - ctx->pos) < looked) { + ctx->saved = 0; + } + ctx->state = sub_start_state; ctx->pos = p + 1; ctx->looked = looked; diff --git a/src/http/modules/perl/nginx.pm b/src/http/modules/perl/nginx.pm --- a/src/http/modules/perl/nginx.pm +++ b/src/http/modules/perl/nginx.pm @@ -47,7 +47,7 @@ our @EXPORT = qw( HTTP_INSUFFICIENT_STORAGE ); -our $VERSION = '0.5.32'; +our $VERSION = '0.5.33'; require XSLoader; XSLoader::load('nginx', $VERSION); diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -73,7 +73,6 @@ static char * ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *rv; - u_char ch; ngx_int_t rc, j; ngx_uint_t mi, m, s, l, p, a, i, n; ngx_uint_t find_config_index, use_rewrite, use_access; @@ -99,6 +98,9 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma ngx_http_core_loc_conf_t *clcf; ngx_http_phase_handler_pt checker; ngx_http_core_main_conf_t *cmcf; +#if (NGX_PCRE) + ngx_uint_t regex; +#endif #if (NGX_WIN32) ngx_iocp_conf_t *iocpcf; #endif @@ -656,41 +658,32 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma return NGX_CONF_ERROR; } +#if (NGX_PCRE) + regex = 0; +#endif + name = in_addr[a].names.elts; + for (s = 0; s < in_addr[a].names.nelts; s++) { - ch = name[s].name.data[0]; - - if (ch == '*' || ch == '.') { +#if (NGX_PCRE) + if (name[s].regex) { + regex++; continue; } +#endif rc = ngx_hash_add_key(&ha, &name[s].name, name[s].core_srv_conf, - 0); + NGX_HASH_WILDCARD_KEY); if (rc == NGX_ERROR) { return NGX_CONF_ERROR; } - if (rc == NGX_BUSY) { - ngx_log_error(NGX_LOG_WARN, cf->log, 0, - "conflicting server name \"%V\" on %s, ignored", + if (rc == NGX_DECLINED) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "invalid server name or wildcard \"%V\" on %s", &name[s].name, in_addr[a].listen_conf->addr); - } - } - - for (s = 0; s < in_addr[a].names.nelts; s++) { - - ch = name[s].name.data[0]; - - if (ch != '*' && ch != '.') { - continue; - } - - rc = ngx_hash_add_key(&ha, &name[s].name, name[s].core_srv_conf, - NGX_HASH_WILDCARD_KEY); - - if (rc == NGX_ERROR) { return NGX_CONF_ERROR; } @@ -718,28 +711,70 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma } } - if (ha.dns_wildcards.nelts) { + if (ha.dns_wc_head.nelts) { + + ngx_qsort(ha.dns_wc_head.elts, + (size_t) ha.dns_wc_head.nelts, + sizeof(ngx_hash_key_t), + ngx_http_cmp_dns_wildcards); + + hash.hash = NULL; + hash.temp_pool = ha.temp_pool; - ngx_qsort(ha.dns_wildcards.elts, - (size_t) ha.dns_wildcards.nelts, + if (ngx_hash_wildcard_init(&hash, ha.dns_wc_head.elts, + ha.dns_wc_head.nelts) + != NGX_OK) + { + ngx_destroy_pool(ha.temp_pool); + return NGX_CONF_ERROR; + } + + in_addr[a].wc_head = (ngx_hash_wildcard_t *) hash.hash; + } + + if (ha.dns_wc_tail.nelts) { + + ngx_qsort(ha.dns_wc_tail.elts, + (size_t) ha.dns_wc_tail.nelts, sizeof(ngx_hash_key_t), ngx_http_cmp_dns_wildcards); hash.hash = NULL; hash.temp_pool = ha.temp_pool; - if (ngx_hash_wildcard_init(&hash, ha.dns_wildcards.elts, - ha.dns_wildcards.nelts) + if (ngx_hash_wildcard_init(&hash, ha.dns_wc_tail.elts, + ha.dns_wc_tail.nelts) != NGX_OK) { ngx_destroy_pool(ha.temp_pool); return NGX_CONF_ERROR; } - in_addr[a].dns_wildcards = (ngx_hash_wildcard_t *) hash.hash; + in_addr[a].wc_tail = (ngx_hash_wildcard_t *) hash.hash; } ngx_destroy_pool(ha.temp_pool); + +#if (NGX_PCRE) + + if (regex == 0) { + continue; + } + + in_addr[a].nregex = regex; + in_addr[a].regex = ngx_palloc(cf->pool, + regex * sizeof(ngx_http_server_name_t)); + + if (in_addr[a].regex == NULL) { + return NGX_CONF_ERROR; + } + + for (i = 0, s = 0; s < in_addr[a].names.nelts; s++) { + if (name[s].regex) { + in_addr[a].regex[i++] = name[s]; + } + } +#endif } in_addr = in_port[p].addrs.elts; @@ -857,8 +892,10 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma hip->addrs[i].core_srv_conf = in_addr[i].core_srv_conf; if (in_addr[i].hash.buckets == NULL - && (in_addr[i].dns_wildcards == NULL - || in_addr[i].dns_wildcards->hash.buckets == NULL)) + && (in_addr[i].wc_head == NULL + || in_addr[i].wc_head->hash.buckets == NULL) + && (in_addr[i].wc_head == NULL + || in_addr[i].wc_head->hash.buckets == NULL)) { continue; } @@ -869,8 +906,13 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma } hip->addrs[i].virtual_names = vn; - vn->hash = in_addr[i].hash; - vn->dns_wildcards = in_addr[i].dns_wildcards; + vn->names.hash = in_addr[i].hash; + vn->names.wc_head = in_addr[i].wc_head; + vn->names.wc_tail = in_addr[i].wc_tail; +#if (NGX_PCRE) + vn->nregex = in_addr[i].nregex; + vn->regex = in_addr[i].regex; +#endif } if (done) { @@ -929,7 +971,8 @@ ngx_http_add_address(ngx_conf_t *cf, ngx if (in_port->addrs.elts == NULL) { if (ngx_array_init(&in_port->addrs, cf->temp_pool, 4, - sizeof(ngx_http_conf_in_addr_t)) != NGX_OK) + sizeof(ngx_http_conf_in_addr_t)) + != NGX_OK) { return NGX_ERROR; } @@ -943,8 +986,13 @@ ngx_http_add_address(ngx_conf_t *cf, ngx in_addr->addr = lscf->addr; in_addr->hash.buckets = NULL; in_addr->hash.size = 0; - in_addr->dns_wildcards = NULL; + in_addr->wc_head = NULL; + in_addr->wc_tail = NULL; in_addr->names.elts = NULL; +#if (NGX_PCRE) + in_addr->nregex = 0; + in_addr->regex = NULL; +#endif in_addr->core_srv_conf = cscf; in_addr->default_server = lscf->conf.default_server; in_addr->bind = lscf->conf.bind; @@ -977,13 +1025,15 @@ ngx_http_add_names(ngx_conf_t *cf, ngx_h if (in_addr->names.elts == NULL) { if (ngx_array_init(&in_addr->names, cf->temp_pool, 4, - sizeof(ngx_http_server_name_t)) != NGX_OK) + sizeof(ngx_http_server_name_t)) + != NGX_OK) { return NGX_ERROR; } } server_names = cscf->server_names.elts; + for (i = 0; i < cscf->server_names.nelts; i++) { for (n = 0; n < server_names[i].name.len; n++) { @@ -994,7 +1044,6 @@ ngx_http_add_names(ngx_conf_t *cf, ngx_h ngx_log_debug1(NGX_LOG_DEBUG_HTTP, cf->log, 0, "name: %V", &server_names[i].name); - name = ngx_array_push(&in_addr->names); if (name == NULL) { return NGX_ERROR; diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -877,7 +877,7 @@ ngx_http_core_content_phase(ngx_http_req if (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "directory index of \"%V\" is forbidden", &path); + "directory index of \"%s\" is forbidden", path.data); } ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN); @@ -2272,9 +2272,12 @@ ngx_http_core_merge_srv_conf(ngx_conf_t return NGX_CONF_ERROR; } +#if (NGX_PCRE) + sn->regex = NULL; +#endif + sn->core_srv_conf = conf; sn->name.len = conf->server_name.len; sn->name.data = conf->server_name.data; - sn->core_srv_conf = conf; } ngx_conf_merge_size_value(conf->connection_pool_size, @@ -2719,19 +2722,30 @@ ngx_http_core_server_name(ngx_conf_t *cf ngx_str_t *value, name; ngx_uint_t i; ngx_http_server_name_t *sn; +#if (NGX_PCRE) + ngx_str_t err; + u_char errstr[NGX_MAX_CONF_ERRSTR]; +#endif value = cf->args->elts; ch = value[1].data[0]; if (cscf->server_name.data == NULL && value[1].len) { - if (ch == '*') { + if (ngx_strchr(value[1].data, '*')) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "first server name \"%V\" must not be wildcard", &value[1]); return NGX_CONF_ERROR; } + if (value[1].data[0] == '~') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "first server name \"%V\" " + "must not be regular expression", &value[1]); + return NGX_CONF_ERROR; + } + name = value[1]; if (ch == '.') { @@ -2775,9 +2789,42 @@ ngx_http_core_server_name(ngx_conf_t *cf return NGX_CONF_ERROR; } +#if (NGX_PCRE) + sn->regex = NULL; +#endif + sn->core_srv_conf = cscf; sn->name.len = value[i].len; sn->name.data = value[i].data; - sn->core_srv_conf = cscf; + + if (value[i].data[0] != '~') { + continue; + } + +#if (NGX_PCRE) + err.len = NGX_MAX_CONF_ERRSTR; + err.data = errstr; + + value[i].len--; + value[i].data++; + + sn->regex = ngx_regex_compile(&value[i], NGX_REGEX_CASELESS, cf->pool, + &err); + + if (sn->regex == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data); + return NGX_CONF_ERROR; + } + + sn->name.len = value[i].len; + sn->name.data = value[i].data; + +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the using of the regex \"%V\" " + "requires PCRE library", &value[i]); + + return NGX_CONF_ERROR; +#endif } return NGX_CONF_OK; diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -151,8 +151,10 @@ typedef struct { typedef struct { in_addr_t addr; + /* the default server configuration for this address:port */ ngx_http_core_srv_conf_t *core_srv_conf; + ngx_http_virtual_names_t *virtual_names; } ngx_http_in_addr_t; @@ -175,10 +177,16 @@ typedef struct { in_addr_t addr; ngx_hash_t hash; - ngx_hash_wildcard_t *dns_wildcards; + ngx_hash_wildcard_t *wc_head; + ngx_hash_wildcard_t *wc_tail; ngx_array_t names; /* array of ngx_http_server_name_t */ +#if (NGX_PCRE) + ngx_uint_t nregex; + ngx_http_server_name_t *regex; +#endif + /* the default server configuration for this address:port */ ngx_http_core_srv_conf_t *core_srv_conf; @@ -189,10 +197,13 @@ typedef struct { } ngx_http_conf_in_addr_t; -typedef struct { +struct ngx_http_server_name_s { +#if (NGX_PCRE) + ngx_regex_t *regex; +#endif + ngx_http_core_srv_conf_t *core_srv_conf; /* virtual name server conf */ ngx_str_t name; - ngx_http_core_srv_conf_t *core_srv_conf; /* virtual name server conf */ -} ngx_http_server_name_t; +}; typedef struct { diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1205,10 +1205,10 @@ static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { - if (ngx_strstr(h->value.data, "close")) { + if (ngx_strcasestrn(h->value.data, "close", 5 - 1)) { r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; - } else if (ngx_strstr(h->value.data, "keep-alive")) { + } else if (ngx_strcasestrn(h->value.data, "keep-alive", 10 - 1)) { r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE; } @@ -1320,7 +1320,8 @@ ngx_http_process_request_header(ngx_http } if (r->headers_in.transfer_encoding - && ngx_strstr(r->headers_in.transfer_encoding->value.data, "chunked")) + && ngx_strcasestrn(r->headers_in.transfer_encoding->value.data, + "chunked", 7 - 1)) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "client sent \"Transfer-Encoding: chunked\" header"); @@ -1352,7 +1353,7 @@ ngx_http_process_request_header(ngx_http user_agent = r->headers_in.user_agent->value.data; - ua = (u_char *) ngx_strstr(user_agent, "MSIE"); + ua = ngx_strstrn(user_agent, "MSIE", 4 - 1); if (ua && ua + 8 < user_agent + r->headers_in.user_agent->value.len) { @@ -1370,7 +1371,7 @@ ngx_http_process_request_header(ngx_http #endif } - if (ngx_strstr(user_agent, "Opera")) { + if (ngx_strstrn(user_agent, "Opera", 5 - 1)) { r->headers_in.opera = 1; r->headers_in.msie = 0; r->headers_in.msie4 = 0; @@ -1378,10 +1379,10 @@ ngx_http_process_request_header(ngx_http if (!r->headers_in.msie && !r->headers_in.opera) { - if (ngx_strstr(user_agent, "Gecko/")) { + if (ngx_strstrn(user_agent, "Gecko/", 6 - 1)) { r->headers_in.gecko = 1; - } else if (ngx_strstr(user_agent, "Konqueror")) { + } else if (ngx_strstrn(user_agent, "Konqueror", 9 - 1)) { r->headers_in.konqueror = 1; } } @@ -1456,26 +1457,55 @@ static void ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, size_t len, ngx_uint_t hash) { - ngx_http_virtual_names_t *vn; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; - - vn = r->virtual_names; - - if (vn->hash.buckets) { - cscf = ngx_hash_find(&vn->hash, hash, host, len); - if (cscf) { +#if (NGX_PCRE) + ngx_int_t n; + ngx_uint_t i; + ngx_str_t name; + ngx_http_server_name_t *sn; +#endif + + cscf = ngx_hash_find_combined(&r->virtual_names->names, hash, host, len); + + if (cscf) { + goto found; + } + +#if (NGX_PCRE) + + if (r->virtual_names->nregex) { + + name.len = len; + name.data = host; + + sn = r->virtual_names->regex; + + for (i = 0; i < r->virtual_names->nregex; i++) { + + n = ngx_regex_exec(sn[i].regex, &name, NULL, 0); + + if (n == NGX_REGEX_NO_MATCHED) { + continue; + } + + if (n < 0) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + ngx_regex_exec_n + " failed: %d on \"%V\" using \"%V\"", + n, &name, &sn[i].name); + return; + } + + /* match */ + + cscf = sn[i].core_srv_conf; + goto found; } } - if (vn->dns_wildcards && vn->dns_wildcards->hash.buckets) { - cscf = ngx_hash_find_wildcard(vn->dns_wildcards, host, len); - - if (cscf) { - goto found; - } - } +#endif cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -275,9 +275,14 @@ typedef struct { } ngx_http_connection_t; +typedef struct ngx_http_server_name_s ngx_http_server_name_t; + + typedef struct { - ngx_hash_t hash; - ngx_hash_wildcard_t *dns_wildcards; + ngx_hash_combined_t names; + + ngx_uint_t nregex; + ngx_http_server_name_t *regex; } ngx_http_virtual_names_t; diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -2632,7 +2632,7 @@ ngx_http_upstream_rewrite_refresh(ngx_ht if (r->upstream->rewrite_redirect) { - p = (u_char *) ngx_strstr(ho->value.data, "url="); + p = ngx_strcasestrn(ho->value.data, "url=", 4 - 1); if (p) { rc = r->upstream->rewrite_redirect(r, ho, p + 4 - ho->value.data); diff --git a/src/mail/ngx_mail.c b/src/mail/ngx_mail.c --- a/src/mail/ngx_mail.c +++ b/src/mail/ngx_mail.c @@ -185,6 +185,8 @@ ngx_mail_block(ngx_conf_t *cf, ngx_comma /* init mail{} main_conf's */ + cf->ctx = ctx; + if (module->init_main_conf) { rv = module->init_main_conf(cf, ctx->main_conf[mi]); if (rv != NGX_CONF_OK) { @@ -197,6 +199,8 @@ ngx_mail_block(ngx_conf_t *cf, ngx_comma /* merge the server{}s' srv_conf's */ + cf->ctx = cscfp[s]->ctx; + if (module->merge_srv_conf) { rv = module->merge_srv_conf(cf, ctx->srv_conf[mi], @@ -209,8 +213,6 @@ ngx_mail_block(ngx_conf_t *cf, ngx_comma } } - /* mail{}'s cf->ctx was needed while the configuration merging */ - *cf = pcf; diff --git a/src/mail/ngx_mail.h b/src/mail/ngx_mail.h --- a/src/mail/ngx_mail.h +++ b/src/mail/ngx_mail.h @@ -73,55 +73,27 @@ typedef struct { #define NGX_MAIL_IMAP_PROTOCOL 1 #define NGX_MAIL_SMTP_PROTOCOL 2 -typedef struct { - ngx_msec_t timeout; + +typedef struct ngx_mail_protocol_s ngx_mail_protocol_t; + - size_t imap_client_buffer_size; +typedef struct { + ngx_mail_protocol_t *protocol; - ngx_uint_t protocol; + ngx_msec_t timeout; ngx_flag_t so_keepalive; - ngx_str_t pop3_capability; - ngx_str_t pop3_starttls_capability; - ngx_str_t pop3_starttls_only_capability; - ngx_str_t pop3_auth_capability; - - ngx_str_t imap_capability; - ngx_str_t imap_starttls_capability; - ngx_str_t imap_starttls_only_capability; + ngx_str_t server_name; - ngx_str_t smtp_capability; - ngx_str_t smtp_starttls_capability; - ngx_str_t smtp_starttls_only_capability; - - ngx_str_t server_name; - ngx_str_t smtp_server_name; - ngx_str_t smtp_greeting; - - ngx_uint_t pop3_auth_methods; - ngx_uint_t imap_auth_methods; - ngx_uint_t smtp_auth_methods; - - ngx_array_t pop3_capabilities; - ngx_array_t imap_capabilities; - ngx_array_t smtp_capabilities; + u_char *file_name; + ngx_int_t line; /* server ctx */ ngx_mail_conf_ctx_t *ctx; } ngx_mail_core_srv_conf_t; -typedef struct { - 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); -} ngx_mail_module_t; - - typedef enum { ngx_pop3_start = 0, ngx_pop3_user, @@ -179,9 +151,9 @@ typedef struct { ngx_uint_t mail_state; + unsigned protocol:3; unsigned blocked:1; unsigned quit:1; - unsigned protocol:2; unsigned quoted:1; unsigned backslash:1; unsigned no_sync_literal:1; @@ -196,6 +168,7 @@ typedef struct { ngx_str_t salt; ngx_str_t tag; ngx_str_t tagged_line; + ngx_str_t text; ngx_str_t *addr_text; ngx_str_t smtp_helo; @@ -205,7 +178,7 @@ typedef struct { ngx_uint_t login_attempt; - /* used to parse IMAP/POP3/SMTP command */ + /* used to parse POP3/IMAP/SMTP command */ ngx_uint_t state; u_char *cmd_start; @@ -279,10 +252,43 @@ typedef struct { #define NGX_MAIL_PARSE_INVALID_COMMAND 20 -#define NGX_MAIL_MODULE 0x4C49414D /* "MAIL" */ +typedef void (*ngx_mail_init_session_pt)(ngx_mail_session_t *s, + ngx_connection_t *c); +typedef void (*ngx_mail_init_protocol_pt)(ngx_event_t *rev); +typedef void (*ngx_mail_auth_state_pt)(ngx_event_t *rev); +typedef ngx_int_t (*ngx_mail_parse_command_pt)(ngx_mail_session_t *s); + + +struct ngx_mail_protocol_s { + ngx_str_t name; + in_port_t port[4]; + ngx_uint_t type; + + ngx_mail_init_session_pt init_session; + ngx_mail_init_protocol_pt init_protocol; + ngx_mail_parse_command_pt parse_command; + ngx_mail_auth_state_pt auth_state; -#define NGX_MAIL_MAIN_CONF 0x02000000 -#define NGX_MAIL_SRV_CONF 0x04000000 + ngx_str_t internal_server_error; +}; + + +typedef struct { + ngx_mail_protocol_t *protocol; + + 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); +} ngx_mail_module_t; + + +#define NGX_MAIL_MODULE 0x4C49414D /* "MAIL" */ + +#define NGX_MAIL_MAIN_CONF 0x02000000 +#define NGX_MAIL_SRV_CONF 0x04000000 #define NGX_MAIL_MAIN_CONF_OFFSET offsetof(ngx_mail_conf_ctx_t, main_conf) @@ -304,17 +310,36 @@ typedef struct { ((ngx_mail_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index] +#if (NGX_MAIL_SSL) +void ngx_mail_starttls_handler(ngx_event_t *rev); +ngx_int_t ngx_mail_starttls_only(ngx_mail_session_t *s, ngx_connection_t *c); +#endif + + void ngx_mail_init_connection(ngx_connection_t *c); + +ngx_int_t ngx_mail_salt(ngx_mail_session_t *s, ngx_connection_t *c, + ngx_mail_core_srv_conf_t *cscf); +ngx_int_t ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, + ngx_uint_t n); +ngx_int_t ngx_mail_auth_login_username(ngx_mail_session_t *s, + ngx_connection_t *c); +ngx_int_t ngx_mail_auth_login_password(ngx_mail_session_t *s, + ngx_connection_t *c); +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_parse(ngx_mail_session_t *s, ngx_connection_t *c); + void ngx_mail_send(ngx_event_t *wev); -void ngx_pop3_auth_state(ngx_event_t *rev); -void ngx_imap_auth_state(ngx_event_t *rev); -void ngx_smtp_auth_state(ngx_event_t *rev); +ngx_int_t ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c); +void ngx_mail_auth(ngx_mail_session_t *s, ngx_connection_t *c); void ngx_mail_close_connection(ngx_connection_t *c); void ngx_mail_session_internal_server_error(ngx_mail_session_t *s); +u_char *ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len); -ngx_int_t ngx_pop3_parse_command(ngx_mail_session_t *s); -ngx_int_t ngx_imap_parse_command(ngx_mail_session_t *s); -ngx_int_t ngx_smtp_parse_command(ngx_mail_session_t *s); + +char *ngx_mail_capabilities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); /* STUB */ diff --git a/src/mail/ngx_mail_auth_http_module.c b/src/mail/ngx_mail_auth_http_module.c --- a/src/mail/ngx_mail_auth_http_module.c +++ b/src/mail/ngx_mail_auth_http_module.c @@ -111,6 +111,8 @@ static ngx_command_t ngx_mail_auth_http static ngx_mail_module_t ngx_mail_auth_http_module_ctx = { + NULL, /* protocol */ + NULL, /* create main configuration */ NULL, /* init main configuration */ @@ -135,7 +137,6 @@ ngx_module_t ngx_mail_auth_http_module }; -static char *ngx_mail_auth_http_protocol[] = { "pop3", "imap", "smtp" }; static ngx_str_t ngx_mail_auth_http_method[] = { ngx_string("plain"), ngx_string("plain"), @@ -145,6 +146,7 @@ static ngx_str_t ngx_mail_auth_http_me static ngx_str_t ngx_mail_smtp_errcode = ngx_string("535 5.7.0"); + void ngx_mail_auth_http_init(ngx_mail_session_t *s) { @@ -239,7 +241,7 @@ ngx_mail_auth_http_write_handler(ngx_eve if (wev->timedout) { ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT, "auth http server %V timed out", ctx->peer.name); - ngx_close_connection(ctx->peer.connection); + ngx_close_connection(c); ngx_destroy_pool(ctx->pool); ngx_mail_session_internal_server_error(s); return; @@ -250,7 +252,7 @@ ngx_mail_auth_http_write_handler(ngx_eve n = ngx_send(c, ctx->request->pos, size); if (n == NGX_ERROR) { - ngx_close_connection(ctx->peer.connection); + ngx_close_connection(c); ngx_destroy_pool(ctx->pool); ngx_mail_session_internal_server_error(s); return; @@ -267,7 +269,7 @@ ngx_mail_auth_http_write_handler(ngx_eve } if (ngx_handle_write_event(wev, 0) == NGX_ERROR) { - ngx_close_connection(ctx->peer.connection); + ngx_close_connection(c); ngx_destroy_pool(ctx->pool); ngx_mail_session_internal_server_error(s); } @@ -302,7 +304,7 @@ ngx_mail_auth_http_read_handler(ngx_even if (rev->timedout) { ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT, "auth http server %V timed out", ctx->peer.name); - ngx_close_connection(ctx->peer.connection); + ngx_close_connection(c); ngx_destroy_pool(ctx->pool); ngx_mail_session_internal_server_error(s); return; @@ -311,7 +313,7 @@ ngx_mail_auth_http_read_handler(ngx_even if (ctx->response == NULL) { ctx->response = ngx_create_temp_buf(ctx->pool, 1024); if (ctx->response == NULL) { - ngx_close_connection(ctx->peer.connection); + ngx_close_connection(c); ngx_destroy_pool(ctx->pool); ngx_mail_session_internal_server_error(s); return; @@ -333,7 +335,7 @@ ngx_mail_auth_http_read_handler(ngx_even return; } - ngx_close_connection(ctx->peer.connection); + ngx_close_connection(c); ngx_destroy_pool(ctx->pool); ngx_mail_session_internal_server_error(s); } @@ -749,7 +751,8 @@ ngx_mail_auth_http_process_headers(ngx_m return; } - if (s->passwd.data == NULL && s->protocol != NGX_MAIL_SMTP_PROTOCOL) + if (s->passwd.data == NULL + && s->protocol != NGX_MAIL_SMTP_PROTOCOL) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "auth http server %V did not send password", @@ -868,45 +871,30 @@ ngx_mail_auth_sleep_handler(ngx_event_t return; } - switch (s->protocol) { - - case NGX_MAIL_POP3_PROTOCOL: - s->mail_state = ngx_pop3_start; - s->connection->read->handler = ngx_pop3_auth_state; - break; + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - case NGX_MAIL_IMAP_PROTOCOL: - s->mail_state = ngx_imap_start; - s->connection->read->handler = ngx_imap_auth_state; - break; + rev->handler = cscf->protocol->auth_state; - default: /* NGX_MAIL_SMTP_PROTOCOL */ - s->mail_state = ngx_smtp_start; - s->connection->read->handler = ngx_smtp_auth_state; - break; - } - + s->mail_state = 0; s->auth_method = NGX_MAIL_AUTH_PLAIN; c->log->action = "in auth state"; - ngx_mail_send(s->connection->write); + ngx_mail_send(c->write); if (c->destroyed) { return; } - cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - ngx_add_timer(rev, cscf->timeout); if (rev->ready) { - s->connection->read->handler(rev); + rev->handler(rev); return; } if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { - ngx_mail_close_connection(s->connection); + ngx_mail_close_connection(c); } return; @@ -914,7 +902,7 @@ ngx_mail_auth_sleep_handler(ngx_event_t if (rev->active) { if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { - ngx_mail_close_connection(s->connection); + ngx_mail_close_connection(c); } } } @@ -1147,9 +1135,10 @@ static ngx_buf_t * ngx_mail_auth_http_create_request(ngx_mail_session_t *s, ngx_pool_t *pool, ngx_mail_auth_http_conf_t *ahcf) { - size_t len; - ngx_buf_t *b; - ngx_str_t login, passwd; + size_t len; + ngx_buf_t *b; + ngx_str_t login, passwd; + ngx_mail_core_srv_conf_t *cscf; if (ngx_mail_auth_http_escape(pool, &s->login, &login) != NGX_OK) { return NULL; @@ -1159,6 +1148,8 @@ ngx_mail_auth_http_create_request(ngx_ma return NULL; } + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + len = sizeof("GET ") - 1 + ahcf->uri.len + sizeof(" HTTP/1.0" CRLF) - 1 + sizeof("Host: ") - 1 + ahcf->host_header.len + sizeof(CRLF) - 1 + sizeof("Auth-Method: ") - 1 @@ -1167,7 +1158,8 @@ ngx_mail_auth_http_create_request(ngx_ma + sizeof("Auth-User: ") - 1 + login.len + sizeof(CRLF) - 1 + sizeof("Auth-Pass: ") - 1 + passwd.len + sizeof(CRLF) - 1 + sizeof("Auth-Salt: ") - 1 + s->salt.len - + sizeof("Auth-Protocol: imap" CRLF) - 1 + + sizeof("Auth-Protocol: ") - 1 + cscf->protocol->name.len + + sizeof(CRLF) - 1 + sizeof("Auth-Login-Attempt: ") - 1 + NGX_INT_T_LEN + sizeof(CRLF) - 1 + sizeof("Client-IP: ") - 1 + s->connection->addr_text.len @@ -1214,8 +1206,8 @@ ngx_mail_auth_http_create_request(ngx_ma b->last = ngx_cpymem(b->last, "Auth-Protocol: ", sizeof("Auth-Protocol: ") - 1); - b->last = ngx_cpymem(b->last, ngx_mail_auth_http_protocol[s->protocol], - sizeof("imap") - 1); + b->last = ngx_cpymem(b->last, cscf->protocol->name.data, + cscf->protocol->name.len); *b->last++ = CR; *b->last++ = LF; b->last = ngx_sprintf(b->last, "Auth-Login-Attempt: %ui" CRLF, diff --git a/src/mail/ngx_mail_core_module.c b/src/mail/ngx_mail_core_module.c --- a/src/mail/ngx_mail_core_module.c +++ b/src/mail/ngx_mail_core_module.c @@ -18,90 +18,10 @@ static char *ngx_mail_core_server(ngx_co void *conf); static char *ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static char *ngx_mail_core_capability(ngx_conf_t *cf, ngx_command_t *cmd, +static char *ngx_mail_core_protocol(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static ngx_conf_enum_t ngx_mail_core_procotol[] = { - { ngx_string("pop3"), NGX_MAIL_POP3_PROTOCOL }, - { ngx_string("imap"), NGX_MAIL_IMAP_PROTOCOL }, - { ngx_string("smtp"), NGX_MAIL_SMTP_PROTOCOL }, - { ngx_null_string, 0 } -}; - - -static ngx_str_t ngx_pop3_default_capabilities[] = { - ngx_string("TOP"), - ngx_string("USER"), - ngx_string("UIDL"), - ngx_null_string -}; - - -static ngx_str_t ngx_imap_default_capabilities[] = { - ngx_string("IMAP4"), - ngx_string("IMAP4rev1"), - ngx_string("UIDPLUS"), - ngx_null_string -}; - - -static ngx_conf_bitmask_t ngx_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_null_string, 0 } -}; - - -static ngx_conf_bitmask_t ngx_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_null_string, 0 } -}; - - -static ngx_conf_bitmask_t ngx_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_null_string, 0 } -}; - - -static ngx_str_t ngx_imap_auth_methods_names[] = { - ngx_string("AUTH=PLAIN"), - ngx_string("AUTH=LOGIN"), - ngx_null_string, /* APOP */ - ngx_string("AUTH=CRAM-MD5") -}; - - -static ngx_str_t ngx_smtp_auth_methods_names[] = { - ngx_string("PLAIN"), - ngx_string("LOGIN"), - ngx_null_string, /* APOP */ - ngx_string("CRAM-MD5") -}; - - -static ngx_str_t ngx_pop3_auth_plain_capability = - ngx_string("+OK methods supported:" CRLF - "LOGIN" CRLF - "PLAIN" CRLF - "." CRLF); - - -static ngx_str_t ngx_pop3_auth_cram_md5_capability = - ngx_string("+OK methods supported:" CRLF - "LOGIN" CRLF - "PLAIN" CRLF - "CRAM-MD5" CRLF - "." CRLF); - - - static ngx_command_t ngx_mail_core_commands[] = { { ngx_string("server"), @@ -114,22 +34,15 @@ static ngx_command_t ngx_mail_core_comm { ngx_string("listen"), NGX_MAIL_SRV_CONF|NGX_CONF_TAKE12, ngx_mail_core_listen, - 0, + NGX_MAIL_SRV_CONF_OFFSET, 0, NULL }, { ngx_string("protocol"), NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_enum_slot, + ngx_mail_core_protocol, NGX_MAIL_SRV_CONF_OFFSET, - offsetof(ngx_mail_core_srv_conf_t, protocol), - &ngx_mail_core_procotol }, - - { ngx_string("imap_client_buffer"), - NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_size_slot, - NGX_MAIL_SRV_CONF_OFFSET, - offsetof(ngx_mail_core_srv_conf_t, imap_client_buffer_size), + 0, NULL }, { ngx_string("so_keepalive"), @@ -146,27 +59,6 @@ static ngx_command_t ngx_mail_core_comm offsetof(ngx_mail_core_srv_conf_t, timeout), NULL }, - { ngx_string("pop3_capabilities"), - NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, - ngx_mail_core_capability, - NGX_MAIL_SRV_CONF_OFFSET, - offsetof(ngx_mail_core_srv_conf_t, pop3_capabilities), - NULL }, - - { ngx_string("imap_capabilities"), - NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, - ngx_mail_core_capability, - NGX_MAIL_SRV_CONF_OFFSET, - offsetof(ngx_mail_core_srv_conf_t, imap_capabilities), - NULL }, - - { ngx_string("smtp_capabilities"), - NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, - ngx_mail_core_capability, - NGX_MAIL_SRV_CONF_OFFSET, - offsetof(ngx_mail_core_srv_conf_t, smtp_capabilities), - NULL }, - { ngx_string("server_name"), NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, @@ -174,39 +66,13 @@ static ngx_command_t ngx_mail_core_comm offsetof(ngx_mail_core_srv_conf_t, server_name), NULL }, - { ngx_string("auth"), - NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, - ngx_conf_set_bitmask_slot, - NGX_MAIL_SRV_CONF_OFFSET, - offsetof(ngx_mail_core_srv_conf_t, pop3_auth_methods), - &ngx_pop3_auth_methods }, - - { ngx_string("pop3_auth"), - NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, - ngx_conf_set_bitmask_slot, - NGX_MAIL_SRV_CONF_OFFSET, - offsetof(ngx_mail_core_srv_conf_t, pop3_auth_methods), - &ngx_pop3_auth_methods }, - - { ngx_string("imap_auth"), - NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, - ngx_conf_set_bitmask_slot, - NGX_MAIL_SRV_CONF_OFFSET, - offsetof(ngx_mail_core_srv_conf_t, imap_auth_methods), - &ngx_imap_auth_methods }, - - { ngx_string("smtp_auth"), - NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, - ngx_conf_set_bitmask_slot, - NGX_MAIL_SRV_CONF_OFFSET, - offsetof(ngx_mail_core_srv_conf_t, smtp_auth_methods), - &ngx_smtp_auth_methods }, - ngx_null_command }; static ngx_mail_module_t ngx_mail_core_module_ctx = { + NULL, /* protocol */ + ngx_mail_core_create_main_conf, /* create main configuration */ NULL, /* init main configuration */ @@ -268,29 +134,15 @@ ngx_mail_core_create_srv_conf(ngx_conf_t return NULL; } - cscf->imap_client_buffer_size = NGX_CONF_UNSET_SIZE; - cscf->protocol = NGX_CONF_UNSET_UINT; + /* + * set by ngx_pcalloc(): + * + * cscf->protocol = NULL; + */ + cscf->timeout = NGX_CONF_UNSET_MSEC; cscf->so_keepalive = NGX_CONF_UNSET; - if (ngx_array_init(&cscf->pop3_capabilities, cf->pool, 4, sizeof(ngx_str_t)) - != NGX_OK) - { - return NULL; - } - - if (ngx_array_init(&cscf->imap_capabilities, cf->pool, 4, sizeof(ngx_str_t)) - != NGX_OK) - { - return NULL; - } - - if (ngx_array_init(&cscf->smtp_capabilities, cf->pool, 4, sizeof(ngx_str_t)) - != NGX_OK) - { - return NULL; - } - return cscf; } @@ -301,35 +153,9 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t ngx_mail_core_srv_conf_t *prev = parent; ngx_mail_core_srv_conf_t *conf = child; - u_char *p, *auth; - size_t size, stls_only_size; - ngx_str_t *c, *d; - ngx_uint_t i, m; - - ngx_conf_merge_size_value(conf->imap_client_buffer_size, - prev->imap_client_buffer_size, - (size_t) ngx_pagesize); ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000); - ngx_conf_merge_uint_value(conf->protocol, prev->protocol, - NGX_MAIL_IMAP_PROTOCOL); - ngx_conf_merge_value(conf->so_keepalive, prev->so_keepalive, 0); - - ngx_conf_merge_bitmask_value(conf->pop3_auth_methods, - prev->pop3_auth_methods, - (NGX_CONF_BITMASK_SET - |NGX_MAIL_AUTH_PLAIN_ENABLED)); - - ngx_conf_merge_bitmask_value(conf->imap_auth_methods, - prev->imap_auth_methods, - (NGX_CONF_BITMASK_SET - |NGX_MAIL_AUTH_PLAIN_ENABLED)); - - ngx_conf_merge_bitmask_value(conf->smtp_auth_methods, - prev->smtp_auth_methods, - (NGX_CONF_BITMASK_SET - |NGX_MAIL_AUTH_PLAIN_ENABLED - |NGX_MAIL_AUTH_LOGIN_ENABLED)); + ngx_conf_merge_value(conf->so_keepalive, prev->so_keepalive, 0); ngx_conf_merge_str_value(conf->server_name, prev->server_name, ""); @@ -343,351 +169,21 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t if (gethostname((char *) conf->server_name.data, NGX_MAXHOSTNAMELEN) == -1) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, - "gethostname() failed"); + ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, + "gethostname() failed"); return NGX_CONF_ERROR; } conf->server_name.len = ngx_strlen(conf->server_name.data); } - - if (conf->pop3_capabilities.nelts == 0) { - conf->pop3_capabilities = prev->pop3_capabilities; - } - - if (conf->pop3_capabilities.nelts == 0) { - - for (d = ngx_pop3_default_capabilities; d->len; d++) { - c = ngx_array_push(&conf->pop3_capabilities); - if (c == NULL) { - return NGX_CONF_ERROR; - } - - *c = *d; - } - } - - size = sizeof("+OK Capability list follows" CRLF) - 1 - + sizeof("." CRLF) - 1; - - stls_only_size = size + sizeof("STLS" CRLF) - 1; - - c = conf->pop3_capabilities.elts; - for (i = 0; i < conf->pop3_capabilities.nelts; i++) { - size += c[i].len + sizeof(CRLF) - 1; - - if (ngx_strcasecmp(c[i].data, (u_char *) "USER") == 0) { - continue; - } - - stls_only_size += c[i].len + sizeof(CRLF) - 1; - } - - if (conf->pop3_auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED) { - size += sizeof("SASL LOGIN PLAIN CRAM-MD5" CRLF) - 1; - - } else { - size += sizeof("SASL LOGIN PLAIN" CRLF) - 1; - } - - p = ngx_palloc(cf->pool, size); - if (p == NULL) { - return NGX_CONF_ERROR; - } - - conf->pop3_capability.len = size; - conf->pop3_capability.data = p; - - p = ngx_cpymem(p, "+OK Capability list follows" CRLF, - sizeof("+OK Capability list follows" CRLF) - 1); - - for (i = 0; i < conf->pop3_capabilities.nelts; i++) { - p = ngx_cpymem(p, c[i].data, c[i].len); - *p++ = CR; *p++ = LF; - } - - if (conf->pop3_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); - - } else { - p = ngx_cpymem(p, "SASL LOGIN PLAIN" CRLF, - sizeof("SASL LOGIN PLAIN" CRLF) - 1); - } - - *p++ = '.'; *p++ = CR; *p = LF; - - - size += sizeof("STLS" CRLF) - 1; - - p = ngx_palloc(cf->pool, size); - if (p == NULL) { - return NGX_CONF_ERROR; - } - - conf->pop3_starttls_capability.len = size; - conf->pop3_starttls_capability.data = p; - - p = ngx_cpymem(p, conf->pop3_capability.data, - conf->pop3_capability.len - (sizeof("." CRLF) - 1)); - - p = ngx_cpymem(p, "STLS" CRLF, sizeof("STLS" CRLF) - 1); - *p++ = '.'; *p++ = CR; *p = LF; - - - if (conf->pop3_auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED) { - conf->pop3_auth_capability = ngx_pop3_auth_cram_md5_capability; - - } else { - conf->pop3_auth_capability = ngx_pop3_auth_plain_capability; - } - - - p = ngx_palloc(cf->pool, stls_only_size); - if (p == NULL) { - return NGX_CONF_ERROR; - } - - conf->pop3_starttls_only_capability.len = stls_only_size; - conf->pop3_starttls_only_capability.data = p; - - p = ngx_cpymem(p, "+OK Capability list follows" CRLF, - sizeof("+OK Capability list follows" CRLF) - 1); - - for (i = 0; i < conf->pop3_capabilities.nelts; i++) { - if (ngx_strcasecmp(c[i].data, (u_char *) "USER") == 0) { - continue; - } - - p = ngx_cpymem(p, c[i].data, c[i].len); - *p++ = CR; *p++ = LF; - } - - p = ngx_cpymem(p, "STLS" CRLF, sizeof("STLS" CRLF) - 1); - *p++ = '.'; *p++ = CR; *p = LF; - - - if (conf->imap_capabilities.nelts == 0) { - conf->imap_capabilities = prev->imap_capabilities; - } - - if (conf->imap_capabilities.nelts == 0) { - - for (d = ngx_imap_default_capabilities; d->len; d++) { - c = ngx_array_push(&conf->imap_capabilities); - if (c == NULL) { - return NGX_CONF_ERROR; - } - - *c = *d; - } - } - - size = sizeof("* CAPABILITY" CRLF) - 1; - - c = conf->imap_capabilities.elts; - for (i = 0; i < conf->imap_capabilities.nelts; i++) { - size += 1 + c[i].len; - } - - for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; - m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED; - m <<= 1, i++) - { - if (m & conf->imap_auth_methods) { - size += 1 + ngx_imap_auth_methods_names[i].len; - } - } - - p = ngx_palloc(cf->pool, size); - if (p == NULL) { + if (conf->protocol == NULL) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "unknown mail protocol for server in %s:%ui", + conf->file_name, conf->line); return NGX_CONF_ERROR; } - conf->imap_capability.len = size; - conf->imap_capability.data = p; - - p = ngx_cpymem(p, "* CAPABILITY", sizeof("* CAPABILITY") - 1); - - for (i = 0; i < conf->imap_capabilities.nelts; i++) { - *p++ = ' '; - p = ngx_cpymem(p, c[i].data, c[i].len); - } - - auth = p; - - for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; - m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED; - m <<= 1, i++) - { - if (m & conf->imap_auth_methods) { - *p++ = ' '; - p = ngx_cpymem(p, ngx_imap_auth_methods_names[i].data, - ngx_imap_auth_methods_names[i].len); - } - } - - *p++ = CR; *p = LF; - - - size += sizeof(" STARTTLS") - 1; - - p = ngx_palloc(cf->pool, size); - if (p == NULL) { - return NGX_CONF_ERROR; - } - - conf->imap_starttls_capability.len = size; - conf->imap_starttls_capability.data = p; - - p = ngx_cpymem(p, conf->imap_capability.data, - conf->imap_capability.len - (sizeof(CRLF) - 1)); - p = ngx_cpymem(p, " STARTTLS", sizeof(" STARTTLS") - 1); - *p++ = CR; *p = LF; - - - size = (auth - conf->imap_capability.data) + sizeof(CRLF) - 1 - + sizeof(" STARTTLS LOGINDISABLED") - 1; - - p = ngx_palloc(cf->pool, size); - if (p == NULL) { - return NGX_CONF_ERROR; - } - - conf->imap_starttls_only_capability.len = size; - conf->imap_starttls_only_capability.data = p; - - p = ngx_cpymem(p, conf->imap_capability.data, - auth - conf->imap_capability.data); - p = ngx_cpymem(p, " STARTTLS LOGINDISABLED", - sizeof(" STARTTLS LOGINDISABLED") - 1); - *p++ = CR; *p = LF; - - - size = sizeof("220 ESMTP ready" CRLF) - 1 + conf->server_name.len; - - p = ngx_palloc(cf->pool, size); - if (p == NULL) { - return NGX_CONF_ERROR; - } - - conf->smtp_greeting.len = size; - conf->smtp_greeting.data = p; - - *p++ = '2'; *p++ = '2'; *p++ = '0'; *p++ = ' '; - p = ngx_cpymem(p, conf->server_name.data, conf->server_name.len); - ngx_memcpy(p, " ESMTP ready" CRLF, sizeof(" ESMTP ready" CRLF) - 1); - - - size = sizeof("250 " CRLF) - 1 + conf->server_name.len; - - p = ngx_palloc(cf->pool, size); - if (p == NULL) { - return NGX_CONF_ERROR; - } - - conf->smtp_server_name.len = size; - conf->smtp_server_name.data = p; - - *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' '; - p = ngx_cpymem(p, conf->server_name.data, conf->server_name.len); - *p++ = CR; *p = LF; - - - if (conf->smtp_capabilities.nelts == 0) { - conf->smtp_capabilities = prev->smtp_capabilities; - } - - size = sizeof("250-") - 1 + conf->server_name.len + sizeof(CRLF) - 1 - + sizeof("250 AUTH") - 1 + sizeof(CRLF) - 1; - - c = conf->smtp_capabilities.elts; - for (i = 0; i < conf->smtp_capabilities.nelts; i++) { - size += sizeof("250 ") - 1 + c[i].len + sizeof(CRLF) - 1; - } - - for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; - m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED; - m <<= 1, i++) - { - if (m & conf->smtp_auth_methods) { - size += 1 + ngx_smtp_auth_methods_names[i].len; - } - } - - p = ngx_palloc(cf->pool, size); - if (p == NULL) { - return NGX_CONF_ERROR; - } - - conf->smtp_capability.len = size; - conf->smtp_capability.data = p; - - *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-'; - p = ngx_cpymem(p, conf->server_name.data, conf->server_name.len); - *p++ = CR; *p++ = LF; - - for (i = 0; i < conf->smtp_capabilities.nelts; i++) { - *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-'; - p = ngx_cpymem(p, c[i].data, c[i].len); - *p++ = CR; *p++ = LF; - } - - auth = p; - - *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' '; - *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 <<= 1, i++) - { - if (m & conf->smtp_auth_methods) { - *p++ = ' '; - p = ngx_cpymem(p, ngx_smtp_auth_methods_names[i].data, - ngx_smtp_auth_methods_names[i].len); - } - } - - *p++ = CR; *p = LF; - - size += sizeof("250 STARTTLS" CRLF) - 1; - - p = ngx_palloc(cf->pool, size); - if (p == NULL) { - return NGX_CONF_ERROR; - } - - conf->smtp_starttls_capability.len = size; - conf->smtp_starttls_capability.data = p; - - p = ngx_cpymem(p, conf->smtp_capability.data, - conf->smtp_capability.len); - - p = ngx_cpymem(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1); - *p++ = CR; *p = LF; - - p = conf->smtp_starttls_capability.data - + (auth - conf->smtp_capability.data) + 3; - *p = '-'; - - size = (auth - conf->smtp_capability.data) - + sizeof("250 STARTTLS" CRLF) - 1; - - p = ngx_palloc(cf->pool, size); - if (p == NULL) { - return NGX_CONF_ERROR; - } - - conf->smtp_starttls_only_capability.len = size; - conf->smtp_starttls_only_capability.data = p; - - p = ngx_cpymem(p, conf->smtp_capability.data, - auth - conf->smtp_capability.data); - - ngx_memcpy(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1); - return NGX_CONF_OK; } @@ -704,7 +200,6 @@ ngx_mail_core_server(ngx_conf_t *cf, ngx ngx_mail_core_srv_conf_t *cscf, **cscfp; ngx_mail_core_main_conf_t *cmcf; - ctx = ngx_pcalloc(cf->pool, sizeof(ngx_mail_conf_ctx_t)); if (ctx == NULL) { return NGX_CONF_ERROR; @@ -742,6 +237,9 @@ ngx_mail_core_server(ngx_conf_t *cf, ngx cscf = ctx->srv_conf[ngx_mail_core_module.ctx_index]; cscf->ctx = ctx; + cscf->file_name = cf->conf_file->file.name.data; + cscf->line = cf->conf_file->line; + cmcf = ctx->main_conf[ngx_mail_core_module.ctx_index]; cscfp = ngx_array_push(&cmcf->servers); @@ -771,10 +269,13 @@ ngx_mail_core_server(ngx_conf_t *cf, ngx static char * ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { + ngx_mail_core_srv_conf_t *cscf = conf; + ngx_str_t *value; ngx_url_t u; - ngx_uint_t i; + ngx_uint_t i, m; ngx_mail_listen_t *imls; + ngx_mail_module_t *module; ngx_mail_core_main_conf_t *cmcf; value = cf->args->elts; @@ -821,6 +322,25 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx imls->family = AF_INET; imls->ctx = cf->ctx; + for (m = 0; ngx_modules[m]; m++) { + if (ngx_modules[m]->type != NGX_MAIL_MODULE) { + continue; + } + + module = ngx_modules[m]->ctx; + + if (module->protocol == NULL) { + continue; + } + + for (i = 0; module->protocol->port[i]; i++) { + if (module->protocol->port[i] == u.port) { + cscf->protocol = module->protocol; + break; + } + } + } + if (cf->args->nelts == 2) { return NGX_CONF_OK; } @@ -837,7 +357,40 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx static char * -ngx_mail_core_capability(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +ngx_mail_core_protocol(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_mail_core_srv_conf_t *cscf = conf; + + ngx_str_t *value; + ngx_uint_t m; + ngx_mail_module_t *module; + + value = cf->args->elts; + + for (m = 0; ngx_modules[m]; m++) { + if (ngx_modules[m]->type != NGX_MAIL_MODULE) { + continue; + } + + module = ngx_modules[m]->ctx; + + if (module->protocol + && ngx_strcmp(module->protocol->name.data, value[1].data) == 0) + { + cscf->protocol = module->protocol; + + return NGX_CONF_OK; + } + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unknown protocol \"%V\"", &value[1]); + return NGX_CONF_ERROR; +} + + +char * +ngx_mail_capabilities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *p = conf; diff --git a/src/mail/ngx_mail_handler.c b/src/mail/ngx_mail_handler.c --- a/src/mail/ngx_mail_handler.c +++ b/src/mail/ngx_mail_handler.c @@ -11,12 +11,6 @@ static void ngx_mail_init_session(ngx_connection_t *c); -static void ngx_mail_init_protocol(ngx_event_t *rev); -static ngx_int_t ngx_mail_decode_auth_plain(ngx_mail_session_t *s, - ngx_str_t *encoded); -static void ngx_mail_do_auth(ngx_mail_session_t *s); -static ngx_int_t ngx_mail_read_command(ngx_mail_session_t *s); -static u_char *ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len); #if (NGX_MAIL_SSL) static void ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c); @@ -24,40 +18,6 @@ static void ngx_mail_ssl_handshake_handl #endif -static ngx_str_t greetings[] = { - ngx_string("+OK POP3 ready" CRLF), - ngx_string("* OK IMAP4 ready" CRLF) - /* SMTP greeting */ -}; - -static ngx_str_t internal_server_errors[] = { - ngx_string("-ERR internal server error" CRLF), - ngx_string("* BAD internal server error" CRLF), - ngx_string("451 4.3.2 Internal server error" CRLF), -}; - -static u_char pop3_ok[] = "+OK" CRLF; -static u_char pop3_next[] = "+ " CRLF; -static u_char pop3_username[] = "+ VXNlcm5hbWU6" CRLF; -static u_char pop3_password[] = "+ UGFzc3dvcmQ6" CRLF; -static u_char pop3_invalid_command[] = "-ERR invalid command" CRLF; - -static u_char imap_star[] = "* "; -static u_char imap_ok[] = "OK completed" CRLF; -static u_char imap_next[] = "+ OK" CRLF; -static u_char imap_bye[] = "* BYE" CRLF; -static u_char imap_invalid_command[] = "BAD invalid command" CRLF; - -static u_char smtp_ok[] = "250 2.0.0 OK" CRLF; -static u_char smtp_bye[] = "221 2.0.0 Bye" CRLF; -static u_char smtp_next[] = "334 " CRLF; -static u_char smtp_username[] = "334 VXNlcm5hbWU6" CRLF; -static u_char smtp_password[] = "334 UGFzc3dvcmQ6" CRLF; -static u_char smtp_invalid_command[] = "500 5.5.1 Invalid command" CRLF; -static u_char smtp_invalid_argument[] = "501 5.5.4 Invalid argument" CRLF; -static u_char smtp_auth_required[] = "530 5.7.1 Authentication required" CRLF; - - void ngx_mail_init_connection(ngx_connection_t *c) { @@ -172,7 +132,7 @@ ngx_mail_init_connection(ngx_connection_ #if (NGX_MAIL_SSL) -static void +void ngx_mail_starttls_handler(ngx_event_t *rev) { ngx_connection_t *c; @@ -222,17 +182,20 @@ ngx_mail_ssl_init_connection(ngx_ssl_t * static void ngx_mail_ssl_handshake_handler(ngx_connection_t *c) { - ngx_mail_session_t *s; + ngx_mail_session_t *s; + ngx_mail_core_srv_conf_t *cscf; if (c->ssl->handshaked) { s = c->data; if (s->starttls) { - c->read->handler = ngx_mail_init_protocol; + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + c->read->handler = cscf->protocol->init_protocol; c->write->handler = ngx_mail_send; - ngx_mail_init_protocol(c->read); + cscf->protocol->init_protocol(c->read); return; } @@ -250,18 +213,14 @@ ngx_mail_ssl_handshake_handler(ngx_conne static void ngx_mail_init_session(ngx_connection_t *c) { - u_char *p; ngx_mail_session_t *s; ngx_mail_core_srv_conf_t *cscf; - c->read->handler = ngx_mail_init_protocol; - c->write->handler = ngx_mail_send; - s = c->data; cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - s->protocol = cscf->protocol; + s->protocol = cscf->protocol->type; s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_mail_max_module); if (s->ctx == NULL) { @@ -269,60 +228,249 @@ ngx_mail_init_session(ngx_connection_t * return; } - if (s->protocol == NGX_MAIL_SMTP_PROTOCOL) { - s->out = cscf->smtp_greeting; + c->write->handler = ngx_mail_send; + + cscf->protocol->init_session(s, c); +} + + +ngx_int_t +ngx_mail_salt(ngx_mail_session_t *s, ngx_connection_t *c, + ngx_mail_core_srv_conf_t *cscf) +{ + s->salt.data = ngx_palloc(c->pool, + sizeof(" <18446744073709551616.@>" CRLF) - 1 + + NGX_TIME_T_LEN + + cscf->server_name.len); + if (s->salt.data == NULL) { + return NGX_ERROR; + } + + s->salt.len = ngx_sprintf(s->salt.data, "<%ul.%T@%V>" CRLF, + ngx_random(), ngx_time(), &cscf->server_name) + - s->salt.data; + + return NGX_OK; +} + + +#if (NGX_MAIL_SSL) + +ngx_int_t +ngx_mail_starttls_only(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_mail_ssl_conf_t *sslcf; + + if (c->ssl) { + return 0; + } + + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); + + if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { + return 1; + } + + return 0; +} - } else { - s->out = greetings[s->protocol]; +#endif + + +ngx_int_t +ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n) +{ + u_char *p, *last; + ngx_str_t *arg, plain; + + arg = s->args.elts; + +#if (NGX_DEBUG_MAIL_PASSWD) + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth plain: \"%V\"", &arg[n]); +#endif + + plain.data = ngx_palloc(c->pool, ngx_base64_decoded_length(arg[n].len)); + if (plain.data == NULL){ + return NGX_ERROR; + } + + if (ngx_decode_base64(&plain, &arg[n]) != NGX_OK) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid base64 encoding in AUTH PLAIN command"); + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + p = plain.data; + last = p + plain.len; + + while (p < last && *p++) { /* void */ } + + if (p == last) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid login in AUTH PLAIN command"); + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + s->login.data = p; + + while (p < last && *p) { p++; } + + if (p == last) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid password in AUTH PLAIN command"); + return NGX_MAIL_PARSE_INVALID_COMMAND; } - if ((s->protocol == NGX_MAIL_POP3_PROTOCOL - && (cscf->pop3_auth_methods - & (NGX_MAIL_AUTH_APOP_ENABLED|NGX_MAIL_AUTH_CRAM_MD5_ENABLED))) + s->login.len = p++ - s->login.data; + + s->passwd.len = last - p; + s->passwd.data = p; + +#if (NGX_DEBUG_MAIL_PASSWD) + ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth plain: \"%V\" \"%V\"", &s->login, &s->passwd); +#endif + + return NGX_DONE; +} + - || (s->protocol == NGX_MAIL_IMAP_PROTOCOL - && (cscf->imap_auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) +ngx_int_t +ngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg; + + arg = s->args.elts; + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth login username: \"%V\"", &arg[0]); + + s->login.data = ngx_palloc(c->pool, ngx_base64_decoded_length(arg[0].len)); + if (s->login.data == NULL){ + return NGX_ERROR; + } - || (s->protocol == NGX_MAIL_SMTP_PROTOCOL - && (cscf->smtp_auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED))) - { - s->salt.data = ngx_palloc(c->pool, - sizeof(" <18446744073709551616.@>" CRLF) - 1 - + NGX_TIME_T_LEN - + cscf->server_name.len); - if (s->salt.data == NULL) { - ngx_mail_session_internal_server_error(s); - return; - } + if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid base64 encoding in AUTH LOGIN command"); + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth login username: \"%V\"", &s->login); + + return NGX_OK; +} + + +ngx_int_t +ngx_mail_auth_login_password(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg; + + arg = s->args.elts; + +#if (NGX_DEBUG_MAIL_PASSWD) + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth login password: \"%V\"", &arg[0]); +#endif + + s->passwd.data = ngx_palloc(c->pool, ngx_base64_decoded_length(arg[0].len)); + if (s->passwd.data == NULL){ + return NGX_ERROR; + } + + if (ngx_decode_base64(&s->passwd, &arg[0]) != NGX_OK) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid base64 encoding in AUTH LOGIN command"); + return NGX_MAIL_PARSE_INVALID_COMMAND; + } - s->salt.len = ngx_sprintf(s->salt.data, "<%ul.%T@%V>" CRLF, - ngx_random(), ngx_time(), &cscf->server_name) - - s->salt.data; +#if (NGX_DEBUG_MAIL_PASSWD) + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth login password: \"%V\"", &s->passwd); +#endif + + return NGX_DONE; +} + + +ngx_int_t +ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s, ngx_connection_t *c, + char *prefix, size_t len) +{ + u_char *p; + ngx_str_t salt; + ngx_uint_t n; + + p = ngx_palloc(c->pool, len + ngx_base64_encoded_length(s->salt.len) + 2); + if (p == NULL) { + return NGX_ERROR; + } + + salt.data = ngx_cpymem(p, prefix, len); + s->salt.len -= 2; + + ngx_encode_base64(&salt, &s->salt); + + s->salt.len += 2; + n = len + salt.len; + p[n++] = CR; p[n++] = LF; + + s->out.len = n; + s->out.data = p; - if (s->protocol == NGX_MAIL_POP3_PROTOCOL) { - s->out.data = ngx_palloc(c->pool, - greetings[0].len + 1 + s->salt.len); - if (s->out.data == NULL) { - ngx_mail_session_internal_server_error(s); - return; - } + return NGX_OK; +} + + +ngx_int_t +ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c) +{ + u_char *p, *last; + ngx_str_t *arg; + + arg = s->args.elts; + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth cram-md5: \"%V\"", &arg[0]); - p = ngx_cpymem(s->out.data, - greetings[0].data, greetings[0].len - 2); - *p++ = ' '; - p = ngx_cpymem(p, s->salt.data, s->salt.len); + s->login.data = ngx_palloc(c->pool, ngx_base64_decoded_length(arg[0].len)); + if (s->login.data == NULL){ + return NGX_ERROR; + } - s->out.len = p - s->out.data; + if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid base64 encoding in AUTH CRAM-MD5 command"); + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + p = s->login.data; + last = p + s->login.len; + + while (p < last) { + if (*p++ == ' ') { + s->login.len = p - s->login.data - 1; + s->passwd.len = last - p; + s->passwd.data = p; + break; } } - ngx_add_timer(c->read, cscf->timeout); - - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { - ngx_mail_close_connection(c); + if (s->passwd.len != 32) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid CRAM-MD5 hash in AUTH CRAM-MD5 command"); + return NGX_MAIL_PARSE_INVALID_COMMAND; } - ngx_mail_send(c->write); + ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth cram-md5: \"%V\" \"%V\"", &s->login, &s->passwd); + + s->auth_method = NGX_MAIL_AUTH_CRAM_MD5; + + return NGX_DONE; } @@ -391,1626 +539,18 @@ ngx_mail_send(ngx_event_t *wev) } -static void -ngx_mail_init_protocol(ngx_event_t *rev) +ngx_int_t +ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c) { - size_t size; - ngx_connection_t *c; - ngx_mail_session_t *s; + ssize_t n; + ngx_int_t rc; + ngx_str_t l; ngx_mail_core_srv_conf_t *cscf; - c = rev->data; - - c->log->action = "in auth state"; - - if (rev->timedout) { - ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); - c->timedout = 1; - ngx_mail_close_connection(c); - return; - } - - s = c->data; - - switch (s->protocol) { - - case NGX_MAIL_POP3_PROTOCOL: - size = 128; - s->mail_state = ngx_pop3_start; - c->read->handler = ngx_pop3_auth_state; - break; - - case NGX_MAIL_IMAP_PROTOCOL: - cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - size = cscf->imap_client_buffer_size; - s->mail_state = ngx_imap_start; - c->read->handler = ngx_imap_auth_state; - break; - - default: /* NGX_MAIL_SMTP_PROTOCOL */ - size = 512; - s->mail_state = ngx_smtp_start; - c->read->handler = ngx_smtp_auth_state; - break; - } - - if (s->buffer == NULL) { - if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) - == NGX_ERROR) - { - ngx_mail_session_internal_server_error(s); - return; - } - - s->buffer = ngx_create_temp_buf(c->pool, size); - if (s->buffer == NULL) { - ngx_mail_session_internal_server_error(s); - return; - } - } - - c->read->handler(rev); -} - - -void -ngx_pop3_auth_state(ngx_event_t *rev) -{ - u_char *p, *last, *text; - ssize_t size; - ngx_int_t rc; - ngx_str_t *arg, salt; - ngx_connection_t *c; - ngx_mail_session_t *s; - ngx_mail_core_srv_conf_t *cscf; -#if (NGX_MAIL_SSL) - ngx_mail_ssl_conf_t *sslcf; -#endif - - c = rev->data; - s = c->data; - - ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 auth state"); - - if (rev->timedout) { - ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); - c->timedout = 1; - ngx_mail_close_connection(c); - return; - } - - if (s->out.len) { - ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 send handler busy"); - s->blocked = 1; - return; - } - - s->blocked = 0; - - rc = ngx_mail_read_command(s); - - if (rc == NGX_AGAIN || rc == NGX_ERROR) { - return; - } - - text = pop3_ok; - size = sizeof(pop3_ok) - 1; - - if (rc == NGX_OK) { - switch (s->mail_state) { - - case ngx_pop3_start: - - switch (s->command) { - - case NGX_POP3_USER: - -#if (NGX_MAIL_SSL) - - if (c->ssl == NULL) { - sslcf = ngx_mail_get_module_srv_conf(s, - ngx_mail_ssl_module); - - if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - } -#endif - - if (s->args.nelts == 1) { - s->mail_state = ngx_pop3_user; - - arg = s->args.elts; - s->login.len = arg[0].len; - s->login.data = ngx_palloc(c->pool, s->login.len); - if (s->login.data == NULL) { - ngx_mail_session_internal_server_error(s); - return; - } - - ngx_memcpy(s->login.data, arg[0].data, s->login.len); - - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "pop3 login: \"%V\"", &s->login); - - break; - } - - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - - case NGX_POP3_CAPA: - cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - -#if (NGX_MAIL_SSL) - - if (c->ssl == NULL) { - sslcf = ngx_mail_get_module_srv_conf(s, - ngx_mail_ssl_module); - - if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) { - size = cscf->pop3_starttls_capability.len; - text = cscf->pop3_starttls_capability.data; - break; - } - - if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { - size = cscf->pop3_starttls_only_capability.len; - text = cscf->pop3_starttls_only_capability.data; - break; - } - } -#endif - - size = cscf->pop3_capability.len; - text = cscf->pop3_capability.data; - break; - - case NGX_POP3_APOP: - -#if (NGX_MAIL_SSL) - - if (c->ssl == NULL) { - sslcf = ngx_mail_get_module_srv_conf(s, - ngx_mail_ssl_module); - - if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - } -#endif - - cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - - if ((cscf->pop3_auth_methods & NGX_MAIL_AUTH_APOP_ENABLED) - && s->args.nelts == 2) - { - arg = s->args.elts; - - s->login.len = arg[0].len; - s->login.data = ngx_palloc(c->pool, s->login.len); - if (s->login.data == NULL) { - ngx_mail_session_internal_server_error(s); - return; - } - - ngx_memcpy(s->login.data, arg[0].data, s->login.len); - - s->passwd.len = arg[1].len; - s->passwd.data = ngx_palloc(c->pool, s->passwd.len); - if (s->passwd.data == NULL) { - ngx_mail_session_internal_server_error(s); - return; - } - - ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len); - - ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, - "pop3 apop: \"%V\" \"%V\"", - &s->login, &s->passwd); - - s->auth_method = NGX_MAIL_AUTH_APOP; - - ngx_mail_do_auth(s); - return; - } - - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - - case NGX_POP3_AUTH: - -#if (NGX_MAIL_SSL) - - if (c->ssl == NULL) { - sslcf = ngx_mail_get_module_srv_conf(s, - ngx_mail_ssl_module); - - if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - } -#endif - - cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - - if (s->args.nelts == 0) { - size = cscf->pop3_auth_capability.len; - text = cscf->pop3_auth_capability.data; - s->state = 0; - break; - } - - arg = s->args.elts; - - if (arg[0].len == 5) { - - if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5) - == 0) - { - - if (s->args.nelts != 1) { - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - s->mail_state = ngx_pop3_auth_login_username; - - size = sizeof(pop3_username) - 1; - text = pop3_username; - - break; - - } else if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN", - 5) - == 0) - { - - if (s->args.nelts == 1) { - s->mail_state = ngx_pop3_auth_plain; - - size = sizeof(pop3_next) - 1; - text = pop3_next; - - break; - } - - if (s->args.nelts == 2) { - - /* - * workaround for Eudora for Mac: it sends - * AUTH PLAIN [base64 encoded] - */ - - rc = ngx_mail_decode_auth_plain(s, &arg[1]); - - if (rc == NGX_OK) { - ngx_mail_do_auth(s); - return; - } - - if (rc == NGX_ERROR) { - ngx_mail_session_internal_server_error(s); - return; - } - - /* rc == NGX_MAIL_PARSE_INVALID_COMMAND */ - - break; - } - - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - } else if (arg[0].len == 8 - && ngx_strncasecmp(arg[0].data, - (u_char *) "CRAM-MD5", 8) - == 0) - { - if (!(cscf->pop3_auth_methods - & NGX_MAIL_AUTH_CRAM_MD5_ENABLED) - || s->args.nelts != 1) - { - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - s->mail_state = ngx_pop3_auth_cram_md5; - - text = ngx_palloc(c->pool, - sizeof("+ " CRLF) - 1 - + ngx_base64_encoded_length(s->salt.len)); - if (text == NULL) { - ngx_mail_session_internal_server_error(s); - return; - } - - text[0] = '+'; text[1]= ' '; - salt.data = &text[2]; - s->salt.len -= 2; - - ngx_encode_base64(&salt, &s->salt); - - s->salt.len += 2; - size = 2 + salt.len; - text[size++] = CR; text[size++] = LF; - - break; - } - - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - - case NGX_POP3_QUIT: - s->quit = 1; - break; - - case NGX_POP3_NOOP: - break; - -#if (NGX_MAIL_SSL) - - case NGX_POP3_STLS: - if (c->ssl == NULL) { - sslcf = ngx_mail_get_module_srv_conf(s, - ngx_mail_ssl_module); - if (sslcf->starttls) { - c->read->handler = ngx_mail_starttls_handler; - break; - } - } - - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; -#endif - - default: - s->mail_state = ngx_pop3_start; - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - break; - - case ngx_pop3_user: - - switch (s->command) { - - case NGX_POP3_PASS: - if (s->args.nelts == 1) { - arg = s->args.elts; - s->passwd.len = arg[0].len; - s->passwd.data = ngx_palloc(c->pool, s->passwd.len); - if (s->passwd.data == NULL) { - ngx_mail_session_internal_server_error(s); - return; - } - - ngx_memcpy(s->passwd.data, arg[0].data, s->passwd.len); - -#if (NGX_DEBUG_MAIL_PASSWD) - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "pop3 passwd: \"%V\"", &s->passwd); -#endif - - ngx_mail_do_auth(s); - return; - } - - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - - case NGX_POP3_CAPA: - cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - size = cscf->pop3_capability.len; - text = cscf->pop3_capability.data; - break; - - case NGX_POP3_QUIT: - s->quit = 1; - break; - - case NGX_POP3_NOOP: - break; - - default: - s->mail_state = ngx_pop3_start; - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - break; - - /* suppress warinings */ - case ngx_pop3_passwd: - break; - - case ngx_pop3_auth_login_username: - arg = s->args.elts; - s->mail_state = ngx_pop3_auth_login_password; - - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "pop3 auth login username: \"%V\"", &arg[0]); - - s->login.data = ngx_palloc(c->pool, - ngx_base64_decoded_length(arg[0].len)); - if (s->login.data == NULL){ - ngx_mail_session_internal_server_error(s); - return; - } - - if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid base64 encoding " - "in AUTH LOGIN command"); - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "pop3 auth login username: \"%V\"", &s->login); - - size = sizeof(pop3_password) - 1; - text = pop3_password; - - break; - - case ngx_pop3_auth_login_password: - arg = s->args.elts; - -#if (NGX_DEBUG_MAIL_PASSWD) - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "pop3 auth login password: \"%V\"", &arg[0]); -#endif - - s->passwd.data = ngx_palloc(c->pool, - ngx_base64_decoded_length(arg[0].len)); - if (s->passwd.data == NULL){ - ngx_mail_session_internal_server_error(s); - return; - } - - if (ngx_decode_base64(&s->passwd, &arg[0]) != NGX_OK) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid base64 encoding " - "in AUTH LOGIN command"); - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - -#if (NGX_DEBUG_MAIL_PASSWD) - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "pop3 auth login password: \"%V\"", &s->passwd); -#endif - - ngx_mail_do_auth(s); - return; - - case ngx_pop3_auth_plain: - arg = s->args.elts; - - rc = ngx_mail_decode_auth_plain(s, &arg[0]); - - if (rc == NGX_OK) { - ngx_mail_do_auth(s); - return; - } - - if (rc == NGX_ERROR) { - ngx_mail_session_internal_server_error(s); - return; - } - - /* rc == NGX_MAIL_PARSE_INVALID_COMMAND */ - - break; - - case ngx_pop3_auth_cram_md5: - arg = s->args.elts; - - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "pop3 auth cram-md5: \"%V\"", &arg[0]); - - s->login.data = ngx_palloc(c->pool, - ngx_base64_decoded_length(arg[0].len)); - if (s->login.data == NULL){ - ngx_mail_session_internal_server_error(s); - return; - } - - if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid base64 encoding " - "in AUTH CRAM-MD5 command"); - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - p = s->login.data; - last = p + s->login.len; - - while (p < last) { - if (*p++ == ' ') { - s->login.len = p - s->login.data - 1; - s->passwd.len = last - p; - s->passwd.data = p; - break; - } - } - - if (s->passwd.len != 32) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid CRAM-MD5 hash " - "in AUTH CRAM-MD5 command"); - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, - "pop3 auth cram-md5: \"%V\" \"%V\"", - &s->login, &s->passwd); - - s->auth_method = NGX_MAIL_AUTH_CRAM_MD5; - - ngx_mail_do_auth(s); - return; - } - } - - if (rc == NGX_MAIL_PARSE_INVALID_COMMAND) { - s->mail_state = ngx_pop3_start; - s->state = 0; - text = pop3_invalid_command; - size = sizeof(pop3_invalid_command) - 1; - } - - s->args.nelts = 0; - s->buffer->pos = s->buffer->start; - s->buffer->last = s->buffer->start; - - if (s->state) { - s->arg_start = s->buffer->start; - } - - s->out.data = text; - s->out.len = size; - - ngx_mail_send(c->write); -} - - -void -ngx_imap_auth_state(ngx_event_t *rev) -{ - u_char *p, *last, *text, *dst, *src, *end; - ssize_t text_len, last_len; - ngx_str_t *arg, salt; - ngx_int_t rc; - ngx_uint_t tag, i; - ngx_connection_t *c; - ngx_mail_session_t *s; - ngx_mail_core_srv_conf_t *cscf; -#if (NGX_MAIL_SSL) - ngx_mail_ssl_conf_t *sslcf; -#endif - - c = rev->data; - s = c->data; - - ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap auth state"); - - if (rev->timedout) { - ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); - c->timedout = 1; - ngx_mail_close_connection(c); - return; - } - - if (s->out.len) { - ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap send handler busy"); - s->blocked = 1; - return; - } - - s->blocked = 0; - - rc = ngx_mail_read_command(s); - - if (rc == NGX_AGAIN || rc == NGX_ERROR) { - return; - } - - tag = 1; - - text = NULL; - text_len = 0; - - last = imap_ok; - last_len = sizeof(imap_ok) - 1; - - if (rc == NGX_OK) { - - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap auth command: %i", - s->command); - - if (s->backslash) { - - arg = s->args.elts; - - for (i = 0; i < s->args.nelts; i++) { - dst = arg[i].data; - end = dst + arg[i].len; - - for (src = dst; src < end; dst++) { - *dst = *src; - if (*src++ == '\\') { - *dst = *src++; - } - } - - arg[i].len = dst - arg[i].data; - } - - s->backslash = 0; - } - - switch (s->mail_state) { - - case ngx_imap_start: - - switch (s->command) { - - case NGX_IMAP_LOGIN: - -#if (NGX_MAIL_SSL) - - if (c->ssl == NULL) { - sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); - - if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - } -#endif - - arg = s->args.elts; - - if (s->args.nelts == 2 && arg[0].len) { - - s->login.len = arg[0].len; - s->login.data = ngx_palloc(c->pool, s->login.len); - if (s->login.data == NULL) { - ngx_mail_session_internal_server_error(s); - return; - } - - ngx_memcpy(s->login.data, arg[0].data, s->login.len); - - s->passwd.len = arg[1].len; - s->passwd.data = ngx_palloc(c->pool, s->passwd.len); - if (s->passwd.data == NULL) { - ngx_mail_session_internal_server_error(s); - return; - } - - ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len); - -#if (NGX_DEBUG_MAIL_PASSWD) - ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, - "imap login:\"%V\" passwd:\"%V\"", - &s->login, &s->passwd); -#else - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "imap login:\"%V\"", &s->login); -#endif - - ngx_mail_do_auth(s); - return; - } - - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - - case NGX_IMAP_AUTHENTICATE: - -#if (NGX_MAIL_SSL) - - if (c->ssl == NULL) { - sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); - - if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - } -#endif - - if (s->args.nelts != 1) { - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - arg = s->args.elts; - - if (arg[0].len == 5) { - - if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5) - == 0) - { - - s->mail_state = ngx_imap_auth_login_username; - - last_len = sizeof(pop3_username) - 1; - last = pop3_username; - tag = 0; - - break; - - } else if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN", - 5) - == 0) - { - - s->mail_state = ngx_imap_auth_plain; - - last_len = sizeof(pop3_next) - 1; - last = pop3_next; - tag = 0; - - break; - } - - } else if (arg[0].len == 8 - && ngx_strncasecmp(arg[0].data, - (u_char *) "CRAM-MD5", 8) - == 0) - { - cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - - if (!(cscf->imap_auth_methods - & NGX_MAIL_AUTH_CRAM_MD5_ENABLED) - || s->args.nelts != 1) - { - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - s->mail_state = ngx_imap_auth_cram_md5; - - last = ngx_palloc(c->pool, - sizeof("+ " CRLF) - 1 - + ngx_base64_encoded_length(s->salt.len)); - if (last == NULL) { - ngx_mail_session_internal_server_error(s); - return; - } - - last[0] = '+'; last[1]= ' '; - salt.data = &last[2]; - s->salt.len -= 2; - - ngx_encode_base64(&salt, &s->salt); - - s->salt.len += 2; - last_len = 2 + salt.len; - last[last_len++] = CR; last[last_len++] = LF; - tag = 0; - - break; - } - - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - - case NGX_IMAP_CAPABILITY: - cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - -#if (NGX_MAIL_SSL) - - if (c->ssl == NULL) { - sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); - - if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) { - text_len = cscf->imap_starttls_capability.len; - text = cscf->imap_starttls_capability.data; - break; - } - - if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { - text_len = cscf->imap_starttls_only_capability.len; - text = cscf->imap_starttls_only_capability.data; - break; - } - } -#endif - - text_len = cscf->imap_capability.len; - text = cscf->imap_capability.data; - break; - - case NGX_IMAP_LOGOUT: - s->quit = 1; - text = imap_bye; - text_len = sizeof(imap_bye) - 1; - break; - - case NGX_IMAP_NOOP: - break; - -#if (NGX_MAIL_SSL) - - case NGX_IMAP_STARTTLS: - if (c->ssl == NULL) { - sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); - if (sslcf->starttls) { - c->read->handler = ngx_mail_starttls_handler; - break; - } - } - - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; -#endif - - default: - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - break; - - case ngx_imap_auth_login_username: - arg = s->args.elts; - s->mail_state = ngx_imap_auth_login_password; - - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "imap auth login username: \"%V\"", &arg[0]); - - s->login.data = ngx_palloc(c->pool, - ngx_base64_decoded_length(arg[0].len)); - if (s->login.data == NULL){ - ngx_mail_session_internal_server_error(s); - return; - } - - if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid base64 encoding " - "in AUTH LOGIN command"); - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "imap auth login username: \"%V\"", &s->login); - - last_len = sizeof(pop3_password) - 1; - last = pop3_password; - tag = 0; - - break; - - case ngx_imap_auth_login_password: - arg = s->args.elts; - -#if (NGX_DEBUG_MAIL_PASSWD) - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "imap auth login password: \"%V\"", &arg[0]); -#endif - - s->passwd.data = ngx_palloc(c->pool, - ngx_base64_decoded_length(arg[0].len)); - if (s->passwd.data == NULL){ - ngx_mail_session_internal_server_error(s); - return; - } - - if (ngx_decode_base64(&s->passwd, &arg[0]) != NGX_OK) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid base64 encoding " - "in AUTH LOGIN command"); - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - -#if (NGX_DEBUG_MAIL_PASSWD) - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "imap auth login password: \"%V\"", &s->passwd); -#endif - - ngx_mail_do_auth(s); - return; - - case ngx_imap_auth_plain: - arg = s->args.elts; - - rc = ngx_mail_decode_auth_plain(s, &arg[0]); - - if (rc == NGX_OK) { - ngx_mail_do_auth(s); - return; - } - - if (rc == NGX_ERROR) { - ngx_mail_session_internal_server_error(s); - return; - } - - /* rc == NGX_MAIL_PARSE_INVALID_COMMAND */ - - break; - - case ngx_imap_auth_cram_md5: - arg = s->args.elts; - - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "imap auth cram-md5: \"%V\"", &arg[0]); - - s->login.data = ngx_palloc(c->pool, - ngx_base64_decoded_length(arg[0].len)); - if (s->login.data == NULL){ - ngx_mail_session_internal_server_error(s); - return; - } - - if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid base64 encoding " - "in AUTH CRAM-MD5 command"); - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - p = s->login.data; - last = p + s->login.len; - - while (p < last) { - if (*p++ == ' ') { - s->login.len = p - s->login.data - 1; - s->passwd.len = last - p; - s->passwd.data = p; - break; - } - } - - if (s->passwd.len != 32) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid CRAM-MD5 hash " - "in AUTH CRAM-MD5 command"); - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, - "imap auth cram-md5: \"%V\" \"%V\"", - &s->login, &s->passwd); - - s->auth_method = NGX_MAIL_AUTH_CRAM_MD5; - - ngx_mail_do_auth(s); - return; - } - - } else if (rc == NGX_IMAP_NEXT) { - last = imap_next; - last_len = sizeof(imap_next) - 1; - tag = 0; - } - - if (rc == NGX_MAIL_PARSE_INVALID_COMMAND) { - s->mail_state = ngx_imap_start; - s->state = 0; - last = imap_invalid_command; - last_len = sizeof(imap_invalid_command) - 1; - } - - if (tag) { - if (s->tag.len == 0) { - s->tag.len = sizeof(imap_star) - 1; - s->tag.data = (u_char *) imap_star; - } - - if (s->tagged_line.len < s->tag.len + text_len + last_len) { - s->tagged_line.len = s->tag.len + text_len + last_len; - s->tagged_line.data = ngx_palloc(c->pool, s->tagged_line.len); - if (s->tagged_line.data == NULL) { - ngx_mail_close_connection(c); - return; - } - } - - s->out.data = s->tagged_line.data; - s->out.len = s->tag.len + text_len + last_len; - - p = s->out.data; - - if (text) { - p = ngx_cpymem(p, text, text_len); - } - p = ngx_cpymem(p, s->tag.data, s->tag.len); - ngx_memcpy(p, last, last_len); - - - } else { - s->out.data = last; - s->out.len = last_len; - } - - if (rc != NGX_IMAP_NEXT) { - s->args.nelts = 0; - - if (s->state) { - /* preserve tag */ - s->arg_start = s->buffer->start + s->tag.len; - s->buffer->pos = s->arg_start; - s->buffer->last = s->arg_start; - - } else { - s->buffer->pos = s->buffer->start; - s->buffer->last = s->buffer->start; - s->tag.len = 0; - } - } - - ngx_mail_send(c->write); -} - - -void -ngx_smtp_auth_state(ngx_event_t *rev) -{ - u_char *p, *last, *text, ch; - ssize_t size; - ngx_int_t rc; - ngx_str_t *arg, salt, l; - ngx_uint_t i; - ngx_connection_t *c; - ngx_mail_session_t *s; - ngx_mail_core_srv_conf_t *cscf; -#if (NGX_MAIL_SSL) - ngx_mail_ssl_conf_t *sslcf; -#endif - - c = rev->data; - s = c->data; - - ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp auth state"); - - if (rev->timedout) { - ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); - c->timedout = 1; - ngx_mail_close_connection(c); - return; - } - - if (s->out.len) { - ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp send handler busy"); - s->blocked = 1; - return; - } - - s->blocked = 0; - - rc = ngx_mail_read_command(s); - - if (rc == NGX_AGAIN || rc == NGX_ERROR) { - return; - } - - text = NULL; - size = 0; - - if (rc == NGX_OK) { - switch (s->mail_state) { - - case ngx_smtp_start: - - switch (s->command) { - - case NGX_SMTP_HELO: - case NGX_SMTP_EHLO: - cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - - if (s->args.nelts != 1) { - text = smtp_invalid_argument; - size = sizeof(smtp_invalid_argument) - 1; - s->state = 0; - break; - } - - arg = s->args.elts; - - s->smtp_helo.len = arg[0].len; - - s->smtp_helo.data = ngx_palloc(c->pool, arg[0].len); - if (s->smtp_helo.data == NULL) { - ngx_mail_session_internal_server_error(s); - return; - } - - ngx_memcpy(s->smtp_helo.data, arg[0].data, arg[0].len); - - if (s->command == NGX_SMTP_HELO) { - size = cscf->smtp_server_name.len; - text = cscf->smtp_server_name.data; - - } else { - s->esmtp = 1; - -#if (NGX_MAIL_SSL) - - if (c->ssl == NULL) { - sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); - - if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) { - size = cscf->smtp_starttls_capability.len; - text = cscf->smtp_starttls_capability.data; - break; - } - - if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { - size = cscf->smtp_starttls_only_capability.len; - text = cscf->smtp_starttls_only_capability.data; - break; - } - } -#endif - - size = cscf->smtp_capability.len; - text = cscf->smtp_capability.data; - } - - break; - - case NGX_SMTP_AUTH: - -#if (NGX_MAIL_SSL) - - if (c->ssl == NULL) { - sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); - - if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - } -#endif - - if (s->args.nelts == 0) { - text = smtp_invalid_argument; - size = sizeof(smtp_invalid_argument) - 1; - s->state = 0; - break; - } - - arg = s->args.elts; - - if (arg[0].len == 5) { - - if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5) - == 0) - { - - if (s->args.nelts != 1) { - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - s->mail_state = ngx_smtp_auth_login_username; - - size = sizeof(smtp_username) - 1; - text = smtp_username; - - break; - - } else if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN", - 5) - == 0) - { - if (s->args.nelts == 1) { - s->mail_state = ngx_smtp_auth_plain; - - size = sizeof(smtp_next) - 1; - text = smtp_next; - - break; - } - - if (s->args.nelts == 2) { - - rc = ngx_mail_decode_auth_plain(s, &arg[1]); - - if (rc == NGX_OK) { - ngx_mail_do_auth(s); - return; - } - - if (rc == NGX_ERROR) { - ngx_mail_session_internal_server_error(s); - return; - } - - /* rc == NGX_MAIL_PARSE_INVALID_COMMAND */ - - break; - } - - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - } else if (arg[0].len == 8 - && ngx_strncasecmp(arg[0].data, - (u_char *) "CRAM-MD5", 8) - == 0) - { - cscf = ngx_mail_get_module_srv_conf(s, - ngx_mail_core_module); - - if (!(cscf->smtp_auth_methods - & NGX_MAIL_AUTH_CRAM_MD5_ENABLED) - || s->args.nelts != 1) - { - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - s->mail_state = ngx_smtp_auth_cram_md5; - - text = ngx_palloc(c->pool, - sizeof("334 " CRLF) - 1 - + ngx_base64_encoded_length(s->salt.len)); - if (text == NULL) { - ngx_mail_session_internal_server_error(s); - return; - } - - text[0] = '3'; text[1]= '3'; text[2] = '4'; text[3]= ' '; - salt.data = &text[4]; - s->salt.len -= 2; - - ngx_encode_base64(&salt, &s->salt); - - s->salt.len += 2; - size = 4 + salt.len; - text[size++] = CR; text[size++] = LF; - - break; - } - - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - - case NGX_SMTP_QUIT: - s->quit = 1; - text = smtp_bye; - size = sizeof(smtp_bye) - 1; - break; - - case NGX_SMTP_MAIL: - - if (s->connection->log->log_level >= NGX_LOG_INFO) { - l.len = s->buffer->last - s->buffer->start; - l.data = s->buffer->start; - - for (i = 0; i < l.len; i++) { - ch = l.data[i]; - - if (ch != CR && ch != LF) { - continue; - } - - l.data[i] = ' '; - } - - while (i) { - if (l.data[i - 1] != ' ') { - break; - } - - i--; - } - - l.len = i; - - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "client was rejected: \"%V\"", &l); - } - - text = smtp_auth_required; - size = sizeof(smtp_auth_required) - 1; - break; - - case NGX_SMTP_NOOP: - case NGX_SMTP_RSET: - text = smtp_ok; - size = sizeof(smtp_ok) - 1; - break; - -#if (NGX_MAIL_SSL) - - case NGX_SMTP_STARTTLS: - if (c->ssl == NULL) { - sslcf = ngx_mail_get_module_srv_conf(s, - ngx_mail_ssl_module); - if (sslcf->starttls) { - c->read->handler = ngx_mail_starttls_handler; - - /* - * RFC3207 requires us to discard any knowledge - * obtained from client before STARTTLS. - */ - - s->smtp_helo.len = 0; - s->smtp_helo.data = NULL; - - text = smtp_ok; - size = sizeof(smtp_ok) - 1; - - break; - } - } - - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; -#endif - - default: - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - break; - - case ngx_smtp_auth_login_username: - arg = s->args.elts; - s->mail_state = ngx_smtp_auth_login_password; - - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "smtp auth login username: \"%V\"", &arg[0]); - - s->login.data = ngx_palloc(c->pool, - ngx_base64_decoded_length(arg[0].len)); - if (s->login.data == NULL){ - ngx_mail_session_internal_server_error(s); - return; - } - - if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid base64 encoding " - "in AUTH LOGIN command"); - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "smtp auth login username: \"%V\"", &s->login); - - size = sizeof(smtp_password) - 1; - text = smtp_password; - - break; - - case ngx_smtp_auth_login_password: - arg = s->args.elts; - -#if (NGX_DEBUG_MAIL_PASSWD) - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "smtp auth login password: \"%V\"", &arg[0]); -#endif - - s->passwd.data = ngx_palloc(c->pool, - ngx_base64_decoded_length(arg[0].len)); - if (s->passwd.data == NULL){ - ngx_mail_session_internal_server_error(s); - return; - } - - if (ngx_decode_base64(&s->passwd, &arg[0]) != NGX_OK) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid base64 encoding " - "in AUTH LOGIN command"); - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - -#if (NGX_DEBUG_MAIL_PASSWD) - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "smtp auth login password: \"%V\"", &s->passwd); -#endif - - ngx_mail_do_auth(s); - return; - - case ngx_smtp_auth_plain: - arg = s->args.elts; - - rc = ngx_mail_decode_auth_plain(s, &arg[0]); - - if (rc == NGX_OK) { - ngx_mail_do_auth(s); - return; - } - - if (rc == NGX_ERROR) { - ngx_mail_session_internal_server_error(s); - return; - } - - /* rc == NGX_MAIL_PARSE_INVALID_COMMAND */ - - break; - - case ngx_smtp_auth_cram_md5: - arg = s->args.elts; - - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "smtp auth cram-md5: \"%V\"", &arg[0]); - - s->login.data = ngx_palloc(c->pool, - ngx_base64_decoded_length(arg[0].len)); - if (s->login.data == NULL){ - ngx_mail_session_internal_server_error(s); - return; - } - - if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid base64 encoding " - "in AUTH CRAM-MD5 command"); - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - p = s->login.data; - last = p + s->login.len; - - while (p < last) { - if (*p++ == ' ') { - s->login.len = p - s->login.data - 1; - s->passwd.len = last - p; - s->passwd.data = p; - break; - } - } - - if (s->passwd.len != 32) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid CRAM-MD5 hash " - "in AUTH CRAM-MD5 command"); - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; - } - - ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, - "smtp auth cram-md5: \"%V\" \"%V\"", - &s->login, &s->passwd); - - s->auth_method = NGX_MAIL_AUTH_CRAM_MD5; - - ngx_mail_do_auth(s); - return; - } - } - - if (rc == NGX_MAIL_PARSE_INVALID_COMMAND) { - s->mail_state = ngx_smtp_start; - s->state = 0; - text = smtp_invalid_command; - size = sizeof(smtp_invalid_command) - 1; - } - - s->args.nelts = 0; - s->buffer->pos = s->buffer->start; - s->buffer->last = s->buffer->start; - - if (s->state) { - s->arg_start = s->buffer->start; - } - - s->out.data = text; - s->out.len = size; - - ngx_mail_send(c->write); -} - - -static ngx_int_t -ngx_mail_decode_auth_plain(ngx_mail_session_t *s, ngx_str_t *encoded) -{ - u_char *p, *last; - ngx_str_t plain; - -#if (NGX_DEBUG_MAIL_PASSWD) - ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0, - "mail auth plain: \"%V\"", encoded); -#endif - - plain.data = ngx_palloc(s->connection->pool, - ngx_base64_decoded_length(encoded->len)); - if (plain.data == NULL){ - return NGX_ERROR; - } - - if (ngx_decode_base64(&plain, encoded) != NGX_OK) { - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "client sent invalid base64 encoding " - "in AUTH PLAIN command"); - return NGX_MAIL_PARSE_INVALID_COMMAND; - } - - p = plain.data; - last = p + plain.len; - - while (p < last && *p++) { /* void */ } - - if (p == last) { - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "client sent invalid login in AUTH PLAIN command"); - return NGX_MAIL_PARSE_INVALID_COMMAND; - } - - s->login.data = p; - - while (p < last && *p) { p++; } - - if (p == last) { - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "client sent invalid password in AUTH PLAIN command"); - return NGX_MAIL_PARSE_INVALID_COMMAND; - } - - s->login.len = p++ - s->login.data; - - s->passwd.len = last - p; - s->passwd.data = p; - -#if (NGX_DEBUG_MAIL_PASSWD) - ngx_log_debug2(NGX_LOG_DEBUG_MAIL, s->connection->log, 0, - "mail auth plain: \"%V\" \"%V\"", - &s->login, &s->passwd); -#endif - - return NGX_OK; -} - - -static void -ngx_mail_do_auth(ngx_mail_session_t *s) -{ - s->args.nelts = 0; - s->buffer->pos = s->buffer->start; - s->buffer->last = s->buffer->start; - s->state = 0; - - if (s->connection->read->timer_set) { - ngx_del_timer(s->connection->read); - } - - s->login_attempt++; - - ngx_mail_auth_http_init(s); -} - - -static ngx_int_t -ngx_mail_read_command(ngx_mail_session_t *s) -{ - ssize_t n; - ngx_int_t rc; - ngx_str_t l; - - n = s->connection->recv(s->connection, s->buffer->last, - s->buffer->end - s->buffer->last); + n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last); if (n == NGX_ERROR || n == 0) { - ngx_mail_close_connection(s->connection); + ngx_mail_close_connection(c); return NGX_ERROR; } @@ -2019,7 +559,7 @@ ngx_mail_read_command(ngx_mail_session_t } if (n == NGX_AGAIN) { - if (ngx_handle_read_event(s->connection->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { ngx_mail_session_internal_server_error(s); return NGX_ERROR; } @@ -2027,19 +567,9 @@ ngx_mail_read_command(ngx_mail_session_t return NGX_AGAIN; } - switch (s->protocol) { - case NGX_MAIL_POP3_PROTOCOL: - rc = ngx_pop3_parse_command(s); - break; + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - case NGX_MAIL_IMAP_PROTOCOL: - rc = ngx_imap_parse_command(s); - break; - - default: /* NGX_MAIL_SMTP_PROTOCOL */ - rc = ngx_smtp_parse_command(s); - break; - } + rc = cscf->protocol->parse_command(s); if (rc == NGX_AGAIN) { @@ -2050,7 +580,7 @@ ngx_mail_read_command(ngx_mail_session_t l.len = s->buffer->last - s->buffer->start; l.data = s->buffer->start; - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent too long command \"%V\"", &l); s->quit = 1; @@ -2063,7 +593,7 @@ ngx_mail_read_command(ngx_mail_session_t } if (rc == NGX_ERROR) { - ngx_mail_close_connection(s->connection); + ngx_mail_close_connection(c); return NGX_ERROR; } @@ -2072,9 +602,31 @@ ngx_mail_read_command(ngx_mail_session_t void +ngx_mail_auth(ngx_mail_session_t *s, ngx_connection_t *c) +{ + s->args.nelts = 0; + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + s->state = 0; + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + s->login_attempt++; + + ngx_mail_auth_http_init(s); +} + + +void ngx_mail_session_internal_server_error(ngx_mail_session_t *s) { - s->out = internal_server_errors[s->protocol]; + ngx_mail_core_srv_conf_t *cscf; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + s->out = cscf->protocol->internal_server_error; s->quit = 1; ngx_mail_send(s->connection->write); @@ -2114,7 +666,7 @@ ngx_mail_close_connection(ngx_connection } -static u_char * +u_char * ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len) { u_char *p; @@ -2139,7 +691,9 @@ ngx_mail_log_error(ngx_log_t *log, u_cha return p; } - p = ngx_snprintf(buf, len, ", server: %V", s->addr_text); + p = ngx_snprintf(buf, len, "%s, server: %V", + s->starttls ? " using starttls" : "", + s->addr_text); len -= p - buf; buf = p; diff --git a/src/mail/ngx_mail_imap_handler.c b/src/mail/ngx_mail_imap_handler.c new file mode 100644 --- /dev/null +++ b/src/mail/ngx_mail_imap_handler.c @@ -0,0 +1,459 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include +#include + + +static ngx_int_t ngx_mail_imap_login(ngx_mail_session_t *s, + ngx_connection_t *c); +static ngx_int_t ngx_mail_imap_authenticate(ngx_mail_session_t *s, + ngx_connection_t *c); +static ngx_int_t ngx_mail_imap_capability(ngx_mail_session_t *s, + ngx_connection_t *c); +static ngx_int_t ngx_mail_imap_starttls(ngx_mail_session_t *s, + ngx_connection_t *c); + + +static u_char imap_greeting[] = "* OK IMAP4 ready" CRLF; +static u_char imap_star[] = "* "; +static u_char imap_ok[] = "OK completed" CRLF; +static u_char imap_next[] = "+ OK" CRLF; +static u_char imap_plain_next[] = "+ " CRLF; +static u_char imap_username[] = "+ VXNlcm5hbWU6" CRLF; +static u_char imap_password[] = "+ UGFzc3dvcmQ6" CRLF; +static u_char imap_bye[] = "* BYE" CRLF; +static u_char imap_invalid_command[] = "BAD invalid command" CRLF; + + +void +ngx_mail_imap_init_session(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_mail_core_srv_conf_t *cscf; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + s->out.len = sizeof(imap_greeting) - 1; + s->out.data = imap_greeting; + + c->read->handler = ngx_mail_imap_init_protocol; + + ngx_add_timer(c->read, cscf->timeout); + + if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + ngx_mail_close_connection(c); + } + + ngx_mail_send(c->write); +} + + +void +ngx_mail_imap_init_protocol(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_mail_imap_srv_conf_t *iscf; + + c = rev->data; + + c->log->action = "in auth state"; + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_mail_close_connection(c); + return; + } + + s = c->data; + + if (s->buffer == NULL) { + if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) + == NGX_ERROR) + { + ngx_mail_session_internal_server_error(s); + return; + } + + iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module); + + s->buffer = ngx_create_temp_buf(c->pool, iscf->client_buffer_size); + if (s->buffer == NULL) { + ngx_mail_session_internal_server_error(s); + return; + } + } + + s->mail_state = ngx_imap_start; + c->read->handler = ngx_mail_imap_auth_state; + + ngx_mail_imap_auth_state(rev); +} + + +void +ngx_mail_imap_auth_state(ngx_event_t *rev) +{ + u_char *p, *dst, *src, *end; + ngx_str_t *arg; + ngx_int_t rc; + ngx_uint_t tag, i; + ngx_connection_t *c; + ngx_mail_session_t *s; + + c = rev->data; + s = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap auth state"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_mail_close_connection(c); + return; + } + + if (s->out.len) { + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap send handler busy"); + s->blocked = 1; + return; + } + + s->blocked = 0; + + rc = ngx_mail_read_command(s, c); + + if (rc == NGX_AGAIN || rc == NGX_ERROR) { + return; + } + + tag = 1; + s->text.len = 0; + s->out.len = sizeof(imap_ok) - 1; + s->out.data = imap_ok; + + if (rc == NGX_OK) { + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap auth command: %i", + s->command); + + if (s->backslash) { + + arg = s->args.elts; + + for (i = 0; i < s->args.nelts; i++) { + dst = arg[i].data; + end = dst + arg[i].len; + + for (src = dst; src < end; dst++) { + *dst = *src; + if (*src++ == '\\') { + *dst = *src++; + } + } + + arg[i].len = dst - arg[i].data; + } + + s->backslash = 0; + } + + switch (s->mail_state) { + + case ngx_imap_start: + + switch (s->command) { + + case NGX_IMAP_LOGIN: + rc = ngx_mail_imap_login(s, c); + break; + + case NGX_IMAP_AUTHENTICATE: + rc = ngx_mail_imap_authenticate(s, c); + tag = (rc != NGX_OK); + break; + + case NGX_IMAP_CAPABILITY: + rc = ngx_mail_imap_capability(s, c); + break; + + case NGX_IMAP_LOGOUT: + s->quit = 1; + s->text.len = sizeof(imap_bye) - 1; + s->text.data = imap_bye; + break; + + case NGX_IMAP_NOOP: + break; + + case NGX_IMAP_STARTTLS: + rc = ngx_mail_imap_starttls(s, c); + break; + + default: + rc = NGX_MAIL_PARSE_INVALID_COMMAND; + break; + } + + break; + + case ngx_imap_auth_login_username: + rc = ngx_mail_auth_login_username(s, c); + + tag = 0; + s->out.len = sizeof(imap_password) - 1; + s->out.data = imap_password; + s->mail_state = ngx_imap_auth_login_password; + + break; + + case ngx_imap_auth_login_password: + rc = ngx_mail_auth_login_password(s, c); + break; + + case ngx_imap_auth_plain: + rc = ngx_mail_auth_plain(s, c, 0); + break; + + case ngx_imap_auth_cram_md5: + rc = ngx_mail_auth_cram_md5(s, c); + break; + } + + } else if (rc == NGX_IMAP_NEXT) { + tag = 0; + s->out.len = sizeof(imap_next) - 1; + s->out.data = imap_next; + } + + switch (rc) { + + case NGX_DONE: + ngx_mail_auth(s, c); + return; + + case NGX_ERROR: + ngx_mail_session_internal_server_error(s); + return; + + case NGX_MAIL_PARSE_INVALID_COMMAND: + s->state = 0; + s->out.len = sizeof(imap_invalid_command) - 1; + s->out.data = imap_invalid_command; + s->mail_state = ngx_imap_start; + break; + } + + if (tag) { + if (s->tag.len == 0) { + s->tag.len = sizeof(imap_star) - 1; + s->tag.data = (u_char *) imap_star; + } + + if (s->tagged_line.len < s->tag.len + s->text.len + s->out.len) { + s->tagged_line.len = s->tag.len + s->text.len + s->out.len; + s->tagged_line.data = ngx_palloc(c->pool, s->tagged_line.len); + if (s->tagged_line.data == NULL) { + ngx_mail_close_connection(c); + return; + } + } + + p = s->tagged_line.data; + + if (s->text.len) { + p = ngx_cpymem(p, s->text.data, s->text.len); + } + + p = ngx_cpymem(p, s->tag.data, s->tag.len); + ngx_memcpy(p, s->out.data, s->out.len); + + s->out.len = s->text.len + s->tag.len + s->out.len; + s->out.data = s->tagged_line.data; + } + + if (rc != NGX_IMAP_NEXT) { + s->args.nelts = 0; + + if (s->state) { + /* preserve tag */ + s->arg_start = s->buffer->start + s->tag.len; + s->buffer->pos = s->arg_start; + s->buffer->last = s->arg_start; + + } else { + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + s->tag.len = 0; + } + } + + ngx_mail_send(c->write); +} + + +static ngx_int_t +ngx_mail_imap_login(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg; + +#if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +#endif + + arg = s->args.elts; + + if (s->args.nelts != 2 || arg[0].len == 0) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + s->login.len = arg[0].len; + s->login.data = ngx_palloc(c->pool, s->login.len); + if (s->login.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->login.data, arg[0].data, s->login.len); + + s->passwd.len = arg[1].len; + s->passwd.data = ngx_palloc(c->pool, s->passwd.len); + if (s->passwd.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len); + +#if (NGX_DEBUG_MAIL_PASSWD) + ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, + "imap login:\"%V\" passwd:\"%V\"", + &s->login, &s->passwd); +#else + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "imap login:\"%V\"", &s->login); +#endif + + return NGX_DONE; +} + + +static ngx_int_t +ngx_mail_imap_authenticate(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_int_t rc; + ngx_mail_core_srv_conf_t *cscf; + ngx_mail_imap_srv_conf_t *iscf; + +#if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +#endif + + rc = ngx_mail_auth_parse(s, c); + + switch (rc) { + + case NGX_MAIL_AUTH_LOGIN: + + s->out.len = sizeof(imap_username) - 1; + s->out.data = imap_username; + s->mail_state = ngx_imap_auth_login_username; + + return NGX_OK; + + case NGX_MAIL_AUTH_PLAIN: + + s->out.len = sizeof(imap_plain_next) - 1; + s->out.data = imap_plain_next; + s->mail_state = ngx_imap_auth_plain; + + return NGX_OK; + + case NGX_MAIL_AUTH_CRAM_MD5: + + iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module); + + if (!(iscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + if (s->salt.data == NULL) { + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + if (ngx_mail_salt(s, c, cscf) != NGX_OK) { + return NGX_ERROR; + } + } + + if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) { + s->mail_state = ngx_imap_auth_cram_md5; + return NGX_OK; + } + + return NGX_ERROR; + } + + return rc; +} + + +static ngx_int_t +ngx_mail_imap_capability(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_mail_imap_srv_conf_t *iscf; +#if (NGX_MAIL_SSL) + ngx_mail_ssl_conf_t *sslcf; +#endif + + iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module); + +#if (NGX_MAIL_SSL) + + if (c->ssl == NULL) { + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); + + if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) { + s->text = iscf->starttls_capability; + return NGX_OK; + } + + if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { + s->text = iscf->starttls_only_capability; + return NGX_OK; + } + } +#endif + + s->text = iscf->capability; + + return NGX_OK; +} + + +static ngx_int_t +ngx_mail_imap_starttls(ngx_mail_session_t *s, ngx_connection_t *c) +{ +#if (NGX_MAIL_SSL) + ngx_mail_ssl_conf_t *sslcf; + + if (c->ssl == NULL) { + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); + if (sslcf->starttls) { + c->read->handler = ngx_mail_starttls_handler; + return NGX_OK; + } + } + +#endif + + return NGX_MAIL_PARSE_INVALID_COMMAND; +} diff --git a/src/mail/ngx_mail_imap_module.c b/src/mail/ngx_mail_imap_module.c new file mode 100644 --- /dev/null +++ b/src/mail/ngx_mail_imap_module.c @@ -0,0 +1,251 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include +#include + + +static void *ngx_mail_imap_create_srv_conf(ngx_conf_t *cf); +static char *ngx_mail_imap_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); + + +static ngx_str_t ngx_mail_imap_default_capabilities[] = { + ngx_string("IMAP4"), + ngx_string("IMAP4rev1"), + ngx_string("UIDPLUS"), + ngx_null_string +}; + + +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_null_string, 0 } +}; + + +static ngx_str_t ngx_mail_imap_auth_methods_names[] = { + ngx_string("AUTH=PLAIN"), + ngx_string("AUTH=LOGIN"), + ngx_null_string, /* APOP */ + ngx_string("AUTH=CRAM-MD5") +}; + + +static ngx_mail_protocol_t ngx_mail_imap_protocol = { + ngx_string("imap"), + { 143, 993, 0, 0 }, + NGX_MAIL_IMAP_PROTOCOL, + + ngx_mail_imap_init_session, + ngx_mail_imap_init_protocol, + ngx_mail_imap_parse_command, + ngx_mail_imap_auth_state, + + ngx_string("* BAD internal server error" CRLF) +}; + + +static ngx_command_t ngx_mail_imap_commands[] = { + + { ngx_string("imap_client_buffer"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_imap_srv_conf_t, client_buffer_size), + NULL }, + + { ngx_string("imap_capabilities"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, + ngx_mail_capabilities, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_imap_srv_conf_t, capabilities), + NULL }, + + { ngx_string("imap_auth"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, + ngx_conf_set_bitmask_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_imap_srv_conf_t, auth_methods), + &ngx_mail_imap_auth_methods }, + + ngx_null_command +}; + + +static ngx_mail_module_t ngx_mail_imap_module_ctx = { + &ngx_mail_imap_protocol, /* protocol */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_mail_imap_create_srv_conf, /* create server configuration */ + ngx_mail_imap_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_mail_imap_module = { + NGX_MODULE_V1, + &ngx_mail_imap_module_ctx, /* module context */ + ngx_mail_imap_commands, /* module directives */ + NGX_MAIL_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static void * +ngx_mail_imap_create_srv_conf(ngx_conf_t *cf) +{ + ngx_mail_imap_srv_conf_t *iscf; + + iscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_imap_srv_conf_t)); + if (iscf == NULL) { + return NULL; + } + + iscf->client_buffer_size = NGX_CONF_UNSET_SIZE; + + if (ngx_array_init(&iscf->capabilities, cf->pool, 4, sizeof(ngx_str_t)) + != NGX_OK) + { + return NULL; + } + + return iscf; +} + + +static char * +ngx_mail_imap_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_mail_imap_srv_conf_t *prev = parent; + ngx_mail_imap_srv_conf_t *conf = child; + + u_char *p, *auth; + size_t size; + ngx_str_t *c, *d; + ngx_uint_t i, m; + + ngx_conf_merge_size_value(conf->client_buffer_size, + prev->client_buffer_size, + (size_t) ngx_pagesize); + + ngx_conf_merge_bitmask_value(conf->auth_methods, + prev->auth_methods, + (NGX_CONF_BITMASK_SET + |NGX_MAIL_AUTH_PLAIN_ENABLED)); + + + if (conf->capabilities.nelts == 0) { + conf->capabilities = prev->capabilities; + } + + if (conf->capabilities.nelts == 0) { + + for (d = ngx_mail_imap_default_capabilities; d->len; d++) { + c = ngx_array_push(&conf->capabilities); + if (c == NULL) { + return NGX_CONF_ERROR; + } + + *c = *d; + } + } + + size = sizeof("* CAPABILITY" CRLF) - 1; + + c = conf->capabilities.elts; + for (i = 0; i < conf->capabilities.nelts; i++) { + size += 1 + c[i].len; + } + + for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; + m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED; + m <<= 1, i++) + { + if (m & conf->auth_methods) { + size += 1 + ngx_mail_imap_auth_methods_names[i].len; + } + } + + p = ngx_palloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->capability.len = size; + conf->capability.data = p; + + p = ngx_cpymem(p, "* CAPABILITY", sizeof("* CAPABILITY") - 1); + + for (i = 0; i < conf->capabilities.nelts; i++) { + *p++ = ' '; + p = ngx_cpymem(p, c[i].data, c[i].len); + } + + auth = p; + + for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; + m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED; + m <<= 1, i++) + { + if (m & conf->auth_methods) { + *p++ = ' '; + p = ngx_cpymem(p, ngx_mail_imap_auth_methods_names[i].data, + ngx_mail_imap_auth_methods_names[i].len); + } + } + + *p++ = CR; *p = LF; + + + size += sizeof(" STARTTLS") - 1; + + p = ngx_palloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->starttls_capability.len = size; + conf->starttls_capability.data = p; + + p = ngx_cpymem(p, conf->capability.data, + conf->capability.len - (sizeof(CRLF) - 1)); + p = ngx_cpymem(p, " STARTTLS", sizeof(" STARTTLS") - 1); + *p++ = CR; *p = LF; + + + size = (auth - conf->capability.data) + sizeof(CRLF) - 1 + + sizeof(" STARTTLS LOGINDISABLED") - 1; + + p = ngx_palloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->starttls_only_capability.len = size; + conf->starttls_only_capability.data = p; + + p = ngx_cpymem(p, conf->capability.data, + auth - conf->capability.data); + p = ngx_cpymem(p, " STARTTLS LOGINDISABLED", + sizeof(" STARTTLS LOGINDISABLED") - 1); + *p++ = CR; *p = LF; + + return NGX_CONF_OK; +} diff --git a/src/mail/ngx_mail_imap_module.h b/src/mail/ngx_mail_imap_module.h new file mode 100644 --- /dev/null +++ b/src/mail/ngx_mail_imap_module.h @@ -0,0 +1,38 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_MAIL_IMAP_MODULE_H_INCLUDED_ +#define _NGX_MAIL_IMAP_MODULE_H_INCLUDED_ + + +#include +#include +#include + + +typedef struct { + size_t client_buffer_size; + + ngx_str_t capability; + ngx_str_t starttls_capability; + ngx_str_t starttls_only_capability; + + ngx_uint_t auth_methods; + + ngx_array_t capabilities; +} ngx_mail_imap_srv_conf_t; + + +void ngx_mail_imap_init_session(ngx_mail_session_t *s, ngx_connection_t *c); +void ngx_mail_imap_init_protocol(ngx_event_t *rev); +void ngx_mail_imap_auth_state(ngx_event_t *rev); +ngx_int_t ngx_mail_imap_parse_command(ngx_mail_session_t *s); + + +extern ngx_module_t ngx_mail_imap_module; + + +#endif /* _NGX_MAIL_IMAP_MODULE_H_INCLUDED_ */ diff --git a/src/mail/ngx_mail_parse.c b/src/mail/ngx_mail_parse.c --- a/src/mail/ngx_mail_parse.c +++ b/src/mail/ngx_mail_parse.c @@ -10,7 +10,8 @@ #include -ngx_int_t ngx_pop3_parse_command(ngx_mail_session_t *s) +ngx_int_t +ngx_mail_pop3_parse_command(ngx_mail_session_t *s) { u_char ch, *p, *c, c0, c1, c2, c3; ngx_str_t *arg; @@ -207,7 +208,8 @@ invalid: } -ngx_int_t ngx_imap_parse_command(ngx_mail_session_t *s) +ngx_int_t +ngx_mail_imap_parse_command(ngx_mail_session_t *s) { u_char ch, *p, *c; ngx_str_t *arg; @@ -613,7 +615,8 @@ invalid: } -ngx_int_t ngx_smtp_parse_command(ngx_mail_session_t *s) +ngx_int_t +ngx_mail_smtp_parse_command(ngx_mail_session_t *s) { u_char ch, *p, *c, c0, c1, c2, c3; ngx_str_t *arg; @@ -822,3 +825,56 @@ invalid: return NGX_MAIL_PARSE_INVALID_COMMAND; } + + +ngx_int_t +ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg; + +#if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +#endif + + arg = s->args.elts; + + if (arg[0].len == 5) { + + if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5) == 0) { + + if (s->args.nelts == 1) { + return NGX_MAIL_AUTH_LOGIN; + } + + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN", 5) == 0) { + + if (s->args.nelts == 1) { + return NGX_MAIL_AUTH_PLAIN; + } + + if (s->args.nelts == 2) { + return ngx_mail_auth_plain(s, c, 1); + } + } + + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + 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) { + return NGX_MAIL_AUTH_CRAM_MD5; + } + } + + return NGX_MAIL_PARSE_INVALID_COMMAND; +} diff --git a/src/mail/ngx_mail_pop3_handler.c b/src/mail/ngx_mail_pop3_handler.c new file mode 100644 --- /dev/null +++ b/src/mail/ngx_mail_pop3_handler.c @@ -0,0 +1,501 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include +#include + + +static ngx_int_t ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_pop3_pass(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c, + ngx_int_t stls); +static ngx_int_t ngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c); + + +static u_char pop3_greeting[] = "+OK POP3 ready" CRLF; +static u_char pop3_ok[] = "+OK" CRLF; +static u_char pop3_next[] = "+ " CRLF; +static u_char pop3_username[] = "+ VXNlcm5hbWU6" CRLF; +static u_char pop3_password[] = "+ UGFzc3dvcmQ6" CRLF; +static u_char pop3_invalid_command[] = "-ERR invalid command" CRLF; + + +void +ngx_mail_pop3_init_session(ngx_mail_session_t *s, ngx_connection_t *c) +{ + u_char *p; + ngx_mail_core_srv_conf_t *cscf; + ngx_mail_pop3_srv_conf_t *pscf; + + pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module); + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + if (pscf->auth_methods + & (NGX_MAIL_AUTH_APOP_ENABLED|NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) + { + if (ngx_mail_salt(s, c, cscf) != NGX_OK) { + ngx_mail_session_internal_server_error(s); + return; + } + + s->out.data = ngx_palloc(c->pool, sizeof(pop3_greeting) + s->salt.len); + if (s->out.data == NULL) { + ngx_mail_session_internal_server_error(s); + return; + } + + p = ngx_cpymem(s->out.data, pop3_greeting, sizeof(pop3_greeting) - 3); + *p++ = ' '; + p = ngx_cpymem(p, s->salt.data, s->salt.len); + + s->out.len = p - s->out.data; + + } else { + s->out.len = sizeof(pop3_greeting) - 1; + s->out.data = pop3_greeting; + } + + c->read->handler = ngx_mail_pop3_init_protocol; + + ngx_add_timer(c->read, cscf->timeout); + + if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + ngx_mail_close_connection(c); + } + + ngx_mail_send(c->write); +} + + +void +ngx_mail_pop3_init_protocol(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + + c = rev->data; + + c->log->action = "in auth state"; + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_mail_close_connection(c); + return; + } + + s = c->data; + + if (s->buffer == NULL) { + if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) + == NGX_ERROR) + { + ngx_mail_session_internal_server_error(s); + return; + } + + s->buffer = ngx_create_temp_buf(c->pool, 128); + if (s->buffer == NULL) { + ngx_mail_session_internal_server_error(s); + return; + } + } + + s->mail_state = ngx_pop3_start; + c->read->handler = ngx_mail_pop3_auth_state; + + ngx_mail_pop3_auth_state(rev); +} + + +void +ngx_mail_pop3_auth_state(ngx_event_t *rev) +{ + ngx_int_t rc; + ngx_connection_t *c; + ngx_mail_session_t *s; + + c = rev->data; + s = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 auth state"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_mail_close_connection(c); + return; + } + + if (s->out.len) { + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 send handler busy"); + s->blocked = 1; + return; + } + + s->blocked = 0; + + rc = ngx_mail_read_command(s, c); + + if (rc == NGX_AGAIN || rc == NGX_ERROR) { + return; + } + + s->out.len = sizeof(pop3_ok) - 1; + s->out.data = pop3_ok; + + if (rc == NGX_OK) { + switch (s->mail_state) { + + case ngx_pop3_start: + + switch (s->command) { + + case NGX_POP3_USER: + rc = ngx_mail_pop3_user(s, c); + break; + + case NGX_POP3_CAPA: + rc = ngx_mail_pop3_capa(s, c, 1); + break; + + case NGX_POP3_APOP: + rc = ngx_mail_pop3_apop(s, c); + break; + + case NGX_POP3_AUTH: + rc = ngx_mail_pop3_auth(s, c); + break; + + case NGX_POP3_QUIT: + s->quit = 1; + break; + + case NGX_POP3_NOOP: + break; + + case NGX_POP3_STLS: + rc = ngx_mail_pop3_stls(s, c); + break; + + default: + rc = NGX_MAIL_PARSE_INVALID_COMMAND; + s->mail_state = ngx_pop3_start; + break; + } + + break; + + case ngx_pop3_user: + + switch (s->command) { + + case NGX_POP3_PASS: + rc = ngx_mail_pop3_pass(s, c); + break; + + case NGX_POP3_CAPA: + rc = ngx_mail_pop3_capa(s, c, 0); + break; + + case NGX_POP3_QUIT: + s->quit = 1; + break; + + case NGX_POP3_NOOP: + break; + + default: + rc = NGX_MAIL_PARSE_INVALID_COMMAND; + s->mail_state = ngx_pop3_start; + break; + } + + break; + + /* suppress warinings */ + case ngx_pop3_passwd: + break; + + case ngx_pop3_auth_login_username: + rc = ngx_mail_auth_login_username(s, c); + + s->out.len = sizeof(pop3_password) - 1; + s->out.data = pop3_password; + s->mail_state = ngx_pop3_auth_login_password; + break; + + case ngx_pop3_auth_login_password: + rc = ngx_mail_auth_login_password(s, c); + break; + + case ngx_pop3_auth_plain: + rc = ngx_mail_auth_plain(s, c, 0); + break; + + case ngx_pop3_auth_cram_md5: + rc = ngx_mail_auth_cram_md5(s, c); + break; + } + } + + switch (rc) { + + case NGX_DONE: + ngx_mail_auth(s, c); + return; + + case NGX_ERROR: + ngx_mail_session_internal_server_error(s); + return; + + case NGX_MAIL_PARSE_INVALID_COMMAND: + s->mail_state = ngx_pop3_start; + s->state = 0; + + s->out.len = sizeof(pop3_invalid_command) - 1; + s->out.data = pop3_invalid_command; + + /* fall through */ + + case NGX_OK: + + s->args.nelts = 0; + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + + if (s->state) { + s->arg_start = s->buffer->start; + } + + ngx_mail_send(c->write); + } +} + +static ngx_int_t +ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg; + +#if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +#endif + + if (s->args.nelts != 1) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + arg = s->args.elts; + s->login.len = arg[0].len; + s->login.data = ngx_palloc(c->pool, s->login.len); + if (s->login.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->login.data, arg[0].data, s->login.len); + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "pop3 login: \"%V\"", &s->login); + + s->mail_state = ngx_pop3_user; + + return NGX_OK; +} + + +static ngx_int_t +ngx_mail_pop3_pass(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg; + + if (s->args.nelts != 1) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + arg = s->args.elts; + s->passwd.len = arg[0].len; + s->passwd.data = ngx_palloc(c->pool, s->passwd.len); + if (s->passwd.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->passwd.data, arg[0].data, s->passwd.len); + +#if (NGX_DEBUG_MAIL_PASSWD) + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "pop3 passwd: \"%V\"", &s->passwd); +#endif + + return NGX_DONE; +} + + +static ngx_int_t +ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c, ngx_int_t stls) +{ + ngx_mail_pop3_srv_conf_t *pscf; +#if (NGX_MAIL_SSL) + ngx_mail_ssl_conf_t *sslcf; +#endif + + pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module); + +#if (NGX_MAIL_SSL) + + if (stls && c->ssl == NULL) { + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); + + if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) { + s->out = pscf->starttls_capability; + return NGX_OK; + } + + if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { + s->out = pscf->starttls_only_capability; + return NGX_OK; + } + } + +#endif + + s->out = pscf->capability; + return NGX_OK; +} + + +static ngx_int_t +ngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c) +{ +#if (NGX_MAIL_SSL) + ngx_mail_ssl_conf_t *sslcf; + + if (c->ssl == NULL) { + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); + if (sslcf->starttls) { + c->read->handler = ngx_mail_starttls_handler; + return NGX_OK; + } + } + +#endif + + return NGX_MAIL_PARSE_INVALID_COMMAND; +} + + +static ngx_int_t +ngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg; + ngx_mail_pop3_srv_conf_t *pscf; + +#if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +#endif + + if (s->args.nelts != 2) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module); + + if (!(pscf->auth_methods & NGX_MAIL_AUTH_APOP_ENABLED)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + arg = s->args.elts; + + s->login.len = arg[0].len; + s->login.data = ngx_palloc(c->pool, s->login.len); + if (s->login.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->login.data, arg[0].data, s->login.len); + + s->passwd.len = arg[1].len; + s->passwd.data = ngx_palloc(c->pool, s->passwd.len); + if (s->passwd.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len); + + ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, + "pop3 apop: \"%V\" \"%V\"", &s->login, &s->passwd); + + s->auth_method = NGX_MAIL_AUTH_APOP; + + return NGX_DONE; +} + + +static ngx_int_t +ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_int_t rc; + ngx_mail_pop3_srv_conf_t *pscf; + +#if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +#endif + + pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module); + + if (s->args.nelts == 0) { + s->out = pscf->auth_capability; + s->state = 0; + + return NGX_OK; + } + + rc = ngx_mail_auth_parse(s, c); + + switch (rc) { + + case NGX_MAIL_AUTH_LOGIN: + + s->out.len = sizeof(pop3_username) - 1; + s->out.data = pop3_username; + s->mail_state = ngx_pop3_auth_login_username; + + return NGX_OK; + + case NGX_MAIL_AUTH_PLAIN: + + s->out.len = sizeof(pop3_next) - 1; + s->out.data = pop3_next; + s->mail_state = ngx_pop3_auth_plain; + + return NGX_OK; + + case NGX_MAIL_AUTH_CRAM_MD5: + + if (!(pscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) { + s->mail_state = ngx_pop3_auth_cram_md5; + return NGX_OK; + } + + return NGX_ERROR; + } + + return rc; +} diff --git a/src/mail/ngx_mail_pop3_module.c b/src/mail/ngx_mail_pop3_module.c new file mode 100644 --- /dev/null +++ b/src/mail/ngx_mail_pop3_module.c @@ -0,0 +1,263 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include +#include + + +static void *ngx_mail_pop3_create_srv_conf(ngx_conf_t *cf); +static char *ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); + + +static ngx_str_t ngx_mail_pop3_default_capabilities[] = { + ngx_string("TOP"), + ngx_string("USER"), + ngx_string("UIDL"), + ngx_null_string +}; + + +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_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_mail_protocol_t ngx_mail_pop3_protocol = { + ngx_string("pop3"), + { 110, 995, 0, 0 }, + NGX_MAIL_POP3_PROTOCOL, + + ngx_mail_pop3_init_session, + ngx_mail_pop3_init_protocol, + ngx_mail_pop3_parse_command, + ngx_mail_pop3_auth_state, + + ngx_string("-ERR internal server error" CRLF) +}; + + +static ngx_command_t ngx_mail_pop3_commands[] = { + + { ngx_string("pop3_capabilities"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, + ngx_mail_capabilities, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_pop3_srv_conf_t, capabilities), + NULL }, + + { ngx_string("pop3_auth"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, + ngx_conf_set_bitmask_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_pop3_srv_conf_t, auth_methods), + &ngx_mail_pop3_auth_methods }, + + ngx_null_command +}; + + +static ngx_mail_module_t ngx_mail_pop3_module_ctx = { + &ngx_mail_pop3_protocol, /* protocol */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_mail_pop3_create_srv_conf, /* create server configuration */ + ngx_mail_pop3_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_mail_pop3_module = { + NGX_MODULE_V1, + &ngx_mail_pop3_module_ctx, /* module context */ + ngx_mail_pop3_commands, /* module directives */ + NGX_MAIL_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static void * +ngx_mail_pop3_create_srv_conf(ngx_conf_t *cf) +{ + ngx_mail_pop3_srv_conf_t *pscf; + + pscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_pop3_srv_conf_t)); + if (pscf == NULL) { + return NULL; + } + + if (ngx_array_init(&pscf->capabilities, cf->pool, 4, sizeof(ngx_str_t)) + != NGX_OK) + { + return NULL; + } + + return pscf; +} + + +static char * +ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_mail_pop3_srv_conf_t *prev = parent; + ngx_mail_pop3_srv_conf_t *conf = child; + + u_char *p; + size_t size, stls_only_size; + ngx_str_t *c, *d; + ngx_uint_t i; + + ngx_conf_merge_bitmask_value(conf->auth_methods, + prev->auth_methods, + (NGX_CONF_BITMASK_SET + |NGX_MAIL_AUTH_PLAIN_ENABLED)); + + if (conf->capabilities.nelts == 0) { + conf->capabilities = prev->capabilities; + } + + if (conf->capabilities.nelts == 0) { + + for (d = ngx_mail_pop3_default_capabilities; d->len; d++) { + c = ngx_array_push(&conf->capabilities); + if (c == NULL) { + return NGX_CONF_ERROR; + } + + *c = *d; + } + } + + size = sizeof("+OK Capability list follows" CRLF) - 1 + + sizeof("." CRLF) - 1; + + stls_only_size = size + sizeof("STLS" CRLF) - 1; + + c = conf->capabilities.elts; + for (i = 0; i < conf->capabilities.nelts; i++) { + size += c[i].len + sizeof(CRLF) - 1; + + if (ngx_strcasecmp(c[i].data, (u_char *) "USER") == 0) { + continue; + } + + 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; + + } else { + size += sizeof("SASL LOGIN PLAIN" CRLF) - 1; + } + + p = ngx_palloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->capability.len = size; + conf->capability.data = p; + + p = ngx_cpymem(p, "+OK Capability list follows" CRLF, + sizeof("+OK Capability list follows" CRLF) - 1); + + for (i = 0; i < conf->capabilities.nelts; i++) { + p = ngx_cpymem(p, c[i].data, c[i].len); + *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); + + } else { + p = ngx_cpymem(p, "SASL LOGIN PLAIN" CRLF, + sizeof("SASL LOGIN PLAIN" CRLF) - 1); + } + + *p++ = '.'; *p++ = CR; *p = LF; + + + size += sizeof("STLS" CRLF) - 1; + + p = ngx_palloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->starttls_capability.len = size; + conf->starttls_capability.data = p; + + p = ngx_cpymem(p, conf->capability.data, + conf->capability.len - (sizeof("." CRLF) - 1)); + + p = ngx_cpymem(p, "STLS" CRLF, sizeof("STLS" CRLF) - 1); + *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; + + } else { + conf->auth_capability = ngx_mail_pop3_auth_plain_capability; + } + + + p = ngx_palloc(cf->pool, stls_only_size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->starttls_only_capability.len = stls_only_size; + conf->starttls_only_capability.data = p; + + p = ngx_cpymem(p, "+OK Capability list follows" CRLF, + sizeof("+OK Capability list follows" CRLF) - 1); + + for (i = 0; i < conf->capabilities.nelts; i++) { + if (ngx_strcasecmp(c[i].data, (u_char *) "USER") == 0) { + continue; + } + + p = ngx_cpymem(p, c[i].data, c[i].len); + *p++ = CR; *p++ = LF; + } + + p = ngx_cpymem(p, "STLS" CRLF, sizeof("STLS" CRLF) - 1); + *p++ = '.'; *p++ = CR; *p = LF; + + return NGX_CONF_OK; +} diff --git a/src/mail/ngx_mail_pop3_module.h b/src/mail/ngx_mail_pop3_module.h new file mode 100644 --- /dev/null +++ b/src/mail/ngx_mail_pop3_module.h @@ -0,0 +1,37 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_MAIL_POP3_MODULE_H_INCLUDED_ +#define _NGX_MAIL_POP3_MODULE_H_INCLUDED_ + + +#include +#include +#include + + +typedef struct { + ngx_str_t capability; + ngx_str_t starttls_capability; + ngx_str_t starttls_only_capability; + ngx_str_t auth_capability; + + ngx_uint_t auth_methods; + + ngx_array_t capabilities; +} ngx_mail_pop3_srv_conf_t; + + +void ngx_mail_pop3_init_session(ngx_mail_session_t *s, ngx_connection_t *c); +void ngx_mail_pop3_init_protocol(ngx_event_t *rev); +void ngx_mail_pop3_auth_state(ngx_event_t *rev); +ngx_int_t ngx_mail_pop3_parse_command(ngx_mail_session_t *s); + + +extern ngx_module_t ngx_mail_pop3_module; + + +#endif /* _NGX_MAIL_POP3_MODULE_H_INCLUDED_ */ diff --git a/src/mail/ngx_mail_proxy_module.c b/src/mail/ngx_mail_proxy_module.c --- a/src/mail/ngx_mail_proxy_module.c +++ b/src/mail/ngx_mail_proxy_module.c @@ -78,6 +78,8 @@ static ngx_command_t ngx_mail_proxy_com static ngx_mail_module_t ngx_mail_proxy_module_ctx = { + NULL, /* protocol */ + NULL, /* create main configuration */ NULL, /* init main configuration */ diff --git a/src/mail/ngx_mail_smtp_handler.c b/src/mail/ngx_mail_smtp_handler.c new file mode 100644 --- /dev/null +++ b/src/mail/ngx_mail_smtp_handler.c @@ -0,0 +1,548 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include +#include + + +static void ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev); +static ngx_int_t ngx_mail_smtp_create_buffer(ngx_mail_session_t *s, + ngx_connection_t *c); + +static ngx_int_t ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_smtp_starttls(ngx_mail_session_t *s, + ngx_connection_t *c); + +static ngx_int_t ngx_mail_smtp_discard_command(ngx_mail_session_t *s, + ngx_connection_t *c, char *err); +static void ngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s, + ngx_connection_t *c, char *err); + + +static u_char smtp_ok[] = "250 2.0.0 OK" CRLF; +static u_char smtp_bye[] = "221 2.0.0 Bye" CRLF; +static u_char smtp_next[] = "334 " CRLF; +static u_char smtp_username[] = "334 VXNlcm5hbWU6" CRLF; +static u_char smtp_password[] = "334 UGFzc3dvcmQ6" CRLF; +static u_char smtp_invalid_command[] = "500 5.5.1 Invalid command" CRLF; +static u_char smtp_invalid_pipelining[] = + "503 5.5.0 Improper use of SMTP command pipelining" CRLF; +static u_char smtp_invalid_argument[] = "501 5.5.4 Invalid argument" CRLF; +static u_char smtp_auth_required[] = "530 5.7.1 Authentication required" CRLF; + + +void +ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_msec_t timeout; + ngx_mail_core_srv_conf_t *cscf; + ngx_mail_smtp_srv_conf_t *sscf; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); + + timeout = sscf->greeting_delay ? sscf->greeting_delay : cscf->timeout; + ngx_add_timer(c->read, timeout); + + if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + ngx_mail_close_connection(c); + } + + if (sscf->greeting_delay) { + c->read->handler = ngx_mail_smtp_invalid_pipelining; + return; + } + + c->read->handler = ngx_mail_smtp_init_protocol; + + s->out = sscf->greeting; + + ngx_mail_send(c->write); +} + + +static void +ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_mail_core_srv_conf_t *cscf; + ngx_mail_smtp_srv_conf_t *sscf; + + c = rev->data; + s = c->data; + + c->log->action = "in delay pipelining state"; + + if (rev->timedout) { + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "delay greeting"); + + rev->timedout = 0; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + c->read->handler = ngx_mail_smtp_init_protocol; + + ngx_add_timer(c->read, cscf->timeout); + + if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + ngx_mail_close_connection(c); + return; + } + + sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); + + s->out = sscf->greeting; + + } else { + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "invalid pipelining"); + + if (s->buffer == NULL) { + if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) { + return; + } + } + + if (ngx_mail_smtp_discard_command(s, c, + "client was rejected before greeting: \"%V\"") + != NGX_OK) + { + return; + } + + s->out.len = sizeof(smtp_invalid_pipelining) - 1; + s->out.data = smtp_invalid_pipelining; + } + + ngx_mail_send(c->write); +} + + +void +ngx_mail_smtp_init_protocol(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + + c = rev->data; + + c->log->action = "in auth state"; + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_mail_close_connection(c); + return; + } + + s = c->data; + + if (s->buffer == NULL) { + if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) { + return; + } + } + + s->mail_state = ngx_smtp_start; + c->read->handler = ngx_mail_smtp_auth_state; + + ngx_mail_smtp_auth_state(rev); +} + + +static ngx_int_t +ngx_mail_smtp_create_buffer(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_mail_smtp_srv_conf_t *sscf; + + if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) == NGX_ERROR) { + ngx_mail_session_internal_server_error(s); + return NGX_ERROR; + } + + sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); + + s->buffer = ngx_create_temp_buf(c->pool, sscf->client_buffer_size); + if (s->buffer == NULL) { + ngx_mail_session_internal_server_error(s); + return NGX_ERROR; + } + + return NGX_OK; +} + + +void +ngx_mail_smtp_auth_state(ngx_event_t *rev) +{ + ngx_int_t rc; + ngx_connection_t *c; + ngx_mail_session_t *s; + + c = rev->data; + s = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp auth state"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_mail_close_connection(c); + return; + } + + if (s->out.len) { + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp send handler busy"); + s->blocked = 1; + return; + } + + s->blocked = 0; + + rc = ngx_mail_read_command(s, c); + + if (rc == NGX_AGAIN || rc == NGX_ERROR) { + return; + } + + s->out.len = sizeof(smtp_ok) - 1; + s->out.data = smtp_ok; + + if (rc == NGX_OK) { + switch (s->mail_state) { + + case ngx_smtp_start: + + switch (s->command) { + + case NGX_SMTP_HELO: + case NGX_SMTP_EHLO: + rc = ngx_mail_smtp_helo(s, c); + break; + + case NGX_SMTP_AUTH: + rc = ngx_mail_smtp_auth(s, c); + break; + + case NGX_SMTP_QUIT: + s->quit = 1; + s->out.len = sizeof(smtp_bye) - 1; + s->out.data = smtp_bye; + break; + + case NGX_SMTP_MAIL: + rc = ngx_mail_smtp_mail(s, c); + break; + + case NGX_SMTP_NOOP: + case NGX_SMTP_RSET: + break; + + case NGX_SMTP_STARTTLS: + rc = ngx_mail_smtp_starttls(s, c); + break; + + default: + rc = NGX_MAIL_PARSE_INVALID_COMMAND; + break; + } + + break; + + case ngx_smtp_auth_login_username: + rc = ngx_mail_auth_login_username(s, c); + + s->out.len = sizeof(smtp_password) - 1; + s->out.data = smtp_password; + s->mail_state = ngx_smtp_auth_login_password; + break; + + case ngx_smtp_auth_login_password: + rc = ngx_mail_auth_login_password(s, c); + break; + + case ngx_smtp_auth_plain: + rc = ngx_mail_auth_plain(s, c, 0); + break; + + case ngx_smtp_auth_cram_md5: + rc = ngx_mail_auth_cram_md5(s, c); + break; + } + } + + switch (rc) { + + case NGX_DONE: + ngx_mail_auth(s, c); + return; + + case NGX_ERROR: + ngx_mail_session_internal_server_error(s); + return; + + case NGX_MAIL_PARSE_INVALID_COMMAND: + s->mail_state = ngx_smtp_start; + s->state = 0; + + s->out.len = sizeof(smtp_invalid_command) - 1; + s->out.data = smtp_invalid_command; + + /* fall through */ + + case NGX_OK: + s->args.nelts = 0; + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + + if (s->state) { + s->arg_start = s->buffer->start; + } + + ngx_mail_send(c->write); + } +} + + +static ngx_int_t +ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg; + ngx_mail_smtp_srv_conf_t *sscf; +#if (NGX_MAIL_SSL) + ngx_mail_ssl_conf_t *sslcf; +#endif + + if (s->args.nelts != 1) { + s->out.len = sizeof(smtp_invalid_argument) - 1; + s->out.data = smtp_invalid_argument; + s->state = 0; + return NGX_OK; + } + + arg = s->args.elts; + + s->smtp_helo.len = arg[0].len; + + s->smtp_helo.data = ngx_palloc(c->pool, arg[0].len); + if (s->smtp_helo.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->smtp_helo.data, arg[0].data, arg[0].len); + + sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); + + if (s->command == NGX_SMTP_HELO) { + s->out = sscf->server_name; + + } else { + s->esmtp = 1; + +#if (NGX_MAIL_SSL) + + if (c->ssl == NULL) { + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); + + if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) { + s->out = sscf->starttls_capability; + return NGX_OK; + } + + if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { + s->out = sscf->starttls_only_capability; + return NGX_OK; + } + } +#endif + + s->out = sscf->capability; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_int_t rc; + ngx_mail_core_srv_conf_t *cscf; + ngx_mail_smtp_srv_conf_t *sscf; + +#if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +#endif + + if (s->args.nelts == 0) { + s->out.len = sizeof(smtp_invalid_argument) - 1; + s->out.data = smtp_invalid_argument; + s->state = 0; + return NGX_OK; + } + + rc = ngx_mail_auth_parse(s, c); + + switch (rc) { + + case NGX_MAIL_AUTH_LOGIN: + + s->out.len = sizeof(smtp_username) - 1; + s->out.data = smtp_username; + s->mail_state = ngx_smtp_auth_login_username; + + return NGX_OK; + + case NGX_MAIL_AUTH_PLAIN: + + s->out.len = sizeof(smtp_next) - 1; + s->out.data = smtp_next; + s->mail_state = ngx_smtp_auth_plain; + + return NGX_OK; + + case NGX_MAIL_AUTH_CRAM_MD5: + + sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); + + if (!(sscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + if (s->salt.data == NULL) { + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + if (ngx_mail_salt(s, c, cscf) != NGX_OK) { + return NGX_ERROR; + } + } + + if (ngx_mail_auth_cram_md5_salt(s, c, "334 ", 4) == NGX_OK) { + s->mail_state = ngx_smtp_auth_cram_md5; + return NGX_OK; + } + + return NGX_ERROR; + } + + return rc; +} + + +static ngx_int_t +ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_mail_smtp_log_rejected_command(s, c, "client was rejected: \"%V\""); + + s->out.len = sizeof(smtp_auth_required) - 1; + s->out.data = smtp_auth_required; + + return NGX_OK; +} + + +static ngx_int_t +ngx_mail_smtp_starttls(ngx_mail_session_t *s, ngx_connection_t *c) +{ +#if (NGX_MAIL_SSL) + ngx_mail_ssl_conf_t *sslcf; + + if (c->ssl == NULL) { + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); + if (sslcf->starttls) { + + /* + * RFC3207 requires us to discard any knowledge + * obtained from client before STARTTLS. + */ + + s->smtp_helo.len = 0; + s->smtp_helo.data = NULL; + + c->read->handler = ngx_mail_starttls_handler; + return NGX_OK; + } + } + +#endif + + return NGX_MAIL_PARSE_INVALID_COMMAND; +} + + +static ngx_int_t +ngx_mail_smtp_discard_command(ngx_mail_session_t *s, ngx_connection_t *c, + char *err) +{ + ssize_t n; + + n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last); + + if (n == NGX_ERROR || n == 0) { + ngx_mail_close_connection(c); + return NGX_ERROR; + } + + if (n > 0) { + s->buffer->last += n; + } + + if (n == NGX_AGAIN) { + if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + ngx_mail_session_internal_server_error(s); + return NGX_ERROR; + } + + return NGX_AGAIN; + } + + ngx_mail_smtp_log_rejected_command(s, c, err); + + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + + return NGX_OK; +} + + +static void +ngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s, ngx_connection_t *c, + char *err) +{ + u_char ch; + ngx_str_t cmd; + ngx_uint_t i; + + if (c->log->log_level < NGX_LOG_INFO) { + return; + } + + cmd.len = s->buffer->last - s->buffer->start; + cmd.data = s->buffer->start; + + for (i = 0; i < cmd.len; i++) { + ch = cmd.data[i]; + + if (ch != CR && ch != LF) { + continue; + } + + cmd.data[i] = '_'; + } + + cmd.len = i; + + ngx_log_error(NGX_LOG_INFO, c->log, 0, err, &cmd); +} diff --git a/src/mail/ngx_mail_smtp_module.c b/src/mail/ngx_mail_smtp_module.c new file mode 100644 --- /dev/null +++ b/src/mail/ngx_mail_smtp_module.c @@ -0,0 +1,285 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include +#include + + +static void *ngx_mail_smtp_create_srv_conf(ngx_conf_t *cf); +static char *ngx_mail_smtp_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); + + +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_null_string, 0 } +}; + + +static ngx_str_t ngx_mail_smtp_auth_methods_names[] = { + ngx_string("PLAIN"), + ngx_string("LOGIN"), + ngx_null_string, /* APOP */ + ngx_string("CRAM-MD5") +}; + + +static ngx_mail_protocol_t ngx_mail_smtp_protocol = { + ngx_string("smtp"), + { 25, 465, 587, 0 }, + NGX_MAIL_SMTP_PROTOCOL, + + ngx_mail_smtp_init_session, + ngx_mail_smtp_init_protocol, + ngx_mail_smtp_parse_command, + ngx_mail_smtp_auth_state, + + ngx_string("451 4.3.2 Internal server error" CRLF) +}; + + +static ngx_command_t ngx_mail_smtp_commands[] = { + + { ngx_string("smtp_client_buffer"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_smtp_srv_conf_t, client_buffer_size), + NULL }, + + { ngx_string("smtp_greeting_delay"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_smtp_srv_conf_t, greeting_delay), + NULL }, + + { ngx_string("smtp_capabilities"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, + ngx_mail_capabilities, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_smtp_srv_conf_t, capabilities), + NULL }, + + { ngx_string("smtp_auth"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, + ngx_conf_set_bitmask_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_smtp_srv_conf_t, auth_methods), + &ngx_mail_smtp_auth_methods }, + + ngx_null_command +}; + + +static ngx_mail_module_t ngx_mail_smtp_module_ctx = { + &ngx_mail_smtp_protocol, /* protocol */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_mail_smtp_create_srv_conf, /* create server configuration */ + ngx_mail_smtp_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_mail_smtp_module = { + NGX_MODULE_V1, + &ngx_mail_smtp_module_ctx, /* module context */ + ngx_mail_smtp_commands, /* module directives */ + NGX_MAIL_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static void * +ngx_mail_smtp_create_srv_conf(ngx_conf_t *cf) +{ + ngx_mail_smtp_srv_conf_t *sscf; + + sscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_smtp_srv_conf_t)); + if (sscf == NULL) { + return NULL; + } + + sscf->client_buffer_size = NGX_CONF_UNSET_SIZE; + sscf->greeting_delay = NGX_CONF_UNSET_MSEC; + + if (ngx_array_init(&sscf->capabilities, cf->pool, 4, sizeof(ngx_str_t)) + != NGX_OK) + { + return NULL; + } + + return sscf; +} + + +static char * +ngx_mail_smtp_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_mail_smtp_srv_conf_t *prev = parent; + ngx_mail_smtp_srv_conf_t *conf = child; + + u_char *p, *auth; + size_t size; + ngx_str_t *c; + ngx_uint_t i, m; + ngx_mail_core_srv_conf_t *cscf; + + ngx_conf_merge_size_value(conf->client_buffer_size, + prev->client_buffer_size, + (size_t) ngx_pagesize); + + ngx_conf_merge_msec_value(conf->greeting_delay, + prev->greeting_delay, 0); + + ngx_conf_merge_bitmask_value(conf->auth_methods, + prev->auth_methods, + (NGX_CONF_BITMASK_SET + |NGX_MAIL_AUTH_PLAIN_ENABLED + |NGX_MAIL_AUTH_LOGIN_ENABLED)); + + + cscf = ngx_mail_conf_get_module_srv_conf(cf, ngx_mail_core_module); + + size = sizeof("220 ESMTP ready" CRLF) - 1 + cscf->server_name.len; + + p = ngx_palloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->greeting.len = size; + conf->greeting.data = p; + + *p++ = '2'; *p++ = '2'; *p++ = '0'; *p++ = ' '; + p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len); + ngx_memcpy(p, " ESMTP ready" CRLF, sizeof(" ESMTP ready" CRLF) - 1); + + + size = sizeof("250 " CRLF) - 1 + cscf->server_name.len; + + p = ngx_palloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->server_name.len = size; + conf->server_name.data = p; + + *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' '; + p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len); + *p++ = CR; *p = LF; + + + if (conf->capabilities.nelts == 0) { + conf->capabilities = prev->capabilities; + } + + size = sizeof("250-") - 1 + cscf->server_name.len + sizeof(CRLF) - 1 + + sizeof("250 AUTH") - 1 + sizeof(CRLF) - 1; + + c = conf->capabilities.elts; + for (i = 0; i < conf->capabilities.nelts; i++) { + size += sizeof("250 ") - 1 + c[i].len + sizeof(CRLF) - 1; + } + + for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; + m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED; + m <<= 1, i++) + { + if (m & conf->auth_methods) { + size += 1 + ngx_mail_smtp_auth_methods_names[i].len; + } + } + + p = ngx_palloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->capability.len = size; + conf->capability.data = p; + + *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-'; + p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len); + *p++ = CR; *p++ = LF; + + for (i = 0; i < conf->capabilities.nelts; i++) { + *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-'; + p = ngx_cpymem(p, c[i].data, c[i].len); + *p++ = CR; *p++ = LF; + } + + auth = p; + + *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' '; + *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 <<= 1, i++) + { + if (m & conf->auth_methods) { + *p++ = ' '; + p = ngx_cpymem(p, ngx_mail_smtp_auth_methods_names[i].data, + ngx_mail_smtp_auth_methods_names[i].len); + } + } + + *p++ = CR; *p = LF; + + size += sizeof("250 STARTTLS" CRLF) - 1; + + p = ngx_palloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->starttls_capability.len = size; + conf->starttls_capability.data = p; + + p = ngx_cpymem(p, conf->capability.data, + conf->capability.len); + + p = ngx_cpymem(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1); + *p++ = CR; *p = LF; + + p = conf->starttls_capability.data + + (auth - conf->capability.data) + 3; + *p = '-'; + + size = (auth - conf->capability.data) + + sizeof("250 STARTTLS" CRLF) - 1; + + p = ngx_palloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->starttls_only_capability.len = size; + conf->starttls_only_capability.data = p; + + p = ngx_cpymem(p, conf->capability.data, + auth - conf->capability.data); + + ngx_memcpy(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1); + + return NGX_CONF_OK; +} diff --git a/src/mail/ngx_mail_smtp_module.h b/src/mail/ngx_mail_smtp_module.h new file mode 100644 --- /dev/null +++ b/src/mail/ngx_mail_smtp_module.h @@ -0,0 +1,44 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_MAIL_SMTP_MODULE_H_INCLUDED_ +#define _NGX_MAIL_SMTP_MODULE_H_INCLUDED_ + + +#include +#include +#include +#include + + +typedef struct { + ngx_msec_t greeting_delay; + + size_t client_buffer_size; + + ngx_str_t capability; + ngx_str_t starttls_capability; + ngx_str_t starttls_only_capability; + + ngx_str_t server_name; + ngx_str_t greeting; + + ngx_uint_t auth_methods; + + ngx_array_t capabilities; +} ngx_mail_smtp_srv_conf_t; + + +void ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c); +void ngx_mail_smtp_init_protocol(ngx_event_t *rev); +void ngx_mail_smtp_auth_state(ngx_event_t *rev); +ngx_int_t ngx_mail_smtp_parse_command(ngx_mail_session_t *s); + + +extern ngx_module_t ngx_mail_smtp_module; + + +#endif /* _NGX_MAIL_SMTP_MODULE_H_INCLUDED_ */ diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c --- a/src/mail/ngx_mail_ssl_module.c +++ b/src/mail/ngx_mail_ssl_module.c @@ -120,6 +120,8 @@ static ngx_command_t ngx_mail_ssl_comma static ngx_mail_module_t ngx_mail_ssl_module_ctx = { + NULL, /* protocol */ + NULL, /* create main configuration */ NULL, /* init main configuration */ diff --git a/src/os/unix/ngx_aio_write_chain.c b/src/os/unix/ngx_aio_write_chain.c --- a/src/os/unix/ngx_aio_write_chain.c +++ b/src/os/unix/ngx_aio_write_chain.c @@ -21,7 +21,7 @@ ngx_aio_write_chain(ngx_connection_t *c, /* the maximum limit size is the maximum size_t value - the page size */ - if (limit == 0 || limit > NGX_MAX_SIZE_T_VALUE - ngx_pagesize) { + if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) { limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize; } diff --git a/src/os/unix/ngx_freebsd_config.h b/src/os/unix/ngx_freebsd_config.h --- a/src/os/unix/ngx_freebsd_config.h +++ b/src/os/unix/ngx_freebsd_config.h @@ -105,7 +105,7 @@ pid_t rfork_thread(int flags, void *stac extern char **environ; -extern char *malloc_options; +extern char *malloc_options; #endif /* _NGX_FREEBSD_CONFIG_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_freebsd_sendfile_chain.c b/src/os/unix/ngx_freebsd_sendfile_chain.c --- a/src/os/unix/ngx_freebsd_sendfile_chain.c +++ b/src/os/unix/ngx_freebsd_sendfile_chain.c @@ -72,7 +72,7 @@ ngx_freebsd_sendfile_chain(ngx_connectio /* the maximum limit size is the maximum size_t value - the page size */ - if (limit == 0 || limit > NGX_MAX_SIZE_T_VALUE - ngx_pagesize) { + if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) { limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize; } diff --git a/src/os/unix/ngx_linux_sendfile_chain.c b/src/os/unix/ngx_linux_sendfile_chain.c --- a/src/os/unix/ngx_linux_sendfile_chain.c +++ b/src/os/unix/ngx_linux_sendfile_chain.c @@ -62,7 +62,7 @@ ngx_linux_sendfile_chain(ngx_connection_ /* the maximum limit size is 2G-1 - the page size */ - if (limit == 0 || limit > NGX_SENDFILE_LIMIT - ngx_pagesize) { + if (limit == 0 || limit > (off_t) (NGX_SENDFILE_LIMIT - ngx_pagesize)) { limit = NGX_SENDFILE_LIMIT - ngx_pagesize; } diff --git a/src/os/unix/ngx_solaris_sendfilev_chain.c b/src/os/unix/ngx_solaris_sendfilev_chain.c --- a/src/os/unix/ngx_solaris_sendfilev_chain.c +++ b/src/os/unix/ngx_solaris_sendfilev_chain.c @@ -67,7 +67,7 @@ ngx_solaris_sendfilev_chain(ngx_connecti /* the maximum limit size is the maximum size_t value - the page size */ - if (limit == 0 || limit > NGX_MAX_SIZE_T_VALUE - ngx_pagesize) { + if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) { limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize; } diff --git a/src/os/unix/ngx_writev_chain.c b/src/os/unix/ngx_writev_chain.c --- a/src/os/unix/ngx_writev_chain.c +++ b/src/os/unix/ngx_writev_chain.c @@ -48,7 +48,7 @@ ngx_writev_chain(ngx_connection_t *c, ng /* the maximum limit size is the maximum size_t value - the page size */ - if (limit == 0 || limit > NGX_MAX_SIZE_T_VALUE - ngx_pagesize) { + if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) { limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize; }