# HG changeset patch # User Igor Sysoev # Date 1227128400 -10800 # Node ID ad0a34a8efa6b727cfb4bbc7d94cab0e882fd87b # Parent b986babf3f57eb24d6f3fa5ef72110c0240d96a5 nginx 0.7.22 *) Feature: the "none" parameter in the "smtp_auth" directive. Thanks to Maxim Dounin. *) Feature: the "$cookie_..." variables. *) Bugfix: the "directio" directive did not work in XFS filesystem. *) Bugfix: the resolver did not understand big DNS responses. Thanks to Zyb. diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,17 @@ +Changes with nginx 0.7.22 20 Nov 2008 + + *) Feature: the "none" parameter in the "smtp_auth" directive. + Thanks to Maxim Dounin. + + *) Feature: the "$cookie_..." variables. + + *) Bugfix: the "directio" directive did not work in XFS filesystem. + + *) Bugfix: the resolver did not understand big DNS responses. + Thanks to Zyb. + + Changes with nginx 0.7.21 11 Nov 2008 *) Changes in the ngx_http_limit_req_module. diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,17 @@ +Изменения в nginx 0.7.22 20.11.2008 + + *) Добавление: параметр none в директиве smtp_auth. + Спасибо Максиму Дунину. + + *) Добавление: переменные "$cookie_...". + + *) Исправление: директива directio не работала с файловой системой XFS. + + *) Исправление: resolver не понимал большие DNS-ответы. + Спасибо Zyb. + + Изменения в nginx 0.7.21 11.11.2008 *) Изменения в модуле ngx_http_limit_req_module. diff --git a/src/core/nginx.h b/src/core/nginx.h --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -8,7 +8,7 @@ #define _NGINX_H_INCLUDED_ -#define NGINX_VERSION "0.7.21" +#define NGINX_VERSION "0.7.22" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff --git a/src/core/ngx_output_chain.c b/src/core/ngx_output_chain.c --- a/src/core/ngx_output_chain.c +++ b/src/core/ngx_output_chain.c @@ -13,6 +13,17 @@ #define NGX_SENDFILE_LIMIT 4096 #endif +/* + * When DIRECTIO is enabled FreeBSD, Solaris, and MacOSX read directly + * to an application memory from a device if parameters are aligned + * to device sector boundary(512 bytes). They fallback to usual read + * operation if the parameters are not aligned. + * Linux allows DIRECTIO only if the parameters are aligned to a filesystem + * sector boundary, otherwise it returns EINVAL. The sector size is + * usually 512 bytes, however, on XFS it may be 4096 bytes. + */ +#define NGX_DIRECTIO_BLOCK 4096 + #define NGX_NONE 1 @@ -327,7 +338,7 @@ ngx_output_chain_align_file_buf(ngx_outp ctx->directio = 1; - size = (size_t) (in->file_pos - (in->file_pos & ~511)); + size = (size_t) (in->file_pos - (in->file_pos & ~(NGX_DIRECTIO_BLOCK - 1))); if (size == 0) { @@ -338,7 +349,7 @@ ngx_output_chain_align_file_buf(ngx_outp size = (size_t) bsize; } else { - size = 512 - size; + size = NGX_DIRECTIO_BLOCK - size; if ((off_t) size > bsize) { size = (size_t) bsize; @@ -413,7 +424,7 @@ ngx_output_chain_get_buf(ngx_output_chai * userland buffer direct usage conjunctly with directio */ - b->start = ngx_pmemalign(ctx->pool, size, 512); + b->start = ngx_pmemalign(ctx->pool, size, NGX_DIRECTIO_BLOCK); if (b->start == NULL) { return NGX_ERROR; } diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c --- a/src/core/ngx_resolver.c +++ b/src/core/ngx_resolver.c @@ -1866,7 +1866,7 @@ ngx_resolver_copy(ngx_resolver_t *r, ngx } if (n & 0xc0) { - n = (n & 0x3f << 8) + *p; + n = ((n & 0x3f) << 8) + *p; p = &buf[n]; } else { @@ -1916,7 +1916,7 @@ done: } } else { - n = (n & 0x3f << 8) + *src; + n = ((n & 0x3f) << 8) + *src; src = &buf[n]; n = *src++; diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -1240,23 +1240,37 @@ ngx_ssl_connection_error(ngx_connection_ n = ERR_GET_REASON(ERR_peek_error()); /* handshake failures */ - if (n == SSL_R_DIGEST_CHECK_FAILED - || n == SSL_R_NO_SHARED_CIPHER - || n == SSL_R_UNEXPECTED_MESSAGE - || n == SSL_R_WRONG_VERSION_NUMBER - || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC + if (n == SSL_R_DIGEST_CHECK_FAILED /* 149 */ + || n == SSL_R_NO_CIPHERS_PASSED /* 182 */ + || n == SSL_R_NO_SHARED_CIPHER /* 193 */ + || n == SSL_R_UNEXPECTED_MESSAGE /* 244 */ + || n == SSL_R_UNEXPECTED_RECORD /* 245 */ + || n == SSL_R_WRONG_VERSION_NUMBER /* 267 */ + || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC /* 281 */ || n == 1000 /* SSL_R_SSLV3_ALERT_CLOSE_NOTIFY */ - || n == SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE - || n == SSL_R_SSLV3_ALERT_BAD_RECORD_MAC - || n == SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE - || n == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE - || n == SSL_R_SSLV3_ALERT_BAD_CERTIFICATE - || n == SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE - || n == SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED - || n == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED - || n == SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN - || n == SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER - || n == SSL_R_TLSV1_ALERT_UNKNOWN_CA) + || n == SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE /* 1010 */ + || n == SSL_R_SSLV3_ALERT_BAD_RECORD_MAC /* 1020 */ + || n == SSL_R_TLSV1_ALERT_DECRYPTION_FAILED /* 1021 */ + || n == SSL_R_TLSV1_ALERT_RECORD_OVERFLOW /* 1022 */ + || n == SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE /* 1030 */ + || n == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE /* 1040 */ + || n == SSL_R_SSLV3_ALERT_NO_CERTIFICATE /* 1041 */ + || n == SSL_R_SSLV3_ALERT_BAD_CERTIFICATE /* 1042 */ + || n == SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE /* 1043 */ + || n == SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED /* 1044 */ + || n == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED /* 1045 */ + || n == SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN /* 1046 */ + || n == SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER /* 1047 */ + || n == SSL_R_TLSV1_ALERT_UNKNOWN_CA /* 1048 */ + || n == SSL_R_TLSV1_ALERT_ACCESS_DENIED /* 1049 */ + || n == SSL_R_TLSV1_ALERT_DECODE_ERROR /* 1050 */ + || n == SSL_R_TLSV1_ALERT_DECRYPT_ERROR /* 1051 */ + || n == SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION /* 1060 */ + || n == SSL_R_TLSV1_ALERT_PROTOCOL_VERSION /* 1070 */ + || n == SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY /* 1071 */ + || n == SSL_R_TLSV1_ALERT_INTERNAL_ERROR /* 1080 */ + || n == SSL_R_TLSV1_ALERT_USER_CANCELLED /* 1090 */ + || n == SSL_R_TLSV1_ALERT_NO_RENEGOTIATION) /* 1100 */ { switch (c->log_error) { diff --git a/src/http/modules/ngx_http_limit_req_module.c b/src/http/modules/ngx_http_limit_req_module.c --- a/src/http/modules/ngx_http_limit_req_module.c +++ b/src/http/modules/ngx_http_limit_req_module.c @@ -15,7 +15,7 @@ typedef struct { u_short len; ngx_queue_t queue; ngx_msec_t last; - float rate; + ngx_uint_t excess; /* integer value, 1 corresponds to 0.001 r/s */ u_char data[1]; } ngx_http_limit_req_node_t; @@ -24,7 +24,7 @@ typedef struct { ngx_rbtree_t *rbtree; ngx_queue_t *queue; ngx_slab_pool_t *shpool; - float rate; + ngx_uint_t rate; /* integer value, 1 corresponds to 0.001 r/s */ ngx_int_t index; ngx_str_t var; } ngx_http_limit_req_ctx_t; @@ -32,8 +32,8 @@ typedef struct { typedef struct { ngx_shm_zone_t *shm_zone; - float burst; - ngx_uint_t nodelay; /* unsigned nodelay:1 */ + ngx_uint_t burst; /* integer value, 1 corresponds to 0.001 r/s */ + ngx_uint_t nodelay;/* unsigned nodelay:1 */ } ngx_http_limit_req_conf_t; @@ -107,10 +107,10 @@ ngx_module_t ngx_http_limit_req_module static ngx_int_t ngx_http_limit_req_handler(ngx_http_request_t *r) { - float rate; size_t len, n; uint32_t hash; ngx_int_t rc; + ngx_uint_t excess; ngx_time_t *tp; ngx_rbtree_node_t *node; ngx_http_variable_value_t *vv; @@ -165,20 +165,21 @@ ngx_http_limit_req_handler(ngx_http_requ ngx_queue_insert_head(ctx->queue, &lz->queue); - rate = lz->rate; + excess = lz->excess; } else { - rate = 0.0; + excess = 0; } - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "limit_req: %i %.3f", rc, rate); + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "limit_req: %i %ui.%03ui", rc, excess / 1000, excess % 1000); if (rc == NGX_BUSY) { ngx_shmtx_unlock(&ctx->shpool->mutex); ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "limiting requests, excess: %.3f", rate); + "limiting requests, excess: %ui.%03ui", + excess / 1000, excess % 1000); return NGX_HTTP_SERVICE_UNAVAILABLE; } @@ -191,7 +192,8 @@ ngx_http_limit_req_handler(ngx_http_requ } ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, - "delaying request, excess: %.3f", rate); + "delaying request, excess: %ui.%03ui", + excess / 1000, excess % 1000); if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; @@ -199,7 +201,7 @@ ngx_http_limit_req_handler(ngx_http_requ r->read_event_handler = ngx_http_test_reading; r->write_event_handler = ngx_http_limit_req_delay; - ngx_add_timer(r->connection->write, (ngx_msec_t) (rate * 1000)); + ngx_add_timer(r->connection->write, (ngx_msec_t) excess); return NGX_AGAIN; } @@ -234,7 +236,7 @@ ngx_http_limit_req_handler(ngx_http_requ tp = ngx_timeofday(); lz->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec); - lz->rate = 0.0; + lz->excess = 0; ngx_memcpy(lz->data, vv->data, len); ngx_rbtree_insert(ctx->rbtree, node); @@ -312,7 +314,7 @@ static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lzcf, ngx_uint_t hash, u_char *data, size_t len, ngx_http_limit_req_node_t **lzp) { - ngx_int_t rc; + ngx_int_t rc, excess; ngx_time_t *tp; ngx_msec_t now; ngx_msec_int_t ms; @@ -351,21 +353,22 @@ ngx_http_limit_req_lookup(ngx_http_limit now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); ms = (ngx_msec_int_t) (now - lz->last); - lz->rate = lz->rate - ctx->rate * ngx_abs(ms) / 1000 + 1; + excess = lz->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; - if (lz->rate < 0.0) { - lz->rate = 0.0; + if (excess < 0) { + excess = 0; } + lz->excess = excess; lz->last = now; *lzp = lz; - if (lz->rate > lzcf->burst) { + if ((ngx_uint_t) excess > lzcf->burst) { return NGX_BUSY; } - if (lz->rate > 0.0) { + if (excess) { return NGX_AGAIN; } @@ -388,7 +391,7 @@ ngx_http_limit_req_lookup(ngx_http_limit static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n) { - float rate; + ngx_int_t excess; ngx_time_t *tp; ngx_msec_t now; ngx_queue_t *q; @@ -425,9 +428,9 @@ ngx_http_limit_req_expire(ngx_http_limit return; } - rate = lz->rate - ctx->rate * ms / 1000; + excess = lz->excess - ctx->rate * ms / 1000; - if (rate > 0.0) { + if (excess > 0) { return; } } @@ -641,7 +644,7 @@ ngx_http_limit_req_zone(ngx_conf_t *cf, return NGX_CONF_ERROR; } - ctx->rate = (float) rate / scale; + ctx->rate = rate * 1000 / scale; shm_zone = ngx_shared_memory_add(cf, &name, size, &ngx_http_limit_req_module); @@ -734,7 +737,7 @@ ngx_http_limit_req(ngx_conf_t *cf, ngx_c return NGX_CONF_ERROR; } - lzcf->burst = (float) burst; + lzcf->burst = burst * 1000; return NGX_CONF_OK; } diff --git a/src/http/modules/perl/nginx.pm b/src/http/modules/perl/nginx.pm --- a/src/http/modules/perl/nginx.pm +++ b/src/http/modules/perl/nginx.pm @@ -47,7 +47,7 @@ our @EXPORT = qw( HTTP_INSUFFICIENT_STORAGE ); -our $VERSION = '0.7.21'; +our $VERSION = '0.7.22'; require XSLoader; XSLoader::load('nginx', $VERSION); diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -1337,14 +1337,20 @@ ngx_http_upstream_process_header(ngx_eve return; } - if (u->buffer.last - u->buffer.pos >= (ssize_t) u->length) { - if (u->input_filter(u->input_filter_ctx, 0) == NGX_ERROR) { + n = u->buffer.last - u->buffer.pos; + + if (n) { + u->buffer.last -= n; + + if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) { ngx_http_upstream_finalize_request(r, u, NGX_ERROR); return; } - ngx_http_upstream_finalize_request(r, u, 0); - return; + if (u->length == 0) { + ngx_http_upstream_finalize_request(r, u, 0); + return; + } } rev->handler = ngx_http_upstream_process_body_in_memory; diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -488,6 +488,15 @@ ngx_http_get_variable(ngx_http_request_t return NULL; } + if (ngx_strncmp(name->data, "cookie_", 7) == 0) { + + if (ngx_http_variable_argument(r, vv, (uintptr_t) name) == NGX_OK) { + return vv; + } + + return NULL; + } + if (ngx_strncmp(name->data, "arg_", 4) == 0) { if (ngx_http_variable_argument(r, vv, (uintptr_t) name) == NGX_OK) { @@ -728,6 +737,34 @@ ngx_http_variable_unknown_header(ngx_htt static ngx_int_t +ngx_http_variable_cookie(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + ngx_str_t *name = (ngx_str_t *) data; + + ngx_str_t cookie, s; + + s.len = name->len - (sizeof("cookie_") - 1); + s.data = name->data + sizeof("cookie_") - 1; + + if (ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &s, &cookie) + == NGX_DECLINED) + { + v->not_found = 1; + return NGX_OK; + } + + v->len = cookie.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = cookie.data; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_variable_argument(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { @@ -1544,6 +1581,13 @@ ngx_http_variables_init_vars(ngx_conf_t continue; } + if (ngx_strncmp(v[i].name.data, "cookie_", 7) == 0) { + v[i].get_handler = ngx_http_variable_cookie; + v[i].data = (uintptr_t) &v[i].name; + + continue; + } + if (ngx_strncmp(v[i].name.data, "arg_", 4) == 0) { v[i].get_handler = ngx_http_variable_argument; v[i].data = (uintptr_t) &v[i].name; diff --git a/src/mail/ngx_mail.h b/src/mail/ngx_mail.h --- a/src/mail/ngx_mail.h +++ b/src/mail/ngx_mail.h @@ -136,8 +136,12 @@ typedef enum { ngx_smtp_auth_plain, ngx_smtp_auth_cram_md5, ngx_smtp_helo, - ngx_smtp_noxclient, - ngx_smtp_xclient + ngx_smtp_helo_xclient, + ngx_smtp_helo_from, + ngx_smtp_xclient, + ngx_smtp_xclient_from, + ngx_smtp_from, + ngx_smtp_to } ngx_smtp_state_e; @@ -173,7 +177,7 @@ typedef struct { unsigned no_sync_literal:1; unsigned starttls:1; unsigned esmtp:1; - unsigned auth_method:2; + unsigned auth_method:3; unsigned auth_wait:1; ngx_str_t login; @@ -187,6 +191,8 @@ typedef struct { ngx_str_t *addr_text; ngx_str_t host; ngx_str_t smtp_helo; + ngx_str_t smtp_from; + ngx_str_t smtp_to; ngx_uint_t command; ngx_array_t args; @@ -256,12 +262,14 @@ typedef struct { #define NGX_MAIL_AUTH_LOGIN 1 #define NGX_MAIL_AUTH_APOP 2 #define NGX_MAIL_AUTH_CRAM_MD5 3 +#define NGX_MAIL_AUTH_NONE 4 #define NGX_MAIL_AUTH_PLAIN_ENABLED 0x0002 #define NGX_MAIL_AUTH_LOGIN_ENABLED 0x0004 #define NGX_MAIL_AUTH_APOP_ENABLED 0x0008 #define NGX_MAIL_AUTH_CRAM_MD5_ENABLED 0x0010 +#define NGX_MAIL_AUTH_NONE_ENABLED 0x0020 #define NGX_MAIL_PARSE_INVALID_COMMAND 20 diff --git a/src/mail/ngx_mail_auth_http_module.c b/src/mail/ngx_mail_auth_http_module.c --- a/src/mail/ngx_mail_auth_http_module.c +++ b/src/mail/ngx_mail_auth_http_module.c @@ -40,7 +40,6 @@ struct ngx_mail_auth_http_ctx_s { ngx_mail_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; @@ -141,7 +140,8 @@ static ngx_str_t ngx_mail_auth_http_me ngx_string("plain"), ngx_string("plain"), ngx_string("apop"), - ngx_string("cram-md5") + ngx_string("cram-md5"), + ngx_string("none") }; static ngx_str_t ngx_mail_smtp_errcode = ngx_string("535 5.7.0"); @@ -914,7 +914,6 @@ ngx_mail_auth_http_parse_header_line(ngx ngx_mail_auth_http_ctx_t *ctx) { u_char c, ch, *p; - ngx_uint_t hash; enum { sw_start = 0, sw_name, @@ -926,7 +925,6 @@ ngx_mail_auth_http_parse_header_line(ngx } state; state = ctx->state; - hash = ctx->hash; for (p = ctx->response->pos; p < ctx->response->last; p++) { ch = *p; @@ -950,12 +948,10 @@ ngx_mail_auth_http_parse_header_line(ngx c = (u_char) (ch | 0x20); if (c >= 'a' && c <= 'z') { - hash = c; break; } if (ch >= '0' && ch <= '9') { - hash = ch; break; } @@ -967,7 +963,6 @@ ngx_mail_auth_http_parse_header_line(ngx case sw_name: c = (u_char) (ch | 0x20); if (c >= 'a' && c <= 'z') { - hash += c; break; } @@ -978,12 +973,10 @@ ngx_mail_auth_http_parse_header_line(ngx } if (ch == '-') { - hash += ch; break; } if (ch >= '0' && ch <= '9') { - hash += ch; break; } @@ -1080,7 +1073,6 @@ ngx_mail_auth_http_parse_header_line(ngx ctx->response->pos = p; ctx->state = state; - ctx->hash = hash; return NGX_AGAIN; @@ -1088,7 +1080,6 @@ done: ctx->response->pos = p + 1; ctx->state = sw_start; - ctx->hash = hash; return NGX_OK; @@ -1165,6 +1156,10 @@ ngx_mail_auth_http_create_request(ngx_ma + sizeof(CRLF) - 1 + sizeof("Client-IP: ") - 1 + s->connection->addr_text.len + sizeof(CRLF) - 1 + + sizeof("Client-Host: ") - 1 + s->host.len + sizeof(CRLF) - 1 + + sizeof("Auth-SMTP-Helo: ") - 1 + s->smtp_helo.len + + sizeof("Auth-SMTP-From: ") - 1 + s->smtp_from.len + + sizeof("Auth-SMTP-To: ") - 1 + s->smtp_to.len + ahcf->header.len + sizeof(CRLF) - 1; @@ -1216,9 +1211,37 @@ ngx_mail_auth_http_create_request(ngx_ma b->last = ngx_cpymem(b->last, "Client-IP: ", sizeof("Client-IP: ") - 1); b->last = ngx_copy(b->last, s->connection->addr_text.data, - s->connection->addr_text.len); + s->connection->addr_text.len); *b->last++ = CR; *b->last++ = LF; + if (s->host.len) { + b->last = ngx_cpymem(b->last, "Client-Host: ", + sizeof("Client-Host: ") - 1); + b->last = ngx_copy(b->last, s->host.data, s->host.len); + *b->last++ = CR; *b->last++ = LF; + } + + if (s->auth_method == NGX_MAIL_AUTH_NONE) { + + /* HELO, MAIL FROM, and RCPT TO can't contain CRLF, no need to escape */ + + b->last = ngx_cpymem(b->last, "Auth-SMTP-Helo: ", + sizeof("Auth-SMTP-Helo: ") - 1); + b->last = ngx_copy(b->last, s->smtp_helo.data, s->smtp_helo.len); + *b->last++ = CR; *b->last++ = LF; + + b->last = ngx_cpymem(b->last, "Auth-SMTP-From: ", + sizeof("Auth-SMTP-From: ") - 1); + b->last = ngx_copy(b->last, s->smtp_from.data, s->smtp_from.len); + *b->last++ = CR; *b->last++ = LF; + + b->last = ngx_cpymem(b->last, "Auth-SMTP-To: ", + sizeof("Auth-SMTP-To: ") - 1); + b->last = ngx_copy(b->last, s->smtp_to.data, s->smtp_to.len); + *b->last++ = CR; *b->last++ = LF; + + } + if (ahcf->header.len) { b->last = ngx_copy(b->last, ahcf->header.data, ahcf->header.len); } diff --git a/src/mail/ngx_mail_imap_module.c b/src/mail/ngx_mail_imap_module.c --- a/src/mail/ngx_mail_imap_module.c +++ b/src/mail/ngx_mail_imap_module.c @@ -36,7 +36,8 @@ static ngx_str_t ngx_mail_imap_auth_met ngx_string("AUTH=PLAIN"), ngx_string("AUTH=LOGIN"), ngx_null_string, /* APOP */ - ngx_string("AUTH=CRAM-MD5") + ngx_string("AUTH=CRAM-MD5"), + ngx_null_string /* NONE */ }; diff --git a/src/mail/ngx_mail_parse.c b/src/mail/ngx_mail_parse.c --- a/src/mail/ngx_mail_parse.c +++ b/src/mail/ngx_mail_parse.c @@ -746,7 +746,7 @@ ngx_mail_smtp_parse_command(ngx_mail_ses s->arg_end = p; goto done; default: - if (s->args.nelts <= 2) { + if (s->args.nelts <= 10) { state = sw_argument; s->arg_start = p; break; diff --git a/src/mail/ngx_mail_proxy_module.c b/src/mail/ngx_mail_proxy_module.c --- a/src/mail/ngx_mail_proxy_module.c +++ b/src/mail/ngx_mail_proxy_module.c @@ -104,7 +104,7 @@ ngx_module_t ngx_mail_proxy_module = { }; -static u_char smtp_ok[] = "235 2.0.0 OK" CRLF; +static u_char smtp_ok[] = "250 2.0.0 OK" CRLF; void @@ -465,6 +465,7 @@ ngx_mail_proxy_smtp_handler(ngx_event_t u_char *p; ngx_int_t rc; ngx_str_t line; + ngx_buf_t *b; ngx_connection_t *c; ngx_mail_session_t *s; ngx_mail_proxy_conf_t *pcf; @@ -520,11 +521,19 @@ ngx_mail_proxy_smtp_handler(ngx_event_t p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len); *p++ = CR; *p = LF; - s->mail_state = pcf->xclient ? ngx_smtp_helo: ngx_smtp_noxclient; + if (pcf->xclient) { + s->mail_state = ngx_smtp_helo_xclient; + + } else if (s->auth_method == NGX_MAIL_AUTH_NONE) { + s->mail_state = ngx_smtp_helo_from; + + } else { + s->mail_state = ngx_smtp_helo; + } break; - case ngx_smtp_helo: + case ngx_smtp_helo_xclient: ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send xclient"); @@ -541,30 +550,73 @@ ngx_mail_proxy_smtp_handler(ngx_event_t return; } - if (s->smtp_helo.len) { - line.len = ngx_sprintf(line.data, - "XCLIENT PROTO=%sSMTP HELO=%V ADDR=%V LOGIN=%V " - "NAME=%V" CRLF, - (s->esmtp ? "E" : ""), &s->smtp_helo, - &s->connection->addr_text, &s->login, &s->host) - - line.data; - } else { - line.len = ngx_sprintf(line.data, - "XCLIENT PROTO=SMTP ADDR=%V LOGIN=%V NAME=%V" CRLF, - &s->connection->addr_text, &s->login, &s->host) - - line.data; + line.len = ngx_sprintf(line.data, + "XCLIENT PROTO=%sSMTP%s%V ADDR=%V%s%V NAME=%V" CRLF, + (s->esmtp ? "E" : ""), + (s->smtp_helo.len ? " HELO=" : ""), &s->smtp_helo, + &s->connection->addr_text, + (s->login.len ? " LOGIN=" : ""), &s->login, &s->host) + - line.data; + + s->mail_state = (s->auth_method == NGX_MAIL_AUTH_NONE) ? + ngx_smtp_xclient_from : ngx_smtp_xclient; + + break; + + case ngx_smtp_helo_from: + case ngx_smtp_xclient_from: + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, + "mail proxy send mail from"); + + s->connection->log->action = "sending MAIL FROM to upstream"; + + line.len = s->smtp_from.len + sizeof(CRLF) - 1; + line.data = ngx_pnalloc(c->pool, line.len); + if (line.data == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; } - s->mail_state = ngx_smtp_xclient; + p = ngx_cpymem(line.data, s->smtp_from.data, s->smtp_from.len); + *p++ = CR; *p = LF; + + s->mail_state = ngx_smtp_from; + break; - case ngx_smtp_noxclient: - case ngx_smtp_xclient: + case ngx_smtp_from: + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, + "mail proxy send rcpt to"); + + s->connection->log->action = "sending RCPT TO to upstream"; + + line.len = s->smtp_to.len + sizeof(CRLF) - 1; + line.data = ngx_pnalloc(c->pool, line.len); + if (line.data == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + p = ngx_cpymem(line.data, s->smtp_to.data, s->smtp_to.len); + *p++ = CR; *p = LF; - ngx_memcpy(s->proxy->buffer->start, smtp_ok, sizeof(smtp_ok) - 1); + s->mail_state = ngx_smtp_to; + + break; + + case ngx_smtp_helo: + case ngx_smtp_xclient: + case ngx_smtp_to: - s->proxy->buffer->pos = s->proxy->buffer->start; - s->proxy->buffer->last = s->proxy->buffer->start + sizeof(smtp_ok) - 1; + b = s->proxy->buffer; + + if (s->auth_method == NGX_MAIL_AUTH_NONE) { + b->pos = b->start; + + } else { + ngx_memcpy(b->start, smtp_ok, sizeof(smtp_ok) - 1); + b->last = b->start + sizeof(smtp_ok) - 1; + } s->connection->read->handler = ngx_mail_proxy_handler; s->connection->write->handler = ngx_mail_proxy_handler; @@ -703,19 +755,30 @@ ngx_mail_proxy_read_response(ngx_mail_se default: /* NGX_MAIL_SMTP_PROTOCOL */ switch (state) { + case ngx_smtp_start: + if (p[0] == '2' && p[1] == '2' && p[2] == '0') { + return NGX_OK; + } + break; + case ngx_smtp_helo: - case ngx_smtp_noxclient: + case ngx_smtp_helo_xclient: + case ngx_smtp_helo_from: + case ngx_smtp_from: if (p[0] == '2' && p[1] == '5' && p[2] == '0') { return NGX_OK; } break; - case ngx_smtp_start: case ngx_smtp_xclient: - if (p[0] == '2' && p[1] == '2' && p[2] == '0') { + case ngx_smtp_xclient_from: + if (p[0] == '2' && (p[1] == '2' || p[1] == '5') && p[2] == '0') { return NGX_OK; } break; + + case ngx_smtp_to: + return NGX_OK; } break; diff --git a/src/mail/ngx_mail_smtp_handler.c b/src/mail/ngx_mail_smtp_handler.c --- a/src/mail/ngx_mail_smtp_handler.c +++ b/src/mail/ngx_mail_smtp_handler.c @@ -23,6 +23,8 @@ static ngx_int_t ngx_mail_smtp_auth(ngx_ static ngx_int_t ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c); static ngx_int_t ngx_mail_smtp_starttls(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c); static ngx_int_t ngx_mail_smtp_discard_command(ngx_mail_session_t *s, ngx_connection_t *c, char *err); @@ -41,6 +43,7 @@ static u_char smtp_invalid_pipelining[] "503 5.5.0 Improper use of SMTP command pipelining" CRLF; static u_char smtp_invalid_argument[] = "501 5.5.4 Invalid argument" CRLF; static u_char smtp_auth_required[] = "530 5.7.1 Authentication required" CRLF; +static u_char smtp_bad_sequence[] = "503 5.5.1 Bad sequence of commands" CRLF; static ngx_str_t smtp_unavailable = ngx_string("[UNAVAILABLE]"); @@ -417,8 +420,15 @@ ngx_mail_smtp_auth_state(ngx_event_t *re rc = ngx_mail_smtp_mail(s, c); break; + case NGX_SMTP_RCPT: + rc = ngx_mail_smtp_rcpt(s, c); + break; + + case NGX_SMTP_RSET: + rc = ngx_mail_smtp_rset(s, c); + break; + case NGX_SMTP_NOOP: - case NGX_SMTP_RSET: break; case NGX_SMTP_STARTTLS: @@ -513,6 +523,11 @@ ngx_mail_smtp_helo(ngx_mail_session_t *s ngx_memcpy(s->smtp_helo.data, arg[0].data, arg[0].len); + s->smtp_from.len = 0; + s->smtp_from.data = NULL; + s->smtp_to.len = 0; + s->smtp_to.data = NULL; + sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); if (s->command == NGX_SMTP_HELO) { @@ -618,10 +633,136 @@ ngx_mail_smtp_auth(ngx_mail_session_t *s static ngx_int_t ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c) { - ngx_mail_smtp_log_rejected_command(s, c, "client was rejected: \"%V\""); + u_char ch; + ngx_str_t l; + ngx_uint_t i; + ngx_mail_smtp_srv_conf_t *sscf; + + sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); + + if (!(sscf->auth_methods & NGX_MAIL_AUTH_NONE_ENABLED)) { + ngx_mail_smtp_log_rejected_command(s, c, "client was rejected: \"%V\""); + + s->out.len = sizeof(smtp_auth_required) - 1; + s->out.data = smtp_auth_required; + + return NGX_OK; + } + + /* auth none */ + + if (s->smtp_from.len) { + s->out.len = sizeof(smtp_bad_sequence) - 1; + s->out.data = smtp_bad_sequence; + return NGX_OK; + } + + l.len = s->buffer->last - s->buffer->start; + l.data = s->buffer->start; + + for (i = 0; i < l.len; i++) { + ch = l.data[i]; + + if (ch != CR && ch != LF) { + continue; + } + + l.data[i] = ' '; + } + + while (i) { + if (l.data[i - 1] != ' ') { + break; + } + + i--; + } + + l.len = i; + + s->smtp_from.len = l.len; + + s->smtp_from.data = ngx_pnalloc(c->pool, l.len); + if (s->smtp_from.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->smtp_from.data, l.data, l.len); + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "smtp mail from:\"%V\"", &s->smtp_from); + + s->out.len = sizeof(smtp_ok) - 1; + s->out.data = smtp_ok; + + return NGX_OK; +} - s->out.len = sizeof(smtp_auth_required) - 1; - s->out.data = smtp_auth_required; + +static ngx_int_t +ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c) +{ + u_char ch; + ngx_str_t l; + ngx_uint_t i; + + if (s->smtp_from.len == 0) { + s->out.len = sizeof(smtp_bad_sequence) - 1; + s->out.data = smtp_bad_sequence; + return NGX_OK; + } + + l.len = s->buffer->last - s->buffer->start; + l.data = s->buffer->start; + + for (i = 0; i < l.len; i++) { + ch = l.data[i]; + + if (ch != CR && ch != LF) { + continue; + } + + l.data[i] = ' '; + } + + while (i) { + if (l.data[i - 1] != ' ') { + break; + } + + i--; + } + + l.len = i; + + s->smtp_to.len = l.len; + + s->smtp_to.data = ngx_pnalloc(c->pool, l.len); + if (s->smtp_to.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->smtp_to.data, l.data, l.len); + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "smtp rcpt to:\"%V\"", &s->smtp_to); + + s->auth_method = NGX_MAIL_AUTH_NONE; + + return NGX_DONE; +} + + +static ngx_int_t +ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c) +{ + s->smtp_from.len = 0; + s->smtp_from.data = NULL; + s->smtp_to.len = 0; + s->smtp_to.data = NULL; + + s->out.len = sizeof(smtp_ok) - 1; + s->out.data = smtp_ok; return NGX_OK; } @@ -644,6 +785,10 @@ ngx_mail_smtp_starttls(ngx_mail_session_ s->smtp_helo.len = 0; s->smtp_helo.data = NULL; + s->smtp_from.len = 0; + s->smtp_from.data = NULL; + s->smtp_to.len = 0; + s->smtp_to.data = NULL; c->read->handler = ngx_mail_starttls_handler; return NGX_OK; diff --git a/src/mail/ngx_mail_smtp_module.c b/src/mail/ngx_mail_smtp_module.c --- a/src/mail/ngx_mail_smtp_module.c +++ b/src/mail/ngx_mail_smtp_module.c @@ -20,6 +20,7 @@ static ngx_conf_bitmask_t ngx_mail_smtp { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED }, { ngx_string("login"), NGX_MAIL_AUTH_LOGIN_ENABLED }, { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED }, + { ngx_string("none"), NGX_MAIL_AUTH_NONE_ENABLED }, { ngx_null_string, 0 } }; @@ -28,7 +29,8 @@ static ngx_str_t ngx_mail_smtp_auth_met ngx_string("PLAIN"), ngx_string("LOGIN"), ngx_null_string, /* APOP */ - ngx_string("CRAM-MD5") + ngx_string("CRAM-MD5"), + ngx_null_string /* NONE */ }; @@ -136,10 +138,10 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t ngx_mail_smtp_srv_conf_t *prev = parent; ngx_mail_smtp_srv_conf_t *conf = child; - u_char *p, *auth; + u_char *p, *auth, *last; size_t size; ngx_str_t *c; - ngx_uint_t i, m; + ngx_uint_t i, m, auth_enabled; ngx_mail_core_srv_conf_t *cscf; ngx_conf_merge_size_value(conf->client_buffer_size, @@ -192,23 +194,29 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t conf->capabilities = prev->capabilities; } - size = sizeof("250-") - 1 + cscf->server_name.len + sizeof(CRLF) - 1 - + sizeof("250 AUTH") - 1 + sizeof(CRLF) - 1; + size = sizeof("250-") - 1 + cscf->server_name.len + sizeof(CRLF) - 1; c = conf->capabilities.elts; for (i = 0; i < conf->capabilities.nelts; i++) { size += sizeof("250 ") - 1 + c[i].len + sizeof(CRLF) - 1; } + auth_enabled = 0; + for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED; m <<= 1, i++) { if (m & conf->auth_methods) { size += 1 + ngx_mail_smtp_auth_methods_names[i].len; + auth_enabled = 1; } } + if (auth_enabled) { + size += sizeof("250 AUTH") - 1 + sizeof(CRLF) - 1; + } + p = ngx_pnalloc(cf->pool, size); if (p == NULL) { return NGX_CONF_ERROR; @@ -217,11 +225,14 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t conf->capability.len = size; conf->capability.data = p; + last = p; + *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-'; p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len); *p++ = CR; *p++ = LF; for (i = 0; i < conf->capabilities.nelts; i++) { + last = p; *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-'; p = ngx_cpymem(p, c[i].data, c[i].len); *p++ = CR; *p++ = LF; @@ -229,21 +240,28 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t auth = p; - *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' '; - *p++ = 'A'; *p++ = 'U'; *p++ = 'T'; *p++ = 'H'; + if (auth_enabled) { + last = p; + + *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' '; + *p++ = 'A'; *p++ = 'U'; *p++ = 'T'; *p++ = 'H'; - for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; - m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED; - m <<= 1, i++) - { - if (m & conf->auth_methods) { - *p++ = ' '; - p = ngx_cpymem(p, ngx_mail_smtp_auth_methods_names[i].data, - ngx_mail_smtp_auth_methods_names[i].len); + for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; + m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED; + m <<= 1, i++) + { + if (m & conf->auth_methods) { + *p++ = ' '; + p = ngx_cpymem(p, ngx_mail_smtp_auth_methods_names[i].data, + ngx_mail_smtp_auth_methods_names[i].len); + } } - } + + *p++ = CR; *p = LF; - *p++ = CR; *p = LF; + } else { + last[3] = ' '; + } size += sizeof("250 STARTTLS" CRLF) - 1; @@ -255,14 +273,13 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t conf->starttls_capability.len = size; conf->starttls_capability.data = p; - p = ngx_cpymem(p, conf->capability.data, - conf->capability.len); + p = ngx_cpymem(p, conf->capability.data, conf->capability.len); p = ngx_cpymem(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1); *p++ = CR; *p = LF; p = conf->starttls_capability.data - + (auth - conf->capability.data) + 3; + + (last - conf->capability.data) + 3; *p = '-'; size = (auth - conf->capability.data) @@ -276,10 +293,15 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t conf->starttls_only_capability.len = size; conf->starttls_only_capability.data = p; - p = ngx_cpymem(p, conf->capability.data, - auth - conf->capability.data); + p = ngx_cpymem(p, conf->capability.data, auth - conf->capability.data); ngx_memcpy(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1); + if (last < auth) { + p = conf->starttls_only_capability.data + + (last - conf->capability.data) + 3; + *p = '-'; + } + return NGX_CONF_OK; }