diff options
author | Igor Sysoev <igor@sysoev.ru> | 2005-07-08 14:36:01 +0000 |
---|---|---|
committer | Jonathan Kolb <jon@b0g.us> | 2005-07-08 14:36:01 +0000 |
commit | 4b06bf9c8c9f12978b3c2c1aa1b363c519f3a03e (patch) | |
tree | 240ea60a633ce2ab1b6a9bb9ddbf06386a8e5814 | |
parent | c33ab6ae8027dac02b16facf1d03e3e247168a50 (diff) | |
download | nginx-4b06bf9c8c9f12978b3c2c1aa1b363c519f3a03e.tar.gz |
Changes with nginx 0.1.38 08 Jul 2005v0.1.38
*) Feature: the "limit_rate" directive is supported in in proxy and
FastCGI mode.
*) Feature: the "X-Accel-Limit-Rate" response header line is supported
in proxy and FastCGI mode.
*) Feature: the "break" directive.
*) Feature: the "log_not_found" directive.
*) Bugfix: the response status code was not changed when request was
redirected by the ""X-Accel-Redirect" header line.
*) Bugfix: the variables set by the "set" directive could not be used
in SSI.
*) Bugfix: the segmentation fault may occurred if the SSI page has more
than one remote subrequest.
*) Bugfix: nginx treated the backend response as invalid if the status
line in the header was transferred in two packets; bug appeared in
0.1.29.
*) Feature: the "ssi_types" directive.
*) Feature: the "autoindex_exact_size" directive.
*) Bugfix: the ngx_http_autoindex_module did not support the long file
names in UTF-8.
*) Feature: the IMAP/POP3 proxy.
39 files changed, 4152 insertions, 141 deletions
@@ -1,4 +1,39 @@ +Changes with nginx 0.1.38 08 Jul 2005 + + *) Feature: the "limit_rate" directive is supported in in proxy and + FastCGI mode. + + *) Feature: the "X-Accel-Limit-Rate" response header line is supported + in proxy and FastCGI mode. + + *) Feature: the "break" directive. + + *) Feature: the "log_not_found" directive. + + *) Bugfix: the response status code was not changed when request was + redirected by the ""X-Accel-Redirect" header line. + + *) Bugfix: the variables set by the "set" directive could not be used + in SSI. + + *) Bugfix: the segmentation fault may occurred if the SSI page has more + than one remote subrequest. + + *) Bugfix: nginx treated the backend response as invalid if the status + line in the header was transferred in two packets; bug appeared in + 0.1.29. + + *) Feature: the "ssi_types" directive. + + *) Feature: the "autoindex_exact_size" directive. + + *) Bugfix: the ngx_http_autoindex_module did not support the long file + names in UTF-8. + + *) Feature: the IMAP/POP3 proxy. + + Changes with nginx 0.1.37 23 Jun 2005 *) Change: now the "\n" is added to the end of the "nginx.pid" file. diff --git a/CHANGES.ru b/CHANGES.ru index e74b84324..d60d4220e 100644 --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,39 @@ +Изменения в nginx 0.1.38 08.07.2005 + + *) Добавление: директива limit_rate поддерживается в режиме прокси и + FastCGI. + + *) Добавление: в режиме прокси и FastCGI поддерживается строка + заголовка "X-Accel-Limit-Rate" в ответе бэкенда. + + *) Добавление: директива break. + + *) Добавление: директива log_not_found. + + *) Исправление: при перенаправлении запроса с помощью строки заголовка + "X-Accel-Redirect" не изменялся код ответа. + + *) Исправление: переменные, установленные директивой set не могли + использоваться в SSI. + + *) Исправление: при включении в SSI более одного удалённого подзапроса + мог произойти segmentation fault. + + *) Исправление: если статусная строка в ответе бэкенда передавалась в + двух пакетах, то nginx считал ответ неверным; ошибка появилась в + 0.1.29. + + *) Добавление: директива ssi_types. + + *) Добавление: директива autoindex_exact_size. + + *) Исправление: модуль ngx_http_autoindex_module не поддерживал длинные + имена файлов в UTF-8. + + *) Добавление: IMAP/POP3 прокси. + + Изменения в nginx 0.1.37 23.06.2005 *) Изменение: в конце файла nginx.pid теперь добавляется "\n". @@ -97,7 +132,7 @@ регулярных выражений. *) Добавление: в режиме прокси и FastCGI поддерживается строка - заголовка X-Accel-Redirect в ответе бэкенда. + заголовка "X-Accel-Redirect" в ответе бэкенда. Изменения в nginx 0.1.31 16.05.2005 diff --git a/conf/nginx.conf b/conf/nginx.conf index ffe1bf626..a4c1fc5e7 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -37,7 +37,7 @@ http { # deny access to .htaccess files # - #location ~ \.ht { + #location ~ /\.ht { # deny all; #} } diff --git a/src/core/nginx.h b/src/core/nginx.h index f9f1a061b..221ba38f0 100644 --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -8,7 +8,7 @@ #define _NGINX_H_INCLUDED_ -#define NGINX_VER "nginx/0.1.37" +#define NGINX_VER "nginx/0.1.38" #define NGINX_VAR "NGINX" #define NGX_NEWPID_EXT ".newbin" diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h index 0224d98dd..10a5cd587 100644 --- a/src/core/ngx_connection.h +++ b/src/core/ngx_connection.h @@ -128,6 +128,7 @@ struct ngx_connection_s { unsigned single_connection:1; unsigned unexpected_eof:1; unsigned timedout:1; + unsigned closed:1; unsigned sendfile:1; unsigned sndlowat:1; diff --git a/src/core/ngx_palloc.c b/src/core/ngx_palloc.c index e7c888285..9bb50f79c 100644 --- a/src/core/ngx_palloc.c +++ b/src/core/ngx_palloc.c @@ -15,7 +15,7 @@ ngx_create_pool(size_t size, ngx_log_t *log) p = ngx_alloc(size, log); if (p == NULL) { - return NULL; + return NULL; } p->last = (u_char *) p + sizeof(ngx_pool_t); diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c index e21a9fcdd..575e60e84 100644 --- a/src/core/ngx_string.c +++ b/src/core/ngx_string.c @@ -753,20 +753,62 @@ ngx_utf_length(ngx_str_t *utf) continue; } - if (c < 0xC0) { - /* invalid utf */ - return utf->len; - } + if (c >= 0xc0) { + for (c <<= 1; c & 0x80; c <<= 1) { + i++; + } - for (c <<= 1; c & 0x80; c <<= 1) { - i++; + continue; } + + /* invalid utf */ + + return utf->len; } return len; } +u_char * +ngx_utf_cpystrn(u_char *dst, u_char *src, size_t n) +{ + u_char c; + + if (n == 0) { + return dst; + } + + for ( /* void */ ; --n; dst++, src++) { + + c = *src; + *dst = c; + + if (c < 0x80) { + if (*dst != '\0') { + continue; + } + + return dst; + } + + if (c >= 0xc0) { + for (c <<= 1; c & 0x80; c <<= 1) { + *++dst = *++src; + } + + continue; + } + + /* invalid utf */ + } + + *dst = '\0'; + + return dst; +} + + uintptr_t ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type) { diff --git a/src/core/ngx_string.h b/src/core/ngx_string.h index ff21619ae..545c6cc85 100644 --- a/src/core/ngx_string.h +++ b/src/core/ngx_string.h @@ -97,6 +97,8 @@ void ngx_encode_base64(ngx_str_t *dst, ngx_str_t *src); ngx_int_t ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src); size_t ngx_utf_length(ngx_str_t *utf); +u_char * ngx_utf_cpystrn(u_char *dst, u_char *src, size_t n); + #define NGX_ESCAPE_URI 0 #define NGX_ESCAPE_ARGS 1 diff --git a/src/event/modules/ngx_rtsig_module.c b/src/event/modules/ngx_rtsig_module.c index 122b0918d..838b27d70 100644 --- a/src/event/modules/ngx_rtsig_module.c +++ b/src/event/modules/ngx_rtsig_module.c @@ -761,7 +761,7 @@ ngx_rtsig_process_overflow(ngx_cycle_t *cycle) } } - ngx_log_error(NGX_LOG_INFO, cycle->log, 0, + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "rt signal queue overflow recovered"); overflow = 0; diff --git a/src/event/ngx_event_pipe.c b/src/event/ngx_event_pipe.c index e18621167..74cfac2d1 100644 --- a/src/event/ngx_event_pipe.c +++ b/src/event/ngx_event_pipe.c @@ -182,7 +182,8 @@ static ngx_int_t ngx_event_pipe_read_upstream(ngx_event_pipe_t *p) } else if (!p->cachable && p->downstream->data == p->output_ctx - && p->downstream->write->ready) + && p->downstream->write->ready + && !p->downstream->write->delayed) { /* * if the bufs are not needed to be saved in a cache and @@ -461,7 +462,8 @@ static ngx_int_t ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p) } if (p->downstream->data != p->output_ctx - || !p->downstream->write->ready) + || !p->downstream->write->ready + || p->downstream->write->delayed) { break; } diff --git a/src/http/modules/ngx_http_autoindex_module.c b/src/http/modules/ngx_http_autoindex_module.c index e26d2947e..d12834083 100644 --- a/src/http/modules/ngx_http_autoindex_module.c +++ b/src/http/modules/ngx_http_autoindex_module.c @@ -35,6 +35,7 @@ typedef struct { typedef struct { ngx_flag_t enable; ngx_flag_t localtime; + ngx_flag_t exact_size; } ngx_http_autoindex_loc_conf_t; @@ -67,6 +68,13 @@ static ngx_command_t ngx_http_autoindex_commands[] = { offsetof(ngx_http_autoindex_loc_conf_t, localtime), NULL }, + { ngx_string("autoindex_exact_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_autoindex_loc_conf_t, exact_size), + NULL }, + ngx_null_command }; @@ -117,10 +125,11 @@ static u_char tail[] = static ngx_int_t ngx_http_autoindex_handler(ngx_http_request_t *r) { - u_char *last; - size_t len; + u_char *last, scale; + off_t length; + size_t len, copy; ngx_tm_t tm; - ngx_int_t rc; + ngx_int_t rc, size; ngx_uint_t i, level; ngx_err_t err; ngx_buf_t *b; @@ -351,7 +360,7 @@ ngx_http_autoindex_handler(ngx_http_request_t *r) + NGX_HTTP_AUTOINDEX_NAME_LEN + sizeof(">") - 2 + sizeof("</a>") - 1 + sizeof(" 28-Sep-1970 12:00 ") - 1 - + 19 + + 20 + 2; } @@ -396,14 +405,27 @@ ngx_http_autoindex_handler(ngx_http_request_t *r) *b->last++ = '"'; *b->last++ = '>'; - b->last = ngx_cpystrn(b->last, entry[i].name.data, - NGX_HTTP_AUTOINDEX_NAME_LEN + 1); - len = entry[i].utf_len; + if (len) { + if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) { + copy = NGX_HTTP_AUTOINDEX_NAME_LEN - 3 + 1; + + } else { + copy = NGX_HTTP_AUTOINDEX_NAME_LEN + 1; + } + + b->last = ngx_utf_cpystrn(b->last, entry[i].name.data, copy); + last = b->last; + + } else { + b->last = ngx_cpystrn(b->last, entry[i].name.data, + NGX_HTTP_AUTOINDEX_NAME_LEN + 1); + last = b->last - 3; + } + if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) { - b->last = ngx_cpymem(b->last - 3, "..></a>", - sizeof("..></a>") - 1); + b->last = ngx_cpymem(last, "..></a>", sizeof("..></a>") - 1); } else { if (entry[i].dir && NGX_HTTP_AUTOINDEX_NAME_LEN - len > 0) { @@ -427,12 +449,55 @@ ngx_http_autoindex_handler(ngx_http_request_t *r) tm.ngx_tm_hour, tm.ngx_tm_min); - if (entry[i].dir) { - b->last = ngx_cpymem(b->last, " -", - sizeof(" -") - 1); + if (alcf->exact_size) { + if (entry[i].dir) { + b->last = ngx_cpymem(b->last, " -", + sizeof(" -") - 1); + } else { + b->last = ngx_sprintf(b->last, "%19O", entry[i].size); + } } else { - b->last = ngx_sprintf(b->last, "%19O", entry[i].size); + if (entry[i].dir) { + b->last = ngx_cpymem(b->last, " -", sizeof(" -") - 1); + + } else { + length = entry[i].size; + + if (length > 1024 * 1024 * 1024 - 1) { + size = (ngx_int_t) (length / (1024 * 1024 * 1024)); + if ((length % (1024 * 1024 * 1024)) + > (1024 * 1024 * 1024 / 2 - 1)) + { + size++; + } + scale = 'G'; + + } else if (length > 1024 * 1024 - 1) { + size = (ngx_int_t) (length / (1024 * 1024)); + if ((length % (1024 * 1024)) > (1024 * 1024 / 2 - 1)) { + size++; + } + scale = 'M'; + + } else if (length > 9999) { + size = (ngx_int_t) (length / 1024); + if (length % 1024 > 511) { + size++; + } + scale = 'K'; + + } else { + size = (ngx_int_t) length; + scale = ' '; + } + + b->last = ngx_sprintf(b->last, "%6i", size); + + if (scale != ' ') { + *b->last++ = scale; + } + } } *b->last++ = CR; @@ -559,6 +624,7 @@ ngx_http_autoindex_create_loc_conf(ngx_conf_t *cf) conf->enable = NGX_CONF_UNSET; conf->localtime = NGX_CONF_UNSET; + conf->exact_size = NGX_CONF_UNSET; return conf; } @@ -572,6 +638,7 @@ ngx_http_autoindex_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->enable, prev->enable, 0); ngx_conf_merge_value(conf->localtime, prev->localtime, 0); + ngx_conf_merge_value(conf->exact_size, prev->exact_size, 1); return NGX_CONF_OK; } diff --git a/src/http/modules/ngx_http_gzip_filter_module.c b/src/http/modules/ngx_http_gzip_filter_module.c index f7cb92c18..596e52403 100644 --- a/src/http/modules/ngx_http_gzip_filter_module.c +++ b/src/http/modules/ngx_http_gzip_filter_module.c @@ -15,7 +15,7 @@ typedef struct { ngx_flag_t enable; ngx_flag_t no_buffer; - ngx_array_t *types; /* array of ngx_http_gzip_type_t */ + ngx_array_t *types; /* array of ngx_str_t */ ngx_bufs_t bufs; @@ -29,12 +29,6 @@ typedef struct { } ngx_http_gzip_conf_t; -typedef struct { - ngx_str_t name; - ngx_uint_t enable; -} ngx_http_gzip_type_t; - - #define NGX_HTTP_GZIP_PROXIED_OFF 0x0002 #define NGX_HTTP_GZIP_PROXIED_EXPIRED 0x0004 #define NGX_HTTP_GZIP_PROXIED_NO_CACHE 0x0008 @@ -91,20 +85,18 @@ static ngx_int_t ngx_http_gzip_filter_init(ngx_cycle_t *cycle); static void *ngx_http_gzip_create_conf(ngx_conf_t *cf); static char *ngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child); -static char *ngx_http_gzip_set_types(ngx_conf_t *cf, ngx_command_t *cmd, +static char *ngx_http_gzip_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static char *ngx_http_gzip_set_window(ngx_conf_t *cf, void *post, void *data); -static char *ngx_http_gzip_set_hash(ngx_conf_t *cf, void *post, void *data); +static char *ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data); +static char *ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data); static ngx_conf_num_bounds_t ngx_http_gzip_comp_level_bounds = { ngx_conf_check_num_bounds, 1, 9 }; -static ngx_conf_post_handler_pt ngx_http_gzip_set_window_p = - ngx_http_gzip_set_window; -static ngx_conf_post_handler_pt ngx_http_gzip_set_hash_p = - ngx_http_gzip_set_hash; +static ngx_conf_post_handler_pt ngx_http_gzip_window_p = ngx_http_gzip_window; +static ngx_conf_post_handler_pt ngx_http_gzip_hash_p = ngx_http_gzip_hash; @@ -148,7 +140,7 @@ static ngx_command_t ngx_http_gzip_filter_commands[] = { { ngx_string("gzip_types"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, - ngx_http_gzip_set_types, + ngx_http_gzip_types, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, @@ -165,14 +157,14 @@ static ngx_command_t ngx_http_gzip_filter_commands[] = { ngx_conf_set_size_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_gzip_conf_t, wbits), - &ngx_http_gzip_set_window_p }, + &ngx_http_gzip_window_p }, { ngx_string("gzip_hash"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_gzip_conf_t, memlevel), - &ngx_http_gzip_set_hash_p }, + &ngx_http_gzip_hash_p }, { ngx_string("gzip_no_buffer"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, @@ -270,10 +262,10 @@ static ngx_http_output_body_filter_pt ngx_http_next_body_filter; static ngx_int_t ngx_http_gzip_header_filter(ngx_http_request_t *r) { - ngx_uint_t i, found; + ngx_str_t *type; + ngx_uint_t i; ngx_http_gzip_ctx_t *ctx; ngx_http_gzip_conf_t *conf; - ngx_http_gzip_type_t *type; conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); @@ -297,24 +289,21 @@ ngx_http_gzip_header_filter(ngx_http_request_t *r) } - found = 0; type = conf->types->elts; - for (i = 0; i < conf->types->nelts; i++) { - if (r->headers_out.content_type.len >= type[i].name.len + if (r->headers_out.content_type.len >= type[i].len && ngx_strncasecmp(r->headers_out.content_type.data, - type[i].name.data, type[i].name.len) == 0) + type[i].data, type[i].len) == 0) { - found = 1; - break; + goto found; } } - if (!found) { - return ngx_http_next_header_filter(r); - } + return ngx_http_next_header_filter(r); +found: + if (r->headers_in.via) { if (conf->proxied & NGX_HTTP_GZIP_PROXIED_OFF) { return ngx_http_next_header_filter(r); @@ -1031,7 +1020,7 @@ ngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child) ngx_http_gzip_conf_t *prev = parent; ngx_http_gzip_conf_t *conf = child; - ngx_http_gzip_type_t *type; + ngx_str_t *type; ngx_conf_merge_value(conf->enable, prev->enable, 0); @@ -1051,8 +1040,7 @@ ngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child) if (conf->types == NULL) { if (prev->types == NULL) { - conf->types = ngx_array_create(cf->pool, 1, - sizeof(ngx_http_gzip_type_t)); + conf->types = ngx_array_create(cf->pool, 1, sizeof(ngx_str_t)); if (conf->types == NULL) { return NGX_CONF_ERROR; } @@ -1062,9 +1050,8 @@ ngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_ERROR; } - type->name.len = sizeof("text/html") - 1; - type->name.data = (u_char *) "text/html"; - type->enable = 1; + type->len = sizeof("text/html") - 1; + type->data = (u_char *) "text/html"; } else { conf->types = prev->types; @@ -1076,17 +1063,15 @@ ngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child) static char * -ngx_http_gzip_set_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +ngx_http_gzip_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_gzip_conf_t *gcf = conf; - ngx_str_t *value; - ngx_uint_t i; - ngx_http_gzip_type_t *type; + ngx_str_t *value, *type; + ngx_uint_t i; if (gcf->types == NULL) { - gcf->types = ngx_array_create(cf->pool, 4, - sizeof(ngx_http_gzip_type_t)); + gcf->types = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t)); if (gcf->types == NULL) { return NGX_CONF_ERROR; } @@ -1096,9 +1081,8 @@ ngx_http_gzip_set_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_ERROR; } - type->name.len = sizeof("text/html") - 1; - type->name.data = (u_char *) "text/html"; - type->enable = 1; + type->len = sizeof("text/html") - 1; + type->data = (u_char *) "text/html"; } value = cf->args->elts; @@ -1114,14 +1098,14 @@ ngx_http_gzip_set_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_ERROR; } - type->name.len = value[i].len; + type->len = value[i].len; - type->name.data = ngx_palloc(cf->pool, type->name.len + 1); - if (type->name.data == NULL) { + type->data = ngx_palloc(cf->pool, type->len + 1); + if (type->data == NULL) { return NGX_CONF_ERROR; } - ngx_cpystrn(type->name.data, value[i].data, type->name.len + 1); + ngx_cpystrn(type->data, value[i].data, type->len + 1); } return NGX_CONF_OK; @@ -1129,7 +1113,7 @@ ngx_http_gzip_set_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) static char * -ngx_http_gzip_set_window(ngx_conf_t *cf, void *post, void *data) +ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data) { int *np = data; @@ -1153,7 +1137,7 @@ ngx_http_gzip_set_window(ngx_conf_t *cf, void *post, void *data) static char * -ngx_http_gzip_set_hash(ngx_conf_t *cf, void *post, void *data) +ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data) { int *np = data; diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c index 83625be7b..85503d0ac 100644 --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -914,7 +914,7 @@ ngx_http_proxy_parse_status_line(ngx_http_request_t *r, ngx_http_proxy_ctx_t *p) } break; - /* end of request line */ + /* end of status line */ case sw_almost_done: p->status_end = pos - 1; switch (ch) { @@ -926,7 +926,7 @@ ngx_http_proxy_parse_status_line(ngx_http_request_t *r, ngx_http_proxy_ctx_t *p) } } - u->header_in.pos = pos + 1; + u->header_in.pos = pos; r->state = state; return NGX_AGAIN; @@ -1803,7 +1803,7 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } for (i = 0; i < plcf->peers->number; i++) { - plcf->peers->peer[i].uri_separator = ":"; + plcf->peers->peer[i].uri_separator = ""; } plcf->host_header = inet_upstream.host_header; diff --git a/src/http/modules/ngx_http_rewrite_module.c b/src/http/modules/ngx_http_rewrite_module.c index ed7abd66a..5f73c026c 100644 --- a/src/http/modules/ngx_http_rewrite_module.c +++ b/src/http/modules/ngx_http_rewrite_module.c @@ -36,6 +36,8 @@ static ngx_int_t ngx_http_rewrite_init(ngx_cycle_t *cycle); static char *ngx_http_rewrite(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_rewrite_return(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_rewrite_break(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static char *ngx_http_rewrite_if(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char * ngx_http_rewrite_if_condition(ngx_conf_t *cf, @@ -66,6 +68,14 @@ static ngx_command_t ngx_http_rewrite_commands[] = { 0, NULL }, + { ngx_string("break"), + NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF + |NGX_CONF_NOARGS, + ngx_http_rewrite_break, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + { ngx_string("if"), NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_1MORE, ngx_http_rewrite_if, @@ -601,6 +611,24 @@ ngx_http_rewrite_return(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) static char * +ngx_http_rewrite_break(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_rewrite_loc_conf_t *lcf = conf; + + ngx_http_script_code_pt *code; + + code = ngx_http_script_start_code(cf->pool, &lcf->codes, sizeof(uintptr_t)); + if (code == NULL) { + return NGX_CONF_ERROR; + } + + *code = ngx_http_script_break_code; + + return NGX_CONF_OK; +} + + +static char * ngx_http_rewrite_if(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_rewrite_loc_conf_t *lcf = conf; diff --git a/src/http/modules/ngx_http_ssi_filter_module.c b/src/http/modules/ngx_http_ssi_filter_module.c index 2992ff59d..856726a35 100644 --- a/src/http/modules/ngx_http_ssi_filter_module.c +++ b/src/http/modules/ngx_http_ssi_filter_module.c @@ -24,6 +24,8 @@ typedef struct { ngx_flag_t silent_errors; ngx_flag_t ignore_recycled_buffers; + ngx_array_t *types; /* array of ngx_str_t */ + size_t min_file_chunk; size_t value_len; } ngx_http_ssi_conf_t; @@ -127,6 +129,8 @@ static ngx_int_t ngx_http_ssi_endif(ngx_http_request_t *r, static ngx_http_variable_value_t * ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r, uintptr_t gmt); +static char *ngx_http_ssi_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + static ngx_int_t ngx_http_ssi_add_variables(ngx_conf_t *cf); static void *ngx_http_ssi_create_conf(ngx_conf_t *cf); static char *ngx_http_ssi_merge_conf(ngx_conf_t *cf, @@ -164,6 +168,13 @@ static ngx_command_t ngx_http_ssi_filter_commands[] = { offsetof(ngx_http_ssi_conf_t, min_file_chunk), NULL }, + { ngx_string("ssi_types"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, + ngx_http_ssi_types, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + ngx_null_command }; @@ -201,7 +212,7 @@ static ngx_int_t (*ngx_http_next_body_filter) (ngx_http_request_t *r, static u_char ngx_http_ssi_string[] = "<!--"; static u_char ngx_http_ssi_error_string[] = - "[an error occurred while processing the directive]"; + "[an error occurred while processing the directive]"; static ngx_str_t ngx_http_ssi_none = ngx_string("(none)"); @@ -280,24 +291,34 @@ static ngx_http_variable_t ngx_http_ssi_vars[] = { static ngx_int_t ngx_http_ssi_header_filter(ngx_http_request_t *r) { + ngx_uint_t i; + ngx_str_t *type; ngx_http_ssi_ctx_t *ctx; ngx_http_ssi_conf_t *conf; conf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module); - if (!conf->enable) { + if (!conf->enable + || r->headers_out.content_type.len == 0) + { return ngx_http_next_header_filter(r); } - /* TODO: "text/html" -> custom types */ - if (r->headers_out.content_type.len == 0 - || ngx_strncasecmp(r->headers_out.content_type.data, "text/html", 5) - != 0) - { - return ngx_http_next_header_filter(r); + type = conf->types->elts; + for (i = 0; i < conf->types->nelts; i++) { + if (r->headers_out.content_type.len >= type[i].len + && ngx_strncasecmp(r->headers_out.content_type.data, + type[i].data, type[i].len) == 0) + { + goto found; + } } + return ngx_http_next_header_filter(r); + + +found: ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_ssi_ctx_t)); if (ctx == NULL) { @@ -1632,6 +1653,56 @@ ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r, uintptr_t gmt) } +static char * +ngx_http_ssi_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_ssi_conf_t *scf = conf; + + ngx_str_t *value, *type; + ngx_uint_t i; + + if (scf->types == NULL) { + scf->types = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t)); + if (scf->types == NULL) { + return NGX_CONF_ERROR; + } + + type = ngx_array_push(scf->types); + if (type == NULL) { + return NGX_CONF_ERROR; + } + + type->len = sizeof("text/html") - 1; + type->data = (u_char *) "text/html"; + } + + value = cf->args->elts; + + for (i = 1; i < cf->args->nelts; i++) { + + if (ngx_strcmp(value[i].data, "text/html") == 0) { + continue; + } + + type = ngx_array_push(scf->types); + if (type == NULL) { + return NGX_CONF_ERROR; + } + + type->len = value[i].len; + + type->data = ngx_palloc(cf->pool, type->len + 1); + if (type->data == NULL) { + return NGX_CONF_ERROR; + } + + ngx_cpystrn(type->data, value[i].data, type->len + 1); + } + + return NGX_CONF_OK; +} + + static ngx_int_t ngx_http_ssi_add_variables(ngx_conf_t *cf) { @@ -1661,6 +1732,12 @@ ngx_http_ssi_create_conf(ngx_conf_t *cf) return NGX_CONF_ERROR; } + /* + * set by ngx_pcalloc(): + * + * conf->types = NULL; + */ + conf->enable = NGX_CONF_UNSET; conf->silent_errors = NGX_CONF_UNSET; conf->ignore_recycled_buffers = NGX_CONF_UNSET; @@ -1678,6 +1755,8 @@ ngx_http_ssi_merge_conf(ngx_conf_t *cf, void *parent, void *child) ngx_http_ssi_conf_t *prev = parent; ngx_http_ssi_conf_t *conf = child; + ngx_str_t *type; + ngx_conf_merge_value(conf->enable, prev->enable, 0); ngx_conf_merge_value(conf->silent_errors, prev->silent_errors, 0); ngx_conf_merge_value(conf->ignore_recycled_buffers, @@ -1686,6 +1765,26 @@ ngx_http_ssi_merge_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_size_value(conf->min_file_chunk, prev->min_file_chunk, 1024); ngx_conf_merge_size_value(conf->value_len, prev->value_len, 256); + if (conf->types == NULL) { + if (prev->types == NULL) { + conf->types = ngx_array_create(cf->pool, 1, sizeof(ngx_str_t)); + if (conf->types == NULL) { + return NGX_CONF_ERROR; + } + + type = ngx_array_push(conf->types); + if (type == NULL) { + return NGX_CONF_ERROR; + } + + type->len = sizeof("text/html") - 1; + type->data = (u_char *) "text/html"; + + } else { + conf->types = prev->types; + } + } + return NGX_CONF_OK; } diff --git a/src/http/modules/ngx_http_static_module.c b/src/http/modules/ngx_http_static_module.c index a84a70eb6..54e624fa8 100644 --- a/src/http/modules/ngx_http_static_module.c +++ b/src/http/modules/ngx_http_static_module.c @@ -210,8 +210,10 @@ ngx_http_static_handler(ngx_http_request_t *r) rc = NGX_HTTP_INTERNAL_SERVER_ERROR; } - ngx_log_error(level, log, err, - ngx_open_file_n " \"%s\" failed", name.data); + if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) { + ngx_log_error(level, log, err, + ngx_open_file_n " \"%s\" failed", name.data); + } return rc; } diff --git a/src/http/ngx_http_copy_filter_module.c b/src/http/ngx_http_copy_filter_module.c index 5561134da..46715482a 100644 --- a/src/http/ngx_http_copy_filter_module.c +++ b/src/http/ngx_http_copy_filter_module.c @@ -68,13 +68,17 @@ ngx_http_copy_filter(ngx_http_request_t *r, ngx_chain_t *in) ngx_output_chain_ctx_t *ctx; ngx_http_copy_filter_conf_t *conf; - if (r->connection->write->error) { - return NGX_ERROR; - } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "copy filter: \"%V\"", &r->uri); + if (r->connection->closed) { + rc = ngx_http_next_filter(r, in); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "copy closed filter: %i \"%V\"", rc, &r->uri); + return rc; + } + ctx = ngx_http_get_module_ctx(r, ngx_http_copy_filter_module); if (ctx == NULL) { diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index 1d399704f..ad5058512 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -326,6 +326,13 @@ static ngx_command_t ngx_http_core_commands[] = { offsetof(ngx_http_core_loc_conf_t, msie_padding), NULL }, + { ngx_string("log_not_found"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, log_not_found), + NULL }, + { ngx_string("error_page"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_2MORE, @@ -633,6 +640,8 @@ ngx_http_find_location_config(ngx_http_request_t *r) return NGX_HTTP_MOVED_PERMANENTLY; } + r->limit_rate = clcf->limit_rate; + if (clcf->handler) { r->content_handler = clcf->handler; } @@ -862,10 +871,6 @@ ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in) { ngx_int_t rc; - if (r->connection->write->error) { - return NGX_ERROR; - } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http output filter \"%V\"", &r->uri); @@ -873,7 +878,7 @@ ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in) if (rc == NGX_ERROR) { /* NGX_ERROR may be returned by any filter */ - r->connection->write->error = 1; + r->connection->closed = 1; } return rc; @@ -1727,6 +1732,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t *cf) lcf->reset_timedout_connection = NGX_CONF_UNSET; lcf->port_in_redirect = NGX_CONF_UNSET; lcf->msie_padding = NGX_CONF_UNSET; + lcf->log_not_found = NGX_CONF_UNSET; return lcf; } @@ -1841,6 +1847,7 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf, prev->reset_timedout_connection, 0); ngx_conf_merge_value(conf->port_in_redirect, prev->port_in_redirect, 1); ngx_conf_merge_value(conf->msie_padding, prev->msie_padding, 1); + ngx_conf_merge_value(conf->log_not_found, prev->log_not_found, 1); if (conf->open_files == NULL) { conf->open_files = prev->open_files; diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h index 11640467e..1f6b185ca 100644 --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -224,6 +224,7 @@ struct ngx_http_core_loc_conf_s { ngx_flag_t reset_timedout_connection; /* reset_timedout_connection */ ngx_flag_t port_in_redirect; /* port_in_redirect */ ngx_flag_t msie_padding; /* msie_padding */ + ngx_flag_t log_not_found; /* log_not_found */ ngx_array_t *error_pages; /* error_page */ diff --git a/src/http/ngx_http_postpone_filter_module.c b/src/http/ngx_http_postpone_filter_module.c index 8e47816fb..d7d338f09 100644 --- a/src/http/ngx_http_postpone_filter_module.c +++ b/src/http/ngx_http_postpone_filter_module.c @@ -48,13 +48,18 @@ ngx_http_postpone_filter(ngx_http_request_t *r, ngx_chain_t *in) ngx_http_request_t *mr; ngx_http_postponed_request_t *pr, **ppr; - if (r->connection->write->error) { - return NGX_ERROR; - } - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http postpone filter \"%V\" %p", &r->uri, in); + if (r->connection->closed) { + + if (r->postponed) { + r->postponed = r->postponed->next; + } + + return NGX_ERROR; + } + if (r != r->connection->data || (r->postponed && in)) { if (r->postponed) { @@ -112,7 +117,7 @@ ngx_http_postpone_filter(ngx_http_request_t *r, ngx_chain_t *in) if (rc == NGX_ERROR) { /* NGX_ERROR may be returned by any filter */ - r->connection->write->error = 1; + r->connection->closed = 1; } return rc; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 9ced028a5..f41a891b7 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -90,11 +90,6 @@ ngx_http_header_t ngx_http_headers_in[] = { { ngx_string("Range"), offsetof(ngx_http_headers_in_t, range), ngx_http_process_header_line }, -#if 0 - { ngx_string("If-Range"), offsetof(ngx_http_headers_in_t, if_range), - ngx_http_process_header_line }, -#endif - #if (NGX_HTTP_GZIP) { ngx_string("Accept-Encoding"), offsetof(ngx_http_headers_in_t, accept_encoding), @@ -1441,6 +1436,8 @@ ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc) r->done = 1; if (r != r->connection->data) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http finalize non-active request: \"%V\"", &r->uri); return; } @@ -1448,12 +1445,18 @@ ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc) pr = r->parent; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http parent request: \"%V\"", &pr->uri); + if (rc != NGX_AGAIN) { pr->connection->data = pr; } if (pr->postponed) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http request: \"%V\" has postponed", &pr->uri); + if (rc != NGX_AGAIN && pr->postponed->request == r) { pr->postponed = pr->postponed->next; @@ -1462,9 +1465,13 @@ ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc) } } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http request: \"%V\" still has postponed", + &pr->uri); + if (pr->done) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http wake request: \"%V\"", &pr->uri); + "http wake parent request: \"%V\"", &pr->uri); pr->write_event_handler(pr); } @@ -1483,7 +1490,7 @@ ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc) ngx_del_timer(r->connection->write); } - if (rc == NGX_HTTP_CLIENT_CLOSED_REQUEST || r->closed) { + if (r->connection->closed) { ngx_http_close_request(r, 0); ngx_http_close_connection(r->connection); return; @@ -1492,13 +1499,15 @@ ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc) ngx_http_finalize_request(r, ngx_http_special_response_handler(r, rc)); return; + } - } else if (rc == NGX_ERROR) { + if (rc == NGX_ERROR || r->connection->closed) { ngx_http_close_request(r, 0); ngx_http_close_connection(r->connection); return; + } - } else if (rc == NGX_AGAIN || r->out) { + if (rc == NGX_AGAIN || r->out) { (void) ngx_http_set_write_handler(r); return; } @@ -1553,6 +1562,10 @@ ngx_http_set_write_handler(ngx_http_request_t *r) r->write_event_handler = ngx_http_writer; + if (r->connection->closed) { + return NGX_OK; + } + wev = r->connection->write; if (wev->ready && wev->delayed) { @@ -1673,6 +1686,9 @@ static ngx_int_t ngx_http_postponed_handler(ngx_http_request_t *r) { ngx_int_t rc; +#if 0 + ngx_http_request_t *mr; +#endif ngx_http_postponed_request_t *pr; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -1687,20 +1703,16 @@ ngx_http_postponed_handler(ngx_http_request_t *r) rc = ngx_http_output_filter(r, NULL); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http postponed output filter: %d", rc); + "http postponed output filter: %d", rc); if (rc == NGX_AGAIN) { return rc; } - if (rc == NGX_ERROR) { - /* NGX_ERROR may be returned by any filter */ - r->connection->write->error = 1; - - ngx_http_finalize_request(r, rc); - - return NGX_DONE; - } + /* + * we treat NGX_ERROR as NGX_OK, because we need to complete + * all postponed requests + */ pr = r->postponed; @@ -1713,7 +1725,7 @@ ngx_http_postponed_handler(ngx_http_request_t *r) r->connection->data = r; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http postponed request \"%V\"", &r->uri); + "http wake child request \"%V\"", &r->uri); r->write_event_handler(r); @@ -1833,7 +1845,7 @@ ngx_http_read_discarded_body(ngx_http_request_t *r) if (n == NGX_ERROR) { - r->closed = 1; + r->connection->closed = 1; /* * if a client request body is discarded then we already set diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index 470f58459..d1ba774ef 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -310,6 +310,8 @@ struct ngx_http_request_s { ngx_http_variable_value_t **variables; + size_t limit_rate; + /* used to learn the Apache compatible response length without a header */ size_t header_size; @@ -361,7 +363,6 @@ struct ngx_http_request_s { unsigned keepalive:1; unsigned lingering_close:1; unsigned internal:1; - unsigned closed:1; unsigned done:1; unsigned utf8:1; diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c index 42edfef52..eb2e6a570 100644 --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -236,7 +236,7 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r) } if (n == 0 || n == NGX_ERROR) { - r->closed = 1; + c->closed = 1; return NGX_HTTP_BAD_REQUEST; } diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c index 89256b355..716a6f054 100644 --- a/src/http/ngx_http_script.c +++ b/src/http/ngx_http_script.c @@ -558,6 +558,7 @@ ngx_http_script_regex_start_code(ngx_http_script_engine_t *e) if (code->break_cycle) { r->valid_location = 0; + r->uri_changed = 0; } else { r->uri_changed = 1; @@ -713,6 +714,15 @@ ngx_http_script_return_code(ngx_http_script_engine_t *e) void +ngx_http_script_break_code(ngx_http_script_engine_t *e) +{ + e->request->uri_changed = 0; + + e->ip = ngx_http_script_exit; +} + + +void ngx_http_script_if_code(ngx_http_script_engine_t *e) { ngx_http_script_if_code_t *code; diff --git a/src/http/ngx_http_script.h b/src/http/ngx_http_script.h index eb6f17ea9..21625427f 100644 --- a/src/http/ngx_http_script.h +++ b/src/http/ngx_http_script.h @@ -166,6 +166,7 @@ void ngx_http_script_regex_start_code(ngx_http_script_engine_t *e); void ngx_http_script_regex_end_code(ngx_http_script_engine_t *e); #endif void ngx_http_script_return_code(ngx_http_script_engine_t *e); +void ngx_http_script_break_code(ngx_http_script_engine_t *e); void ngx_http_script_if_code(ngx_http_script_engine_t *e); void ngx_http_script_complex_value_code(ngx_http_script_engine_t *e); void ngx_http_script_value_code(ngx_http_script_engine_t *e); diff --git a/src/http/ngx_http_special_response.c b/src/http/ngx_http_special_response.c index 7a341599a..14d4c8022 100644 --- a/src/http/ngx_http_special_response.c +++ b/src/http/ngx_http_special_response.c @@ -244,6 +244,9 @@ ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error) ngx_http_err_page_t *err_page; ngx_http_core_loc_conf_t *clcf; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http special response: %d, \"%V\"", error, &r->uri); + rc = ngx_http_discard_body(r); if (rc == NGX_HTTP_INTERNAL_SERVER_ERROR) { diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index d7ee08f72..8571b869b 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -39,6 +39,8 @@ static ngx_int_t ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_ignore_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_process_limit_rate(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_copy_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t @@ -143,6 +145,10 @@ ngx_http_upstream_header_t ngx_http_upstream_headers_in[] = { offsetof(ngx_http_upstream_headers_in_t, x_accel_redirect), ngx_http_upstream_ignore_header_line, 0, 0 }, + { ngx_string("X-Accel-Limit-Rate"), + ngx_http_upstream_process_limit_rate, 0, + ngx_http_upstream_ignore_header_line, 0, 0 }, + #if (NGX_HTTP_GZIP) { ngx_string("Content-Encoding"), ngx_http_upstream_process_header_line, @@ -299,12 +305,19 @@ ngx_http_upstream_check_broken_connection(ngx_http_request_t *r, ngx_connection_t *c; ngx_http_upstream_t *u; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, 0, - "http upstream check client, write event:%d", ev->write); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "http upstream check client, write event:%d, \"%V\"", + ev->write, &r->uri); c = r->connection; u = r->upstream; + if (c->closed) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_CLIENT_CLOSED_REQUEST); + return; + } + if (u->peer.connection == NULL) { return; } @@ -318,6 +331,7 @@ ngx_http_upstream_check_broken_connection(ngx_http_request_t *r, } ev->eof = 1; + c->closed = 1; if (ev->kq_errno) { ev->error = 1; @@ -325,9 +339,8 @@ ngx_http_upstream_check_broken_connection(ngx_http_request_t *r, if (!u->cachable && u->peer.connection) { ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno, - "kevent() reported that client closed " - "prematurely connection, " - "so upstream connection is closed too"); + "kevent() reported that client closed prematurely " + "connection, so upstream connection is closed too"); ngx_http_upstream_finalize_request(r, u, NGX_HTTP_CLIENT_CLOSED_REQUEST); return; @@ -374,6 +387,7 @@ ngx_http_upstream_check_broken_connection(ngx_http_request_t *r, } ev->eof = 1; + c->closed = 1; if (n == -1) { if (err == NGX_EAGAIN) { @@ -924,6 +938,8 @@ ngx_http_upstream_process_header(ngx_event_t *rev) } } + r->headers_out.status_line.len = 0; + ngx_http_internal_redirect(r, &r->upstream->headers_in.x_accel_redirect->value, NULL); @@ -1155,9 +1171,33 @@ ngx_http_upstream_process_body(ngx_event_t *ev) if (ev->timedout) { if (ev->write) { - p->downstream_error = 1; - ngx_log_error(NGX_LOG_ERR, c->log, NGX_ETIMEDOUT, - "client timed out"); + if (ev->delayed) { + + ev->timedout = 0; + ev->delayed = 0; + + if (!ev->ready) { + ngx_add_timer(ev, p->send_timeout); + + if (ngx_handle_write_event(ev, p->send_lowat) == NGX_ERROR) + { + ngx_http_upstream_finalize_request(r, u, 0); + return; + } + + return; + } + + if (ngx_event_pipe(p, ev->write) == NGX_ABORT) { + ngx_http_upstream_finalize_request(r, u, 0); + return; + } + + } else { + p->downstream_error = 1; + ngx_log_error(NGX_LOG_ERR, c->log, NGX_ETIMEDOUT, + "client timed out"); + } } else { p->upstream_error = 1; @@ -1166,6 +1206,17 @@ ngx_http_upstream_process_body(ngx_event_t *ev) } } else { + if (ev->write && ev->delayed) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http downstream delayed"); + + if (ngx_handle_write_event(ev, p->send_lowat) == NGX_ERROR) { + return; + } + + return; + } + if (ngx_event_pipe(p, ev->write) == NGX_ABORT) { ngx_http_upstream_finalize_request(r, u, 0); return; @@ -1281,6 +1332,7 @@ ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u, } if (r->connection->write->eof) { + r->connection->closed = 1; ngx_http_upstream_finalize_request(r, u, NGX_HTTP_CLIENT_CLOSED_REQUEST); return; @@ -1432,6 +1484,24 @@ ngx_http_upstream_ignore_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, static ngx_int_t +ngx_http_upstream_process_limit_rate(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + ngx_int_t n; + + r->upstream->headers_in.x_accel_limit_rate = h; + + n = ngx_atoi(h->value.data, h->value.len); + + if (n != NGX_ERROR) { + r->limit_rate = (size_t) n; + } + + return NGX_OK; +} + + +static ngx_int_t ngx_http_upstream_copy_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h index 00a64aca6..428253c42 100644 --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -48,6 +48,7 @@ typedef struct { ngx_msec_t connect_timeout; ngx_msec_t send_timeout; ngx_msec_t read_timeout; + ngx_msec_t timeout; size_t send_lowat; size_t header_buffer_size; @@ -103,6 +104,7 @@ typedef struct { ngx_table_elt_t *etag; ngx_table_elt_t *x_accel_expires; ngx_table_elt_t *x_accel_redirect; + ngx_table_elt_t *x_accel_limit_rate; ngx_table_elt_t *content_type; ngx_table_elt_t *content_length; @@ -120,8 +122,6 @@ typedef struct { struct ngx_http_upstream_s { - ngx_http_request_t *request; - ngx_peer_connection_t peer; ngx_event_pipe_t pipe; @@ -146,6 +146,8 @@ struct ngx_http_upstream_s { ngx_int_t (*rewrite_redirect)(ngx_http_request_t *r, ngx_table_elt_t *h, size_t prefix); + ngx_msec_t timeout; + ngx_uint_t method; ngx_http_log_handler_pt saved_log_handler; diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c index d66f0dc6c..2a5cf7dcd 100644 --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -706,7 +706,9 @@ ngx_http_variables_init_vars(ngx_conf_t *cf) { v[i].handler = av[n].handler; v[i].data = av[n].data; - v[i].flags = av[n].flags | NGX_HTTP_VAR_INDEXED; + + av[n].flags |= NGX_HTTP_VAR_INDEXED; + v[i].flags = av[n].flags; goto next; } diff --git a/src/http/ngx_http_write_filter_module.c b/src/http/ngx_http_write_filter_module.c index b872f3dd1..af95c99cd 100644 --- a/src/http/ngx_http_write_filter_module.c +++ b/src/http/ngx_http_write_filter_module.c @@ -47,6 +47,12 @@ ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in) ngx_connection_t *c; ngx_http_core_loc_conf_t *clcf; + c = r->connection; + + if (c->closed) { + return NGX_ERROR; + } + size = 0; flush = 0; last = 0; @@ -151,8 +157,6 @@ ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in) *ll = NULL; - c = r->connection; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http write filter: l:%d f:%d s:%O", last, flush, size); @@ -197,19 +201,20 @@ ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in) sent = c->sent; - chain = c->send_chain(c, r->out, clcf->limit_rate); + chain = c->send_chain(c, r->out, r->limit_rate); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http write filter %p", chain); - if (clcf->limit_rate) { + if (r->limit_rate) { sent = c->sent - sent; c->write->delayed = 1; ngx_add_timer(r->connection->write, - (ngx_msec_t) (sent * 1000 / clcf->limit_rate)); + (ngx_msec_t) (sent * 1000 / r->limit_rate)); } if (chain == NGX_CHAIN_ERROR) { + c->closed = 1; return NGX_ERROR; } diff --git a/src/imap/ngx_imap.c b/src/imap/ngx_imap.c new file mode 100644 index 000000000..aa28b2d6a --- /dev/null +++ b/src/imap/ngx_imap.c @@ -0,0 +1,189 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_event.h> +#include <ngx_imap.h> + + +static char *ngx_imap_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + + +ngx_uint_t ngx_imap_max_module; + + +static ngx_command_t ngx_imap_commands[] = { + + { ngx_string("imap"), + NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_imap_block, + 0, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_core_module_t ngx_imap_module_ctx = { + ngx_string("imap"), + NULL, + NULL +}; + + +ngx_module_t ngx_imap_module = { + NGX_MODULE_V1, + &ngx_imap_module_ctx, /* module context */ + ngx_imap_commands, /* module directives */ + NGX_CORE_MODULE, /* module type */ + NULL, /* init module */ + NULL /* init process */ +}; + + +static char * +ngx_imap_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *rv; + ngx_uint_t m, mi, s; + ngx_conf_t pcf; + ngx_imap_module_t *module; + ngx_imap_conf_ctx_t *ctx; + ngx_imap_core_srv_conf_t **cscfp; + ngx_imap_core_main_conf_t *cmcf; + + /* the main imap context */ + + ctx = ngx_pcalloc(cf->pool, sizeof(ngx_imap_conf_ctx_t)); + if (ctx == NULL) { + return NGX_CONF_ERROR; + } + + *(ngx_imap_conf_ctx_t **) conf = ctx; + + /* count the number of the http modules and set up their indices */ + + ngx_imap_max_module = 0; + for (m = 0; ngx_modules[m]; m++) { + if (ngx_modules[m]->type != NGX_IMAP_MODULE) { + continue; + } + + ngx_modules[m]->ctx_index = ngx_imap_max_module++; + } + + + /* the imap main_conf context, it is the same in the all imap contexts */ + + ctx->main_conf = ngx_pcalloc(cf->pool, + sizeof(void *) * ngx_imap_max_module); + if (ctx->main_conf == NULL) { + return NGX_CONF_ERROR; + } + + + /* + * the imap null srv_conf context, it is used to merge + * the server{}s' srv_conf's + */ + + ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_imap_max_module); + if (ctx->srv_conf == NULL) { + return NGX_CONF_ERROR; + } + + + /* + * create the main_conf's, the null srv_conf's, and the null loc_conf's + * of the all imap modules + */ + + for (m = 0; ngx_modules[m]; m++) { + if (ngx_modules[m]->type != NGX_IMAP_MODULE) { + continue; + } + + module = ngx_modules[m]->ctx; + mi = ngx_modules[m]->ctx_index; + + if (module->create_main_conf) { + ctx->main_conf[mi] = module->create_main_conf(cf); + if (ctx->main_conf[mi] == NULL) { + return NGX_CONF_ERROR; + } + } + + if (module->create_srv_conf) { + ctx->srv_conf[mi] = module->create_srv_conf(cf); + if (ctx->srv_conf[mi] == NULL) { + return NGX_CONF_ERROR; + } + } + } + + + /* parse inside the imap{} block */ + + pcf = *cf; + cf->ctx = ctx; + + cf->module_type = NGX_IMAP_MODULE; + cf->cmd_type = NGX_IMAP_MAIN_CONF; + rv = ngx_conf_parse(cf, NULL); + + if (rv != NGX_CONF_OK) { + *cf = pcf; + return rv; + } + + + /* init imap{} main_conf's, merge the server{}s' srv_conf's */ + + cmcf = ctx->main_conf[ngx_imap_core_module.ctx_index]; + cscfp = cmcf->servers.elts; + + for (m = 0; ngx_modules[m]; m++) { + if (ngx_modules[m]->type != NGX_IMAP_MODULE) { + continue; + } + + module = ngx_modules[m]->ctx; + mi = ngx_modules[m]->ctx_index; + + /* init imap{} main_conf's */ + + if (module->init_main_conf) { + rv = module->init_main_conf(cf, ctx->main_conf[mi]); + if (rv != NGX_CONF_OK) { + *cf = pcf; + return rv; + } + } + + for (s = 0; s < cmcf->servers.nelts; s++) { + + /* merge the server{}s' srv_conf's */ + + if (module->merge_srv_conf) { + rv = module->merge_srv_conf(cf, + ctx->srv_conf[mi], + cscfp[s]->ctx->srv_conf[mi]); + if (rv != NGX_CONF_OK) { + *cf = pcf; + return rv; + } + } + } + } + + /* imap{}'s cf->ctx was needed while the configuration merging */ + + *cf = pcf; + + return NGX_CONF_OK; +} diff --git a/src/imap/ngx_imap.h b/src/imap/ngx_imap.h new file mode 100644 index 000000000..8a506db64 --- /dev/null +++ b/src/imap/ngx_imap.h @@ -0,0 +1,189 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_IMAP_H_INCLUDED_ +#define _NGX_IMAP_H_INCLUDED_ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_event.h> +#include <ngx_event_connect.h> + + +typedef struct { + void **main_conf; + void **srv_conf; +} ngx_imap_conf_ctx_t; + + +typedef struct { + ngx_array_t servers; /* ngx_imap_core_srv_conf_t */ +} ngx_imap_core_main_conf_t; + + +#define NGX_IMAP_POP3_PROTOCOL 0 +#define NGX_IMAP_IMAP_PROTOCOL 1 + +typedef struct { + ngx_msec_t timeout; + + size_t imap_client_buffer_size; + size_t proxy_buffer_size; + + ngx_uint_t protocol; + + ngx_buf_t *pop3_capability; + ngx_buf_t *imap_capability; + + ngx_array_t pop3_capabilities; + ngx_array_t imap_capabilities; + + /* server ctx */ + ngx_imap_conf_ctx_t *ctx; +} ngx_imap_core_srv_conf_t; + + +typedef struct { + void *(*create_main_conf)(ngx_conf_t *cf); + char *(*init_main_conf)(ngx_conf_t *cf, void *conf); + + void *(*create_srv_conf)(ngx_conf_t *cf); + char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf); +} ngx_imap_module_t; + + +typedef enum { + ngx_imap_start = 0, + ngx_imap_login, + ngx_imap_user, + ngx_imap_passwd, +} ngx_imap_state_e; + + +typedef enum { + ngx_pop3_start = 0, + ngx_pop3_user, + ngx_pop3_passwd +} ngx_po3_state_e; + + +typedef struct { + ngx_peer_connection_t upstream; + ngx_buf_t *buffer; +} ngx_imap_proxy_ctx_t; + + +typedef struct { + uint32_t signature; /* "IMAP" */ + + ngx_connection_t *connection; + + ngx_buf_t *buffer; + ngx_str_t out; + + void **ctx; + void **main_conf; + void **srv_conf; + + ngx_imap_proxy_ctx_t *proxy; + + ngx_uint_t imap_state; + + unsigned protocol:1; + unsigned quoted:1; + + ngx_str_t login; + ngx_str_t passwd; + + ngx_str_t tag; + + ngx_uint_t command; + ngx_array_t args; + + ngx_uint_t login_attempt; + + /* used to parse IMAP/POP3 command */ + + ngx_uint_t state; + u_char *cmd_start; + u_char *arg_start; + u_char *arg_end; + ngx_uint_t literal_len; +} ngx_imap_session_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_APOP 6 +#define NGX_POP3_STAT 7 +#define NGX_POP3_LIST 8 +#define NGX_POP3_RETR 9 +#define NGX_POP3_DELE 10 +#define NGX_POP3_RSET 11 +#define NGX_POP3_TOP 12 +#define NGX_POP3_UIDL 13 + + +#define NGX_IMAP_LOGIN 1 +#define NGX_IMAP_LOGOUT 2 +#define NGX_IMAP_CAPABILITY 3 +#define NGX_IMAP_NOOP 4 + +#define NGX_IMAP_NEXT 5 + + +#define NGX_IMAP_PARSE_INVALID_COMMAND 20 + + +#define NGX_IMAP_PROXY_INVALID 10 +#define NGX_IMAP_PROXY_ERROR 11 + + +#define NGX_IMAP_MODULE 0x50414D49 /* "IMAP" */ + +#define NGX_IMAP_MAIN_CONF 0x02000000 +#define NGX_IMAP_SRV_CONF 0x04000000 + + +#define NGX_IMAP_MAIN_CONF_OFFSET offsetof(ngx_imap_conf_ctx_t, main_conf) +#define NGX_IMAP_SRV_CONF_OFFSET offsetof(ngx_imap_conf_ctx_t, srv_conf) + + +#define ngx_imap_get_module_ctx(s, module) (s)->ctx[module.ctx_index] +#define ngx_imap_set_ctx(s, c, module) s->ctx[module.ctx_index] = c; +#define ngx_imap_delete_ctx(s, module) s->ctx[module.ctx_index] = NULL; + + +#define ngx_imap_get_module_main_conf(s, module) \ + (s)->main_conf[module.ctx_index] +#define ngx_imap_get_module_srv_conf(s, module) (s)->srv_conf[module.ctx_index] + + +void ngx_imap_init_connection(ngx_connection_t *c); +void ngx_imap_auth_state(ngx_event_t *rev); +void ngx_pop3_auth_state(ngx_event_t *rev); +void ngx_imap_close_connection(ngx_connection_t *c); +void ngx_imap_session_internal_server_error(ngx_imap_session_t *s); + +ngx_int_t ngx_imap_parse_command(ngx_imap_session_t *s); +ngx_int_t ngx_pop3_parse_command(ngx_imap_session_t *s); + + +/* STUB */ +void ngx_imap_proxy_init(ngx_imap_session_t *s, ngx_peers_t *peers); +void ngx_imap_auth_http_init(ngx_imap_session_t *s); +/**/ + + +extern ngx_uint_t ngx_imap_max_module; +extern ngx_module_t ngx_imap_core_module; + + +#endif /* _NGX_IMAP_H_INCLUDED_ */ diff --git a/src/imap/ngx_imap_auth_http_module.c b/src/imap/ngx_imap_auth_http_module.c new file mode 100644 index 000000000..e66593a4a --- /dev/null +++ b/src/imap/ngx_imap_auth_http_module.c @@ -0,0 +1,1067 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_event.h> +#include <ngx_event_connect.h> +#include <ngx_imap.h> + + +typedef struct { + ngx_peers_t *peers; + + ngx_msec_t timeout; + + ngx_str_t host_header; + ngx_str_t uri; +} ngx_imap_auth_http_conf_t; + + +typedef struct ngx_imap_auth_http_ctx_s ngx_imap_auth_http_ctx_t; + +typedef void (*ngx_imap_auth_http_handler_pt)(ngx_imap_session_t *s, + ngx_imap_auth_http_ctx_t *ctx); + +struct ngx_imap_auth_http_ctx_s { + ngx_buf_t *request; + ngx_buf_t *response; + ngx_peer_connection_t peer; + + ngx_imap_auth_http_handler_pt handler; + + ngx_uint_t state; + ngx_uint_t hash; /* no needed ? */ + + u_char *header_name_start; + u_char *header_name_end; + u_char *header_start; + u_char *header_end; + + ngx_str_t addr; + ngx_str_t port; + ngx_str_t err; + + ngx_msec_t sleep; + + ngx_peers_t *peers; +}; + + +static void ngx_imap_auth_http_write_handler(ngx_event_t *wev); +static void ngx_imap_auth_http_read_handler(ngx_event_t *rev); +static void ngx_imap_auth_http_ignore_status_line(ngx_imap_session_t *s, + ngx_imap_auth_http_ctx_t *ctx); +static void ngx_imap_auth_http_process_headers(ngx_imap_session_t *s, + ngx_imap_auth_http_ctx_t *ctx); +static void ngx_imap_auth_sleep_handler(ngx_event_t *rev); +static ngx_int_t ngx_imap_auth_http_parse_header_line(ngx_imap_session_t *s, + ngx_imap_auth_http_ctx_t *ctx); +static void ngx_imap_auth_http_block_read(ngx_event_t *rev); +static void ngx_imap_auth_http_dummy_handler(ngx_event_t *ev); +static ngx_buf_t *ngx_imap_auth_http_create_request(ngx_imap_session_t *s, + ngx_imap_auth_http_conf_t *ahcf); + +static void *ngx_imap_auth_http_create_conf(ngx_conf_t *cf); +static char *ngx_imap_auth_http_merge_conf(ngx_conf_t *cf, void *parent, + void *child); +static char *ngx_imap_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + + +static ngx_command_t ngx_imap_auth_http_commands[] = { + + { ngx_string("auth_http"), + NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1, + ngx_imap_auth_http, + NGX_IMAP_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("auth_http_timeout"), + NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_IMAP_SRV_CONF_OFFSET, + offsetof(ngx_imap_auth_http_conf_t, timeout), + NULL }, + + ngx_null_command +}; + + +static ngx_imap_module_t ngx_imap_auth_http_module_ctx = { + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_imap_auth_http_create_conf, /* create server configuration */ + ngx_imap_auth_http_merge_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_imap_auth_http_module = { + NGX_MODULE_V1, + &ngx_imap_auth_http_module_ctx, /* module context */ + ngx_imap_auth_http_commands, /* module directives */ + NGX_IMAP_MODULE, /* module type */ + NULL, /* init module */ + NULL /* init process */ +}; + + +static char *ngx_imap_auth_http_protocol[] = { "pop3", "imap" }; + + +void +ngx_imap_auth_http_init(ngx_imap_session_t *s) +{ + ngx_int_t rc; + ngx_imap_auth_http_ctx_t *ctx; + ngx_imap_auth_http_conf_t *ahcf; + + ctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_imap_auth_http_ctx_t)); + if (ctx == NULL) { + ngx_imap_session_internal_server_error(s); + return; + } + + ahcf = ngx_imap_get_module_srv_conf(s, ngx_imap_auth_http_module); + + ctx->request = ngx_imap_auth_http_create_request(s, ahcf); + if (ctx->request == NULL) { + ngx_imap_session_internal_server_error(s); + return; + } + + ngx_imap_set_ctx(s, ctx, ngx_imap_auth_http_module); + + ctx->peer.peers = ahcf->peers; + ctx->peer.log = s->connection->log; + ctx->peer.log_error = NGX_ERROR_ERR; + + rc = ngx_event_connect_peer(&ctx->peer); + + if (rc == NGX_ERROR) { + ngx_imap_session_internal_server_error(s); + return; + } + + ctx->peer.connection->data = s; + ctx->peer.connection->pool = s->connection->pool; + + s->connection->read->handler = ngx_imap_auth_http_block_read; + ctx->peer.connection->read->handler = ngx_imap_auth_http_read_handler; + ctx->peer.connection->write->handler = ngx_imap_auth_http_write_handler; + + ctx->handler = ngx_imap_auth_http_ignore_status_line; + + if (rc == NGX_OK) { + ngx_imap_auth_http_write_handler(ctx->peer.connection->write); + return; + } + + ngx_add_timer(ctx->peer.connection->read, ahcf->timeout); + ngx_add_timer(ctx->peer.connection->write, ahcf->timeout); +} + + +static void +ngx_imap_auth_http_write_handler(ngx_event_t *wev) +{ + ssize_t n, size; + ngx_connection_t *c; + ngx_imap_session_t *s; + ngx_imap_auth_http_ctx_t *ctx; + ngx_imap_auth_http_conf_t *ahcf; + + c = wev->data; + s = c->data; + + ctx = ngx_imap_get_module_ctx(s, ngx_imap_auth_http_module); + + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, wev->log, 0, + "imap auth http write handler"); + + if (wev->timedout) { + ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT, + "auth http server timed out"); + ngx_close_connection(ctx->peer.connection); + ngx_imap_session_internal_server_error(s); + return; + } + + size = ctx->request->last - ctx->request->pos; + + n = ngx_send(c, ctx->request->pos, size); + + if (n == NGX_ERROR) { + ngx_close_connection(ctx->peer.connection); + ngx_imap_session_internal_server_error(s); + return; + } + + if (n > 0) { + ctx->request->pos += n; + + if (n == size) { + wev->handler = ngx_imap_auth_http_dummy_handler; + + if (wev->timer_set) { + ngx_del_timer(wev); + } + + return; + } + } + + if (!wev->timer_set) { + ahcf = ngx_imap_get_module_srv_conf(s, ngx_imap_auth_http_module); + ngx_add_timer(wev, ahcf->timeout); + } +} + + +static void +ngx_imap_auth_http_read_handler(ngx_event_t *rev) +{ + ssize_t n, size; + ngx_connection_t *c; + ngx_imap_session_t *s; + ngx_imap_auth_http_ctx_t *ctx; + + c = rev->data; + s = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, + "imap auth http read handler"); + + ctx = ngx_imap_get_module_ctx(s, ngx_imap_auth_http_module); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT, + "auth http server timed out"); + ngx_close_connection(ctx->peer.connection); + ngx_imap_session_internal_server_error(s); + return; + } + + if (ctx->response == NULL) { + ctx->response = ngx_create_temp_buf(s->connection->pool, 1024); + if (ctx->response == NULL) { + ngx_close_connection(ctx->peer.connection); + ngx_imap_session_internal_server_error(s); + return; + } + } + + size = ctx->response->end - ctx->response->last; + + n = ngx_recv(c, ctx->response->pos, size); + + if (n > 0) { + ctx->response->last += n; + + ctx->handler(s, ctx); + return; + } + + if (n == NGX_AGAIN) { + return; + } + + ngx_close_connection(ctx->peer.connection); + ngx_imap_session_internal_server_error(s); +} + + +static void +ngx_imap_auth_http_ignore_status_line(ngx_imap_session_t *s, + ngx_imap_auth_http_ctx_t *ctx) +{ + u_char *p, ch; + enum { + sw_start = 0, + sw_H, + sw_HT, + sw_HTT, + sw_HTTP, + sw_skip, + sw_almost_done + } state; + + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, s->connection->log, 0, + "imap auth http process status line"); + + state = ctx->state; + + for (p = ctx->response->pos; p < ctx->response->last; p++) { + ch = *p; + + switch (state) { + + /* "HTTP/" */ + case sw_start: + if (ch == 'H') { + state = sw_H; + break; + } + goto next; + + case sw_H: + if (ch == 'T') { + state = sw_HT; + break; + } + goto next; + + case sw_HT: + if (ch == 'T') { + state = sw_HTT; + break; + } + goto next; + + case sw_HTT: + if (ch == 'P') { + state = sw_HTTP; + break; + } + goto next; + + case sw_HTTP: + if (ch == '/') { + state = sw_skip; + break; + } + goto next; + + /* any text until end of line */ + case sw_skip: + switch (ch) { + case CR: + state = sw_almost_done; + + break; + case LF: + goto done; + } + break; + + /* end of status line */ + case sw_almost_done: + if (ch == LF) { + goto done; + } + + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "auth http server sent invalid response"); + ngx_close_connection(ctx->peer.connection); + ngx_imap_session_internal_server_error(s); + return; + } + } + + ctx->response->pos = p; + ctx->state = state; + + return; + +next: + + p = ctx->response->start - 1; + +done: + + ctx->response->pos = p + 1; + ctx->state = 0; + ctx->handler = ngx_imap_auth_http_process_headers; + ctx->handler(s, ctx); +} + + +static void +ngx_imap_auth_http_process_headers(ngx_imap_session_t *s, + ngx_imap_auth_http_ctx_t *ctx) +{ + u_char *p; + size_t len, size; + ngx_int_t rc, port, n; + struct sockaddr_in *sin; + + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, s->connection->log, 0, + "imap auth http process headers"); + + for ( ;; ) { + rc = ngx_imap_auth_http_parse_header_line(s, ctx); + + if (rc == NGX_OK) { + +#if (NGX_DEBUG) + { + ngx_str_t key, value; + + key.len = ctx->header_name_end - ctx->header_name_start; + key.data = ctx->header_name_start; + value.len = ctx->header_end - ctx->header_start; + value.data = ctx->header_start; + + ngx_log_debug2(NGX_LOG_DEBUG_IMAP, s->connection->log, 0, + "auth http header: \"%V: %V\"", + &key, &value); + } +#endif + + len = ctx->header_name_end - ctx->header_name_start; + + if (len == sizeof("Auth-Status") - 1 + && ngx_strncasecmp(ctx->header_name_start, "Auth-Status", + sizeof("Auth-Status") - 1) == 0) + { + len = ctx->header_end - ctx->header_start; + + if (len == 2 + && ctx->header_start[0] == 'O' + && ctx->header_start[1] == 'K') + { + continue; + } + + if (s->protocol == NGX_IMAP_POP3_PROTOCOL) { + size = sizeof("-ERR") - 1 + len + sizeof(CRLF) - 1; + + } else { + size = s->tag.len + sizeof("NO") - 1 + len + + sizeof(CRLF) - 1; + } + + p = ngx_pcalloc(s->connection->pool, size); + if (p == NULL) { + ngx_imap_session_internal_server_error(s); + return; + } + + ctx->err.data = p; + + if (s->protocol == NGX_IMAP_POP3_PROTOCOL) { + *p++ = '-'; *p++ = 'E'; *p++ = 'R'; *p++ = 'R'; + + } else { + p = ngx_cpymem(p, s->tag.data, s->tag.len); + *p++ = 'N'; *p++ = 'O'; + } + + *p++ = ' '; + p = ngx_cpymem(p, ctx->header_start, len); + *p++ = CR; *p++ = LF; + + ctx->err.len = p - ctx->err.data; + + continue; + } + + if (len == sizeof("Auth-Server") - 1 + && ngx_strncasecmp(ctx->header_name_start, "Auth-Server", + sizeof("Auth-Server") - 1) == 0) + { + ctx->addr.len = ctx->header_end - ctx->header_start; + ctx->addr.data = ctx->header_start; + + continue; + } + + if (len == sizeof("Auth-Port") - 1 + && ngx_strncasecmp(ctx->header_name_start, "Auth-Port", + sizeof("Auth-Port") - 1) == 0) + { + ctx->port.len = ctx->header_end - ctx->header_start; + ctx->port.data = ctx->header_start; + + continue; + } + + if (len == sizeof("Auth-User") - 1 + && ngx_strncasecmp(ctx->header_name_start, "Auth-User", + sizeof("Auth-User") - 1) == 0) + { + s->login.len = ctx->header_end - ctx->header_start; + s->login.data = ctx->header_start; + + continue; + } + + if (len == sizeof("Auth-Wait") - 1 + && ngx_strncasecmp(ctx->header_name_start, "Auth-Wait", + sizeof("Auth-Wait") - 1) == 0) + { + n = ngx_atoi(ctx->header_start, + ctx->header_end - ctx->header_start); + + if (n != NGX_ERROR) { + ctx->sleep = n; + } + + continue; + } + + /* ignore other headers */ + + continue; + } + + if (rc == NGX_DONE) { + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, s->connection->log, 0, + "auth http header done"); + + ngx_close_connection(ctx->peer.connection); + + if (ctx->err.len) { + (void) ngx_send(s->connection, ctx->err.data, ctx->err.len); + + if (ctx->sleep == 0) { + ngx_imap_close_connection(s->connection); + return; + } + + ngx_add_timer(s->connection->read, ctx->sleep * 1000); + + s->connection->read->handler = ngx_imap_auth_sleep_handler; + + return; + } + + if (ctx->addr.len == 0 || ctx->port.len == 0) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "auth http server did not send server or port"); + ngx_imap_session_internal_server_error(s); + return; + } + + ctx->peers = ngx_pcalloc(s->connection->pool, sizeof(ngx_peers_t)); + if (ctx->peers == NULL) { + ngx_imap_session_internal_server_error(s); + return; + } + + sin = ngx_pcalloc(s->connection->pool, sizeof(struct sockaddr_in)); + if (sin == NULL) { + ngx_imap_session_internal_server_error(s); + return; + } + + sin->sin_family = AF_INET; + + port = ngx_atoi(ctx->port.data, ctx->port.len); + if (port == NGX_ERROR || port < 1 || port > 65536) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "auth http server sent invalid server " + "port:\"%V\"", &ctx->port); + ngx_imap_session_internal_server_error(s); + return; + } + + sin->sin_port = htons((in_port_t) port); + + ctx->addr.data[ctx->addr.len] = '\0'; + sin->sin_addr.s_addr = inet_addr((char *) ctx->addr.data); + if (sin->sin_addr.s_addr == INADDR_NONE) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "auth http server sent invalid server " + "address:\"%V\"", &ctx->addr); + ngx_imap_session_internal_server_error(s); + return; + } + + ctx->peers->number = 1; + + ctx->peers->peer[0].sockaddr = (struct sockaddr *) sin; + ctx->peers->peer[0].socklen = sizeof(struct sockaddr_in); + + len = ctx->addr.len + 1 + ctx->port.len; + + ctx->peers->peer[0].name.len = len; + + ctx->peers->peer[0].name.data = ngx_palloc(s->connection->pool, + len); + if (ctx->peers->peer[0].name.data == NULL) { + ngx_imap_session_internal_server_error(s); + return; + } + + len = ctx->addr.len; + + ngx_memcpy(ctx->peers->peer[0].name.data, ctx->addr.data, len); + + ctx->peers->peer[0].name.data[len++] = ':'; + + ngx_memcpy(ctx->peers->peer[0].name.data + len, + ctx->port.data, ctx->port.len); + + ctx->peers->peer[0].uri_separator = ""; + + ngx_imap_proxy_init(s, ctx->peers); + + return; + } + + if (rc == NGX_AGAIN ) { + return; + } + + /* rc == NGX_ERROR */ + + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "auth http server sent invalid header in response"); + ngx_close_connection(ctx->peer.connection); + ngx_imap_session_internal_server_error(s); + + return; + } +} + + +static void +ngx_imap_auth_sleep_handler(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_imap_session_t *s; + + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap auth sleep handler"); + + c = rev->data; + s = c->data; + + if (rev->timedout) { + + rev->timedout = 0; + + if (s->protocol == NGX_IMAP_POP3_PROTOCOL) { + s->imap_state = ngx_pop3_start; + s->connection->read->handler = ngx_pop3_auth_state; + + } else { + s->imap_state = ngx_imap_start; + s->connection->read->handler = ngx_imap_auth_state; + } + + if (rev->ready) { + s->connection->read->handler(rev); + return; + } + + if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + ngx_imap_close_connection(s->connection); + } + + return; + } + + if (rev->active) { + if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + ngx_imap_close_connection(s->connection); + } + } +} + + +static ngx_int_t +ngx_imap_auth_http_parse_header_line(ngx_imap_session_t *s, + ngx_imap_auth_http_ctx_t *ctx) +{ + u_char c, ch, *p; + ngx_uint_t hash; + enum { + sw_start = 0, + sw_name, + sw_space_before_value, + sw_value, + sw_space_after_value, + sw_almost_done, + sw_header_almost_done + } state; + + state = ctx->state; + hash = ctx->hash; + + for (p = ctx->response->pos; p < ctx->response->last; p++) { + ch = *p; + + switch (state) { + + /* first char */ + case sw_start: + + switch (ch) { + case CR: + ctx->header_end = p; + state = sw_header_almost_done; + break; + case LF: + ctx->header_end = p; + goto header_done; + default: + state = sw_name; + ctx->header_name_start = p; + + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'z') { + hash = c; + break; + } + + if (ch >= '0' && ch <= '9') { + hash = ch; + break; + } + + return NGX_ERROR; + } + break; + + /* header name */ + case sw_name: + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'z') { + hash += c; + break; + } + + if (ch == ':') { + ctx->header_name_end = p; + state = sw_space_before_value; + break; + } + + if (ch == '-') { + hash += ch; + break; + } + + if (ch >= '0' && ch <= '9') { + hash += ch; + break; + } + + if (ch == CR) { + ctx->header_name_end = p; + ctx->header_start = p; + ctx->header_end = p; + state = sw_almost_done; + break; + } + + if (ch == LF) { + ctx->header_name_end = p; + ctx->header_start = p; + ctx->header_end = p; + goto done; + } + + return NGX_ERROR; + + /* space* before header value */ + case sw_space_before_value: + switch (ch) { + case ' ': + break; + case CR: + ctx->header_start = p; + ctx->header_end = p; + state = sw_almost_done; + break; + case LF: + ctx->header_start = p; + ctx->header_end = p; + goto done; + default: + ctx->header_start = p; + state = sw_value; + break; + } + break; + + /* header value */ + case sw_value: + switch (ch) { + case ' ': + ctx->header_end = p; + state = sw_space_after_value; + break; + case CR: + ctx->header_end = p; + state = sw_almost_done; + break; + case LF: + ctx->header_end = p; + goto done; + } + break; + + /* space* before end of header line */ + case sw_space_after_value: + switch (ch) { + case ' ': + break; + case CR: + state = sw_almost_done; + break; + case LF: + goto done; + default: + state = sw_value; + break; + } + break; + + /* end of header line */ + case sw_almost_done: + switch (ch) { + case LF: + goto done; + default: + return NGX_ERROR; + } + + /* end of header */ + case sw_header_almost_done: + switch (ch) { + case LF: + goto header_done; + default: + return NGX_ERROR; + } + } + } + + ctx->response->pos = p; + ctx->state = state; + ctx->hash = hash; + + return NGX_AGAIN; + +done: + + ctx->response->pos = p + 1; + ctx->state = sw_start; + ctx->hash = hash; + + return NGX_OK; + +header_done: + + ctx->response->pos = p + 1; + ctx->state = sw_start; + + return NGX_DONE; +} + + +static void +ngx_imap_auth_http_block_read(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_imap_session_t *s; + ngx_imap_auth_http_ctx_t *ctx; + + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, + "imap auth http block read"); + + if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + c = rev->data; + s = c->data; + + ctx = ngx_imap_get_module_ctx(s, ngx_imap_auth_http_module); + + ngx_close_connection(ctx->peer.connection); + ngx_imap_session_internal_server_error(s); + } +} + + +static void +ngx_imap_auth_http_dummy_handler(ngx_event_t *ev) +{ + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, ev->log, 0, + "imap auth http dummy handler"); +} + + +static ngx_buf_t * +ngx_imap_auth_http_create_request(ngx_imap_session_t *s, + ngx_imap_auth_http_conf_t *ahcf) +{ + size_t len; + ngx_buf_t *b; + + len = sizeof("GET ") - 1 + ahcf->uri.len + sizeof(" HTTP/1.0" CRLF) - 1 + + sizeof("Host: ") - 1 + ahcf->host_header.len + sizeof(CRLF) - 1 + + sizeof("Auth-Method: plain" CRLF) - 1 + + sizeof("Auth-User: ") - 1 + s->login.len + sizeof(CRLF) - 1 + + sizeof("Auth-Pass: ") - 1 + s->passwd.len + sizeof(CRLF) - 1 + + sizeof("Auth-Protocol: imap" CRLF) - 1 + + sizeof("Auth-Login-Attempt: ") - 1 + NGX_INT_T_LEN + + sizeof(CRLF) - 1 + + sizeof("Client-IP: ") - 1 + s->connection->addr_text.len + + sizeof(CRLF) - 1 + + sizeof(CRLF) - 1; + + b = ngx_create_temp_buf(s->connection->pool, len); + if (b == NULL) { + return NULL; + } + + b->last = ngx_cpymem(b->last, "GET ", sizeof("GET ") - 1); + b->last = ngx_cpymem(b->last, ahcf->uri.data, ahcf->uri.len); + b->last = ngx_cpymem(b->last, " HTTP/1.0" CRLF, + sizeof(" HTTP/1.0" CRLF) - 1); + + b->last = ngx_cpymem(b->last, "Host: ", sizeof("Host: ") - 1); + b->last = ngx_cpymem(b->last, ahcf->host_header.data, + ahcf->host_header.len); + *b->last++ = CR; *b->last++ = LF; + + b->last = ngx_cpymem(b->last, "Auth-Method: plain" CRLF, + sizeof("Auth-Method: plain" CRLF) - 1); + + b->last = ngx_cpymem(b->last, "Auth-User: ", sizeof("Auth-User: ") - 1); + b->last = ngx_cpymem(b->last, s->login.data, s->login.len); + *b->last++ = CR; *b->last++ = LF; + + b->last = ngx_cpymem(b->last, "Auth-Pass: ", sizeof("Auth-Pass: ") - 1); + b->last = ngx_cpymem(b->last, s->passwd.data, s->passwd.len); + *b->last++ = CR; *b->last++ = LF; + + b->last = ngx_cpymem(b->last, "Auth-Protocol: ", + sizeof("Auth-Protocol: ") - 1); + b->last = ngx_cpymem(b->last, ngx_imap_auth_http_protocol[s->protocol], + sizeof("imap") - 1); + *b->last++ = CR; *b->last++ = LF; + + b->last = ngx_sprintf(b->last, "Auth-Login-Attempt: %ui" CRLF, + s->login_attempt); + + b->last = ngx_cpymem(b->last, "Client-IP: ", sizeof("Client-IP: ") - 1); + b->last = ngx_cpymem(b->last, s->connection->addr_text.data, + s->connection->addr_text.len); + *b->last++ = CR; *b->last++ = LF; + + /* add "\r\n" at the header end */ + *b->last++ = CR; *b->last++ = LF; + +#if (NGX_DEBUG) + { + ngx_str_t l; + + l.len = b->last - b->pos; + l.data = b->pos; + ngx_log_debug1(NGX_LOG_DEBUG_IMAP, s->connection->log, 0, + "imap auth http header:\n\"%V\"", &l); + } +#endif + + return b; +} + + +static void * +ngx_imap_auth_http_create_conf(ngx_conf_t *cf) +{ + ngx_imap_auth_http_conf_t *ahcf; + + ahcf = ngx_pcalloc(cf->pool, sizeof(ngx_imap_auth_http_conf_t)); + if (ahcf == NULL) { + return NGX_CONF_ERROR; + } + + ahcf->timeout = NGX_CONF_UNSET_MSEC; + + return ahcf; +} + + +static char * +ngx_imap_auth_http_merge_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_imap_auth_http_conf_t *prev = parent; + ngx_imap_auth_http_conf_t *conf = child; + + if (conf->peers == NULL) { + conf->peers = prev->peers; + conf->host_header = prev->host_header; + conf->uri = prev->uri; + } + + ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000); + + return NGX_CONF_OK; +} + + +static char * +ngx_imap_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_imap_auth_http_conf_t *ahcf = conf; + + ngx_uint_t i; + ngx_str_t *value, *url; + ngx_inet_upstream_t inet_upstream; +#if (NGX_HAVE_UNIX_DOMAIN) + ngx_unix_domain_upstream_t unix_upstream; +#endif + + value = cf->args->elts; + + url = &value[1]; + + if (ngx_strncasecmp(url->data, "unix:", 5) == 0) { + +#if (NGX_HAVE_UNIX_DOMAIN) + + ngx_memzero(&unix_upstream, sizeof(ngx_unix_domain_upstream_t)); + + unix_upstream.name = *url; + unix_upstream.url = *url; + unix_upstream.uri_part = 1; + + ahcf->peers = ngx_unix_upstream_parse(cf, &unix_upstream); + if (ahcf->peers == NULL) { + return NGX_CONF_ERROR; + } + + ahcf->peers->peer[0].uri_separator = ":"; + + ahcf->host_header.len = sizeof("localhost") - 1; + ahcf->host_header.data = (u_char *) "localhost"; + ahcf->uri = unix_upstream.uri; + +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the unix domain sockets are not supported " + "on this platform"); + return NGX_CONF_ERROR; + +#endif + + } else { + ngx_memzero(&inet_upstream, sizeof(ngx_inet_upstream_t)); + + inet_upstream.name = *url; + inet_upstream.url = *url; + inet_upstream.default_port_value = 80; + inet_upstream.uri_part = 1; + + ahcf->peers = ngx_inet_upstream_parse(cf, &inet_upstream); + if (ahcf->peers == NULL) { + return NGX_CONF_ERROR; + } + + for (i = 0; i < ahcf->peers->number; i++) { + ahcf->peers->peer[i].uri_separator = ""; + } + + ahcf->host_header = inet_upstream.host_header; + ahcf->uri = inet_upstream.uri; + } + + return NGX_CONF_OK; +} diff --git a/src/imap/ngx_imap_core_module.c b/src/imap/ngx_imap_core_module.c new file mode 100644 index 000000000..e5d7a2050 --- /dev/null +++ b/src/imap/ngx_imap_core_module.c @@ -0,0 +1,455 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_event.h> +#include <ngx_imap.h> + + +static void *ngx_imap_core_create_main_conf(ngx_conf_t *cf); +static void *ngx_imap_core_create_srv_conf(ngx_conf_t *cf); +static char *ngx_imap_core_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); +static char *ngx_imap_core_server(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_imap_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_imap_core_capability(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + + +static ngx_conf_enum_t ngx_imap_core_procotol[] = { + { ngx_string("pop3"), NGX_IMAP_POP3_PROTOCOL }, + { ngx_string("imap"), NGX_IMAP_IMAP_PROTOCOL }, + { ngx_null_string, 0 } +}; + + +static ngx_str_t ngx_pop3_default_capabilities[] = { + ngx_string("TOP"), + ngx_string("USER"), + ngx_string("UIDL"), + ngx_null_string +}; + + +static ngx_str_t ngx_imap_default_capabilities[] = { + ngx_string("IMAP4"), + ngx_string("IMAP4rev1"), + ngx_string("UIDPLUS"), + ngx_null_string +}; + + +static ngx_command_t ngx_imap_core_commands[] = { + + { ngx_string("server"), + NGX_IMAP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_imap_core_server, + 0, + 0, + NULL }, + + { ngx_string("listen"), + NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1, + ngx_imap_core_listen, + 0, + 0, + NULL }, + + { ngx_string("protocol"), + NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_enum_slot, + NGX_IMAP_SRV_CONF_OFFSET, + offsetof(ngx_imap_core_srv_conf_t, protocol), + &ngx_imap_core_procotol }, + + { ngx_string("imap_client_buffer"), + NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_IMAP_SRV_CONF_OFFSET, + offsetof(ngx_imap_core_srv_conf_t, imap_client_buffer_size), + NULL }, + + { ngx_string("proxy_buffer"), + NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_IMAP_SRV_CONF_OFFSET, + offsetof(ngx_imap_core_srv_conf_t, proxy_buffer_size), + NULL }, + + { ngx_string("timeout"), + NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_IMAP_SRV_CONF_OFFSET, + offsetof(ngx_imap_core_srv_conf_t, timeout), + NULL }, + + { ngx_string("pop3_capabilities"), + NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_1MORE, + ngx_imap_core_capability, + NGX_IMAP_SRV_CONF_OFFSET, + offsetof(ngx_imap_core_srv_conf_t, pop3_capabilities), + NULL }, + + { ngx_string("imap_capabilities"), + NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_1MORE, + ngx_imap_core_capability, + NGX_IMAP_SRV_CONF_OFFSET, + offsetof(ngx_imap_core_srv_conf_t, imap_capabilities), + NULL }, + + ngx_null_command +}; + + +static ngx_imap_module_t ngx_imap_core_module_ctx = { + ngx_imap_core_create_main_conf, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_imap_core_create_srv_conf, /* create server configuration */ + ngx_imap_core_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_imap_core_module = { + NGX_MODULE_V1, + &ngx_imap_core_module_ctx, /* module context */ + ngx_imap_core_commands, /* module directives */ + NGX_IMAP_MODULE, /* module type */ + NULL, /* init module */ + NULL /* init process */ +}; + + +static void * +ngx_imap_core_create_main_conf(ngx_conf_t *cf) +{ + ngx_imap_core_main_conf_t *cmcf; + + cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_imap_core_main_conf_t)); + if (cmcf == NULL) { + return NGX_CONF_ERROR; + } + + if (ngx_array_init(&cmcf->servers, cf->pool, 4, + sizeof(ngx_imap_core_srv_conf_t *)) == NGX_ERROR) + { + return NGX_CONF_ERROR; + } + + return cmcf; +} + + +static void * +ngx_imap_core_create_srv_conf(ngx_conf_t *cf) +{ + ngx_imap_core_srv_conf_t *cscf; + + cscf = ngx_pcalloc(cf->pool, sizeof(ngx_imap_core_srv_conf_t)); + if (cscf == NULL) { + return NULL; + } + + cscf->imap_client_buffer_size = NGX_CONF_UNSET_SIZE; + cscf->proxy_buffer_size = NGX_CONF_UNSET_SIZE; + cscf->timeout = NGX_CONF_UNSET_MSEC; + cscf->protocol = NGX_CONF_UNSET_UINT; + + if (ngx_array_init(&cscf->pop3_capabilities, cf->pool, 4, sizeof(ngx_str_t)) + != NGX_OK) + { + return NULL; + } + + if (ngx_array_init(&cscf->imap_capabilities, cf->pool, 4, sizeof(ngx_str_t)) + != NGX_OK) + { + return NULL; + } + + return cscf; +} + + +static char * +ngx_imap_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_imap_core_srv_conf_t *prev = parent; + ngx_imap_core_srv_conf_t *conf = child; + + size_t size; + ngx_buf_t *b; + ngx_str_t *c, *d; + ngx_uint_t i; + + ngx_conf_merge_size_value(conf->imap_client_buffer_size, + prev->imap_client_buffer_size, + (size_t) ngx_pagesize); + ngx_conf_merge_size_value(conf->proxy_buffer_size, prev->proxy_buffer_size, + (size_t) ngx_pagesize); + ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000); + ngx_conf_merge_unsigned_value(conf->protocol, prev->protocol, + NGX_IMAP_IMAP_PROTOCOL); + + + if (conf->pop3_capabilities.nelts == 0) { + conf->pop3_capabilities = prev->pop3_capabilities; + } + + if (conf->pop3_capabilities.nelts == 0) { + + for (d = ngx_pop3_default_capabilities; d->len; d++) { + c = ngx_array_push(&conf->pop3_capabilities); + if (c == NULL) { + return NGX_CONF_ERROR; + } + + *c = *d; + } + } + + size = sizeof("+OK Capability list follows" CRLF) - 1 + + sizeof("." CRLF) - 1; + + c = conf->pop3_capabilities.elts; + for (i = 0; i < conf->pop3_capabilities.nelts; i++) { + size += c[i].len + sizeof(CRLF) - 1; + } + + b = ngx_create_temp_buf(cf->pool, size); + if (b == NULL) { + return NGX_CONF_ERROR; + } + + b->last = ngx_cpymem(b->last, "+OK Capability list follows" CRLF, + sizeof("+OK Capability list follows" CRLF) - 1); + + for (i = 0; i < conf->pop3_capabilities.nelts; i++) { + b->last = ngx_cpymem(b->last, c[i].data, c[i].len); + *b->last++ = CR; *b->last++ = LF; + } + + *b->last++ = '.'; *b->last++ = CR; *b->last++ = LF; + + conf->pop3_capability = b; + + + if (conf->imap_capabilities.nelts == 0) { + conf->imap_capabilities = prev->imap_capabilities; + } + + if (conf->imap_capabilities.nelts == 0) { + + for (d = ngx_imap_default_capabilities; d->len; d++) { + c = ngx_array_push(&conf->imap_capabilities); + if (c == NULL) { + return NGX_CONF_ERROR; + } + + *c = *d; + } + } + + size = sizeof("* CAPABILITY") - 1 + sizeof(CRLF) - 1; + + c = conf->imap_capabilities.elts; + for (i = 0; i < conf->imap_capabilities.nelts; i++) { + size += 1 + c[i].len; + } + + b = ngx_create_temp_buf(cf->pool, size); + if (b == NULL) { + return NGX_CONF_ERROR; + } + + b->last = ngx_cpymem(b->last, "* CAPABILITY", sizeof("* CAPABILITY") - 1); + + for (i = 0; i < conf->imap_capabilities.nelts; i++) { + *b->last++ = ' '; + b->last = ngx_cpymem(b->last, c[i].data, c[i].len); + } + + *b->last++ = CR; *b->last++ = LF; + + conf->imap_capability = b; + + return NGX_CONF_OK; +} + + +static char * +ngx_imap_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *rv; + void *mconf; + ngx_uint_t m; + ngx_conf_t pcf; + ngx_imap_module_t *module; + ngx_imap_conf_ctx_t *ctx, *imap_ctx; + ngx_imap_core_srv_conf_t *cscf, **cscfp; + ngx_imap_core_main_conf_t *cmcf; + + + ctx = ngx_pcalloc(cf->pool, sizeof(ngx_imap_conf_ctx_t)); + if (ctx == NULL) { + return NGX_CONF_ERROR; + } + + imap_ctx = cf->ctx; + ctx->main_conf = imap_ctx->main_conf; + + /* the server{}'s srv_conf */ + + ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_imap_max_module); + if (ctx->srv_conf == NULL) { + return NGX_CONF_ERROR; + } + + for (m = 0; ngx_modules[m]; m++) { + if (ngx_modules[m]->type != NGX_IMAP_MODULE) { + continue; + } + + module = ngx_modules[m]->ctx; + + if (module->create_srv_conf) { + mconf = module->create_srv_conf(cf); + if (mconf == NULL) { + return NGX_CONF_ERROR; + } + + ctx->srv_conf[ngx_modules[m]->ctx_index] = mconf; + } + } + + /* the server configuration context */ + + cscf = ctx->srv_conf[ngx_imap_core_module.ctx_index]; + cscf->ctx = ctx; + + cmcf = ctx->main_conf[ngx_imap_core_module.ctx_index]; + + cscfp = ngx_array_push(&cmcf->servers); + if (cscfp == NULL) { + return NGX_CONF_ERROR; + } + + *cscfp = cscf; + + + /* parse inside server{} */ + + pcf = *cf; + cf->ctx = ctx; + cf->cmd_type = NGX_IMAP_SRV_CONF; + + rv = ngx_conf_parse(cf, NULL); + + *cf = pcf; + + return rv; +} + + +/* AF_INET only */ + +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; + + value = cf->args->elts; + + ngx_memzero(&inet_upstream, sizeof(ngx_inet_upstream_t)); + + inet_upstream.url = value[1]; + inet_upstream.port_only = 1; + + err = ngx_inet_parse_host_port(&inet_upstream); + + if (err) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "%s in \"%V\" of the \"listen\" directive", + err, &inet_upstream.url); + return NGX_CONF_ERROR; + } + + if (inet_upstream.host.len) { + inet_upstream.host.data[inet_upstream.host.len] = '\0'; + + in_addr = inet_addr((const char *) inet_upstream.host.data); + + if (in_addr == INADDR_NONE) { + h = gethostbyname((const char *) inet_upstream.host.data); + + if (h == NULL || h->h_addr_list[0] == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "can not resolve host \"%s\" " + "in the \"listen\" directive", + inet_upstream.host.data); + return NGX_CONF_ERROR; + } + + in_addr = *(in_addr_t *)(h->h_addr_list[0]); + } + + } else { + in_addr = INADDR_ANY; + } + + + ls = ngx_listening_inet_stream_socket(cf, in_addr, inet_upstream.port); + if (ls == NULL) { + return NGX_CONF_ERROR; + } + + ls->backlog = -1; + ls->addr_ntop = 1; + ls->handler = ngx_imap_init_connection; + ls->pool_size = 256; + + ls->ctx = cf->ctx; + + /* STUB */ + ls->log = cf->cycle->new_log; + /**/ + + return NGX_CONF_OK; +} + + +static char * +ngx_imap_core_capability(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ngx_str_t *c, *value; + ngx_uint_t i; + ngx_array_t *a; + + a = (ngx_array_t *) (p + cmd->offset); + + value = cf->args->elts; + + for (i = 1; i < cf->args->nelts; i++) { + c = ngx_array_push(a); + if (c == NULL) { + return NGX_CONF_ERROR; + } + + *c = value[i]; + } + + return NGX_CONF_OK; +} diff --git a/src/imap/ngx_imap_handler.c b/src/imap/ngx_imap_handler.c new file mode 100644 index 000000000..6464d4a85 --- /dev/null +++ b/src/imap/ngx_imap_handler.c @@ -0,0 +1,561 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_event.h> +#include <ngx_imap.h> + + +static void ngx_imap_init_session(ngx_event_t *rev); +static ngx_int_t ngx_imap_read_command(ngx_imap_session_t *s); + + +static ngx_str_t greetings[] = { + ngx_string("+OK POP3 ready" CRLF), + ngx_string("* OK IMAP ready" CRLF) +}; + +static ngx_str_t internal_server_errors[] = { + ngx_string("-ERR internal server error" CRLF), + ngx_string("* BAD internal server error" CRLF), +}; + +static u_char pop3_ok[] = "+OK" CRLF; +static u_char pop3_invalid_command[] = "-ERR invalid command" CRLF; + +static u_char imap_ok[] = "OK" CRLF; +static u_char imap_next[] = "+ OK" CRLF; +static u_char imap_bye[] = "* BYE" CRLF; +static u_char imap_invalid_command[] = "BAD invalid command" CRLF; + + +void +ngx_imap_init_connection(ngx_connection_t *c) +{ + ssize_t size; + ngx_imap_conf_ctx_t *ctx; + ngx_imap_core_srv_conf_t *cscf; + + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, c->log, 0, "imap init connection"); + + c->log_error = NGX_ERROR_INFO; + + ctx = c->ctx; + cscf = ngx_imap_get_module_srv_conf(ctx, ngx_imap_core_module); + + size = greetings[cscf->protocol].len; + + if (ngx_send(c, greetings[cscf->protocol].data, size) < size) { + /* + * we treat the incomplete sending as NGX_ERROR + * because it is very strange here + */ + ngx_imap_close_connection(c); + return; + } + + c->read->handler = ngx_imap_init_session; + + ngx_add_timer(c->read, cscf->timeout); + + if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + ngx_imap_close_connection(c); + } +} + + +static void +ngx_imap_init_session(ngx_event_t *rev) +{ + size_t size; + ngx_connection_t *c; + ngx_imap_session_t *s; + ngx_imap_conf_ctx_t *ctx; + ngx_imap_core_srv_conf_t *cscf; + + c = rev->data; + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + ngx_imap_close_connection(c); + return; + } + + s = ngx_pcalloc(c->pool, sizeof(ngx_imap_session_t)); + if (s == NULL) { + ngx_imap_session_internal_server_error(s); + return; + } + + c->data = s; + s->connection = c; + + s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_imap_max_module); + if (s->ctx == NULL) { + ngx_imap_session_internal_server_error(s); + return; + } + + ctx = c->ctx; + s->main_conf = ctx->main_conf; + s->srv_conf = ctx->srv_conf; + + if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) == NGX_ERROR) { + ngx_imap_session_internal_server_error(s); + return; + } + + cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module); + + s->protocol = cscf->protocol; + + if (cscf->protocol == NGX_IMAP_POP3_PROTOCOL) { + size = 128; + s->imap_state = ngx_pop3_start; + c->read->handler = ngx_pop3_auth_state; + + } else { + size = cscf->imap_client_buffer_size; + s->imap_state = ngx_imap_start; + c->read->handler = ngx_imap_auth_state; + } + + s->buffer = ngx_create_temp_buf(c->pool, size); + if (s->buffer == NULL) { + ngx_imap_session_internal_server_error(s); + return; + } + + c->read->handler(rev); +} + + +void +ngx_imap_auth_state(ngx_event_t *rev) +{ + u_char *text, *last, *out, *p; + ssize_t size, text_len, last_len; + ngx_str_t *arg; + ngx_int_t rc; + ngx_uint_t quit, tag; + ngx_connection_t *c; + ngx_imap_session_t *s; + ngx_imap_core_srv_conf_t *cscf; + + c = rev->data; + s = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, c->log, 0, "imap auth state"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + ngx_imap_close_connection(c); + return; + } + + rc = ngx_imap_read_command(s); + + ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0, "imap auth: %i", rc); + + if (rc == NGX_AGAIN || rc == NGX_ERROR) { + return; + } + + quit = 0; + tag = 1; + + text = NULL; + text_len = 0; + + last = imap_ok; + last_len = sizeof(imap_ok) - 1; + + if (rc == NGX_OK) { + + ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0, "imap auth command: %i", + s->command); + + switch (s->command) { + + case NGX_IMAP_LOGIN: + if (s->args.nelts == 2) { + + arg = s->args.elts; + + s->login.len = arg[0].len; + s->login.data = ngx_palloc(c->pool, s->login.len); + if (s->login.data == NULL) { + ngx_imap_session_internal_server_error(s); + return; + } + + ngx_memcpy(s->login.data, arg[0].data, s->login.len); + + s->passwd.len = arg[1].len; + s->passwd.data = ngx_palloc(c->pool, s->passwd.len); + if (s->passwd.data == NULL) { + ngx_imap_session_internal_server_error(s); + return; + } + + ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len); + + ngx_log_debug2(NGX_LOG_DEBUG_IMAP, c->log, 0, + "imap login:\"%V\" passwd:\"%V\"", + &s->login, &s->passwd); + + s->args.nelts = 0; + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + + if (rev->timer_set) { + ngx_del_timer(rev); + } + + s->login_attempt++; + + ngx_imap_auth_http_init(s); + + return; + + } else { + rc = NGX_IMAP_PARSE_INVALID_COMMAND; + } + + break; + + case NGX_IMAP_CAPABILITY: + cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module); + text = cscf->imap_capability->pos; + text_len = cscf->imap_capability->last - cscf->imap_capability->pos; + break; + + case NGX_IMAP_LOGOUT: + text = imap_bye; + text_len = sizeof(imap_bye) - 1; + quit = 1; + break; + + case NGX_IMAP_NOOP: + break; + + default: + rc = NGX_IMAP_PARSE_INVALID_COMMAND; + break; + } + + } else if (rc == NGX_IMAP_NEXT) { + last = imap_next; + last_len = sizeof(imap_next) - 1; + tag = 0; + } + + if (rc == NGX_IMAP_PARSE_INVALID_COMMAND) { + last = imap_invalid_command; + last_len = sizeof(imap_invalid_command) - 1; + } + + if (tag) { + if (s->out.len < text_len + s->tag.len + last_len) { + + s->out.len = text_len + s->tag.len + last_len; + s->out.data = ngx_palloc(c->pool, s->out.len); + if (s->out.data == NULL) { + ngx_imap_close_connection(c); + return; + } + } + + out = s->out.data; + p = out; + + if (text) { + p = ngx_cpymem(p, text, text_len); + } + p = ngx_cpymem(p, s->tag.data, s->tag.len); + ngx_memcpy(p, last, last_len); + + size = text_len + s->tag.len + last_len; + + } else { + out = last; + size = last_len; + } + + if (ngx_send(c, out, size) < size) { + /* + * we treat the incomplete sending as NGX_ERROR + * because it is very strange here + */ + ngx_imap_close_connection(c); + return; + } + + if (rc == NGX_IMAP_NEXT) { + return; + } + + if (quit) { + ngx_imap_close_connection(c); + return; + } + + s->args.nelts = 0; + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + s->tag.len = 0; +} + + +void +ngx_pop3_auth_state(ngx_event_t *rev) +{ + u_char *text; + ssize_t size; + ngx_int_t rc; + ngx_uint_t quit; + ngx_str_t *arg; + ngx_connection_t *c; + ngx_imap_session_t *s; + ngx_imap_core_srv_conf_t *cscf; + + c = rev->data; + s = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, c->log, 0, "pop3 auth state"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + ngx_imap_close_connection(c); + return; + } + + rc = ngx_imap_read_command(s); + + if (rc == NGX_AGAIN || rc == NGX_ERROR) { + return; + } + + quit = 0; + text = pop3_ok; + size = sizeof(pop3_ok) - 1; + + if (rc == NGX_OK) { + switch (s->imap_state) { + + case ngx_pop3_start: + + switch (s->command) { + + case NGX_POP3_USER: + if (s->args.nelts == 1) { + s->imap_state = ngx_pop3_user; + + arg = s->args.elts; + s->login.len = arg[0].len; + s->login.data = ngx_palloc(c->pool, s->login.len); + if (s->login.data == NULL) { + ngx_imap_session_internal_server_error(s); + return; + } + + ngx_memcpy(s->login.data, arg[0].data, s->login.len); + + ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0, + "pop3 login: \"%V\"", &s->login); + + } else { + rc = NGX_IMAP_PARSE_INVALID_COMMAND; + } + + break; + + case NGX_POP3_CAPA: + cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module); + text = cscf->pop3_capability->pos; + size = cscf->pop3_capability->last - cscf->pop3_capability->pos; + break; + + case NGX_POP3_QUIT: + quit = 1; + break; + + case NGX_POP3_NOOP: + break; + + default: + s->imap_state = ngx_pop3_start; + rc = NGX_IMAP_PARSE_INVALID_COMMAND; + break; + } + + break; + + case ngx_pop3_user: + + switch (s->command) { + + case NGX_POP3_PASS: + if (s->args.nelts == 1) { + /* STUB */ s->imap_state = ngx_pop3_start; + + arg = s->args.elts; + s->passwd.len = arg[0].len; + s->passwd.data = ngx_palloc(c->pool, s->passwd.len); + if (s->passwd.data == NULL) { + ngx_imap_session_internal_server_error(s); + return; + } + + ngx_memcpy(s->passwd.data, arg[0].data, s->passwd.len); + + ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0, + "pop3 passwd: \"%V\"", &s->passwd); + + s->args.nelts = 0; + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + + if (rev->timer_set) { + ngx_del_timer(rev); + } + + ngx_imap_auth_http_init(s); + + return; + + } else { + rc = NGX_IMAP_PARSE_INVALID_COMMAND; + } + + break; + + case NGX_POP3_CAPA: + cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module); + text = cscf->pop3_capability->pos; + size = cscf->pop3_capability->last - cscf->pop3_capability->pos; + break; + + case NGX_POP3_QUIT: + quit = 1; + break; + + case NGX_POP3_NOOP: + break; + + default: + s->imap_state = ngx_pop3_start; + rc = NGX_IMAP_PARSE_INVALID_COMMAND; + break; + } + + break; + + /* suppress warinings */ + case ngx_pop3_passwd: + break; + } + } + + if (rc == NGX_IMAP_PARSE_INVALID_COMMAND) { + text = pop3_invalid_command; + size = sizeof(pop3_invalid_command) - 1; + } + + if (ngx_send(c, text, size) < size) { + /* + * we treat the incomplete sending as NGX_ERROR + * because it is very strange here + */ + ngx_imap_close_connection(c); + return; + } + + if (quit) { + ngx_imap_close_connection(c); + return; + } + + s->args.nelts = 0; + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; +} + + +static ngx_int_t +ngx_imap_read_command(ngx_imap_session_t *s) +{ + ssize_t n; + ngx_int_t rc; + + n = ngx_recv(s->connection, s->buffer->last, + s->buffer->end - s->buffer->last); + + if (n == NGX_ERROR || n == 0) { + ngx_imap_close_connection(s->connection); + return NGX_ERROR; + } + + if (n > 0) { + s->buffer->last += n; + } + + if (n == NGX_AGAIN) { + if (ngx_handle_read_event(s->connection->read, 0) == NGX_ERROR) { + ngx_imap_session_internal_server_error(s); + return NGX_ERROR; + } + + return NGX_AGAIN; + } + + if (s->protocol == NGX_IMAP_POP3_PROTOCOL) { + rc = ngx_pop3_parse_command(s); + } else { + rc = ngx_imap_parse_command(s); + } + + if (rc == NGX_AGAIN + || rc == NGX_IMAP_NEXT + || rc == NGX_IMAP_PARSE_INVALID_COMMAND) + { + return rc; + } + + if (rc == NGX_ERROR) { + ngx_imap_close_connection(s->connection); + return NGX_ERROR; + } + + return NGX_OK; +} + + +void +ngx_imap_session_internal_server_error(ngx_imap_session_t *s) +{ + (void) ngx_send(s->connection, internal_server_errors[s->protocol].data, + internal_server_errors[s->protocol].len); + + ngx_imap_close_connection(s->connection); +} + + +void +ngx_imap_close_connection(ngx_connection_t *c) +{ + ngx_pool_t *pool; + + ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0, + "close imap connection: %d", c->fd); + + pool = c->pool; + + ngx_close_connection(c); + + ngx_destroy_pool(pool); +} diff --git a/src/imap/ngx_imap_parse.c b/src/imap/ngx_imap_parse.c new file mode 100644 index 000000000..e3923e892 --- /dev/null +++ b/src/imap/ngx_imap_parse.c @@ -0,0 +1,500 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_event.h> +#include <ngx_imap.h> + + +ngx_int_t ngx_imap_parse_command(ngx_imap_session_t *s) +{ + u_char ch, *p, *c; + ngx_str_t *arg; + enum { + sw_start = 0, + sw_spaces_before_command, + sw_command, + sw_spaces_before_argument, + sw_argument, + sw_literal, + sw_start_literal_argument, + sw_literal_argument, + sw_end_literal_argument, + sw_almost_done + } state; + + state = s->state; + + for (p = s->buffer->pos; p < s->buffer->last; p++) { + ch = *p; + + switch (state) { + + /* IMAP tag */ + case sw_start: + switch (ch) { + case ' ': + s->tag.len = p - s->buffer->start + 1; + s->tag.data = s->buffer->start; + state = sw_spaces_before_command; + break; + case CR: + s->state = sw_start; + return NGX_IMAP_PARSE_INVALID_COMMAND; + case LF: + s->state = sw_start; + return NGX_IMAP_PARSE_INVALID_COMMAND; + } + break; + + case sw_spaces_before_command: + switch (ch) { + case ' ': + break; + case CR: + s->state = sw_start; + return NGX_IMAP_PARSE_INVALID_COMMAND; + case LF: + s->state = sw_start; + return NGX_IMAP_PARSE_INVALID_COMMAND; + default: + s->cmd_start = p; + state = sw_command; + break; + } + break; + + case sw_command: + if (ch == ' ' || ch == CR || ch == LF) { + + c = s->cmd_start; + + switch (p - c) { + + case 4: + if ((c[0] == 'N' || c[0] == 'n') + && (c[1] == 'O'|| c[1] == 'o') + && (c[2] == 'O'|| c[2] == 'o') + && (c[3] == 'P'|| c[3] == 'p')) + { + s->command = NGX_IMAP_NOOP; + + } else { + goto invalid; + } + break; + + case 5: + if ((c[0] == 'L'|| c[0] == 'l') + && (c[1] == 'O'|| c[1] == 'o') + && (c[2] == 'G'|| c[2] == 'g') + && (c[3] == 'I'|| c[3] == 'i') + && (c[4] == 'N'|| c[4] == 'n')) + { + s->command = NGX_IMAP_LOGIN; + + } else { + goto invalid; + } + break; + + case 6: + if ((c[0] == 'L'|| c[0] == 'l') + && (c[1] == 'O'|| c[1] == 'o') + && (c[2] == 'G'|| c[2] == 'g') + && (c[3] == 'O'|| c[3] == 'o') + && (c[4] == 'U'|| c[4] == 'u') + && (c[5] == 'T'|| c[5] == 't')) + { + s->command = NGX_IMAP_LOGOUT; + + } else { + goto invalid; + } + break; + + case 10: + if ((c[0] == 'C'|| c[0] == 'c') + && (c[1] == 'A'|| c[1] == 'a') + && (c[2] == 'P'|| c[2] == 'p') + && (c[3] == 'A'|| c[3] == 'a') + && (c[4] == 'B'|| c[4] == 'b') + && (c[5] == 'I'|| c[5] == 'i') + && (c[6] == 'L'|| c[6] == 'l') + && (c[7] == 'I'|| c[7] == 'i') + && (c[8] == 'T'|| c[8] == 't') + && (c[9] == 'Y'|| c[9] == 'y')) + { + s->command = NGX_IMAP_CAPABILITY; + + } else { + goto invalid; + } + break; + + default: + goto invalid; + } + + switch (ch) { + case ' ': + state = sw_spaces_before_argument; + break; + case CR: + state = sw_almost_done; + break; + case LF: + goto done; + } + break; + } + + if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) { + goto invalid; + } + + break; + + case sw_spaces_before_argument: + switch (ch) { + case ' ': + break; + case CR: + state = sw_almost_done; + s->arg_end = p; + break; + case LF: + s->arg_end = p; + goto done; + case '"': + if (s->args.nelts <= 2) { + s->quoted = 1; + s->arg_start = p + 1; + state = sw_argument; + break; + } + goto invalid; + case '{': + if (s->args.nelts <= 2) { + state = sw_literal; + break; + } + goto invalid; + default: + if (s->args.nelts <= 2) { + s->arg_start = p; + state = sw_argument; + break; + } + goto invalid; + } + break; + + case sw_argument: + switch (ch) { + case '"': + if (!s->quoted) { + break; + } + s->quoted = 0; + /* fall through */ + case ' ': + case CR: + case LF: + arg = ngx_array_push(&s->args); + if (arg == NULL) { + return NGX_ERROR; + } + arg->len = p - s->arg_start; + arg->data = s->arg_start; + s->arg_start = NULL; + + switch (ch) { + case '"': + case ' ': + state = sw_spaces_before_argument; + break; + case CR: + state = sw_almost_done; + break; + case LF: + goto done; + } + break; + } + break; + + case sw_literal: + if (ch >= '0' && ch <= '9') { + s->literal_len = s->literal_len * 10 + (ch - '0'); + break; + } + if (ch == '}') { + state = sw_start_literal_argument; + break; + } + goto invalid; + + case sw_start_literal_argument: + switch (ch) { + case CR: + break; + case LF: + s->buffer->pos = p + 1; + s->arg_start = p + 1; + s->state = sw_literal_argument; + return NGX_IMAP_NEXT; + } + goto invalid; + + case sw_literal_argument: + if (--s->literal_len) { + break; + } + + arg = ngx_array_push(&s->args); + if (arg == NULL) { + return NGX_ERROR; + } + arg->len = p + 1 - s->arg_start; + arg->data = s->arg_start; + s->arg_start = NULL; + state = sw_end_literal_argument; + + break; + + case sw_end_literal_argument: + switch (ch) { + case '{': + if (s->args.nelts <= 2) { + state = sw_literal; + break; + } + goto invalid; + case CR: + state = sw_almost_done; + break; + case LF: + goto done; + default: + goto invalid; + } + break; + + case sw_almost_done: + switch (ch) { + case LF: + goto done; + default: + goto invalid; + } + } + } + + s->buffer->pos = p; + s->state = state; + + return NGX_AGAIN; + +done: + + s->buffer->pos = p + 1; + + if (s->arg_start) { + arg = ngx_array_push(&s->args); + if (arg == NULL) { + return NGX_ERROR; + } + arg->len = s->arg_end - s->arg_start; + arg->data = s->arg_start; + s->arg_start = NULL; + s->cmd_start = NULL; + s->quoted = 0; + s->literal_len = 0; + } + + s->state = sw_start; + + return NGX_OK; + +invalid: + + s->state = sw_start; + s->quoted = 0; + s->literal_len = 0; + + return NGX_IMAP_PARSE_INVALID_COMMAND; +} + + +ngx_int_t ngx_pop3_parse_command(ngx_imap_session_t *s) +{ + u_char ch, *p, *c, c0, c1, c2, c3; + ngx_str_t *arg; + enum { + sw_start = 0, + sw_spaces_before_argument, + sw_argument, + sw_almost_done + } state; + + state = s->state; + + for (p = s->buffer->pos; p < s->buffer->last; p++) { + ch = *p; + + switch (state) { + + /* POP3 command */ + case sw_start: + if (ch == ' ' || ch == CR || ch == LF) { + c = s->buffer->start; + + if (p - c == 4) { + + c0 = ngx_toupper(c[0]); + c1 = ngx_toupper(c[1]); + c2 = ngx_toupper(c[2]); + c3 = ngx_toupper(c[3]); + + if (c0 == 'U' && c1 == 'S' && c2 == 'E' && c3 == 'R') + { + s->command = NGX_POP3_USER; + + } else if (c0 == 'P' && c1 == 'A' && c2 == 'S' && c3 == 'S') + { + s->command = NGX_POP3_PASS; + + } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T') + { + s->command = NGX_POP3_QUIT; + + } else if (c0 == 'C' && c1 == 'A' && c2 == 'P' && c3 == 'A') + { + s->command = NGX_POP3_CAPA; + + } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P') + { + s->command = NGX_POP3_NOOP; + + } else { + goto invalid; + } + + } else { + goto invalid; + } + + switch (ch) { + case ' ': + state = sw_spaces_before_argument; + break; + case CR: + state = sw_almost_done; + break; + case LF: + goto done; + } + break; + } + + if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) { + goto invalid; + } + + break; + + case sw_spaces_before_argument: + switch (ch) { + case ' ': + break; + case CR: + state = sw_almost_done; + s->arg_end = p; + break; + case LF: + s->arg_end = p; + goto done; + default: + if (s->args.nelts <= 2) { + state = sw_argument; + s->arg_start = p; + break; + } + goto invalid; + } + break; + + case sw_argument: + switch (ch) { + case ' ': + case CR: + case LF: + arg = ngx_array_push(&s->args); + if (arg == NULL) { + return NGX_ERROR; + } + arg->len = p - s->arg_start; + arg->data = s->arg_start; + s->arg_start = NULL; + + switch (ch) { + case ' ': + state = sw_spaces_before_argument; + break; + case CR: + state = sw_almost_done; + break; + case LF: + goto done; + } + break; + + default: + break; + } + break; + + case sw_almost_done: + switch (ch) { + case LF: + goto done; + default: + goto invalid; + } + } + } + + s->buffer->pos = p; + s->state = state; + + return NGX_AGAIN; + +done: + + s->buffer->pos = p + 1; + + if (s->arg_start) { + arg = ngx_array_push(&s->args); + if (arg == NULL) { + return NGX_ERROR; + } + arg->len = s->arg_end - s->arg_start; + arg->data = s->arg_start; + s->arg_start = NULL; + } + + s->state = sw_start; + + return NGX_OK; + +invalid: + + s->state = sw_start; + + return NGX_IMAP_PARSE_INVALID_COMMAND; +} diff --git a/src/imap/ngx_imap_proxy_module.c b/src/imap/ngx_imap_proxy_module.c new file mode 100644 index 000000000..5177e289f --- /dev/null +++ b/src/imap/ngx_imap_proxy_module.c @@ -0,0 +1,623 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_event.h> +#include <ngx_event_connect.h> +#include <ngx_imap.h> + + +typedef struct { + ngx_flag_t enable; +} ngx_imap_proxy_conf_t; + + +static void ngx_imap_proxy_block_read(ngx_event_t *rev); +static void ngx_imap_proxy_imap_handler(ngx_event_t *rev); +static void ngx_imap_proxy_pop3_handler(ngx_event_t *rev); +static void ngx_imap_proxy_dummy_handler(ngx_event_t *ev); +static ngx_int_t ngx_imap_proxy_read_response(ngx_imap_session_t *s, + ngx_uint_t what); +static void ngx_imap_proxy_handler(ngx_event_t *ev); +static void ngx_imap_proxy_internal_server_error(ngx_imap_session_t *s); +static void ngx_imap_proxy_close_session(ngx_imap_session_t *s); +static void *ngx_imap_proxy_create_conf(ngx_conf_t *cf); +static char *ngx_imap_proxy_merge_conf(ngx_conf_t *cf, void *parent, + void *child); + + +#define NGX_IMAP_WAIT_OK 0 +#define NGX_IMAP_WAIT_NEXT 1 + + +static ngx_command_t ngx_imap_proxy_commands[] = { + { ngx_string("proxy"), + NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_IMAP_SRV_CONF_OFFSET, + offsetof(ngx_imap_proxy_conf_t, enable), + NULL }, + + ngx_null_command +}; + + +static ngx_imap_module_t ngx_imap_proxy_module_ctx = { + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_imap_proxy_create_conf, /* create server configuration */ + ngx_imap_proxy_merge_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_imap_proxy_module = { + NGX_MODULE_V1, + &ngx_imap_proxy_module_ctx, /* module context */ + ngx_imap_proxy_commands, /* module directives */ + NGX_IMAP_MODULE, /* module type */ + NULL, /* init module */ + NULL /* init process */ +}; + + +void +ngx_imap_proxy_init(ngx_imap_session_t *s, ngx_peers_t *peers) +{ + ngx_int_t rc; + ngx_imap_proxy_ctx_t *p; + ngx_imap_core_srv_conf_t *cscf; + + p = ngx_pcalloc(s->connection->pool, sizeof(ngx_imap_proxy_ctx_t)); + if (p == NULL) { + ngx_imap_session_internal_server_error(s); + return; + } + + s->proxy = p; + + p->upstream.peers = peers; + p->upstream.log = s->connection->log; + p->upstream.log_error = NGX_ERROR_ERR; + + rc = ngx_event_connect_peer(&p->upstream); + + if (rc == NGX_ERROR) { + ngx_imap_session_internal_server_error(s); + return; + } + + cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module); + ngx_add_timer(p->upstream.connection->read, cscf->timeout); + + p->upstream.connection->data = s; + p->upstream.connection->pool = s->connection->pool; + + s->connection->read->handler = ngx_imap_proxy_block_read; + p->upstream.connection->write->handler = ngx_imap_proxy_dummy_handler; + + if (s->protocol == NGX_IMAP_POP3_PROTOCOL) { + p->upstream.connection->read->handler = ngx_imap_proxy_pop3_handler; + s->imap_state = ngx_pop3_start; + + } else { + p->upstream.connection->read->handler = ngx_imap_proxy_imap_handler; + s->imap_state = ngx_imap_start; + } +} + + +static void +ngx_imap_proxy_block_read(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_imap_session_t *s; + + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy block read"); + + if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + c = rev->data; + s = c->data; + + ngx_imap_proxy_close_session(s); + } +} + + +static void +ngx_imap_proxy_imap_handler(ngx_event_t *rev) +{ + u_char *p; + ngx_int_t rc; + ngx_str_t line; + ngx_connection_t *c; + ngx_imap_session_t *s; + ngx_imap_core_srv_conf_t *cscf; + + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, + "imap proxy imap auth handler"); + + c = rev->data; + s = c->data; + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, + "upstream timed out"); + ngx_imap_proxy_internal_server_error(s); + return; + } + + if (s->proxy->buffer == NULL) { + cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module); + + s->proxy->buffer = ngx_create_temp_buf(c->pool, + cscf->proxy_buffer_size); + if (s->proxy->buffer == NULL) { + ngx_imap_proxy_internal_server_error(s); + return; + } + } + + rc = ngx_imap_proxy_read_response(s, s->imap_state == ngx_imap_start ? + NGX_IMAP_WAIT_OK : NGX_IMAP_WAIT_NEXT); + + if (rc == NGX_AGAIN) { + return; + } + + if (rc == NGX_ERROR) { + ngx_imap_proxy_internal_server_error(s); + return; + } + + switch (s->imap_state) { + + case ngx_imap_start: + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, + "imap proxy send login"); + + line.len = s->tag.len + sizeof("LOGIN ") - 1 + + 1 + NGX_SIZE_T_LEN + 1 + 2; + line.data = ngx_palloc(c->pool, line.len); + if (line.data == NULL) { + ngx_imap_proxy_internal_server_error(s); + return; + } + + line.len = ngx_sprintf(line.data, "%VLOGIN {%uz}" CRLF, + &s->tag, s->login.len) + - line.data; + + s->imap_state = ngx_imap_login; + break; + + case ngx_imap_login: + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy send user"); + + line.len = s->login.len + 1 + NGX_SIZE_T_LEN + 1 + 2; + line.data = ngx_palloc(c->pool, line.len); + if (line.data == NULL) { + ngx_imap_proxy_internal_server_error(s); + return; + } + + line.len = ngx_sprintf(line.data, "%V{%uz}" CRLF, + &s->login, s->passwd.len) + - line.data; + + s->imap_state = ngx_imap_user; + break; + + case ngx_imap_user: + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, + "imap proxy send passwd"); + + line.len = s->passwd.len + 2; + line.data = ngx_palloc(c->pool, line.len); + if (line.data == NULL) { + ngx_imap_proxy_internal_server_error(s); + return; + } + + p = ngx_cpymem(line.data, s->passwd.data, s->passwd.len); + *p++ = CR; *p = LF; + + s->imap_state = ngx_imap_passwd; + break; + + default: +#if (NGX_SUPPRESS_WARN) + line.len = 0; + line.data = NULL; +#endif + break; + } + + if (ngx_send(c, line.data, line.len) < (ssize_t) line.len) { + /* + * we treat the incomplete sending as NGX_ERROR + * because it is very strange here + */ + ngx_imap_proxy_internal_server_error(s); + return; + } + + s->proxy->buffer->pos = s->proxy->buffer->start; + s->proxy->buffer->last = s->proxy->buffer->start; + + if (s->imap_state == ngx_imap_passwd) { + s->connection->read->handler = ngx_imap_proxy_handler; + s->connection->write->handler = ngx_imap_proxy_handler; + rev->handler = ngx_imap_proxy_handler; + c->write->handler = ngx_imap_proxy_handler; + } +} + + +static void +ngx_imap_proxy_pop3_handler(ngx_event_t *rev) +{ + u_char *p; + ngx_int_t rc; + ngx_str_t line; + ngx_connection_t *c; + ngx_imap_session_t *s; + ngx_imap_core_srv_conf_t *cscf; + + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, + "imap proxy pop3 auth handler"); + + c = rev->data; + s = c->data; + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, + "upstream timed out"); + ngx_imap_proxy_internal_server_error(s); + return; + } + + if (s->proxy->buffer == NULL) { + cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module); + + s->proxy->buffer = ngx_create_temp_buf(c->pool, + cscf->proxy_buffer_size); + if (s->proxy->buffer == NULL) { + ngx_imap_proxy_internal_server_error(s); + return; + } + } + + rc = ngx_imap_proxy_read_response(s, NGX_IMAP_WAIT_OK); + + if (rc == NGX_AGAIN) { + return; + } + + if (rc == NGX_ERROR) { + ngx_imap_proxy_internal_server_error(s); + return; + } + + switch (s->imap_state) { + + case ngx_pop3_start: + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy send user"); + + line.len = sizeof("USER ") - 1 + s->login.len + 2; + line.data = ngx_palloc(c->pool, line.len); + if (line.data == NULL) { + ngx_imap_proxy_internal_server_error(s); + return; + } + + p = ngx_cpymem(line.data, "USER ", sizeof("USER ") - 1); + p = ngx_cpymem(p, s->login.data, s->login.len); + *p++ = CR; *p = LF; + + s->imap_state = ngx_pop3_user; + break; + + case ngx_pop3_user: + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy send pass"); + + line.len = sizeof("PASS ") - 1 + s->passwd.len + 2; + line.data = ngx_palloc(c->pool, line.len); + if (line.data == NULL) { + ngx_imap_proxy_internal_server_error(s); + return; + } + + p = ngx_cpymem(line.data, "PASS ", sizeof("PASS ") - 1); + p = ngx_cpymem(p, s->passwd.data, s->passwd.len); + *p++ = CR; *p = LF; + + s->imap_state = ngx_pop3_passwd; + break; + + default: +#if (NGX_SUPPRESS_WARN) + line.len = 0; + line.data = NULL; +#endif + break; + } + + if (ngx_send(c, line.data, line.len) < (ssize_t) line.len) { + /* + * we treat the incomplete sending as NGX_ERROR + * because it is very strange here + */ + ngx_imap_proxy_internal_server_error(s); + return; + } + + s->proxy->buffer->pos = s->proxy->buffer->start; + s->proxy->buffer->last = s->proxy->buffer->start; + + if (s->imap_state == ngx_pop3_passwd) { + s->connection->read->handler = ngx_imap_proxy_handler; + s->connection->write->handler = ngx_imap_proxy_handler; + rev->handler = ngx_imap_proxy_handler; + c->write->handler = ngx_imap_proxy_handler; + } +} + + +static void +ngx_imap_proxy_dummy_handler(ngx_event_t *ev) +{ + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, ev->log, 0, "imap proxy dummy handler"); +} + + +static ngx_int_t +ngx_imap_proxy_read_response(ngx_imap_session_t *s, ngx_uint_t what) +{ + u_char *p; + ssize_t n; + ngx_buf_t *b; + + b = s->proxy->buffer; + + n = ngx_recv(s->proxy->upstream.connection, b->last, b->end - b->last); + + if (n == NGX_ERROR || n == 0) { + return NGX_ERROR; + } + + if (n == NGX_AGAIN) { + return NGX_AGAIN; + } + + b->last += n; + + if (b->last - b->pos < 5) { + return NGX_AGAIN; + } + + if (*(b->last - 2) != CR || *(b->last - 1) != LF) { + if (b->last == b->end) { + *(b->last - 1) = '\0'; + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "upstream sent too long response line: \"%s\"", + b->pos); + return NGX_IMAP_PROXY_INVALID; + } + + return NGX_AGAIN; + } + + p = b->pos; + + if (s->protocol == NGX_IMAP_POP3_PROTOCOL) { + if (p[0] == '+' && p[1] == 'O' && p[2] == 'K') { + return NGX_OK; + } + + if (p[0] == '-' && p[1] == 'E' && p[2] == 'R' && p[3] == 'R') { + return NGX_IMAP_PROXY_ERROR; + } + + } else { + if (what == NGX_IMAP_WAIT_OK) { + if (p[0] == '*' && p[1] == ' ' && p[2] == 'O' && p[3] == 'K') { + return NGX_OK; + } + + } else { + if (p[0] == '+' && p[1] == ' ' && p[2] == 'O' && p[3] == 'K') { + return NGX_OK; + } + } + } + + *(b->last - 2) = '\0'; + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "upstream sent invalid response: \"%s\"", p); + + return NGX_IMAP_PROXY_INVALID; +} + + +static void +ngx_imap_proxy_handler(ngx_event_t *ev) +{ + size_t size; + ssize_t n; + ngx_buf_t *b; + ngx_uint_t again, do_write; + ngx_connection_t *c, *src, *dst; + ngx_imap_session_t *s; + + c = ev->data; + s = c->data; + + if (ev->timedout) { + if (c == s->connection) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, + "client timed out"); + } else { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, + "upstream timed out"); + } + + ngx_imap_proxy_close_session(s); + return; + } + + if (c == s->connection) { + if (ev->write) { + src = s->proxy->upstream.connection; + dst = c; + b = s->proxy->buffer; + + } else { + src = c; + dst = s->proxy->upstream.connection; + b = s->buffer; + } + + } else { + if (ev->write) { + src = s->connection; + dst = c; + b = s->buffer; + + } else { + src = c; + dst = s->connection; + b = s->proxy->buffer; + } + } + + do_write = ev->write ? 1 : 0; + + ngx_log_debug3(NGX_LOG_DEBUG_IMAP, ev->log, 0, + "imap proxy handler: %d, #%d > #%d", + do_write, src->fd, dst->fd); + + do { + again = 0; + + if (do_write == 1) { + + size = b->last - b->pos; + + if (size && dst->write->ready) { + n = ngx_send(dst, b->pos, size); + + if (n == NGX_ERROR) { + ngx_imap_proxy_close_session(s); + return; + } + + if (n > 0) { + again = 1; + b->pos += n; + + if (b->pos == b->last) { + b->pos = b->start; + b->last = b->start; + } + } + + if (n == NGX_AGAIN || n < (ssize_t) size) { + if (ngx_handle_write_event(dst->write, /* TODO: LOWAT */ 0) + == NGX_ERROR) + { + ngx_imap_proxy_close_session(s); + return; + } + } + } + } + + size = b->end - b->last; + + if (size && src->read->ready) { + n = ngx_recv(src, b->last, size); + + if (n == NGX_ERROR || n == 0) { + ngx_imap_proxy_close_session(s); + return; + } + + if (n > 0) { + again = 1; + do_write = 1; + b->last += n; + } + + if (n == NGX_AGAIN || n < (ssize_t) size) { + if (ngx_handle_read_event(src->read, 0) == NGX_ERROR) { + ngx_imap_proxy_close_session(s); + return; + } + } + } + + } while (again); +} + + +static void +ngx_imap_proxy_internal_server_error(ngx_imap_session_t *s) +{ + if (s->proxy->upstream.connection) { + ngx_log_debug1(NGX_LOG_DEBUG_IMAP, s->connection->log, 0, + "close imap proxy connection: %d", + s->proxy->upstream.connection->fd); + + ngx_close_connection(s->proxy->upstream.connection); + } + + ngx_imap_session_internal_server_error(s); +} + + +static void +ngx_imap_proxy_close_session(ngx_imap_session_t *s) +{ + if (s->proxy->upstream.connection) { + ngx_log_debug1(NGX_LOG_DEBUG_IMAP, s->connection->log, 0, + "close imap proxy connection: %d", + s->proxy->upstream.connection->fd); + + ngx_close_connection(s->proxy->upstream.connection); + } + + ngx_imap_close_connection(s->connection); +} + + +static void * +ngx_imap_proxy_create_conf(ngx_conf_t *cf) +{ + ngx_imap_proxy_conf_t *pcf; + + pcf = ngx_pcalloc(cf->pool, sizeof(ngx_imap_proxy_conf_t)); + if (pcf == NULL) { + return NGX_CONF_ERROR; + } + + pcf->enable = NGX_CONF_UNSET; + + return pcf; +} + + +static char * +ngx_imap_proxy_merge_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_imap_proxy_conf_t *prev = parent; + ngx_imap_proxy_conf_t *conf = child; + + ngx_conf_merge_msec_value(conf->enable, prev->enable, 0); + + return NGX_CONF_OK; +} diff --git a/src/os/unix/ngx_linux_config.h b/src/os/unix/ngx_linux_config.h index 5bad6451c..7bd262a19 100644 --- a/src/os/unix/ngx_linux_config.h +++ b/src/os/unix/ngx_linux_config.h @@ -88,6 +88,11 @@ extern ssize_t sendfile(int s, int fd, int32_t *offset, size_t size); #endif +#ifndef NGX_HAVE_GNU_CRYPT_R +#define NGX_HAVE_GNU_CRYPT_R 1 +#endif + + #ifndef NGX_HAVE_INHERITED_NONBLOCK #define NGX_HAVE_INHERITED_NONBLOCK 0 #endif diff --git a/src/os/unix/ngx_user.c b/src/os/unix/ngx_user.c index 22eafd20e..cbb08a852 100644 --- a/src/os/unix/ngx_user.c +++ b/src/os/unix/ngx_user.c @@ -20,7 +20,7 @@ #if (NGX_CRYPT) -#if (NGX_LINUX) +#if (NGX_HAVE_GNU_CRYPT_R) ngx_int_t ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted) @@ -33,6 +33,8 @@ ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted) ngx_set_errno(0); cd.initialized = 0; + /* work around the glibc-2.2.5 bug */ + cd.current_saltbits = 0; value = crypt_r((char *) key, (char *) salt, &cd); |