# HG changeset patch # User Igor Sysoev # Date 1190577600 -14400 # Node ID 7cf404023f507c768ae47adf1baa6909df0cb0aa # Parent 85aeb2da6e4c94237b0c0bf580967dc15caa4f5e nginx 0.5.32 *) Change: now nginx tries to set the "worker_priority", "worker_rlimit_nofile", "worker_rlimit_core", and "worker_rlimit_sigpending" without super-user privileges. *) Change: now nginx escapes space and "%" in request to a mail proxy authentication server. *) Change: now nginx escapes "%" in $memcached_key variable. *) Change: the special make target "upgrade1" was defined for online upgrade of 0.1.x versions. *) Feature: the "add_header Last-Modified ..." directive changes the "Last-Modified" response header line. *) Feature: the mail proxy supports AUTHENTICATE in IMAP mode. Thanks to Maxim Dounin. *) Feature: the mail proxy supports STARTTLS in SMTP mode. Thanks to Maxim Dounin. *) Bugfix: nginx did not close directory file on HEAD request if autoindex was used. Thanks to Arkadiusz Patyk. *) Bugfix: the "proxy_hide_header" and "fastcgi_hide_header" directives did not hide response header lines whose name was longer than 32 characters. Thanks to Manlio Perillo. *) Bugfix: active connection counter always increased if mail proxy was used. *) Bugfix: if backend returned response header only using non-buffered proxy, then nginx closed backend connection on timeout. *) Bugfix: nginx did not support several "Connection" request header lines. *) Bugfix: a charset set by the "charset" directive was not appended to the "Content-Type" header set by $r->send_http_header(). *) Bugfix: a segmentation fault might occur in worker process if /dev/poll method was used. *) Bugfix: nginx did not work on FreeBSD/sparc64. *) Bugfix: a segmentation fault occurred in worker process if invalid address was set in the "auth_http" directive. *) Bugfix: now nginx uses default listen backlog value 511 on all platforms except FreeBSD. Thanks to Jiang Hong. *) Bugfix: now Solaris sendfilev() is not used to transfer the client request body to FastCGI-server via the unix domain socket. *) Bugfix: if the same host without specified port was used as backend for HTTP and HTTPS, then nginx used only one port - 80 or 443. *) Bugfix: the "proxy_ignore_client_abort" and "fastcgi_ignore_client_abort" directives did not work; bug appeared in 0.5.13. diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,71 @@ +Changes with nginx 0.5.32 24 Sep 2007 + + *) Change: now nginx tries to set the "worker_priority", + "worker_rlimit_nofile", "worker_rlimit_core", and + "worker_rlimit_sigpending" without super-user privileges. + + *) Change: now nginx escapes space and "%" in request to a mail proxy + authentication server. + + *) Change: now nginx escapes "%" in $memcached_key variable. + + *) Change: the special make target "upgrade1" was defined for online + upgrade of 0.1.x versions. + + *) Feature: the "add_header Last-Modified ..." directive changes the + "Last-Modified" response header line. + + *) Feature: the mail proxy supports AUTHENTICATE in IMAP mode. + Thanks to Maxim Dounin. + + *) Feature: the mail proxy supports STARTTLS in SMTP mode. + Thanks to Maxim Dounin. + + *) Bugfix: nginx did not close directory file on HEAD request if + autoindex was used. + Thanks to Arkadiusz Patyk. + + *) Bugfix: the "proxy_hide_header" and "fastcgi_hide_header" directives + did not hide response header lines whose name was longer than 32 + characters. + Thanks to Manlio Perillo. + + *) Bugfix: active connection counter always increased if mail proxy was + used. + + *) Bugfix: if backend returned response header only using non-buffered + proxy, then nginx closed backend connection on timeout. + + *) Bugfix: nginx did not support several "Connection" request header + lines. + + *) Bugfix: a charset set by the "charset" directive was not appended to + the "Content-Type" header set by $r->send_http_header(). + + *) Bugfix: a segmentation fault might occur in worker process if + /dev/poll method was used. + + *) Bugfix: nginx did not work on FreeBSD/sparc64. + + *) Bugfix: a segmentation fault occurred in worker process if invalid + address was set in the "auth_http" directive. + + *) Bugfix: now nginx uses default listen backlog value 511 on all + platforms except FreeBSD. + Thanks to Jiang Hong. + + *) Bugfix: now Solaris sendfilev() is not used to transfer the client + request body to FastCGI-server via the unix domain socket. + + *) Bugfix: if the same host without specified port was used as backend + for HTTP and HTTPS, then nginx used only one port - 80 or 443. + + *) Bugfix: the "proxy_ignore_client_abort" and + "fastcgi_ignore_client_abort" directives did not work; bug appeared + in 0.5.13. + + Changes with nginx 0.5.31 15 Aug 2007 *) Feature: named locations. diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,75 @@ +Изменения в nginx 0.5.32 24.09.2007 + + *) Изменение: теперь nginx пытается установить директивы + worker_priority, worker_rlimit_nofile, worker_rlimit_core, + worker_rlimit_sigpending без привилегий root'а. + + *) Изменение: теперь nginx экранирует символы пробела и "%" при + передаче запроса серверу аутентификации почтового прокси-сервера. + + *) Изменение: теперь nginx экранирует символ "%" в переменной + $memcached_key. + + *) Изменение: для обновления на лету версий 0.1.x создан специальный + сценарий make upgrade1. + + *) Добавление: директива "add_header Last-Modified ..." меняет строку + "Last-Modified" в заголовке ответа. + + *) Добавление: почтовый прокси-сервер поддерживает AUTHENTICATE в + режиме IMAP. + Спасибо Максиму Дунину. + + *) Добавление: почтовый прокси-сервер поддерживает STARTTLS в режиме + SMTP. + Спасибо Максиму Дунину. + + *) Исправление: nginx не закрывал файл каталога для запроса HEAD, если + использовался autoindex + Спасибо Arkadiusz Patyk. + + *) Исправление: директивы proxy_hide_header и fastcgi_hide_header не + скрывали строки заголовка ответа с именем больше 32 символов. + Спасибо Manlio Perillo. + + *) Исправление: счётчик активных соединений всегда рос при + использовании почтового прокси-сервера. + + *) Исправление: если бэкенд возвращал только заголовок ответа при + небуферизированном проксировании, то nginx закрывал соединение с + бэкендом по таймауту. + + *) Исправление: nginx не поддерживал несколько строк "Connection" в + заголовке запроса. + + *) Исправление: В строку заголовка ответа "Content-Type", указанную в + методе $r->send_http_header(), не добавлялась кодировка, указанная в + директиве charset. + + *) Исправление: при использовании метода /dev/poll в рабочем процессе + мог произойти segmentation fault. + + *) Исправление: nginx не работал на FreeBSD/sparc64. + + *) Исправление: если в директиве auth_http был задан неправильный + адрес, то в рабочем процессе происходил segmentation fault. + + *) Исправление: теперь по умолчанию nginx использует значение 511 для + listen backlog на всех платформах, кроме FreeBSD. + Спасибо Jiang Hong. + + *) Исправление: sendfilev() в Solaris теперь не используется при + передаче тела запроса FastCGI-серверу через unix domain сокет. + + *) Исправление: при использовании одного хоста в качестве бэкендов для + протоколов HTTP и HTTPS без явного указания портов, nginx + использовал только один порт - 80 или 443. + + *) Исправление: директивы proxy_ignore_client_abort и + fastcgi_ignore_client_abort не работали; ошибка появилась в 0.5.13. + + Изменения в nginx 0.5.31 15.08.2007 *) Добавление: именованные location'ы. diff --git a/auto/init b/auto/init --- a/auto/init +++ b/auto/init @@ -55,15 +55,24 @@ clean: upgrade: $NGX_SBIN_PATH -t - # upgrade compatibility from 0.1.x to 0.2.x + kill -USR2 \`cat $NGX_PID_PATH\` + sleep 1 + test -f $NGX_PID_PATH.oldbin + + kill -QUIT \`cat $NGX_PID_PATH.oldbin\` + +upgrade1: + # upgrade 0.1.x to 0.2+ + + $NGX_SBIN_PATH -t + cp $NGX_PID_PATH $NGX_PID_PATH.oldbin kill -USR2 \`cat $NGX_PID_PATH\` sleep 1 test -f $NGX_PID_PATH.oldbin - # upgrade compatibility from 0.1.x to 0.2.x cp $NGX_PID_PATH $NGX_PID_PATH.newbin - kill -WINCH \`cat $NGX_PID_PATH.oldbin\` + kill -QUIT \`cat $NGX_PID_PATH.oldbin\` END diff --git a/conf/mime.types b/conf/mime.types --- a/conf/mime.types +++ b/conf/mime.types @@ -20,6 +20,7 @@ types { image/x-icon ico; image/x-jng jng; image/x-ms-bmp bmp; + image/svg+xml svg; application/java-archive jar war ear; application/mac-binhex40 hqx; 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.31" +#define NGINX_VERSION "0.5.32" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff --git a/src/core/ngx_conf_file.c b/src/core/ngx_conf_file.c --- a/src/core/ngx_conf_file.c +++ b/src/core/ngx_conf_file.c @@ -366,7 +366,7 @@ not_allowed: invalid: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid number arguments in \"%s\" directive", + "invalid number of arguments in \"%s\" directive", name->data); return NGX_ERROR; diff --git a/src/core/ngx_conf_file.h b/src/core/ngx_conf_file.h --- a/src/core/ngx_conf_file.h +++ b/src/core/ngx_conf_file.h @@ -257,8 +257,8 @@ char *ngx_conf_check_num_bounds(ngx_conf } #define ngx_conf_merge_ptr_value(conf, prev, default) \ - if (conf == NULL) { \ - conf = (prev == NULL) ? default : prev; \ + if (conf == NGX_CONF_UNSET_PTR) { \ + conf = (prev == NGX_CONF_UNSET_PTR) ? default : prev; \ } #define ngx_conf_merge_uint_value(conf, prev, default) \ diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c --- a/src/core/ngx_connection.c +++ b/src/core/ngx_connection.c @@ -123,7 +123,7 @@ ngx_set_inherited_sockets(ngx_cycle_t *c ntohs(sin->sin_port)) - ls[i].addr_text.data; - ls[i].backlog = -1; + ls[i].backlog = NGX_LISTEN_BACKLOG; olen = sizeof(int); @@ -735,7 +735,7 @@ ngx_close_connection(ngx_connection_t *c /* we use ngx_cycle->log because c->log was in c->pool */ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno, - ngx_close_socket_n " failed"); + ngx_close_socket_n " %d failed", fd); } } diff --git a/src/core/ngx_inet.c b/src/core/ngx_inet.c --- a/src/core/ngx_inet.c +++ b/src/core/ngx_inet.c @@ -214,7 +214,13 @@ ngx_ptocidr(ngx_str_t *text, void *cidr) in_cidr->mask = htonl((ngx_uint_t) (0 - (1 << (32 - m)))); - return NGX_OK; + if (in_cidr->addr == (in_cidr->addr & in_cidr->mask)) { + return NGX_OK; + } + + in_cidr->addr &= in_cidr->mask; + + return NGX_DONE; } 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 @@ -59,8 +59,9 @@ ngx_pstrdup(ngx_pool_t *pool, ngx_str_t * %P ngx_pid_t * %M ngx_msec_t * %r rlim_t - * %p pointer - * %V pointer to ngx_str_t + * %p void * + * %V ngx_str_t * + * %v ngx_variable_value_t * * %s null-terminated string * %Z '\0' * %N '\n' @@ -117,7 +118,8 @@ ngx_vsnprintf(u_char *buf, size_t max, c uint64_t ui64; ngx_msec_t ms; ngx_uint_t width, sign, hexadecimal, max_width; - ngx_variable_value_t *v; + ngx_str_t *v; + ngx_variable_value_t *vv; static u_char hex[] = "0123456789abcdef"; static u_char HEX[] = "0123456789ABCDEF"; @@ -188,7 +190,7 @@ ngx_vsnprintf(u_char *buf, size_t max, c switch (*fmt) { case 'V': - v = va_arg(args, ngx_variable_value_t *); + v = va_arg(args, ngx_str_t *); len = v->len; len = (buf + len < last) ? len : (size_t) (last - buf); @@ -198,6 +200,17 @@ ngx_vsnprintf(u_char *buf, size_t max, c continue; + case 'v': + vv = va_arg(args, ngx_variable_value_t *); + + len = vv->len; + len = (buf + len < last) ? len : (size_t) (last - buf); + + buf = ngx_cpymem(buf, vv->data, len); + fmt++; + + continue; + case 's': p = va_arg(args, u_char *); @@ -1019,7 +1032,7 @@ ngx_escape_uri(u_char *dst, u_char *src, 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ }; - /* " ", """, "%", "'", %00-%1F, %7F-%FF */ + /* " ", "#", """, "%", "'", %00-%1F, %7F-%FF */ static uint32_t html[] = { 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ @@ -1039,13 +1052,13 @@ ngx_escape_uri(u_char *dst, u_char *src, 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ }; - /* " ", """, "'", %00-%1F, %7F-%FF */ + /* " ", """, "%", "'", %00-%1F, %7F-%FF */ static uint32_t refresh[] = { 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ - 0x00000085, /* 0000 0000 0000 0000 0000 0000 1000 0101 */ + 0x000000a5, /* 0000 0000 0000 0000 0000 0000 1010 0101 */ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ @@ -1059,13 +1072,13 @@ ngx_escape_uri(u_char *dst, u_char *src, 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ }; - /* " ", %00-%1F */ + /* " ", "%", %00-%1F */ static uint32_t memcached[] = { 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ - 0x00000001, /* 0000 0000 0000 0000 0000 0000 0000 0001 */ + 0x00000021, /* 0000 0000 0000 0000 0000 0000 0010 0001 */ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ @@ -1079,7 +1092,10 @@ ngx_escape_uri(u_char *dst, u_char *src, 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ }; - static uint32_t *map[] = { uri, args, html, refresh, memcached }; + /* mail_auth is the same as memcached */ + + static uint32_t *map[] = + { uri, args, html, refresh, memcached, memcached }; escape = map[type]; 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 @@ -155,6 +155,7 @@ u_char *ngx_utf_cpystrn(u_char *dst, u_c #define NGX_ESCAPE_HTML 2 #define NGX_ESCAPE_REFRESH 3 #define NGX_ESCAPE_MEMCACHED 4 +#define NGX_ESCAPE_MAIL_AUTH 5 #define NGX_UNESCAPE_URI 1 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 @@ -15,6 +15,7 @@ #define POLLREMOVE 0x0800 #define DP_POLL 0xD001 +#define DP_ISPOLLED 0xD002 struct dvpoll { struct pollfd *dp_fds; @@ -252,10 +253,16 @@ ngx_devpoll_del_event(ngx_event_t *ev, i ev->active = 0; if (flags & NGX_CLOSE_EVENT) { + e = (event == POLLIN) ? c->write : c->read; + + if (e) { + e->active = 0; + } + return NGX_OK; } - /* restore the paired event if it exists */ + /* restore the pair event if it exists */ if (event == POLLIN) { e = c->write; @@ -327,13 +334,15 @@ ngx_int_t ngx_devpoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) { - int events, revents; + int events, revents, rc; size_t n; + ngx_fd_t fd; ngx_err_t err; ngx_int_t i; ngx_uint_t level; ngx_event_t *rev, *wev, **queue; ngx_connection_t *c; + struct pollfd pfd; struct dvpoll dvp; /* NGX_TIMER_INFINITE == INFTIM */ @@ -398,34 +407,77 @@ ngx_devpoll_process_events(ngx_cycle_t * ngx_mutex_lock(ngx_posted_events_mutex); for (i = 0; i < events; i++) { - c = ngx_cycle->files[event_list[i].fd]; + + fd = event_list[i].fd; + revents = event_list[i].revents; + + c = ngx_cycle->files[fd]; + + if (c == NULL || c->fd == -1) { + + pfd.fd = fd; + pfd.events = 0; + pfd.revents = 0; + + rc = ioctl(dp, DP_ISPOLLED, &pfd); + + switch (rc) { + + case -1: + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "ioctl(DP_ISPOLLED) failed for socket %d, event", + fd, revents); + break; - if (c->fd == -1) { - if (c->read->closed) { - continue; + case 0: + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "phantom event %04Xd for closed and removed socket %d", + revents, fd); + break; + + default: + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "unexpected event %04Xd for closed and removed socket %d, ", + "ioctl(DP_ISPOLLED) returned rc:%d, fd:%d, event %04Xd", + revents, fd, rc, pfd.fd, pfd.revents); + + pfd.fd = fd; + pfd.events = POLLREMOVE; + pfd.revents = 0; + + if (write(dp, &pfd, sizeof(struct pollfd)) + != (ssize_t) sizeof(struct pollfd)) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "write(/dev/poll) for %d failed, fd"); + } + + if (close(fd) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "close(%d) failed", fd); + } + + break; } - ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "unexpected event"); continue; } - revents = event_list[i].revents; - ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "devpoll: fd:%d, ev:%04Xd, rev:%04Xd", - event_list[i].fd, event_list[i].events, revents); + fd, event_list[i].events, revents); if (revents & (POLLERR|POLLHUP|POLLNVAL)) { ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "ioctl(DP_POLL) error fd:%d ev:%04Xd rev:%04Xd", - event_list[i].fd, event_list[i].events, revents); + fd, event_list[i].events, revents); } if (revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) { ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "strange ioctl(DP_POLL) events " "fd:%d ev:%04Xd rev:%04Xd", - event_list[i].fd, event_list[i].events, revents); + fd, event_list[i].events, revents); } if ((revents & (POLLERR|POLLHUP|POLLNVAL)) 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 @@ -404,7 +404,7 @@ ngx_kqueue_set_event(ngx_event_t *ev, in c = ev->data; - ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0, "kevent set event: %d: ft:%d fl:%04Xd", c->fd, filter, flags); 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 @@ -1038,8 +1038,9 @@ ngx_event_debug_connection(ngx_conf_t *c #if (NGX_DEBUG) ngx_event_conf_t *ecf = conf; + ngx_int_t rc; + ngx_str_t *value; ngx_event_debug_t *dc; - ngx_str_t *value; struct hostent *h; ngx_inet_cidr_t in_cidr; @@ -1056,13 +1057,21 @@ ngx_event_debug_connection(ngx_conf_t *c if (dc->addr != INADDR_NONE) { dc->mask = 0xffffffff; - return NGX_OK; + return NGX_CONF_OK; } - if (ngx_ptocidr(&value[1], &in_cidr) == NGX_OK) { + rc = ngx_ptocidr(&value[1], &in_cidr); + + if (rc == NGX_DONE) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "low address bits of %V are meaningless", &value[1]); + rc = NGX_OK; + } + + if (rc == NGX_OK) { dc->mask = in_cidr.mask; dc->addr = in_cidr.addr; - return NGX_OK; + return NGX_CONF_OK; } h = gethostbyname((char *) value[1].data); @@ -1084,7 +1093,7 @@ ngx_event_debug_connection(ngx_conf_t *c #endif - return NGX_OK; + return NGX_CONF_OK; } 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 @@ -41,7 +41,7 @@ struct ngx_event_s { unsigned accept:1; - /* used to detect the stale events in kqueue, rt signals and epoll */ + /* used to detect the stale events in kqueue, rtsig, and epoll */ unsigned instance:1; /* @@ -247,8 +247,7 @@ extern ngx_event_actions_t ngx_event_a #define NGX_USE_LOWAT_EVENT 0x00000010 /* - * The event filter requires to do i/o operation until EAGAIN: - * epoll, rt signals. + * The event filter requires to do i/o operation until EAGAIN: epoll, rtsig. */ #define NGX_USE_GREEDY_EVENT 0x00000020 @@ -258,7 +257,7 @@ extern ngx_event_actions_t ngx_event_a #define NGX_USE_EPOLL_EVENT 0x00000040 /* - * No need to add or delete the event filters: rt signals. + * No need to add or delete the event filters: rtsig. */ #define NGX_USE_RTSIG_EVENT 0x00000080 @@ -276,13 +275,13 @@ extern ngx_event_actions_t ngx_event_a /* * The event filter has no opaque data and requires file descriptors table: - * poll, /dev/poll, rt signals. + * poll, /dev/poll, rtsig. */ #define NGX_USE_FD_EVENT 0x00000400 /* * The event module handles periodic or absolute timer event by itself: - * kqueue in FreeBSD 4.4 and NetBSD 2.0, Solaris 10's event ports. + * kqueue in FreeBSD 4.4, NetBSD 2.0, and MacOSX 10.4, Solaris 10's event ports. */ #define NGX_USE_TIMER_EVENT 0x00000800 @@ -290,17 +289,26 @@ extern ngx_event_actions_t ngx_event_a * All event filters on file descriptor are deleted after a notification: * Solaris 10's event ports. */ -#define NGX_USE_EVENTPORT_EVENT 0x00001000 +#define NGX_USE_EVENTPORT_EVENT 0x00001000 /* - * The event filter is deleted before the closing file. - * Has no meaning for select, poll, kqueue, epoll. - * /dev/poll: we need to flush POLLREMOVE event before closing file + * The event filter is deleted just before the closing file. + * Has no meaning for select and poll. + * kqueue, epoll, rtsig, eventport: allows to avoid explicit delete, + * because filter automatically is deleted + * on file close, + * + * /dev/poll: we need to flush POLLREMOVE event + * before closing file. */ +#define NGX_CLOSE_EVENT 1 -#define NGX_CLOSE_EVENT 1 +/* + * disable temporarily event filter, this may avoid locks + * in kernel malloc()/free(): kqueue. + */ #define NGX_DISABLE_EVENT 2 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 @@ -85,6 +85,8 @@ ngx_event_connect_peer(ngx_peer_connecti c->recv_chain = ngx_recv_chain; c->send_chain = ngx_send_chain; + c->sendfile = 1; + c->log_error = pc->log_error; if (pc->sockaddr->sa_family != AF_INET) { 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 @@ -1588,7 +1588,7 @@ ngx_ssl_expire_sessions(ngx_ssl_session_ } if (n++ != 0 && sess_id->expire > tp->sec) { - break; + return; } sess_id->next->prev = sess_id->prev; diff --git a/src/http/modules/ngx_http_access_module.c b/src/http/modules/ngx_http_access_module.c --- a/src/http/modules/ngx_http_access_module.c +++ b/src/http/modules/ngx_http_access_module.c @@ -137,6 +137,7 @@ ngx_http_access_rule(ngx_conf_t *cf, ngx { ngx_http_access_loc_conf_t *alcf = conf; + ngx_int_t rc; ngx_str_t *value; ngx_inet_cidr_t in_cidr; ngx_http_access_rule_t *rule; @@ -173,12 +174,19 @@ ngx_http_access_rule(ngx_conf_t *cf, ngx return NGX_CONF_OK; } - if (ngx_ptocidr(&value[1], &in_cidr) == NGX_ERROR) { + rc = ngx_ptocidr(&value[1], &in_cidr); + + if (rc == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", &value[1]); return NGX_CONF_ERROR; } + if (rc == NGX_DONE) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "low address bits of %V are meaningless", &value[1]); + } + rule->mask = in_cidr.mask; rule->addr = in_cidr.addr; diff --git a/src/http/modules/ngx_http_autoindex_module.c b/src/http/modules/ngx_http_autoindex_module.c --- a/src/http/modules/ngx_http_autoindex_module.c +++ b/src/http/modules/ngx_http_autoindex_module.c @@ -236,6 +236,11 @@ ngx_http_autoindex_handler(ngx_http_requ rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { + if (ngx_close_dir(&dir) == NGX_ERROR) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno, + ngx_close_dir_n " \"%V\" failed", &path); + } + return rc; } 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 @@ -128,11 +128,7 @@ ngx_http_empty_gif_handler(ngx_http_requ if (r->method == NGX_HTTP_HEAD) { r->headers_out.status = NGX_HTTP_OK; - rc = ngx_http_send_header(r); - - if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { - return rc; - } + return ngx_http_send_header(r); } b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c --- a/src/http/modules/ngx_http_fastcgi_module.c +++ b/src/http/modules/ngx_http_fastcgi_module.c @@ -1135,7 +1135,7 @@ ngx_http_fastcgi_process_header(ngx_http } else { for (i = 0; i < h->key.len; i++) { - h->lowcase_key[i] = ngx_tolower(h->lowcase_key[i]); + h->lowcase_key[i] = ngx_tolower(h->key.data[i]); } } @@ -1640,7 +1640,6 @@ ngx_http_fastcgi_create_loc_conf(ngx_con * conf->upstream.hide_headers_hash = { NULL, 0 }; * conf->upstream.hide_headers = NULL; * conf->upstream.pass_headers = NULL; - * conf->upstream.catch_stderr = NULL; * conf->upstream.schema = { 0, NULL }; * conf->upstream.uri = { 0, NULL }; * conf->upstream.location = NULL; @@ -1675,6 +1674,8 @@ ngx_http_fastcgi_create_loc_conf(ngx_con /* "fastcgi_cyclic_temp_file" is disabled */ conf->upstream.cyclic_temp_file = 0; + conf->catch_stderr = NGX_CONF_UNSET_PTR; + return conf; } diff --git a/src/http/modules/ngx_http_geo_module.c b/src/http/modules/ngx_http_geo_module.c --- a/src/http/modules/ngx_http_geo_module.c +++ b/src/http/modules/ngx_http_geo_module.c @@ -86,7 +86,7 @@ ngx_http_geo_variable(ngx_http_request_t *v = *vv; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http geo: %V %V", &r->connection->addr_text, v); + "http geo: %V %v", &r->connection->addr_text, v); return NGX_OK; } @@ -100,8 +100,8 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c ngx_conf_t save; ngx_pool_t *pool; ngx_radix_tree_t *tree; + ngx_http_variable_t *var; ngx_http_geo_conf_ctx_t ctx; - ngx_http_variable_t *var; value = cf->args->elts; @@ -212,12 +212,20 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command cidrin.mask = 0; } else { - if (ngx_ptocidr(&value[0], &cidrin) == NGX_ERROR) { + rc = ngx_ptocidr(&value[0], &cidrin); + + if (rc == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", &value[0]); return NGX_CONF_ERROR; } + if (rc == NGX_DONE) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "low address bits of %V are meaningless", + &value[0]); + } + cidrin.addr = ntohl(cidrin.addr); cidrin.mask = ntohl(cidrin.mask); } @@ -277,9 +285,8 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command ngx_radix32tree_find(ctx->tree, cidrin.addr & cidrin.mask); ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "duplicate parameter \"%V\", value: \"%V\", " - "old value: \"%V\"", - &value[0], var, old); + "duplicate parameter \"%V\", value: \"%v\", old value: \"%v\"", + &value[0], var, old); rc = ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask); diff --git a/src/http/modules/ngx_http_headers_filter_module.c b/src/http/modules/ngx_http_headers_filter_module.c --- a/src/http/modules/ngx_http_headers_filter_module.c +++ b/src/http/modules/ngx_http_headers_filter_module.c @@ -9,17 +9,31 @@ #include -typedef struct { - ngx_table_elt_t value; - ngx_array_t *lengths; - ngx_array_t *values; -} ngx_http_header_val_t; +typedef struct ngx_http_header_val_s ngx_http_header_val_t; + +typedef ngx_int_t (*ngx_http_set_header_pt)(ngx_http_request_t *r, + ngx_http_header_val_t *hv, ngx_str_t *value); typedef struct { - time_t expires; - ngx_str_t cache_control; - ngx_array_t *headers; + ngx_str_t name; + ngx_uint_t offset; + ngx_http_set_header_pt handler; +} ngx_http_set_header_t; + + +struct ngx_http_header_val_s { + ngx_table_elt_t value; + ngx_uint_t offset; + ngx_http_set_header_pt handler; + ngx_array_t *lengths; + ngx_array_t *values; +}; + + +typedef struct { + time_t expires; + ngx_array_t *headers; } ngx_http_headers_conf_t; @@ -29,6 +43,13 @@ typedef struct { #define NGX_HTTP_EXPIRES_MAX -2147483644 +static ngx_int_t ngx_http_set_expires(ngx_http_request_t *r, + ngx_http_headers_conf_t *conf); +static ngx_int_t ngx_http_add_cache_control(ngx_http_request_t *r, + ngx_http_header_val_t *hv, ngx_str_t *value); +static ngx_int_t ngx_http_set_last_modified(ngx_http_request_t *r, + ngx_http_header_val_t *hv, ngx_str_t *value); + static void *ngx_http_headers_create_conf(ngx_conf_t *cf); static char *ngx_http_headers_merge_conf(ngx_conf_t *cf, void *parent, void *child); @@ -39,6 +60,18 @@ static char *ngx_http_headers_add(ngx_co void *conf); +static ngx_http_set_header_t ngx_http_set_headers[] = { + + { ngx_string("Cache-Control"), 0, ngx_http_add_cache_control }, + + { ngx_string("Last-Modified"), + offsetof(ngx_http_headers_out_t, last_modified), + ngx_http_set_last_modified }, + + { ngx_null_string, 0, NULL } +}; + + static ngx_command_t ngx_http_headers_filter_commands[] = { { ngx_string("expires"), @@ -98,13 +131,15 @@ static ngx_http_output_header_filter_pt static ngx_int_t ngx_http_headers_filter(ngx_http_request_t *r) { - size_t len; + ngx_str_t value; ngx_uint_t i; - ngx_table_elt_t *expires, *cc, **ccp, *out; ngx_http_header_val_t *h; ngx_http_headers_conf_t *conf; - if (r != r->main + conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module); + + if ((conf->expires == NGX_HTTP_EXPIRES_OFF && conf->headers == NULL) + || r != r->main || (r->headers_out.status != NGX_HTTP_OK && r->headers_out.status != NGX_HTTP_NO_CONTENT && r->headers_out.status != NGX_HTTP_MOVED_PERMANENTLY @@ -114,124 +149,73 @@ ngx_http_headers_filter(ngx_http_request return ngx_http_next_header_filter(r); } - conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module); - if (conf->expires != NGX_HTTP_EXPIRES_OFF) { - - expires = r->headers_out.expires; - - if (expires == NULL) { + if (ngx_http_set_expires(r, conf) != NGX_OK) { + return NGX_ERROR; + } + } - expires = ngx_list_push(&r->headers_out.headers); - if (expires == NULL) { - return NGX_ERROR; - } - - r->headers_out.expires = expires; - - expires->hash = 1; - expires->key.len = sizeof("Expires") - 1; - expires->key.data = (u_char *) "Expires"; - } + if (conf->headers) { + h = conf->headers->elts; + for (i = 0; i < conf->headers->nelts; i++) { - len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT"); - expires->value.len = len - 1; - - ccp = r->headers_out.cache_control.elts; - - if (ccp == NULL) { + if (h[i].lengths == NULL) { + value = h[i].value.value; - if (ngx_array_init(&r->headers_out.cache_control, r->pool, - 1, sizeof(ngx_table_elt_t *)) - != NGX_OK) - { - return NGX_ERROR; - } - - ccp = ngx_array_push(&r->headers_out.cache_control); - if (ccp == NULL) { - return NGX_ERROR; - } - - cc = ngx_list_push(&r->headers_out.headers); - if (cc == NULL) { - return NGX_ERROR; + } else { + if (ngx_http_script_run(r, &value, h[i].lengths->elts, 0, + h[i].values->elts) + == NULL) + { + return NGX_ERROR; + } } - cc->hash = 1; - cc->key.len = sizeof("Cache-Control") - 1; - cc->key.data = (u_char *) "Cache-Control"; - - *ccp = cc; - - } else { - for (i = 1; i < r->headers_out.cache_control.nelts; i++) { - ccp[i]->hash = 0; - } - - cc = ccp[0]; - } - - if (conf->expires == NGX_HTTP_EXPIRES_EPOCH) { - expires->value.data = (u_char *) "Thu, 01 Jan 1970 00:00:01 GMT"; - - cc->value.len = sizeof("no-cache") - 1; - cc->value.data = (u_char *) "no-cache"; - - } else if (conf->expires == NGX_HTTP_EXPIRES_MAX) { - expires->value.data = (u_char *) "Thu, 31 Dec 2037 23:55:55 GMT"; - - /* 10 years */ - cc->value.len = sizeof("max-age=315360000") - 1; - cc->value.data = (u_char *) "max-age=315360000"; - - } else { - expires->value.data = ngx_palloc(r->pool, len); - if (expires->value.data == NULL) { + if (h[i].handler(r, &h[i], &value) != NGX_OK) { return NGX_ERROR; } - - if (conf->expires == 0) { - ngx_memcpy(expires->value.data, ngx_cached_http_time.data, - ngx_cached_http_time.len + 1); - - cc->value.len = sizeof("max-age=0") - 1; - cc->value.data = (u_char *) "max-age=0"; - - } else { - ngx_http_time(expires->value.data, ngx_time() + conf->expires); - - if (conf->expires < 0) { - cc->value.len = sizeof("no-cache") - 1; - cc->value.data = (u_char *) "no-cache"; - - } else { - cc->value.data = ngx_palloc(r->pool, sizeof("max-age=") - + NGX_TIME_T_LEN + 1); - if (cc->value.data == NULL) { - return NGX_ERROR; - } - - cc->value.len = ngx_sprintf(cc->value.data, "max-age=%T", - conf->expires) - - cc->value.data; - } - } } } - if (conf->cache_control.len) { + return ngx_http_next_header_filter(r); +} + - ccp = r->headers_out.cache_control.elts; +static ngx_int_t +ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf) +{ + size_t len; + ngx_uint_t i; + ngx_table_elt_t *expires, *cc, **ccp; - if (ccp == NULL) { + expires = r->headers_out.expires; + + if (expires == NULL) { + + expires = ngx_list_push(&r->headers_out.headers); + if (expires == NULL) { + return NGX_ERROR; + } - if (ngx_array_init(&r->headers_out.cache_control, r->pool, - 1, sizeof(ngx_table_elt_t *)) - != NGX_OK) - { - return NGX_ERROR; - } + r->headers_out.expires = expires; + + expires->hash = 1; + expires->key.len = sizeof("Expires") - 1; + expires->key.data = (u_char *) "Expires"; + } + + len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT"); + expires->value.len = len - 1; + + ccp = r->headers_out.cache_control.elts; + + if (ccp == NULL) { + + if (ngx_array_init(&r->headers_out.cache_control, r->pool, + 1, sizeof(ngx_table_elt_t *)) + != NGX_OK) + { + return NGX_ERROR; } ccp = ngx_array_push(&r->headers_out.cache_control); @@ -247,37 +231,161 @@ ngx_http_headers_filter(ngx_http_request cc->hash = 1; cc->key.len = sizeof("Cache-Control") - 1; cc->key.data = (u_char *) "Cache-Control"; - cc->value = conf->cache_control; *ccp = cc; + + } else { + for (i = 1; i < r->headers_out.cache_control.nelts; i++) { + ccp[i]->hash = 0; + } + + cc = ccp[0]; + } + + if (conf->expires == NGX_HTTP_EXPIRES_EPOCH) { + expires->value.data = (u_char *) "Thu, 01 Jan 1970 00:00:01 GMT"; + + cc->value.len = sizeof("no-cache") - 1; + cc->value.data = (u_char *) "no-cache"; + + return NGX_OK; + } + + if (conf->expires == NGX_HTTP_EXPIRES_MAX) { + expires->value.data = (u_char *) "Thu, 31 Dec 2037 23:55:55 GMT"; + + /* 10 years */ + cc->value.len = sizeof("max-age=315360000") - 1; + cc->value.data = (u_char *) "max-age=315360000"; + + return NGX_OK; + } + + expires->value.data = ngx_palloc(r->pool, len); + if (expires->value.data == NULL) { + return NGX_ERROR; + } + + if (conf->expires == 0) { + ngx_memcpy(expires->value.data, ngx_cached_http_time.data, + ngx_cached_http_time.len + 1); + + cc->value.len = sizeof("max-age=0") - 1; + cc->value.data = (u_char *) "max-age=0"; + + return NGX_OK; } - if (conf->headers) { - h = conf->headers->elts; - for (i = 0; i < conf->headers->nelts; i++) { - out = ngx_list_push(&r->headers_out.headers); - if (out == NULL) { - return NGX_ERROR; - } + ngx_http_time(expires->value.data, ngx_time() + conf->expires); + + if (conf->expires < 0) { + cc->value.len = sizeof("no-cache") - 1; + cc->value.data = (u_char *) "no-cache"; + + return NGX_OK; + } - out->hash = h[i].value.hash; - out->key = h[i].value.key; + cc->value.data = ngx_palloc(r->pool, + sizeof("max-age=") + NGX_TIME_T_LEN + 1); + if (cc->value.data == NULL) { + return NGX_ERROR; + } + + cc->value.len = ngx_sprintf(cc->value.data, "max-age=%T", conf->expires) + - cc->value.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_add_header(ngx_http_request_t *r, ngx_http_header_val_t *hv, + ngx_str_t *value) +{ + ngx_table_elt_t *h; - if (h[i].lengths == NULL) { - out->value = h[i].value.value; - continue; - } + h = ngx_list_push(&r->headers_out.headers); + if (h == NULL) { + return NGX_ERROR; + } + + h->hash = hv->value.hash; + h->key = hv->value.key; + h->value = *value; + + return NGX_OK; +} + - if (ngx_http_script_run(r, &out->value, h[i].lengths->elts, 0, - h[i].values->elts) - == NULL) - { - return NGX_ERROR; - } +static ngx_int_t +ngx_http_add_cache_control(ngx_http_request_t *r, ngx_http_header_val_t *hv, + ngx_str_t *value) +{ + ngx_table_elt_t *cc, **ccp; + + ccp = r->headers_out.cache_control.elts; + + if (ccp == NULL) { + + if (ngx_array_init(&r->headers_out.cache_control, r->pool, + 1, sizeof(ngx_table_elt_t *)) + != NGX_OK) + { + return NGX_ERROR; } } - return ngx_http_next_header_filter(r); + ccp = ngx_array_push(&r->headers_out.cache_control); + if (ccp == NULL) { + return NGX_ERROR; + } + + cc = ngx_list_push(&r->headers_out.headers); + if (cc == NULL) { + return NGX_ERROR; + } + + cc->hash = 1; + cc->key.len = sizeof("Cache-Control") - 1; + cc->key.data = (u_char *) "Cache-Control"; + cc->value = *value; + + *ccp = cc; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_set_last_modified(ngx_http_request_t *r, ngx_http_header_val_t *hv, + ngx_str_t *value) +{ + ngx_table_elt_t *h, **old; + + if (hv->offset) { + old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset); + + } else { + old = NULL; + } + + if (old == NULL || *old == NULL) { + h = ngx_list_push(&r->headers_out.headers); + if (h == NULL) { + return NGX_ERROR; + } + + } else { + h = *old; + } + + h->hash = hv->value.hash; + h->key = hv->value.key; + h->value = *value; + + r->headers_out.last_modified_time = -1; + + return NGX_OK; } @@ -294,8 +402,6 @@ ngx_http_headers_create_conf(ngx_conf_t /* * set by ngx_pcalloc(): * - * conf->cache_control.len = 0; - * conf->cache_control.data = NULL; * conf->headers = NULL; */ @@ -313,11 +419,7 @@ ngx_http_headers_merge_conf(ngx_conf_t * if (conf->expires == NGX_HTTP_EXPIRES_UNSET) { conf->expires = (prev->expires == NGX_HTTP_EXPIRES_UNSET) ? - NGX_HTTP_EXPIRES_OFF : prev->expires; - } - - if (conf->cache_control.data == NULL) { - conf->cache_control = prev->cache_control; + NGX_HTTP_EXPIRES_OFF : prev->expires; } if (conf->headers == NULL) { @@ -406,16 +508,13 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx ngx_int_t n; ngx_str_t *value; + ngx_uint_t i; ngx_http_header_val_t *h; + ngx_http_set_header_t *sh; ngx_http_script_compile_t sc; value = cf->args->elts; - if (ngx_strcasecmp(value[1].data, (u_char *) "cache-control") == 0) { - hcf->cache_control = value[2]; - return NGX_CONF_OK; - } - if (hcf->headers == NULL) { hcf->headers = ngx_array_create(cf->pool, 1, sizeof(ngx_http_header_val_t)); @@ -432,9 +531,22 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx h->value.hash = 1; h->value.key = value[1]; h->value.value = value[2]; + h->offset = 0; + h->handler = ngx_http_add_header; h->lengths = NULL; h->values = NULL; + sh = ngx_http_set_headers; + for (i = 0; sh[i].name.len; i++) { + if (ngx_strcasecmp(value[1].data, sh[i].name.data) != 0) { + continue; + } + + h->offset = sh[i].offset; + h->handler = sh[i].handler; + break; + } + n = ngx_http_script_variables_count(&value[2]); if (n == 0) { diff --git a/src/http/modules/ngx_http_limit_zone_module.c b/src/http/modules/ngx_http_limit_zone_module.c --- a/src/http/modules/ngx_http_limit_zone_module.c +++ b/src/http/modules/ngx_http_limit_zone_module.c @@ -141,7 +141,7 @@ ngx_http_limit_zone_handler(ngx_http_req if (len > 255) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the value of the \"%V\" variable " - "is more than 255 bytes: \"%V\"", + "is more than 255 bytes: \"%v\"", &ctx->var, vv); return NGX_DECLINED; } diff --git a/src/http/modules/ngx_http_log_module.c b/src/http/modules/ngx_http_log_module.c --- a/src/http/modules/ngx_http_log_module.c +++ b/src/http/modules/ngx_http_log_module.c @@ -423,6 +423,11 @@ ngx_http_log_bytes_sent(ngx_http_request } +/* + * although there is a real $body_bytes_sent variable, + * this log operation code function is more optimized for logging + */ + static u_char * ngx_http_log_body_bytes_sent(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op) 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 @@ -167,7 +167,7 @@ ngx_http_map_variable(ngx_http_request_t } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http map: \"%V\" \"%V\"", vv, v); + "http map: \"%v\" \"%v\"", vv, v); return NGX_OK; } diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -553,7 +553,7 @@ ngx_http_proxy_create_request(ngx_http_r } else { unparsed_uri = 0; - if (r->quoted_uri) { + if (r->quoted_uri || r->internal) { escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len, r->uri.len - loc_len, NGX_ESCAPE_URI); } @@ -1141,7 +1141,7 @@ ngx_http_proxy_process_header(ngx_http_r } else { for (i = 0; i < h->key.len; i++) { - h->lowcase_key[i] = ngx_tolower(h->lowcase_key[i]); + h->lowcase_key[i] = ngx_tolower(h->key.data[i]); } } @@ -2237,6 +2237,7 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_ if (port == 80) { plcf->port.len = sizeof("80") - 1; plcf->port.data = (u_char *) "80"; + } else { plcf->port.len = sizeof("443") - 1; plcf->port.data = (u_char *) "443"; diff --git a/src/http/modules/ngx_http_realip_module.c b/src/http/modules/ngx_http_realip_module.c --- a/src/http/modules/ngx_http_realip_module.c +++ b/src/http/modules/ngx_http_realip_module.c @@ -188,6 +188,7 @@ ngx_http_realip_from(ngx_conf_t *cf, ngx { ngx_http_realip_loc_conf_t *rlcf = conf; + ngx_int_t rc; ngx_str_t *value; ngx_inet_cidr_t in_cidr; ngx_http_realip_from_t *from; @@ -215,12 +216,19 @@ ngx_http_realip_from(ngx_conf_t *cf, ngx return NGX_CONF_OK; } - if (ngx_ptocidr(&value[1], &in_cidr) == NGX_ERROR) { + rc = ngx_ptocidr(&value[1], &in_cidr); + + if (rc == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", &value[1]); return NGX_CONF_ERROR; } + if (rc == NGX_DONE) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "low address bits of %V are meaningless", &value[1]); + } + from->mask = in_cidr.mask; from->addr = in_cidr.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 @@ -47,7 +47,7 @@ our @EXPORT = qw( HTTP_INSUFFICIENT_STORAGE ); -our $VERSION = '0.5.31'; +our $VERSION = '0.5.32'; require XSLoader; XSLoader::load('nginx', $VERSION); diff --git a/src/http/modules/perl/nginx.xs b/src/http/modules/perl/nginx.xs --- a/src/http/modules/perl/nginx.xs +++ b/src/http/modules/perl/nginx.xs @@ -131,6 +131,8 @@ send_http_header(r, ...) XSRETURN_EMPTY; } + r->headers_out.content_type_len = r->headers_out.content_type.len; + } else { if (ngx_http_set_content_type(r) != NGX_OK) { XSRETURN_EMPTY; 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 @@ -551,8 +551,8 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma if (in_addr[a].default_server) { ngx_log_error(NGX_LOG_ERR, cf->log, 0, - "the duplicate default server in %V:%d", - &lscf[l].file_name, lscf[l].line); + "the duplicate default server in %s:%ui", + &lscf[l].file_name, lscf[l].line); return NGX_CONF_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 @@ -620,6 +620,8 @@ ngx_int_t ngx_http_core_find_config_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) { + u_char *p; + size_t len; ngx_int_t rc; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; @@ -680,7 +682,25 @@ ngx_http_core_find_config_phase(ngx_http * r->headers_out.location->key fields */ - r->headers_out.location->value = clcf->name; + if (r->args.len == 0) { + r->headers_out.location->value = clcf->name; + + } else { + len = clcf->name.len + 1 + r->args.len; + p = ngx_palloc(r->pool, len); + + if (p == NULL) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_OK; + } + + r->headers_out.location->value.len = len; + r->headers_out.location->value.data = p; + + p = ngx_cpymem(p, clcf->name.data, clcf->name.len); + *p++ = '?'; + ngx_memcpy(p, r->args.data, r->args.len); + } ngx_http_finalize_request(r, NGX_HTTP_MOVED_PERMANENTLY); return NGX_OK; @@ -2226,7 +2246,7 @@ ngx_http_core_merge_srv_conf(ngx_conf_t #endif ls->family = AF_INET; - ls->conf.backlog = -1; + ls->conf.backlog = NGX_LISTEN_BACKLOG; ls->conf.rcvbuf = -1; ls->conf.sndbuf = -1; } @@ -2575,9 +2595,9 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx ls->family = AF_INET; ls->addr = u.addr.in_addr; ls->port = u.port; - ls->file_name = cf->conf_file->file.name; + ls->file_name = cf->conf_file->file.name.data; ls->line = cf->conf_file->line; - ls->conf.backlog = -1; + ls->conf.backlog = NGX_LISTEN_BACKLOG; ls->conf.rcvbuf = -1; ls->conf.sndbuf = -1; 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 @@ -38,8 +38,8 @@ typedef struct { in_port_t port; int family; - ngx_str_t file_name; - ngx_int_t line; + u_char *file_name; + ngx_uint_t line; ngx_http_listen_conf_t conf; } ngx_http_listen_t; 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 @@ -21,6 +21,8 @@ static ngx_int_t ngx_http_process_header ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_process_unique_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_process_cookie(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); @@ -34,6 +36,7 @@ static ngx_int_t ngx_http_set_write_hand static void ngx_http_writer(ngx_http_request_t *r); static void ngx_http_block_read(ngx_http_request_t *r); +static void ngx_http_test_read(ngx_http_request_t *r); static void ngx_http_set_keepalive(ngx_http_request_t *r); static void ngx_http_keepalive_handler(ngx_event_t *ev); static void ngx_http_set_lingering_close(ngx_http_request_t *r); @@ -71,11 +74,11 @@ ngx_http_header_t ngx_http_headers_in[] ngx_http_process_unique_header_line }, { ngx_string("Connection"), offsetof(ngx_http_headers_in_t, connection), - ngx_http_process_unique_header_line }, + ngx_http_process_connection }, { ngx_string("If-Modified-Since"), offsetof(ngx_http_headers_in_t, if_modified_since), - ngx_http_process_header_line }, + ngx_http_process_unique_header_line }, { ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent), ngx_http_process_header_line }, @@ -836,7 +839,7 @@ ngx_http_process_request_headers(ngx_eve ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent too long header line: \"%V\"", &header); - ngx_http_close_request(r, NGX_HTTP_BAD_REQUEST); + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); return; } } @@ -948,7 +951,7 @@ ngx_http_process_request_headers(ngx_eve ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent invalid header line: \"%V\\r...\"", &header); - ngx_http_close_request(r, NGX_HTTP_BAD_REQUEST); + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); return; } } @@ -1199,6 +1202,21 @@ ngx_http_process_unique_header_line(ngx_ 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")) { + r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; + + } else if (ngx_strstr(h->value.data, "keep-alive")) { + r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE; + } + + return NGX_OK; +} + + +static ngx_int_t ngx_http_process_cookie(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { @@ -1294,7 +1312,7 @@ ngx_http_process_request_header(ngx_http return NGX_ERROR; } - if (r->method & (NGX_HTTP_TRACE)) { + if (r->method & NGX_HTTP_TRACE) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "client sent TRACE method"); ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED); @@ -1317,26 +1335,11 @@ ngx_http_process_request_header(ngx_http return NGX_ERROR; } - if (r->headers_in.connection) { - if (r->headers_in.connection->value.len == 5 - && ngx_strcasecmp(r->headers_in.connection->value.data, - (u_char *) "close") - == 0) - { - r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; - - } else if (r->headers_in.connection->value.len == 10 - && ngx_strcasecmp(r->headers_in.connection->value.data, - (u_char *) "keep-alive") - == 0) - { - r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE; - - if (r->headers_in.keep_alive) { - r->headers_in.keep_alive_n = - ngx_atotm(r->headers_in.keep_alive->value.data, - r->headers_in.keep_alive->value.len); - } + if (r->headers_in.connection_type == NGX_HTTP_CONNECTION_KEEP_ALIVE) { + if (r->headers_in.keep_alive) { + r->headers_in.keep_alive_n = + ngx_atotm(r->headers_in.keep_alive->value.data, + r->headers_in.keep_alive->value.len); } } @@ -1710,7 +1713,7 @@ ngx_http_set_write_handler(ngx_http_requ r->http_state = NGX_HTTP_WRITING_REQUEST_STATE; - r->read_event_handler = ngx_http_block_read; + r->read_event_handler = ngx_http_test_read; r->write_event_handler = ngx_http_writer; wev = r->connection->write; @@ -1823,6 +1826,26 @@ ngx_http_writer(ngx_http_request_t *r) static void ngx_http_block_read(ngx_http_request_t *r) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http read blocked"); + + /* aio does not call this handler */ + + if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) + && r->connection->read->active) + { + if (ngx_del_event(r->connection->read, NGX_READ_EVENT, 0) + == NGX_ERROR) + { + ngx_http_close_request(r, 0); + } + } +} + + +static void +ngx_http_test_read(ngx_http_request_t *r) +{ int n; char buf[1]; ngx_err_t err; @@ -1832,7 +1855,7 @@ ngx_http_block_read(ngx_http_request_t * c = r->connection; rev = c->read; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http read blocked"); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http test read"); #if (NGX_HAVE_KQUEUE) diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c --- a/src/http/ngx_http_script.c +++ b/src/http/ngx_http_script.c @@ -1191,7 +1191,7 @@ ngx_http_script_var_code(ngx_http_script if (value && !value->not_found) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, - "http script var: \"%V\"", value); + "http script var: \"%v\"", value); *e->sp = *value; e->sp++; 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 @@ -581,7 +581,7 @@ ngx_http_upstream_connect(ngx_http_reque c->write->handler = ngx_http_upstream_send_request_handler; c->read->handler = ngx_http_upstream_process_header; - c->sendfile = r->connection->sendfile; + c->sendfile &= r->connection->sendfile; c->pool = r->pool; c->read->log = c->write->log = c->log = r->connection->log; @@ -1288,6 +1288,7 @@ ngx_http_upstream_test_connect(ngx_conne if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { if (c->write->pending_eof) { + c->log->action = "connecting to upstream"; (void) ngx_connection_error(c, c->write->kq_errno, "kevent() reported that connect() failed"); return NGX_ERROR; @@ -1311,6 +1312,7 @@ ngx_http_upstream_test_connect(ngx_conne } if (err) { + c->log->action = "connecting to upstream"; (void) ngx_connection_error(c, err, "connect() failed"); return NGX_ERROR; } @@ -1488,6 +1490,11 @@ ngx_http_upstream_send_response(ngx_http ngx_http_upstream_finalize_request(r, u, 0); return; } + + if (u->peer.connection->read->ready) { + ngx_http_upstream_process_non_buffered_body( + u->peer.connection->read); + } } return; @@ -3201,7 +3208,7 @@ ngx_http_upstream_add(ngx_conf_t *cf, ng ngx_log_error(NGX_LOG_WARN, cf->log, 0, "upstream \"%V\" may not have port %d in %s:%ui", &u->host, uscfp[i]->port, - uscfp[i]->file_name.data, uscfp[i]->line); + uscfp[i]->file_name, uscfp[i]->line); return NULL; } @@ -3209,6 +3216,12 @@ ngx_http_upstream_add(ngx_conf_t *cf, ng continue; } + if (uscfp[i]->default_port && u->default_port + && uscfp[i]->default_port != u->default_port) + { + continue; + } + return uscfp[i]; } @@ -3219,7 +3232,7 @@ ngx_http_upstream_add(ngx_conf_t *cf, ng uscf->flags = flags; uscf->host = u->host; - uscf->file_name = cf->conf_file->file.name; + uscf->file_name = cf->conf_file->file.name.data; uscf->line = cf->conf_file->line; uscf->port = u->port; uscf->default_port = u->default_port; diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -91,7 +91,7 @@ struct ngx_http_upstream_srv_conf_s { ngx_uint_t flags; ngx_str_t host; - ngx_str_t file_name; + u_char *file_name; ngx_uint_t line; in_port_t port; in_port_t default_port; diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c --- a/src/http/ngx_http_upstream_round_robin.c +++ b/src/http/ngx_http_upstream_round_robin.c @@ -64,7 +64,7 @@ ngx_http_upstream_init_round_robin(ngx_c if (us->port == 0 && us->default_port == 0) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "no port in upstream \"%V\" in %s:%ui", - &us->host, us->file_name.data, us->line); + &us->host, us->file_name, us->line); return NGX_ERROR; } @@ -77,7 +77,7 @@ ngx_http_upstream_init_round_robin(ngx_c if (u.err) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "%s in upstream \"%V\" in %s:%ui", - u.err, &us->host, us->file_name.data, us->line); + u.err, &us->host, us->file_name, us->line); } return NGX_ERROR; @@ -428,8 +428,8 @@ ngx_http_upstream_set_round_robin_peer_s rc = ngx_ssl_set_session(pc->connection, ssl_session); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, - "set session: %p:%d", - ssl_session, ssl_session ? ssl_session->references : 0); + "set session: %p:%d", + ssl_session, ssl_session ? ssl_session->references : 0); /* ngx_unlock_mutex(rrp->peers->mutex); */ @@ -453,7 +453,7 @@ ngx_http_upstream_save_round_robin_peer_ } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, - "save session: %p:%d", ssl_session, ssl_session->references); + "save session: %p:%d", ssl_session, ssl_session->references); peer = &rrp->peers->peer[rrp->current]; @@ -468,7 +468,7 @@ ngx_http_upstream_save_round_robin_peer_ if (old_ssl_session) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, - "old session: %p:%d", + "old session: %p:%d", old_ssl_session, old_ssl_session->references); /* TODO: may block */ diff --git a/src/http/ngx_http_write_filter_module.c b/src/http/ngx_http_write_filter_module.c --- a/src/http/ngx_http_write_filter_module.c +++ b/src/http/ngx_http_write_filter_module.c @@ -249,7 +249,11 @@ ngx_http_write_filter(ngx_http_request_t c->write->delayed = 1; ngx_add_timer(c->write, (ngx_msec_t) (sent * 1000 / r->limit_rate + 1)); - } else if (c->write->ready && clcf->sendfile_max_chunk) { + } else if (c->write->ready + && clcf->sendfile_max_chunk + && (size_t) (c->sent - sent) + >= clcf->sendfile_max_chunk - 2 * ngx_pagesize) + { c->write->delayed = 1; ngx_add_timer(c->write, 1); } 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 @@ -300,7 +300,7 @@ ngx_mail_block(ngx_conf_t *cf, ngx_comma return NGX_CONF_ERROR; } - ls->backlog = -1; + ls->backlog = NGX_LISTEN_BACKLOG; ls->rcvbuf = -1; ls->sndbuf = -1; 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 @@ -92,12 +92,15 @@ typedef struct { ngx_str_t imap_starttls_only_capability; 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; @@ -132,6 +135,10 @@ typedef enum { typedef enum { ngx_imap_start = 0, + ngx_imap_auth_login_username, + ngx_imap_auth_login_password, + ngx_imap_auth_plain, + ngx_imap_auth_cram_md5, ngx_imap_login, ngx_imap_user, ngx_imap_passwd @@ -214,39 +221,47 @@ typedef struct { } ngx_mail_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_AUTH 8 -#define NGX_POP3_STAT 9 -#define NGX_POP3_LIST 10 -#define NGX_POP3_RETR 11 -#define NGX_POP3_DELE 12 -#define NGX_POP3_RSET 13 -#define NGX_POP3_TOP 14 -#define NGX_POP3_UIDL 15 +#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_AUTH 8 +#define NGX_POP3_STAT 9 +#define NGX_POP3_LIST 10 +#define NGX_POP3_RETR 11 +#define NGX_POP3_DELE 12 +#define NGX_POP3_RSET 13 +#define NGX_POP3_TOP 14 +#define NGX_POP3_UIDL 15 -#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_AUTHENTICATE 7 -#define NGX_SMTP_HELO 1 -#define NGX_SMTP_EHLO 2 -#define NGX_SMTP_AUTH 3 -#define NGX_SMTP_QUIT 4 -#define NGX_SMTP_NOOP 5 -#define NGX_SMTP_MAIL 6 -#define NGX_SMTP_RSET 7 +#define NGX_SMTP_HELO 1 +#define NGX_SMTP_EHLO 2 +#define NGX_SMTP_AUTH 3 +#define NGX_SMTP_QUIT 4 +#define NGX_SMTP_NOOP 5 +#define NGX_SMTP_MAIL 6 +#define NGX_SMTP_RSET 7 +#define NGX_SMTP_RCPT 8 +#define NGX_SMTP_DATA 9 +#define NGX_SMTP_VRFY 10 +#define NGX_SMTP_EXPN 11 +#define NGX_SMTP_HELP 12 +#define NGX_SMTP_STARTTLS 13 #define NGX_MAIL_AUTH_PLAIN 0 @@ -285,6 +300,8 @@ typedef struct { #define ngx_mail_conf_get_module_main_conf(cf, module) \ ((ngx_mail_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index] +#define ngx_mail_conf_get_module_srv_conf(cf, module) \ + ((ngx_mail_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index] void ngx_mail_init_connection(ngx_connection_t *c); 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 @@ -21,6 +21,9 @@ typedef struct { ngx_str_t header; ngx_array_t *headers; + + u_char *file; + ngx_uint_t line; } ngx_mail_auth_http_conf_t; @@ -1248,18 +1251,10 @@ ngx_mail_auth_http_create_request(ngx_ma static ngx_int_t ngx_mail_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text, ngx_str_t *escaped) { - u_char ch, *p; - ngx_uint_t i, n; - - n = 0; + u_char *p; + uintptr_t n; - for (i = 0; i < text->len; i++) { - ch = text->data[i]; - - if (ch == CR || ch == LF) { - n++; - } - } + n = ngx_escape_uri(NULL, text->data, text->len, NGX_ESCAPE_MAIL_AUTH); if (n == 0) { *escaped = *text; @@ -1273,27 +1268,9 @@ ngx_mail_auth_http_escape(ngx_pool_t *po return NGX_ERROR; } - escaped->data = p; - - for (i = 0; i < text->len; i++) { - ch = text->data[i]; + (void) ngx_escape_uri(p, text->data, text->len, NGX_ESCAPE_MAIL_AUTH); - if (ch == CR) { - *p++ = '%'; - *p++ = '0'; - *p++ = 'D'; - continue; - } - - if (ch == LF) { - *p++ = '%'; - *p++ = '0'; - *p++ = 'A'; - continue; - } - - *p++ = ch; - } + escaped->data = p; return NGX_OK; } @@ -1311,6 +1288,9 @@ ngx_mail_auth_http_create_conf(ngx_conf_ ahcf->timeout = NGX_CONF_UNSET_MSEC; + ahcf->file = cf->conf_file->file.name.data; + ahcf->line = cf->conf_file->line; + return ahcf; } @@ -1330,6 +1310,14 @@ ngx_mail_auth_http_merge_conf(ngx_conf_t conf->peer = prev->peer; conf->host_header = prev->host_header; conf->uri = prev->uri; + + if (conf->peer == NULL) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"http_auth\" is defined for server in %s:%ui", + conf->file, conf->line); + + return NGX_CONF_ERROR; + } } ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000); @@ -1383,11 +1371,18 @@ ngx_mail_auth_http(ngx_conf_t *cf, ngx_c u.uri_part = 1; u.one_addr = 1; + if (ngx_strncmp(u.url.data, "http://", 7) == 0) { + u.url.len -= 7; + u.url.data += 7; + } + if (ngx_parse_url(cf, &u) != NGX_OK) { if (u.err) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s in auth_http \"%V\"", u.err, &u.url); } + + return NGX_CONF_ERROR; } ahcf->peer = u.addrs; 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 @@ -54,6 +54,14 @@ static ngx_conf_bitmask_t ngx_pop3_auth }; +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 }, @@ -62,6 +70,14 @@ static ngx_conf_bitmask_t ngx_smtp_auth }; +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"), @@ -172,6 +188,13 @@ static ngx_command_t ngx_mail_core_comm 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, @@ -278,7 +301,7 @@ 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; + u_char *p, *auth; size_t size, stls_only_size; ngx_str_t *c, *d; ngx_uint_t i, m; @@ -297,6 +320,11 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t (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 @@ -463,6 +491,15 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t 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) { return NGX_CONF_ERROR; @@ -478,6 +515,19 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t 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; @@ -497,7 +547,8 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t *p++ = CR; *p = LF; - size += sizeof(" LOGINDISABLED") - 1; + size = (auth - conf->imap_capability.data) + sizeof(CRLF) - 1 + + sizeof(" STARTTLS LOGINDISABLED") - 1; p = ngx_palloc(cf->pool, size); if (p == NULL) { @@ -507,9 +558,10 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t conf->imap_starttls_only_capability.len = size; conf->imap_starttls_only_capability.data = p; - p = ngx_cpymem(p, conf->imap_starttls_capability.data, - conf->imap_starttls_capability.len - (sizeof(CRLF) - 1)); - p = ngx_cpymem(p, " LOGINDISABLED", sizeof(" LOGINDISABLED") - 1); + 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; @@ -582,6 +634,8 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t *p++ = CR; *p++ = LF; } + auth = p; + *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' '; *p++ = 'A'; *p++ = 'U'; *p++ = 'T'; *p++ = 'H'; @@ -598,6 +652,42 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t *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; } 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 @@ -280,6 +280,9 @@ ngx_mail_init_session(ngx_connection_t * && (cscf->pop3_auth_methods & (NGX_MAIL_AUTH_APOP_ENABLED|NGX_MAIL_AUTH_CRAM_MD5_ENABLED))) + || (s->protocol == NGX_MAIL_IMAP_PROTOCOL + && (cscf->imap_auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) + || (s->protocol == NGX_MAIL_SMTP_PROTOCOL && (cscf->smtp_auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED))) { @@ -985,7 +988,7 @@ 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; + ngx_str_t *arg, salt; ngx_int_t rc; ngx_uint_t tag, i; ngx_connection_t *c; @@ -1055,113 +1058,342 @@ ngx_imap_auth_state(ngx_event_t *rev) s->backslash = 0; } - switch (s->command) { - - case NGX_IMAP_LOGIN: + 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) { + 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; - - 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); + 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_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); + 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; } - rc = NGX_MAIL_PARSE_INVALID_COMMAND; + if (rc == NGX_ERROR) { + ngx_mail_session_internal_server_error(s); + return; + } + + /* 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; + 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; } } -#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; - } + + 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; } - rc = NGX_MAIL_PARSE_INVALID_COMMAND; - break; -#endif - - default: - 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) { @@ -1171,6 +1403,8 @@ ngx_imap_auth_state(ngx_event_t *rev) } 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; } @@ -1209,9 +1443,18 @@ ngx_imap_auth_state(ngx_event_t *rev) if (rc != NGX_IMAP_NEXT) { s->args.nelts = 0; - s->buffer->pos = s->buffer->start; - s->buffer->last = s->buffer->start; - s->tag.len = 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); @@ -1229,6 +1472,9 @@ ngx_smtp_auth_state(ngx_event_t *rev) 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; @@ -1295,6 +1541,26 @@ ngx_smtp_auth_state(ngx_event_t *rev) } 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; } @@ -1303,6 +1569,18 @@ ngx_smtp_auth_state(ngx_event_t *rev) 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; @@ -1453,6 +1731,38 @@ ngx_smtp_auth_state(ngx_event_t *rev) 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; @@ -1790,6 +2100,10 @@ ngx_mail_close_connection(ngx_connection #endif +#if (NGX_STAT_STUB) + ngx_atomic_fetch_add(ngx_stat_active, -1); +#endif + c->destroyed = 1; pool = c->pool; 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 @@ -354,6 +354,27 @@ ngx_int_t ngx_imap_parse_command(ngx_mai } break; + case 12: + if ((c[0] == 'A'|| c[0] == 'a') + && (c[1] == 'U'|| c[1] == 'u') + && (c[2] == 'T'|| c[2] == 't') + && (c[3] == 'H'|| c[3] == 'h') + && (c[4] == 'E'|| c[4] == 'e') + && (c[5] == 'N'|| c[5] == 'n') + && (c[6] == 'T'|| c[6] == 't') + && (c[7] == 'I'|| c[7] == 'i') + && (c[8] == 'C'|| c[8] == 'c') + && (c[9] == 'A'|| c[9] == 'a') + && (c[10] == 'T'|| c[10] == 't') + && (c[11] == 'E'|| c[11] == 'e')) + { + s->command = NGX_IMAP_AUTHENTICATE; + + } else { + goto invalid; + } + break; + default: goto invalid; } @@ -413,6 +434,10 @@ ngx_int_t ngx_imap_parse_command(ngx_mai break; case sw_argument: + if (ch == ' ' && s->quoted) { + break; + } + switch (ch) { case '"': if (!s->quoted) { @@ -573,7 +598,7 @@ done: s->literal_len = 0; } - s->state = sw_start; + s->state = (s->command != NGX_IMAP_AUTHENTICATE) ? sw_start : sw_argument; return NGX_OK; @@ -646,10 +671,43 @@ ngx_int_t ngx_smtp_parse_command(ngx_mai { s->command = NGX_SMTP_RSET; + } else if (c0 == 'R' && c1 == 'C' && c2 == 'P' && c3 == 'T') + { + s->command = NGX_SMTP_RCPT; + + } else if (c0 == 'V' && c1 == 'R' && c2 == 'F' && c3 == 'Y') + { + s->command = NGX_SMTP_VRFY; + + } else if (c0 == 'E' && c1 == 'X' && c2 == 'P' && c3 == 'N') + { + s->command = NGX_SMTP_EXPN; + + } else if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'P') + { + s->command = NGX_SMTP_HELP; + } else { goto invalid; } +#if (NGX_MAIL_SSL) + } else if (p - c == 8) { + if ((c[0] == 'S'|| c[0] == 's') + && (c[1] == 'T'|| c[1] == 't') + && (c[2] == 'A'|| c[2] == 'a') + && (c[3] == 'R'|| c[3] == 'r') + && (c[4] == 'T'|| c[4] == 't') + && (c[5] == 'T'|| c[5] == 't') + && (c[6] == 'L'|| c[6] == 'l') + && (c[7] == 'S'|| c[7] == 's')) + { + s->command = NGX_SMTP_STARTTLS; + + } else { + goto invalid; + } +#endif } else { goto invalid; } 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 @@ -77,6 +77,9 @@ #endif +#define NGX_LISTEN_BACKLOG -1 + + #if (defined SO_ACCEPTFILTER && !defined NGX_HAVE_DEFERRED_ACCEPT) #define NGX_HAVE_DEFERRED_ACCEPT 1 #endif diff --git a/src/os/unix/ngx_linux_config.h b/src/os/unix/ngx_linux_config.h --- a/src/os/unix/ngx_linux_config.h +++ b/src/os/unix/ngx_linux_config.h @@ -78,6 +78,9 @@ extern ssize_t sendfile(int s, int fd, i #endif +#define NGX_LISTEN_BACKLOG 511 + + #if defined TCP_DEFER_ACCEPT && !defined NGX_HAVE_DEFERRED_ACCEPT #define NGX_HAVE_DEFERRED_ACCEPT 1 #endif diff --git a/src/os/unix/ngx_posix_config.h b/src/os/unix/ngx_posix_config.h --- a/src/os/unix/ngx_posix_config.h +++ b/src/os/unix/ngx_posix_config.h @@ -88,6 +88,9 @@ #endif +#define NGX_LISTEN_BACKLOG 511 + + #if (__FreeBSD__) && (__FreeBSD_version < 400017) #include /* ALIGN() */ diff --git a/src/os/unix/ngx_process_cycle.c b/src/os/unix/ngx_process_cycle.c --- a/src/os/unix/ngx_process_cycle.c +++ b/src/os/unix/ngx_process_cycle.c @@ -62,6 +62,11 @@ u_long cpu_affinity; static u_char master_process[] = "master process"; +static ngx_cycle_t ngx_exit_cycle; +static ngx_log_t ngx_exit_log; +static ngx_open_file_t ngx_exit_log_file; + + void ngx_master_process_cycle(ngx_cycle_t *cycle) { @@ -649,13 +654,21 @@ ngx_master_process_exit(ngx_cycle_t *cyc } /* - * we do not destroy cycle->pool here because a signal handler - * that uses cycle->log can be called at this point + * Copy ngx_cycle->log related data to the special static exit cycle, + * log, and log file structures enough to allow a signal handler to log. + * The handler may be called when standard ngx_cycle->log allocated from + * ngx_cycle->pool is already destroyed. */ -#if 0 + ngx_exit_log_file.fd = ngx_cycle->log->file->fd; + + ngx_exit_log = *ngx_cycle->log; + ngx_exit_log.file = &ngx_exit_log_file; + + ngx_exit_cycle.log = &ngx_exit_log; + ngx_cycle = &ngx_exit_cycle; + ngx_destroy_pool(cycle->pool); -#endif exit(0); } @@ -792,49 +805,49 @@ ngx_worker_process_init(ngx_cycle_t *cyc ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); - if (geteuid() == 0) { - if (priority && ccf->priority != 0) { - if (setpriority(PRIO_PROCESS, 0, ccf->priority) == -1) { - ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, - "setpriority(%d) failed", ccf->priority); - } + if (priority && ccf->priority != 0) { + if (setpriority(PRIO_PROCESS, 0, ccf->priority) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "setpriority(%d) failed", ccf->priority); } + } - if (ccf->rlimit_nofile != NGX_CONF_UNSET) { - rlmt.rlim_cur = (rlim_t) ccf->rlimit_nofile; - rlmt.rlim_max = (rlim_t) ccf->rlimit_nofile; + if (ccf->rlimit_nofile != NGX_CONF_UNSET) { + rlmt.rlim_cur = (rlim_t) ccf->rlimit_nofile; + rlmt.rlim_max = (rlim_t) ccf->rlimit_nofile; - if (setrlimit(RLIMIT_NOFILE, &rlmt) == -1) { - ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, - "setrlimit(RLIMIT_NOFILE, %i) failed", - ccf->rlimit_nofile); - } + if (setrlimit(RLIMIT_NOFILE, &rlmt) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "setrlimit(RLIMIT_NOFILE, %i) failed", + ccf->rlimit_nofile); } + } - if (ccf->rlimit_core != NGX_CONF_UNSET_SIZE) { - rlmt.rlim_cur = (rlim_t) ccf->rlimit_core; - rlmt.rlim_max = (rlim_t) ccf->rlimit_core; + if (ccf->rlimit_core != NGX_CONF_UNSET_SIZE) { + rlmt.rlim_cur = (rlim_t) ccf->rlimit_core; + rlmt.rlim_max = (rlim_t) ccf->rlimit_core; - if (setrlimit(RLIMIT_CORE, &rlmt) == -1) { - ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, - "setrlimit(RLIMIT_CORE, %i) failed", - ccf->rlimit_core); - } + if (setrlimit(RLIMIT_CORE, &rlmt) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "setrlimit(RLIMIT_CORE, %i) failed", + ccf->rlimit_core); } + } #ifdef RLIMIT_SIGPENDING - if (ccf->rlimit_sigpending != NGX_CONF_UNSET) { - rlmt.rlim_cur = (rlim_t) ccf->rlimit_sigpending; - rlmt.rlim_max = (rlim_t) ccf->rlimit_sigpending; + if (ccf->rlimit_sigpending != NGX_CONF_UNSET) { + rlmt.rlim_cur = (rlim_t) ccf->rlimit_sigpending; + rlmt.rlim_max = (rlim_t) ccf->rlimit_sigpending; - if (setrlimit(RLIMIT_SIGPENDING, &rlmt) == -1) { - ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, - "setrlimit(RLIMIT_SIGPENDING, %i) failed", - ccf->rlimit_sigpending); - } + if (setrlimit(RLIMIT_SIGPENDING, &rlmt) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "setrlimit(RLIMIT_SIGPENDING, %i) failed", + ccf->rlimit_sigpending); } + } #endif + if (geteuid() == 0) { if (setgid(ccf->group) == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "setgid(%d) failed", ccf->group); @@ -996,13 +1009,23 @@ ngx_worker_process_exit(ngx_cycle_t *cyc } /* - * we do not destroy cycle->pool here because a signal handler - * that uses cycle->log can be called at this point + * Copy ngx_cycle->log related data to the special static exit cycle, + * log, and log file structures enough to allow a signal handler to log. + * The handler may be called when standard ngx_cycle->log allocated from + * ngx_cycle->pool is already destroyed. */ -#if 0 + ngx_exit_log_file.fd = ngx_cycle->log->file->fd; + + ngx_exit_log = *ngx_cycle->log; + ngx_exit_log.file = &ngx_exit_log_file; + + ngx_exit_cycle.log = &ngx_exit_log; + ngx_cycle = &ngx_exit_cycle; + ngx_destroy_pool(cycle->pool); -#endif + + ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, "exit"); exit(0); } diff --git a/src/os/unix/ngx_solaris_config.h b/src/os/unix/ngx_solaris_config.h --- a/src/os/unix/ngx_solaris_config.h +++ b/src/os/unix/ngx_solaris_config.h @@ -82,6 +82,9 @@ #endif +#define NGX_LISTEN_BACKLOG 511 + + #ifndef NGX_HAVE_INHERITED_NONBLOCK #define NGX_HAVE_INHERITED_NONBLOCK 1 #endif