# HG changeset patch # User Igor Sysoev # Date 1161547200 -14400 # Node ID fbf2b2f66c9fecf6f597aba60020fbdd9b339b33 # Parent 7a34085272cb94fe6600b70f87c076a9a0488e4f nginx 0.4.10 *) Feature: the POP3 proxy supports the APOP command. *) Bugfix: if the select, poll or /dev/poll methods were used, then while waiting authentication server response the IMAP/POP3 proxy hogged CPU. *) Bugfix: a segmentation fault might occur if the $server_addr variable was used in the "map" directive. *) Bugfix: the ngx_http_flv_module did not support the byte ranges for full responses; bug appeared in 0.4.7. *) Bugfix: nginx could not be built on Debian amd64; bug appeared in 0.4.9. diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,22 @@ +Changes with nginx 0.4.10 23 Oct 2006 + + *) Feature: the IMAP/POP3 proxy supports the APOP command. + + *) Bugfix: if the select, poll or /dev/poll methods were used, then + while waiting authentication server response the IMAP/POP3 proxy + hogged CPU. + + *) Bugfix: a segmentation fault might occur if the $server_addr + variable was used in the "map" directive. + + *) Bugfix: the ngx_http_flv_module did not support the byte ranges for + full responses; bug appeared in 0.4.7. + + *) Bugfix: nginx could not be built on Debian amd64; bug appeared in + 0.4.9. + + Changes with nginx 0.4.9 13 Oct 2006 *) Feature: the "set" parameter in the "include" SSI command. @@ -1576,7 +1594,7 @@ Changes with nginx 0.1.29 Changes with nginx 0.1.28 08 Apr 2005 - *) Bugfix: nginx hogs CPU while proxing the huge files. + *) Bugfix: nginx hogs CPU while proxying the huge files. *) Bugfix: nginx could not be built by gcc 4.0 on Linux. @@ -1687,7 +1705,7 @@ Changes with nginx 0.1.23 Changes with nginx 0.1.22 22 Feb 2005 *) Bugfix: the ngx_http_stub_status_module showed incorrect handled - connections statistics if the proxing or FastCGI server were used. + connections statistics if the proxying or FastCGI server were used. *) Bugfix: the installation paths were incorrectly quoted on Linux and Solaris; bug appeared in 0.1.21. @@ -2051,7 +2069,7 @@ Changes with nginx 0.1.1 *) Feature: the setproctitle() emulation for Linux and Solaris. - *) Bugfix: the "Location" header rewrite bug fixed while the proxing. + *) Bugfix: the "Location" header rewrite bug fixed while the proxying. *) Bugfix: the ngx_http_chunked_module module may get caught in an endless loop. @@ -2059,7 +2077,7 @@ Changes with nginx 0.1.1 *) Bugfix: the /dev/poll module bugs fixed. *) Bugfix: the responses were corrupted when the temporary files were - used while the proxing. + used while the proxying. *) Bugfix: the unescaped requests were passed to the backend. diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,22 @@ +Изменения в nginx 0.4.10 23.10.2006 + + *) Добавление: IMAP/POP3 прокси поддерживает APOP. + + *) Исправление: при использовании методов select, poll и /dev/poll во + время ожидания ответа от сервера аутентификации IMAP/POP3 прокси + нагружал процессор. + + *) Исправление: при использовании переменной $server_addr в директиве + map мог произойти segmentation fault. + + *) Исправление: модуль ngx_http_flv_module не поддерживал byte ranges + для полных ответов; ошибка появилась в 0.4.7. + + *) Исправление: nginx не собирался на Debian amd64; ошибка появилась в + 0.4.9. + + Изменения в nginx 0.4.9 13.10.2006 *) Добавление: параметр set в команде SSI include. diff --git a/auto/sources b/auto/sources --- a/auto/sources +++ b/auto/sources @@ -20,6 +20,7 @@ CORE_DEPS="src/core/nginx.h \ src/core/ngx_inet.h \ src/core/ngx_file.h \ src/core/ngx_crc.h \ + src/core/ngx_crc32.h \ src/core/ngx_rbtree.h \ src/core/ngx_radix_tree.h \ src/core/ngx_times.h \ @@ -42,6 +43,7 @@ CORE_SRCS="src/core/nginx.c \ src/core/ngx_parse.c \ src/core/ngx_inet.c \ src/core/ngx_file.c \ + src/core/ngx_crc32.c \ src/core/ngx_rbtree.c \ src/core/ngx_radix_tree.c \ src/core/ngx_times.c \ diff --git a/src/core/nginx.c b/src/core/nginx.c --- a/src/core/nginx.c +++ b/src/core/nginx.c @@ -283,6 +283,10 @@ main(int argc, char *const *argv) ngx_os_status(cycle->log); + if (ngx_crc32_init(cycle->pool) != NGX_OK) { + return 1; + } + ngx_cycle = cycle; ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); 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.4.9" +#define NGINX_VERSION "0.4.10" #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 @@ -49,6 +49,8 @@ #define ngx_signal_helper(n) SIG##n #define ngx_signal_value(n) ngx_signal_helper(n) +#define ngx_random random + /* TODO: #ifndef */ #define NGX_SHUTDOWN_SIGNAL QUIT #define NGX_TERMINATE_SIGNAL TERM diff --git a/src/core/ngx_core.h b/src/core/ngx_core.h --- a/src/core/ngx_core.h +++ b/src/core/ngx_core.h @@ -57,6 +57,7 @@ typedef void (*ngx_connection_handler_pt #include #include #include +#include #if (NGX_PCRE) #include #endif diff --git a/src/core/ngx_crc.h b/src/core/ngx_crc.h --- a/src/core/ngx_crc.h +++ b/src/core/ngx_crc.h @@ -8,10 +8,14 @@ #define _NGX_CRC_H_INCLUDED_ +#include +#include + + /* 32-bit crc16 */ static ngx_inline uint32_t -ngx_crc(char *data, size_t len) +ngx_crc(u_char *data, size_t len) { uint32_t sum; diff --git a/src/core/ngx_crc32.c b/src/core/ngx_crc32.c new file mode 100644 --- /dev/null +++ b/src/core/ngx_crc32.c @@ -0,0 +1,128 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +/* + * The code and lookup tables are based on the algorithm + * described at http://www.w3.org/TR/PNG/ + * + * The 256 element lookup table takes 1024 bytes, and it may be completely + * cached after processing about 30-60 bytes. So for short messages + * we use the 16 element lookup table that takes only 64 bytes and align it + * to CPU cache line size. Of course, the small table adds code inside + * CRC32 cycle, but cache misses overhead is bigger than overhead of + * the additional code. For example, ngx_crc32_short() of 16 byte message + * takes half as much CPU clocks than ngx_crc32_long(). + */ + + +static uint32_t ngx_crc32_table16[] = { + 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, + 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, + 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c +}; + + +uint32_t ngx_crc32_table256[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + + +uint32_t *ngx_crc32_table_short = ngx_crc32_table16; + + +ngx_int_t +ngx_crc32_init(ngx_pool_t *pool) +{ + void *p; + + if (((uintptr_t) ngx_crc32_table_short + & ~((uintptr_t) ngx_cacheline_size - 1)) + == (uintptr_t) ngx_crc32_table_short) + { + return NGX_OK; + } + + p = ngx_palloc(pool, 16 * sizeof(uint32_t) + ngx_cacheline_size); + if (p == NULL) { + return NGX_ERROR; + } + + p = ngx_align_ptr(p, ngx_cacheline_size); + + ngx_memcpy(p, ngx_crc32_table16, 16 * sizeof(uint32_t)); + + ngx_crc32_table_short = p; + + return NGX_OK; +} diff --git a/src/core/ngx_crc32.h b/src/core/ngx_crc32.h new file mode 100644 --- /dev/null +++ b/src/core/ngx_crc32.h @@ -0,0 +1,55 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_CRC32_H_INCLUDED_ +#define _NGX_CRC32_H_INCLUDED_ + + +#include +#include + + +extern uint32_t *ngx_crc32_table_short; +extern uint32_t ngx_crc32_table256[]; + + +static ngx_inline uint32_t +ngx_crc32_short(u_char *p, size_t len) +{ + u_char c; + uint32_t crc; + + crc = 0xffffffff; + + while (len--) { + c = *p++; + crc = ngx_crc32_table_short[(crc ^ (c & 0xf)) & 0xf] ^ (crc >> 4); + crc = ngx_crc32_table_short[(crc ^ (c >> 4)) & 0xf] ^ (crc >> 4); + } + + return crc ^ 0xffffffff; +} + + +static ngx_inline uint32_t +ngx_crc32_long(u_char *p, size_t len) +{ + uint32_t crc; + + crc = 0xffffffff; + + while (len--) { + crc = ngx_crc32_table256[(crc ^ *p++) & 0xff] ^ (crc >> 8); + } + + return crc ^ 0xffffffff; +} + + +ngx_int_t ngx_crc32_init(ngx_pool_t *pool); + + +#endif /* _NGX_CRC32_H_INCLUDED_ */ diff --git a/src/core/ngx_file.c b/src/core/ngx_file.c --- a/src/core/ngx_file.c +++ b/src/core/ngx_file.c @@ -9,7 +9,7 @@ static ngx_atomic_uint_t ngx_temp_number; -static ngx_atomic_uint_t ngx_random; +static ngx_atomic_uint_t ngx_random_number; ssize_t @@ -216,7 +216,7 @@ void ngx_init_temp_number(void) { ngx_temp_number = 0; - ngx_random = 123456; + ngx_random_number = 123456; } @@ -224,7 +224,7 @@ ngx_atomic_uint_t ngx_next_temp_number(ngx_uint_t collision) { if (collision) { - ngx_temp_number += ngx_random; + ngx_temp_number += ngx_random_number; } return ngx_temp_number++; 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 @@ -593,7 +593,7 @@ ngx_atotm(u_char *line, size_t n) ngx_int_t ngx_hextoi(u_char *line, size_t n) { - u_char ch; + u_char c, ch; ngx_int_t value; if (n == 0) { @@ -608,13 +608,10 @@ ngx_hextoi(u_char *line, size_t n) continue; } - if (ch >= 'A' && ch <= 'F') { - value = value * 16 + (ch - 'A' + 10); - continue; - } + c = (u_char) (ch | 0x20); - if (ch >= 'a' && ch <= 'f') { - value = value * 16 + (ch - 'a' + 10); + if (c >= 'a' && c <= 'f') { + value = value * 16 + (c - 'a' + 10); continue; } diff --git a/src/http/modules/ngx_http_charset_filter_module.c b/src/http/modules/ngx_http_charset_filter_module.c --- a/src/http/modules/ngx_http_charset_filter_module.c +++ b/src/http/modules/ngx_http_charset_filter_module.c @@ -250,6 +250,10 @@ ngx_http_charset_header_filter(ngx_http_ vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR); + if (vv == NULL || vv->not_found) { + return NGX_ERROR; + } + charset = ngx_http_charset_get_charset(charsets, n, (ngx_str_t *) vv); } @@ -293,6 +297,10 @@ ngx_http_charset_header_filter(ngx_http_ vv = ngx_http_get_indexed_variable(r, source_charset - NGX_HTTP_CHARSET_VAR); + if (vv == NULL || vv->not_found) { + return NGX_ERROR; + } + source_charset = ngx_http_charset_get_charset(charsets, n, (ngx_str_t *) vv); } 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 @@ -214,8 +214,12 @@ ngx_http_flv_handler(ngx_http_request_t out[0].buf = b; out[0].next = &out[1]; + + } else { + r->allow_ranges = 1; } + b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; 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 @@ -115,6 +115,11 @@ ngx_http_map_variable(ngx_http_request_t vv = ngx_http_get_flushed_variable(r, map->index); + if (vv == NULL || vv->not_found) { + *v = *map->default_value; + return NGX_OK; + } + len = vv->len; if (len && map->hostnames && vv->data[len - 1] == '.') { 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 @@ -1554,7 +1554,7 @@ ngx_http_ssi_get_variable(ngx_http_reque if (part->next == NULL) { break; } - + part = part->next; var = part->elts; i = 0; @@ -1843,7 +1843,7 @@ ngx_http_ssi_include(ngx_http_request_t ngx_str_t *uri, *file, *wait, *set, *stub, args; ngx_buf_t *b; ngx_uint_t flags, i; - ngx_chain_t *cl, *tl, **ll; + ngx_chain_t *cl, *tl, **ll, *out; ngx_http_request_t *sr; ngx_http_ssi_var_t *var; ngx_http_ssi_ctx_t *mctx; @@ -1947,7 +1947,7 @@ ngx_http_ssi_include(ngx_http_request_t if (bl[i].count++) { - ll = (ngx_chain_t **) &psr->data; + ll = &out; for (tl = bl[i].bufs; tl; tl = tl->next) { @@ -1979,6 +1979,8 @@ ngx_http_ssi_include(ngx_http_request_t ll = &cl->next; } + psr->data = out; + } else { psr->data = bl[i].bufs; } diff --git a/src/http/modules/ngx_http_userid_filter_module.c b/src/http/modules/ngx_http_userid_filter_module.c --- a/src/http/modules/ngx_http_userid_filter_module.c +++ b/src/http/modules/ngx_http_userid_filter_module.c @@ -348,10 +348,12 @@ ngx_http_userid_set_uid(ngx_http_request if (r->in_addr == 0) { slen = sizeof(struct sockaddr_in); if (getsockname(r->connection->fd, - (struct sockaddr *) &sin, &slen) == -1) + (struct sockaddr *) &sin, &slen) + == -1) { - ngx_log_error(NGX_LOG_CRIT, r->connection->log, - ngx_socket_errno, "getsockname() failed"); + ngx_connection_error(r->connection, ngx_socket_errno, + "getsockname() failed"); + return NGX_ERROR; } r->in_addr = sin.sin_addr.s_addr; 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 @@ -17,7 +17,7 @@ our @EXPORT = qw( HTTP_SERVER_ERROR ); -our $VERSION = '0.4.9'; +our $VERSION = '0.4.10'; require XSLoader; XSLoader::load('nginx', $VERSION); diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -537,6 +537,8 @@ ngx_http_parse_header_line(ngx_http_requ sw_header_almost_done } state; + /* the last '\0' is not needed because string is zero terminated */ + static u_char lowcase[] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0" 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 @@ -1167,7 +1167,7 @@ ngx_http_upstream_process_header(ngx_eve if (hh && hh->redirect) { if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) { - ngx_http_finalize_request(r, + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -749,8 +749,7 @@ ngx_http_variable_server_addr(ngx_http_r if (r->in_addr == 0) { len = sizeof(struct sockaddr_in); if (getsockname(c->fd, (struct sockaddr *) &sin, &len) == -1) { - ngx_log_error(NGX_LOG_CRIT, c->log, - ngx_socket_errno, "getsockname() failed"); + ngx_connection_error(c, ngx_socket_errno, "getsockname() failed"); return NGX_ERROR; } diff --git a/src/imap/ngx_imap.h b/src/imap/ngx_imap.h --- a/src/imap/ngx_imap.h +++ b/src/imap/ngx_imap.h @@ -87,6 +87,10 @@ typedef struct { ngx_str_t imap_starttls_capability; ngx_str_t imap_starttls_only_capability; + ngx_str_t server_name; + + ngx_uint_t auth_methods; + ngx_array_t pop3_capabilities; ngx_array_t imap_capabilities; @@ -149,10 +153,12 @@ typedef struct { unsigned backslash:1; unsigned no_sync_literal:1; unsigned starttls:1; + unsigned auth_method:1; ngx_str_t login; ngx_str_t passwd; + ngx_str_t salt; ngx_str_t tag; ngx_str_t tagged_line; @@ -179,29 +185,37 @@ typedef struct { } ngx_imap_log_ctx_t; -#define NGX_POP3_USER 1 -#define NGX_POP3_PASS 2 -#define NGX_POP3_CAPA 3 -#define NGX_POP3_QUIT 4 -#define NGX_POP3_NOOP 5 -#define NGX_POP3_STLS 6 -#define NGX_POP3_APOP 7 -#define NGX_POP3_STAT 8 -#define NGX_POP3_LIST 9 -#define NGX_POP3_RETR 10 -#define NGX_POP3_DELE 11 -#define NGX_POP3_RSET 12 -#define NGX_POP3_TOP 13 -#define NGX_POP3_UIDL 14 +#define NGX_POP3_USER 1 +#define NGX_POP3_PASS 2 +#define NGX_POP3_CAPA 3 +#define NGX_POP3_QUIT 4 +#define NGX_POP3_NOOP 5 +#define NGX_POP3_STLS 6 +#define NGX_POP3_APOP 7 +#define NGX_POP3_STAT 8 +#define NGX_POP3_LIST 9 +#define NGX_POP3_RETR 10 +#define NGX_POP3_DELE 11 +#define NGX_POP3_RSET 12 +#define NGX_POP3_TOP 13 +#define NGX_POP3_UIDL 14 -#define NGX_IMAP_LOGIN 1 -#define NGX_IMAP_LOGOUT 2 -#define NGX_IMAP_CAPABILITY 3 -#define NGX_IMAP_NOOP 4 -#define NGX_IMAP_STARTTLS 5 +#define NGX_IMAP_LOGIN 1 +#define NGX_IMAP_LOGOUT 2 +#define NGX_IMAP_CAPABILITY 3 +#define NGX_IMAP_NOOP 4 +#define NGX_IMAP_STARTTLS 5 + +#define NGX_IMAP_NEXT 6 -#define NGX_IMAP_NEXT 6 + +#define NGX_IMAP_AUTH_PLAIN 0 +#define NGX_IMAP_AUTH_APOP 1 + + +#define NGX_IMAP_AUTH_PLAIN_ENABLED 0x0002 +#define NGX_IMAP_AUTH_APOP_ENABLED 0x0004 #define NGX_IMAP_PARSE_INVALID_COMMAND 20 diff --git a/src/imap/ngx_imap_auth_http_module.c b/src/imap/ngx_imap_auth_http_module.c --- a/src/imap/ngx_imap_auth_http_module.c +++ b/src/imap/ngx_imap_auth_http_module.c @@ -131,7 +131,10 @@ ngx_module_t ngx_imap_auth_http_module }; -static char *ngx_imap_auth_http_protocol[] = { "pop3", "imap" }; +static char *ngx_imap_auth_http_protocol[] = { "pop3", "imap" }; +static ngx_str_t ngx_imap_auth_http_method[] = { + ngx_string("plain"), ngx_string("apop") +}; void @@ -250,6 +253,12 @@ ngx_imap_auth_http_write_handler(ngx_eve ngx_del_timer(wev); } + if (ngx_handle_write_event(wev, 0) == NGX_ERROR) { + ngx_close_connection(ctx->peer.connection); + ngx_destroy_pool(ctx->pool); + ngx_imap_session_internal_server_error(s); + } + return; } } @@ -552,6 +561,25 @@ ngx_imap_auth_http_process_headers(ngx_i continue; } + if (len == sizeof("Auth-Pass") - 1 + && ngx_strncasecmp(ctx->header_name_start, "Auth-Pass", + sizeof("Auth-Pass") - 1) == 0) + { + s->passwd.len = ctx->header_end - ctx->header_start; + + s->passwd.data = ngx_palloc(s->connection->pool, s->passwd.len); + if (s->passwd.data == NULL) { + ngx_close_connection(ctx->peer.connection); + ngx_destroy_pool(ctx->pool); + ngx_imap_session_internal_server_error(s); + return; + } + + ngx_memcpy(s->passwd.data, ctx->header_start, s->passwd.len); + + continue; + } + if (len == sizeof("Auth-Wait") - 1 && ngx_strncasecmp(ctx->header_name_start, "Auth-Wait", sizeof("Auth-Wait") - 1) == 0) @@ -608,6 +636,15 @@ ngx_imap_auth_http_process_headers(ngx_i return; } + if (s->passwd.data == NULL) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "auth http server %V did not send password", + &ctx->peer.peers->peer[0].name); + ngx_destroy_pool(ctx->pool); + ngx_imap_session_internal_server_error(s); + return; + } + peers = ngx_pcalloc(s->connection->pool, sizeof(ngx_peers_t)); if (peers == NULL) { ngx_destroy_pool(ctx->pool); @@ -725,6 +762,8 @@ ngx_imap_auth_sleep_handler(ngx_event_t s->connection->read->handler = ngx_imap_auth_state; } + s->auth_method = NGX_IMAP_AUTH_PLAIN; + c->log->action = "in auth state"; ngx_imap_send(s->connection->write); @@ -1001,6 +1040,7 @@ ngx_imap_auth_http_create_request(ngx_im + sizeof("Auth-Method: plain" CRLF) - 1 + 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-Login-Attempt: ") - 1 + NGX_INT_T_LEN + sizeof(CRLF) - 1 @@ -1023,8 +1063,12 @@ ngx_imap_auth_http_create_request(ngx_im ahcf->host_header.len); *b->last++ = CR; *b->last++ = LF; - b->last = ngx_cpymem(b->last, "Auth-Method: plain" CRLF, - sizeof("Auth-Method: plain" CRLF) - 1); + b->last = ngx_cpymem(b->last, "Auth-Method: ", + sizeof("Auth-Method: ") - 1); + b->last = ngx_cpymem(b->last, + ngx_imap_auth_http_method[s->auth_method].data, + ngx_imap_auth_http_method[s->auth_method].len); + *b->last++ = CR; *b->last++ = LF; b->last = ngx_cpymem(b->last, "Auth-User: ", sizeof("Auth-User: ") - 1); b->last = ngx_copy(b->last, login.data, login.len); @@ -1034,6 +1078,13 @@ ngx_imap_auth_http_create_request(ngx_im b->last = ngx_copy(b->last, passwd.data, passwd.len); *b->last++ = CR; *b->last++ = LF; + if (s->salt.len) { + b->last = ngx_cpymem(b->last, "Auth-Salt: ", sizeof("Auth-Salt: ") - 1); + b->last = ngx_copy(b->last, s->salt.data, s->salt.len); + + s->passwd.data = NULL; + } + b->last = ngx_cpymem(b->last, "Auth-Protocol: ", sizeof("Auth-Protocol: ") - 1); b->last = ngx_cpymem(b->last, ngx_imap_auth_http_protocol[s->protocol], diff --git a/src/imap/ngx_imap_core_module.c b/src/imap/ngx_imap_core_module.c --- a/src/imap/ngx_imap_core_module.c +++ b/src/imap/ngx_imap_core_module.c @@ -45,6 +45,13 @@ static ngx_str_t ngx_imap_default_capab }; +static ngx_conf_bitmask_t ngx_imap_auth_methods[] = { + { ngx_string("plain"), NGX_IMAP_AUTH_PLAIN_ENABLED }, + { ngx_string("apop"), NGX_IMAP_AUTH_APOP_ENABLED }, + { ngx_null_string, 0 } +}; + + static ngx_command_t ngx_imap_core_commands[] = { { ngx_string("server"), @@ -103,6 +110,20 @@ static ngx_command_t ngx_imap_core_comm offsetof(ngx_imap_core_srv_conf_t, imap_capabilities), NULL }, + { ngx_string("server_name"), + NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_IMAP_SRV_CONF_OFFSET, + offsetof(ngx_imap_core_srv_conf_t, server_name), + NULL }, + + { ngx_string("auth"), + NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_1MORE, + ngx_conf_set_bitmask_slot, + NGX_IMAP_SRV_CONF_OFFSET, + offsetof(ngx_imap_core_srv_conf_t, auth_methods), + &ngx_imap_auth_methods }, + ngx_null_command }; @@ -210,6 +231,30 @@ ngx_imap_core_merge_srv_conf(ngx_conf_t ngx_conf_merge_value(conf->so_keepalive, prev->so_keepalive, 0); + ngx_conf_merge_bitmask_value(conf->auth_methods, prev->auth_methods, + (NGX_CONF_BITMASK_SET|NGX_IMAP_AUTH_PLAIN_ENABLED)); + + + ngx_conf_merge_str_value(conf->server_name, prev->server_name, ""); + + if (conf->server_name.len == 0) { + conf->server_name.data = ngx_palloc(cf->pool, NGX_MAXHOSTNAMELEN); + if (conf->server_name.data == NULL) { + return NGX_CONF_ERROR; + } + + if (gethostname((char *) conf->server_name.data, NGX_MAXHOSTNAMELEN) + == -1) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 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; } diff --git a/src/imap/ngx_imap_handler.c b/src/imap/ngx_imap_handler.c --- a/src/imap/ngx_imap_handler.c +++ b/src/imap/ngx_imap_handler.c @@ -233,6 +233,7 @@ ngx_imap_ssl_handshake_handler(ngx_conne static void ngx_imap_init_session(ngx_connection_t *c) { + u_char *p; ngx_imap_session_t *s; ngx_imap_core_srv_conf_t *cscf; @@ -253,6 +254,35 @@ ngx_imap_init_session(ngx_connection_t * s->out = greetings[s->protocol]; + if ((cscf->auth_methods & NGX_IMAP_AUTH_APOP_ENABLED) + && s->protocol == NGX_IMAP_POP3_PROTOCOL) + { + 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_imap_session_internal_server_error(s); + return; + } + + s->salt.len = ngx_sprintf(s->salt.data, "<%ul.%T@%V>" CRLF, + ngx_random(), ngx_time(), &cscf->server_name) + - s->salt.data; + + s->out.data = ngx_palloc(c->pool, greetings[0].len + 1 + s->salt.len); + if (s->out.data == NULL) { + ngx_imap_session_internal_server_error(s); + return; + } + + 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->out.len = p - s->out.data; + } + ngx_add_timer(c->read, cscf->timeout); if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { @@ -726,6 +756,56 @@ ngx_pop3_auth_state(ngx_event_t *rev) text = cscf->pop3_capability.data; break; + case NGX_POP3_APOP: + cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module); + + if ((cscf->auth_methods & NGX_IMAP_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_imap_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_imap_session_internal_server_error(s); + return; + } + + ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len); + + ngx_log_debug2(NGX_LOG_DEBUG_IMAP, c->log, 0, + "pop3 apop: \"%V\" \"%V\"", + &s->login, &s->passwd); + + s->auth_method = NGX_IMAP_AUTH_APOP; + + s->args.nelts = 0; + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + + if (rev->timer_set) { + ngx_del_timer(rev); + } + + ngx_imap_auth_http_init(s); + + return; + + } else { + rc = NGX_IMAP_PARSE_INVALID_COMMAND; + } + + break; + case NGX_POP3_QUIT: s->quit = 1; break; @@ -763,8 +843,6 @@ ngx_pop3_auth_state(ngx_event_t *rev) case NGX_POP3_PASS: if (s->args.nelts == 1) { - /* STUB */ s->imap_state = ngx_pop3_start; - arg = s->args.elts; s->passwd.len = arg[0].len; s->passwd.data = ngx_palloc(c->pool, s->passwd.len); diff --git a/src/imap/ngx_imap_parse.c b/src/imap/ngx_imap_parse.c --- a/src/imap/ngx_imap_parse.c +++ b/src/imap/ngx_imap_parse.c @@ -429,6 +429,10 @@ ngx_int_t ngx_pop3_parse_command(ngx_ima { s->command = NGX_POP3_PASS; + } else if (c0 == 'A' && c1 == 'P' && c2 == 'O' && c3 == 'P') + { + s->command = NGX_POP3_APOP; + } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T') { s->command = NGX_POP3_QUIT; @@ -496,12 +500,20 @@ ngx_int_t ngx_pop3_parse_command(ngx_ima case sw_argument: switch (ch) { - /* - * the space should be considered part of the at username - * or password, but not of argument in other commands - * - * case ' ': - */ + case ' ': + + /* + * the space should be considered as part of the at username + * or password, but not of argument in other commands + */ + + if (s->command == NGX_POP3_USER + || s->command == NGX_POP3_PASS) + { + break; + } + + /* fall through */ case CR: case LF: diff --git a/src/os/unix/ngx_posix_init.c b/src/os/unix/ngx_posix_init.c --- a/src/os/unix/ngx_posix_init.c +++ b/src/os/unix/ngx_posix_init.c @@ -61,6 +61,8 @@ ngx_os_init(ngx_log_t *log) ngx_inherited_nonblocking = 0; #endif + srandom(ngx_time()); + return NGX_OK; }