# HG changeset patch # User Igor Sysoev # Date 1145995200 -14400 # Node ID 3689cd4e32288cc63dc6acdce90bdc1824c42d5a # Parent fca7768a4aca335ffa7c7fb5356efd658be18e09 nginx 0.3.42 *) Feature: the "bind" option of the "listen" directive in IMAP/POP3 proxy. *) Bugfix: if the same capture in the "rewrite" directive was used more then once. *) Bugfix: the $sent_http_content_type, $sent_http_content_length, $sent_http_last_modified, $sent_http_connection, $sent_http_keep_alive, and $sent_http_transfer_encoding variables were not written to access log. *) Bugfix: the $sent_http_cache_control returned value of the single "Cache-Control" response header line. diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,21 @@ +Changes with nginx 0.3.42 26 Apr 2006 + + *) Feature: the "bind" option of the "listen" directive in IMAP/POP3 + proxy. + + *) Bugfix: if the same capture in the "rewrite" directive was used more + then once. + + *) Bugfix: the $sent_http_content_type, $sent_http_content_length, + $sent_http_last_modified, $sent_http_connection, + $sent_http_keep_alive, and $sent_http_transfer_encoding variables + were not written to access log. + + *) Bugfix: the $sent_http_cache_control returned value of the single + "Cache-Control" response header line. + + Changes with nginx 0.3.41 21 Apr 2006 *) Feature: the -v switch. diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,20 @@ +Изменения в nginx 0.3.42 26.04.2006 + + *) Добавление: параметр bind в директиве listen в IMAP/POP3 прокси. + + *) Исправление: ошибки при использовании в директиве rewrite одного и + того же выделения более одного раза. + + *) Исправление: в лог не записывались переменные + $sent_http_content_type, $sent_http_content_length, + $sent_http_last_modified, $sent_http_connection, + $sent_http_keep_alive и $sent_http_transfer_encoding. + + *) Исправление: переменная $sent_http_cache_control возвращала + содержимое только одной строки "Cache-Control" в заголовке ответа. + + Изменения в nginx 0.3.41 21.04.2006 *) Добавление: ключ -v. diff --git a/auto/modules b/auto/modules --- a/auto/modules +++ b/auto/modules @@ -61,6 +61,12 @@ if [ $HTTP_SSI = YES ]; then HTTP_POSTPONE=YES fi + +if [ $HTTP_ADDITION = YES ]; then + HTTP_POSTPONE=YES +fi + + # the module order is important # ngx_http_autoindex_module # ngx_http_index_module diff --git a/src/core/nginx.c b/src/core/nginx.c --- a/src/core/nginx.c +++ b/src/core/nginx.c @@ -239,7 +239,9 @@ main(int argc, char *const *argv) ngx_write_fd(ngx_stderr_fileno, "built by " NGX_COMPILER CRLF, sizeof("built by " NGX_COMPILER CRLF) - 1); #endif - return 0; + if (!ngx_test_config) { + return 0; + } } if (ngx_test_config) { 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_VER "nginx/0.3.41" +#define NGINX_VER "nginx/0.3.42" #define NGINX_VAR "NGINX" #define NGX_OLDPID_EXT ".oldbin" diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h --- a/src/core/ngx_connection.h +++ b/src/core/ngx_connection.h @@ -33,7 +33,6 @@ struct ngx_listening_s { /* handler of accepted connection */ ngx_connection_handler_pt handler; - void *ctx; /* ngx_http_conf_ctx_t, for example */ void *servers; /* array of ngx_http_in_addr_t, for example */ ngx_log_t log; @@ -113,10 +112,6 @@ struct ngx_connection_s { off_t sent; - void *ctx; - void *servers; - - ngx_log_t *log; ngx_pool_t *pool; 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 @@ -432,8 +432,10 @@ ngx_event_module_init(ngx_cycle_t *cycle ecf = (*cf)[ngx_event_core_module.ctx_index]; - ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, - "using the \"%s\" event method", ecf->name); + if (!ngx_test_config) { + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, + "using the \"%s\" event method", ecf->name); + } ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); @@ -738,9 +740,6 @@ ngx_event_process_init(ngx_cycle_t *cycl c->listening = &ls[i]; ls[i].connection = c; - c->ctx = ls[i].ctx; - c->servers = ls[i].servers; - rev = c->read; rev->log = c->log; diff --git a/src/event/ngx_event_accept.c b/src/event/ngx_event_accept.c --- a/src/event/ngx_event_accept.c +++ b/src/event/ngx_event_accept.c @@ -156,9 +156,6 @@ ngx_event_accept(ngx_event_t *ev) c->unexpected_eof = 1; - c->ctx = lc->ctx; - c->servers = lc->servers; - rev = c->read; wev = c->write; diff --git a/src/http/modules/ngx_http_rewrite_module.c b/src/http/modules/ngx_http_rewrite_module.c --- a/src/http/modules/ngx_http_rewrite_module.c +++ b/src/http/modules/ngx_http_rewrite_module.c @@ -395,7 +395,7 @@ ngx_http_rewrite(ngx_conf_t *cf, ngx_com regex->size = sc.size; regex->args = sc.args; - if (sc.variables == 0) { + if (sc.variables == 0 && !sc.dup_capture) { regex->lengths = NULL; } 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 @@ -734,6 +734,19 @@ ngx_http_ssi_output(ngx_http_request_t * ngx_buf_t *b; ngx_chain_t *cl; +#if 1 + b = NULL; + for (cl = ctx->out; cl; cl = cl->next) { + if (cl->buf == b) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "the same buf was used in ssi"); + ngx_debug_point(); + return NGX_ERROR; + } + b = cl->buf; + } +#endif + rc = ngx_http_next_body_filter(r, ctx->out); if (ctx->busy == NULL) { 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 @@ -725,8 +725,6 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma ls->deferred_accept = in_addr[a].listen_conf->deferred_accept; #endif - ls->ctx = ctx; - hip = ngx_palloc(cf->pool, sizeof(ngx_http_in_port_t)); if (hip == NULL) { 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 @@ -1328,6 +1328,7 @@ ngx_http_internal_redirect(ngx_http_requ ngx_http_update_location_config(r); r->internal = 1; + r->method = NGX_HTTP_GET; ngx_http_handler(r); @@ -2528,6 +2529,7 @@ ngx_http_core_root(ngx_conf_t *cf, ngx_c static ngx_http_method_name_t ngx_methods_names[] = { { "GET", (uint32_t) ~NGX_HTTP_GET }, { "HEAD", (uint32_t) ~NGX_HTTP_HEAD }, + { "POST", (uint32_t) ~NGX_HTTP_POST }, { NULL, 0 } }; diff --git a/src/http/ngx_http_postpone_filter_module.c b/src/http/ngx_http_postpone_filter_module.c --- a/src/http/ngx_http_postpone_filter_module.c +++ b/src/http/ngx_http_postpone_filter_module.c @@ -89,6 +89,22 @@ ngx_http_postpone_filter(ngx_http_reques return NGX_ERROR; } +#if 1 + { + ngx_chain_t *cl; + ngx_buf_t *b = NULL; + for (cl = pr->out; cl; cl = cl->next) { + if (cl->buf == b) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "the same buf was used in postponed"); + ngx_debug_point(); + return NGX_ERROR; + } + b = cl->buf; + } + } +#endif + if (r != r->connection->data || r->postponed->request) { return NGX_AGAIN; } 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 @@ -201,11 +201,11 @@ ngx_http_init_connection(ngx_connection_ } -static -void ngx_http_init_request(ngx_event_t *rev) +static void +ngx_http_init_request(ngx_event_t *rev) { + socklen_t len; ngx_uint_t i; - socklen_t len; struct sockaddr_in sin; ngx_connection_t *c; ngx_http_request_t *r; @@ -274,7 +274,7 @@ void ngx_http_init_request(ngx_event_t * /* AF_INET only */ - hip = c->servers; + hip = c->listening->servers; hia = hip->addrs; r->port = hip->port; @@ -311,7 +311,7 @@ void ngx_http_init_request(ngx_event_t * r->in_addr = sin.sin_addr.s_addr; } - /* the last in_port->addrs address is "*" */ + /* the last address is "*" */ for ( /* void */ ; i < hip->naddrs - 1; i++) { if (hia[i].addr == r->in_addr) { @@ -1405,6 +1405,12 @@ ngx_http_finalize_request(ngx_http_reque || rc == NGX_HTTP_REQUEST_TIME_OUT || r->connection->error) { + if (rc == NGX_HTTP_CLIENT_CLOSED_REQUEST + && r->headers_out.status == 0) + { + r->headers_out.status = NGX_HTTP_CLIENT_CLOSED_REQUEST; + } + if (ngx_http_post_action(r) == NGX_OK) { return; } 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 @@ -91,6 +91,14 @@ ngx_http_script_compile(ngx_http_script_ if (sc->source->data[i] >= '1' && sc->source->data[i] <= '9') { + n = sc->source->data[i] - '0'; + + if (sc->captures_mask & (1 << n)) { + sc->dup_capture = 1; + } + + sc->captures_mask |= 1 << n; + copy_capture = ngx_http_script_add_code(*sc->lengths, sizeof(ngx_http_script_copy_capture_code_t), NULL); @@ -100,7 +108,8 @@ ngx_http_script_compile(ngx_http_script_ copy_capture->code = (ngx_http_script_code_pt) ngx_http_script_copy_capture_len_code; - copy_capture->n = 2 * (sc->source->data[i] - '0'); + copy_capture->n = 2 * n; + copy_capture = ngx_http_script_add_code(*sc->values, sizeof(ngx_http_script_copy_capture_code_t), @@ -110,14 +119,12 @@ ngx_http_script_compile(ngx_http_script_ } copy_capture->code = ngx_http_script_copy_capture_code; - copy_capture->n = sc->source->data[i] - '0'; + copy_capture->n = 2 * n; - if (sc->ncaptures < copy_capture->n) { - sc->ncaptures = copy_capture->n; + if (sc->ncaptures < n) { + sc->ncaptures = n; } - copy_capture->n *= 2; - i++; continue; diff --git a/src/http/ngx_http_script.h b/src/http/ngx_http_script.h --- a/src/http/ngx_http_script.h +++ b/src/http/ngx_http_script.h @@ -47,6 +47,7 @@ typedef struct { ngx_uint_t variables; ngx_uint_t ncaptures; + ngx_uint_t captures_mask; ngx_uint_t size; void *main; @@ -54,6 +55,7 @@ typedef struct { unsigned compile_args:1; unsigned complete_lengths:1; unsigned complete_values:1; + unsigned dup_capture:1; unsigned args:1; } ngx_http_script_compile_t; diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -170,6 +170,10 @@ ngx_http_upstream_header_t ngx_http_ups ngx_http_upstream_ignore_header_line, 0, ngx_http_upstream_ignore_header_line, 0, 0 }, + { ngx_string("Keep-Alive"), + ngx_http_upstream_ignore_header_line, 0, + ngx_http_upstream_ignore_header_line, 0, 0 }, + { ngx_string("X-Pad"), ngx_http_upstream_ignore_header_line, 0, ngx_http_upstream_ignore_header_line, 0, 0 }, @@ -626,6 +630,8 @@ ngx_http_upstream_ssl_init_connection(ng return; } + r->connection->log->action = "SSL handshaking to upstream"; + rc = ngx_ssl_handshake(c); if (rc == NGX_AGAIN) { @@ -852,7 +858,6 @@ ngx_http_upstream_send_request_handler(n "http upstream send request handler"); if (wev->timedout) { - c->log->action = "sending request to upstream"; ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT); 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 @@ -49,6 +49,19 @@ static ngx_int_t ngx_http_variable_body_ static ngx_int_t ngx_http_variable_request_completion(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_sent_content_type(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_sent_content_length(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_sent_last_modified(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_sent_connection(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_sent_keep_alive(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_sent_transfer_encoding(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + /* * TODO: @@ -59,6 +72,13 @@ static ngx_int_t ngx_http_variable_reque * Apache SSI: DOCUMENT_NAME, LAST_MODIFIED, USER_NAME (file owner) */ +/* + * the $http_host, $http_user_agent, $http_referer, $http_via, + * and $http_x_forwarded_for variables may be handled by generic + * ngx_http_variable_unknown_header_in(), but for perfomance reasons + * they are handled using dedicated entries + */ + static ngx_http_variable_t ngx_http_core_variables[] = { { ngx_string("http_host"), NULL, ngx_http_variable_header, @@ -146,6 +166,27 @@ static ngx_http_variable_t ngx_http_cor ngx_http_variable_request_completion, 0, 0, 0 }, + { ngx_string("sent_http_content_type"), NULL, + ngx_http_variable_sent_content_type, 0, 0, 0 }, + + { ngx_string("sent_http_content_length"), NULL, + ngx_http_variable_sent_content_length, 0, 0, 0 }, + + { ngx_string("sent_http_last_modified"), NULL, + ngx_http_variable_sent_last_modified, 0, 0, 0 }, + + { ngx_string("sent_http_connection"), NULL, + ngx_http_variable_sent_connection, 0, 0, 0 }, + + { ngx_string("sent_http_keep_alive"), NULL, + ngx_http_variable_sent_keep_alive, 0, 0, 0 }, + + { ngx_string("sent_http_transfer_encoding"), NULL, + ngx_http_variable_sent_transfer_encoding, 0, 0, 0 }, + + { ngx_string("sent_http_cache_control"), NULL, ngx_http_variable_headers, + offsetof(ngx_http_request_t, headers_out.cache_control), 0, 0 }, + { ngx_string("limit_rate"), ngx_http_variable_request_set_size, ngx_http_variable_request, offsetof(ngx_http_request_t, limit_rate), @@ -847,6 +888,178 @@ ngx_http_variable_body_bytes_sent(ngx_ht static ngx_int_t +ngx_http_variable_sent_content_type(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->headers_out.content_type.len) { + v->len = r->headers_out.content_type.len; + v->valid = 1; + v->no_cachable = 0; + v->not_found = 0; + v->data = r->headers_out.content_type.data; + + } else { + v->not_found = 1; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_sent_content_length(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + if (r->headers_out.content_length) { + v->len = r->headers_out.content_length->value.len; + v->valid = 1; + v->no_cachable = 0; + v->not_found = 0; + v->data = r->headers_out.content_length->value.data; + + return NGX_OK; + } + + if (r->headers_out.content_length_n >= 0) { + p = ngx_palloc(r->pool, NGX_OFF_T_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(p, "%O", r->headers_out.content_length_n) - p; + v->valid = 1; + v->no_cachable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; + } + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_sent_last_modified(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + if (r->headers_out.last_modified) { + v->len = r->headers_out.last_modified->value.len; + v->valid = 1; + v->no_cachable = 0; + v->not_found = 0; + v->data = r->headers_out.last_modified->value.data; + + return NGX_OK; + } + + if (r->headers_out.last_modified_time >= 0) { + p = ngx_palloc(r->pool, + sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT") - 1); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_http_time(p, r->headers_out.last_modified_time) - p; + v->valid = 1; + v->no_cachable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; + } + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_sent_connection(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + size_t len; + char *p; + + if (r->keepalive) { + len = sizeof("keep-alive") - 1; + p = "keep-alive"; + + } else { + len = sizeof("close") - 1; + p = "close"; + } + + v->len = len; + v->valid = 1; + v->no_cachable = 0; + v->not_found = 0; + v->data = (u_char *) p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_sent_keep_alive(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + ngx_http_core_loc_conf_t *clcf; + + if (r->keepalive) { + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->keepalive_header) { + + p = ngx_palloc(r->pool, sizeof("timeout=") - 1 + NGX_TIME_T_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(p, "timeout=%T", clcf->keepalive_header) - p; + v->valid = 1; + v->no_cachable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; + } + } + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_sent_transfer_encoding(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->chunked) { + v->len = sizeof("chunked") - 1; + v->valid = 1; + v->no_cachable = 0; + v->not_found = 0; + v->data = (u_char *) "chunked"; + + } else { + v->not_found = 1; + } + + return NGX_OK; +} + + +static ngx_int_t ngx_http_variable_request_completion(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { diff --git a/src/imap/ngx_imap.c b/src/imap/ngx_imap.c --- a/src/imap/ngx_imap.c +++ b/src/imap/ngx_imap.c @@ -11,6 +11,8 @@ static char *ngx_imap_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static int ngx_libc_cdecl ngx_imap_cmp_conf_in_addrs(const void *one, + const void *two); ngx_uint_t ngx_imap_max_module; @@ -56,10 +58,18 @@ static char * ngx_imap_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *rv; - ngx_uint_t m, mi, s; + u_char *text; + size_t len; + ngx_uint_t i, a, l, m, mi, s, p, last, bind_all, done; ngx_conf_t pcf; + ngx_array_t in_ports; + ngx_listening_t *ls; + ngx_imap_listen_t *imls; ngx_imap_module_t *module; + ngx_imap_in_port_t *imip; ngx_imap_conf_ctx_t *ctx; + ngx_imap_conf_in_port_t *in_port; + ngx_imap_conf_in_addr_t *in_addr; ngx_imap_core_srv_conf_t **cscfp; ngx_imap_core_main_conf_t *cmcf; @@ -191,5 +201,203 @@ ngx_imap_block(ngx_conf_t *cf, ngx_comma *cf = pcf; + + if (ngx_array_init(&in_ports, cf->temp_pool, 4, + sizeof(ngx_imap_conf_in_port_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + imls = cmcf->listen.elts; + + for (l = 0; l < cmcf->listen.nelts; l++) { + + /* AF_INET only */ + + in_port = in_ports.elts; + for (p = 0; p < in_ports.nelts; p++) { + if (in_port[p].port == imls[l].port) { + in_port = &in_port[p]; + goto found; + } + } + + in_port = ngx_array_push(&in_ports); + if (in_port == NULL) { + return NGX_CONF_ERROR; + } + + in_port->port = imls[l].port; + + if (ngx_array_init(&in_port->addrs, cf->temp_pool, 2, + sizeof(ngx_imap_conf_in_addr_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + found: + + in_addr = ngx_array_push(&in_port->addrs); + if (in_addr == NULL) { + return NGX_CONF_ERROR; + } + + in_addr->addr = imls[l].addr; + in_addr->ctx = imls[l].ctx; + in_addr->bind = imls[l].bind; + } + + /* optimize the lists of ports and addresses */ + + /* AF_INET only */ + + in_port = in_ports.elts; + for (p = 0; p < in_ports.nelts; p++) { + + ngx_qsort(in_port[p].addrs.elts, (size_t) in_port[p].addrs.nelts, + sizeof(ngx_imap_conf_in_addr_t), ngx_imap_cmp_conf_in_addrs); + + in_addr = in_port[p].addrs.elts; + last = in_port[p].addrs.nelts; + + /* + * if there is the binding to the "*:port" then we need to bind() + * to the "*:port" only and ignore the other bindings + */ + + if (in_addr[last - 1].addr == INADDR_ANY) { + in_addr[last - 1].bind = 1; + bind_all = 0; + + } else { + bind_all = 1; + } + + for (a = 0; a < last; /* void */ ) { + + if (!bind_all && !in_addr[a].bind) { + a++; + continue; + } + + ls = ngx_listening_inet_stream_socket(cf, in_addr[a].addr, + in_port[p].port); + if (ls == NULL) { + return NGX_CONF_ERROR; + } + + ls->backlog = -1; + ls->rcvbuf = -1; + ls->sndbuf = -1; + + ls->addr_ntop = 1; + ls->handler = ngx_imap_init_connection; + ls->pool_size = 256; + + /* STUB */ + ls->log = *cf->cycle->new_log; + ls->log.data = &ls->addr_text; + ls->log.handler = ngx_accept_log_error; + /**/ + + imip = ngx_palloc(cf->pool, sizeof(ngx_imap_in_port_t)); + if (imip == NULL) { + return NGX_CONF_ERROR; + } + + ls->servers = imip; + + in_addr = in_port[p].addrs.elts; + + if (in_addr[a].bind && in_addr[a].addr != INADDR_ANY) { + imip->naddrs = 1; + done = 0; + + } else if (in_port[p].addrs.nelts > 1 + && in_addr[last - 1].addr == INADDR_ANY) + { + imip->naddrs = last; + done = 1; + + } else { + imip->naddrs = 1; + done = 0; + } + +#if 0 + ngx_log_error(NGX_LOG_ALERT, cf->log, 0, + "%ui: %V %d %ui %ui", + a, &ls->addr_text, in_addr[a].bind, + imip->naddrs, last); +#endif + + imip->addrs = ngx_pcalloc(cf->pool, + imip->naddrs * sizeof(ngx_imap_in_addr_t)); + if (imip->addrs == NULL) { + return NGX_CONF_ERROR; + } + + for (i = 0; i < imip->naddrs; i++) { + imip->addrs[i].addr = in_addr[i].addr; + imip->addrs[i].ctx = in_addr[i].ctx; + + text = ngx_palloc(cf->pool, + INET_ADDRSTRLEN - 1 + sizeof(":65535") - 1); + if (text == NULL) { + return NGX_CONF_ERROR; + } + + len = ngx_inet_ntop(AF_INET, &in_addr[i].addr, text, + INET_ADDRSTRLEN); + + len = ngx_sprintf(text + len, ":%d", in_port[p].port) - text; + + imip->addrs[i].addr_text.len = len; + imip->addrs[i].addr_text.data = text; + } + + if (done) { + break; + } + + in_addr++; + in_port[p].addrs.elts = in_addr; + last--; + + a = 0; + } + } + return NGX_CONF_OK; } + + +static int ngx_libc_cdecl +ngx_imap_cmp_conf_in_addrs(const void *one, const void *two) +{ + ngx_imap_conf_in_addr_t *first, *second; + + first = (ngx_imap_conf_in_addr_t *) one; + second = (ngx_imap_conf_in_addr_t *) two; + + if (first->addr == INADDR_ANY) { + /* the INADDR_ANY must be the last resort, shift it to the end */ + return 1; + } + + if (first->bind && !second->bind) { + /* shift explicit bind()ed addresses to the start */ + return -1; + } + + if (!first->bind && second->bind) { + /* shift explicit bind()ed addresses to the start */ + return 1; + } + + /* do not sort by default */ + + return 0; +} 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 @@ -26,7 +26,46 @@ typedef struct { typedef struct { - ngx_array_t servers; /* ngx_imap_core_srv_conf_t */ + in_addr_t addr; + in_port_t port; + int family; + + /* server ctx */ + ngx_imap_conf_ctx_t *ctx; + + unsigned bind:1; +} ngx_imap_listen_t; + + +typedef struct { + in_addr_t addr; + ngx_imap_conf_ctx_t *ctx; + ngx_str_t addr_text; +} ngx_imap_in_addr_t; + + +typedef struct { + ngx_imap_in_addr_t *addrs; /* array of ngx_imap_in_addr_t */ + ngx_uint_t naddrs; +} ngx_imap_in_port_t; + + +typedef struct { + in_port_t port; + ngx_array_t addrs; /* array of ngx_imap_conf_in_addr_t */ +} ngx_imap_conf_in_port_t; + + +typedef struct { + in_addr_t addr; + ngx_imap_conf_ctx_t *ctx; + unsigned bind:1; +} ngx_imap_conf_in_addr_t; + + +typedef struct { + ngx_array_t servers; /* ngx_imap_core_srv_conf_t */ + ngx_array_t listen; /* ngx_imap_listen_t */ } ngx_imap_core_main_conf_t; @@ -52,7 +91,7 @@ typedef struct { ngx_array_t imap_capabilities; /* server ctx */ - ngx_imap_conf_ctx_t *ctx; + ngx_imap_conf_ctx_t *ctx; } ngx_imap_core_srv_conf_t; @@ -109,6 +148,7 @@ typedef struct { unsigned quoted:1; unsigned backslash:1; unsigned no_sync_literal:1; + unsigned starttls:1; ngx_str_t login; ngx_str_t passwd; @@ -116,6 +156,8 @@ typedef struct { ngx_str_t tag; ngx_str_t tagged_line; + ngx_str_t *addr_text; + ngx_uint_t command; ngx_array_t args; @@ -184,6 +226,9 @@ typedef struct { (s)->main_conf[module.ctx_index] #define ngx_imap_get_module_srv_conf(s, module) (s)->srv_conf[module.ctx_index] +#define ngx_imap_conf_get_module_main_conf(cf, module) \ + ((ngx_imap_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index] + void ngx_imap_init_connection(ngx_connection_t *c); void ngx_imap_send(ngx_event_t *wev); 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 @@ -55,7 +55,7 @@ static ngx_command_t ngx_imap_core_comm NULL }, { ngx_string("listen"), - NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1, + NGX_IMAP_SRV_CONF|NGX_CONF_TAKE12, ngx_imap_core_listen, 0, 0, @@ -143,7 +143,14 @@ ngx_imap_core_create_main_conf(ngx_conf_ } if (ngx_array_init(&cmcf->servers, cf->pool, 4, - sizeof(ngx_imap_core_srv_conf_t *)) == NGX_ERROR) + sizeof(ngx_imap_core_srv_conf_t *)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + if (ngx_array_init(&cmcf->listen, cf->pool, 4, sizeof(ngx_imap_listen_t)) + != NGX_OK) { return NGX_CONF_ERROR; } @@ -419,12 +426,14 @@ ngx_imap_core_server(ngx_conf_t *cf, ngx static char * ngx_imap_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - char *err; - ngx_str_t *value; - in_addr_t in_addr; - struct hostent *h; - ngx_listening_t *ls; - ngx_inet_upstream_t inet_upstream; + char *err; + ngx_str_t *value; + in_addr_t in_addr; + ngx_uint_t i; + struct hostent *h; + ngx_imap_listen_t *imls; + ngx_inet_upstream_t inet_upstream; + ngx_imap_core_main_conf_t *cmcf; value = cf->args->elts; @@ -469,29 +478,46 @@ ngx_imap_core_listen(ngx_conf_t *cf, ngx in_addr = INADDR_ANY; } + cmcf = ngx_imap_conf_get_module_main_conf(cf, ngx_imap_core_module); - ls = ngx_listening_inet_stream_socket(cf, in_addr, inet_upstream.port); - if (ls == NULL) { + imls = cmcf->listen.elts; + + for (i = 0; i < cmcf->listen.nelts; i++) { + + if (imls[i].addr != in_addr || imls[i].port != inet_upstream.port) { + continue; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate \"%V\" address and port pair", + &inet_upstream.url); return NGX_CONF_ERROR; } - ls->backlog = -1; - ls->rcvbuf = -1; - ls->sndbuf = -1; + imls = ngx_array_push(&cmcf->listen); + if (imls == NULL) { + return NGX_CONF_ERROR; + } - ls->addr_ntop = 1; - ls->handler = ngx_imap_init_connection; - ls->pool_size = 256; + ngx_memzero(imls, sizeof(ngx_imap_listen_t)); + + imls->addr = in_addr; + imls->port = inet_upstream.port; + imls->family = AF_INET; + imls->ctx = cf->ctx; - ls->ctx = cf->ctx; + if (cf->args->nelts == 2) { + return NGX_CONF_OK; + } - /* STUB */ - ls->log = *cf->cycle->new_log; - ls->log.data = &ls->addr_text; - ls->log.handler = ngx_accept_log_error; - /**/ + if (ngx_strcmp(value[2].data, "bind") == 0) { + imls->bind = 1; + return NGX_CONF_OK; + } - return NGX_CONF_OK; + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the invalid \"%V\" parameter", &value[2]); + return NGX_CONF_ERROR; } 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 @@ -44,35 +44,103 @@ static u_char imap_invalid_command[] = void ngx_imap_init_connection(ngx_connection_t *c) { - ngx_imap_log_ctx_t *lctx; + in_addr_t in_addr; + socklen_t len; + ngx_uint_t i; + struct sockaddr_in sin; + ngx_imap_log_ctx_t *ctx; + ngx_imap_in_port_t *imip; + ngx_imap_in_addr_t *imia; + ngx_imap_session_t *s; #if (NGX_IMAP_SSL) - ngx_imap_conf_ctx_t *ctx; ngx_imap_ssl_conf_t *sslcf; #endif - ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%ui client %V connected to %V", - c->number, &c->addr_text, &c->listening->addr_text); + + /* find the server configuration for the address:port */ + + /* AF_INET only */ + + imip = c->listening->servers; + imia = imip->addrs; + + i = 0; + + if (imip->naddrs > 1) { + + /* + * There are several addresses on this port and one of them + * is the "*:port" wildcard so getsockname() is needed to determine + * the server address. + * + * AcceptEx() already gave this address. + */ + +#if (NGX_WIN32) + if (c->local_sockaddr) { + in_addr = + ((struct sockaddr_in *) c->local_sockaddr)->sin_addr.s_addr; - lctx = ngx_palloc(c->pool, sizeof(ngx_imap_log_ctx_t)); - if (lctx == NULL) { + } else +#endif + { + len = sizeof(struct sockaddr_in); + if (getsockname(c->fd, (struct sockaddr *) &sin, &len) == -1) { + ngx_connection_error(c, ngx_socket_errno, + "getsockname() failed"); + ngx_imap_close_connection(c); + return; + } + + in_addr = sin.sin_addr.s_addr; + } + + /* the last address is "*" */ + + for ( /* void */ ; i < imip->naddrs - 1; i++) { + if (in_addr == imia[i].addr) { + break; + } + } + } + + + s = ngx_pcalloc(c->pool, sizeof(ngx_imap_session_t)); + if (s == NULL) { ngx_imap_close_connection(c); return; } - lctx->client = &c->addr_text; - lctx->session = NULL; + s->main_conf = imia[i].ctx->main_conf; + s->srv_conf = imia[i].ctx->srv_conf; + + s->addr_text = &imia[i].addr_text; + + c->data = s; + s->connection = c; + + ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%ui client %V connected to %V", + c->number, &c->addr_text, s->addr_text); + + ctx = ngx_palloc(c->pool, sizeof(ngx_imap_log_ctx_t)); + if (ctx == NULL) { + ngx_imap_close_connection(c); + return; + } + + ctx->client = &c->addr_text; + ctx->session = s; c->log->connection = c->number; c->log->handler = ngx_imap_log_error; - c->log->data = lctx; + c->log->data = ctx; c->log->action = "sending client greeting line"; c->log_error = NGX_ERROR_INFO; #if (NGX_IMAP_SSL) - ctx = c->ctx; - sslcf = ngx_imap_get_module_srv_conf(ctx, ngx_imap_ssl_module); + sslcf = ngx_imap_get_module_srv_conf(s, ngx_imap_ssl_module); if (sslcf->enable) { ngx_imap_ssl_init_connection(&sslcf->ssl, c); @@ -96,6 +164,7 @@ ngx_imap_starttls_handler(ngx_event_t *r c = rev->data; s = c->data; + s->starttls = 1; c->log->action = "in starttls state"; @@ -108,7 +177,7 @@ ngx_imap_starttls_handler(ngx_event_t *r static void ngx_imap_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c) { - ngx_imap_conf_ctx_t *ctx; + ngx_imap_session_t *s; ngx_imap_core_srv_conf_t *cscf; if (ngx_ssl_create_connection(ssl, c, 0) == NGX_ERROR) { @@ -118,8 +187,9 @@ ngx_imap_ssl_init_connection(ngx_ssl_t * if (ngx_ssl_handshake(c) == NGX_AGAIN) { - ctx = c->ctx; - cscf = ngx_imap_get_module_srv_conf(ctx, ngx_imap_core_module); + s = c->data; + + cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module); ngx_add_timer(c->read, cscf->timeout); @@ -135,9 +205,13 @@ ngx_imap_ssl_init_connection(ngx_ssl_t * static void ngx_imap_ssl_handshake_handler(ngx_connection_t *c) { + ngx_imap_session_t *s; + if (c->ssl->handshaked) { - if (c->data) { + s = c->data; + + if (s->starttls) { c->read->handler = ngx_imap_init_protocol; c->write->handler = ngx_imap_send; @@ -160,24 +234,14 @@ static void ngx_imap_init_session(ngx_connection_t *c) { ngx_imap_session_t *s; - ngx_imap_log_ctx_t *lctx; - ngx_imap_conf_ctx_t *ctx; ngx_imap_core_srv_conf_t *cscf; c->read->handler = ngx_imap_init_protocol; c->write->handler = ngx_imap_send; - s = ngx_pcalloc(c->pool, sizeof(ngx_imap_session_t)); - if (s == NULL) { - ngx_imap_close_connection(c); - return; - } + s = c->data; - ctx = c->ctx; - cscf = ngx_imap_get_module_srv_conf(ctx, ngx_imap_core_module); - - c->data = s; - s->connection = c; + cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module); s->protocol = cscf->protocol; @@ -187,14 +251,8 @@ ngx_imap_init_session(ngx_connection_t * return; } - s->main_conf = ctx->main_conf; - s->srv_conf = ctx->srv_conf; - s->out = greetings[s->protocol]; - lctx = c->log->data; - lctx->session = s; - ngx_add_timer(c->read, cscf->timeout); if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { @@ -896,8 +954,7 @@ ngx_imap_log_error(ngx_log_t *log, u_cha return p; } - p = ngx_snprintf(buf, len, ", server: %V", - &s->connection->listening->addr_text); + p = ngx_snprintf(buf, len, ", server: %V", s->addr_text); len -= p - buf; buf = p;