summaryrefslogtreecommitdiffhomepage
path: root/src/mail
diff options
context:
space:
mode:
authorSergey Kandaurov <pluknet@nginx.com>2021-05-28 13:33:08 +0300
committerSergey Kandaurov <pluknet@nginx.com>2021-05-28 13:33:08 +0300
commitb2b8637f98698fa8795079922d6227a2d5a3a0ad (patch)
treeb61cb2817764a4c1b49d1e9c42f31f0c834bfeb8 /src/mail
parent03fcff287db0d6b620f837de95116ad3a3b7e1e9 (diff)
parent798813e96b0a948b4713e92b67ecae8116f9d08f (diff)
downloadnginx-b2b8637f98698fa8795079922d6227a2d5a3a0ad.tar.gz
nginx-b2b8637f98698fa8795079922d6227a2d5a3a0ad.tar.bz2
Merged with the default branch.
Diffstat (limited to 'src/mail')
-rw-r--r--src/mail/ngx_mail.h5
-rw-r--r--src/mail/ngx_mail_core_module.c10
-rw-r--r--src/mail/ngx_mail_handler.c40
-rw-r--r--src/mail/ngx_mail_imap_handler.c43
-rw-r--r--src/mail/ngx_mail_parse.c175
-rw-r--r--src/mail/ngx_mail_pop3_handler.c15
-rw-r--r--src/mail/ngx_mail_proxy_module.c17
7 files changed, 189 insertions, 116 deletions
diff --git a/src/mail/ngx_mail.h b/src/mail/ngx_mail.h
index b865a3b9e..21178c3e2 100644
--- a/src/mail/ngx_mail.h
+++ b/src/mail/ngx_mail.h
@@ -115,6 +115,8 @@ typedef struct {
ngx_msec_t timeout;
ngx_msec_t resolver_timeout;
+ ngx_uint_t max_errors;
+
ngx_str_t server_name;
u_char *file_name;
@@ -231,14 +233,15 @@ typedef struct {
ngx_uint_t command;
ngx_array_t args;
+ ngx_uint_t errors;
ngx_uint_t login_attempt;
/* used to parse POP3/IMAP/SMTP command */
ngx_uint_t state;
+ u_char *tag_start;
u_char *cmd_start;
u_char *arg_start;
- u_char *arg_end;
ngx_uint_t literal_len;
} ngx_mail_session_t;
diff --git a/src/mail/ngx_mail_core_module.c b/src/mail/ngx_mail_core_module.c
index 408312423..115671ca4 100644
--- a/src/mail/ngx_mail_core_module.c
+++ b/src/mail/ngx_mail_core_module.c
@@ -85,6 +85,13 @@ static ngx_command_t ngx_mail_core_commands[] = {
offsetof(ngx_mail_core_srv_conf_t, resolver_timeout),
NULL },
+ { ngx_string("max_errors"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_core_srv_conf_t, max_errors),
+ NULL },
+
ngx_null_command
};
@@ -163,6 +170,8 @@ ngx_mail_core_create_srv_conf(ngx_conf_t *cf)
cscf->timeout = NGX_CONF_UNSET_MSEC;
cscf->resolver_timeout = NGX_CONF_UNSET_MSEC;
+ cscf->max_errors = NGX_CONF_UNSET_UINT;
+
cscf->resolver = NGX_CONF_UNSET_PTR;
cscf->file_name = cf->conf_file->file.name.data;
@@ -182,6 +191,7 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_msec_value(conf->resolver_timeout, prev->resolver_timeout,
30000);
+ ngx_conf_merge_uint_value(conf->max_errors, prev->max_errors, 5);
ngx_conf_merge_str_value(conf->server_name, prev->server_name, "");
diff --git a/src/mail/ngx_mail_handler.c b/src/mail/ngx_mail_handler.c
index 0aaa0e786..246ba97cf 100644
--- a/src/mail/ngx_mail_handler.c
+++ b/src/mail/ngx_mail_handler.c
@@ -833,20 +833,23 @@ ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c)
ngx_str_t l;
ngx_mail_core_srv_conf_t *cscf;
- n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last);
+ if (s->buffer->last < s->buffer->end) {
- if (n == NGX_ERROR || n == 0) {
- ngx_mail_close_connection(c);
- return NGX_ERROR;
- }
+ n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last);
- if (n > 0) {
- s->buffer->last += n;
- }
+ if (n == NGX_ERROR || n == 0) {
+ ngx_mail_close_connection(c);
+ return NGX_ERROR;
+ }
+
+ if (n > 0) {
+ s->buffer->last += n;
+ }
- if (n == NGX_AGAIN) {
- if (s->buffer->pos == s->buffer->last) {
- return NGX_AGAIN;
+ if (n == NGX_AGAIN) {
+ if (s->buffer->pos == s->buffer->last) {
+ return NGX_AGAIN;
+ }
}
}
@@ -871,7 +874,20 @@ ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c)
return NGX_MAIL_PARSE_INVALID_COMMAND;
}
- if (rc == NGX_IMAP_NEXT || rc == NGX_MAIL_PARSE_INVALID_COMMAND) {
+ if (rc == NGX_MAIL_PARSE_INVALID_COMMAND) {
+
+ s->errors++;
+
+ if (s->errors >= cscf->max_errors) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent too many invalid commands");
+ s->quit = 1;
+ }
+
+ return rc;
+ }
+
+ if (rc == NGX_IMAP_NEXT) {
return rc;
}
diff --git a/src/mail/ngx_mail_imap_handler.c b/src/mail/ngx_mail_imap_handler.c
index 5dfdd7601..291e87a4d 100644
--- a/src/mail/ngx_mail_imap_handler.c
+++ b/src/mail/ngx_mail_imap_handler.c
@@ -101,10 +101,9 @@ ngx_mail_imap_init_protocol(ngx_event_t *rev)
void
ngx_mail_imap_auth_state(ngx_event_t *rev)
{
- u_char *p, *dst, *src, *end;
- ngx_str_t *arg;
+ u_char *p;
ngx_int_t rc;
- ngx_uint_t tag, i;
+ ngx_uint_t tag;
ngx_connection_t *c;
ngx_mail_session_t *s;
@@ -158,27 +157,6 @@ ngx_mail_imap_auth_state(ngx_event_t *rev)
ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap auth command: %i",
s->command);
- if (s->backslash) {
-
- arg = s->args.elts;
-
- for (i = 0; i < s->args.nelts; i++) {
- dst = arg[i].data;
- end = dst + arg[i].len;
-
- for (src = dst; src < end; dst++) {
- *dst = *src;
- if (*src++ == '\\') {
- *dst = *src++;
- }
- }
-
- arg[i].len = dst - arg[i].data;
- }
-
- s->backslash = 0;
- }
-
switch (s->mail_state) {
case ngx_imap_start:
@@ -248,6 +226,10 @@ ngx_mail_imap_auth_state(ngx_event_t *rev)
ngx_str_set(&s->out, imap_next);
}
+ if (s->buffer->pos < s->buffer->last) {
+ s->blocked = 1;
+ }
+
switch (rc) {
case NGX_DONE:
@@ -297,13 +279,14 @@ ngx_mail_imap_auth_state(ngx_event_t *rev)
if (s->state) {
/* preserve tag */
- s->arg_start = s->buffer->start + s->tag.len;
- s->buffer->pos = s->arg_start;
- s->buffer->last = s->arg_start;
+ s->arg_start = s->buffer->pos;
} else {
- s->buffer->pos = s->buffer->start;
- s->buffer->last = s->buffer->start;
+ if (s->buffer->pos == s->buffer->last) {
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
+ }
+
s->tag.len = 0;
}
}
@@ -481,6 +464,8 @@ ngx_mail_imap_starttls(ngx_mail_session_t *s, ngx_connection_t *c)
if (c->ssl == NULL) {
sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
if (sslcf->starttls) {
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
c->read->handler = ngx_mail_starttls_handler;
return NGX_OK;
}
diff --git a/src/mail/ngx_mail_parse.c b/src/mail/ngx_mail_parse.c
index 2c2cdffa1..4db1f18d3 100644
--- a/src/mail/ngx_mail_parse.c
+++ b/src/mail/ngx_mail_parse.c
@@ -21,6 +21,8 @@ ngx_mail_pop3_parse_command(ngx_mail_session_t *s)
ngx_str_t *arg;
enum {
sw_start = 0,
+ sw_command,
+ sw_invalid,
sw_spaces_before_argument,
sw_argument,
sw_almost_done
@@ -35,8 +37,14 @@ ngx_mail_pop3_parse_command(ngx_mail_session_t *s)
/* POP3 command */
case sw_start:
+ s->cmd_start = p;
+ state = sw_command;
+
+ /* fall through */
+
+ case sw_command:
if (ch == ' ' || ch == CR || ch == LF) {
- c = s->buffer->start;
+ c = s->cmd_start;
if (p - c == 4) {
@@ -85,6 +93,9 @@ ngx_mail_pop3_parse_command(ngx_mail_session_t *s)
goto invalid;
}
+ s->cmd.data = s->cmd_start;
+ s->cmd.len = p - s->cmd_start;
+
switch (ch) {
case ' ':
state = sw_spaces_before_argument;
@@ -104,16 +115,17 @@ ngx_mail_pop3_parse_command(ngx_mail_session_t *s)
break;
+ case sw_invalid:
+ goto invalid;
+
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) {
@@ -188,37 +200,39 @@ ngx_mail_pop3_parse_command(ngx_mail_session_t *s)
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 = (s->command != NGX_POP3_AUTH) ? sw_start : sw_argument;
return NGX_OK;
invalid:
- s->state = sw_start;
- s->arg_start = NULL;
+ s->state = sw_invalid;
- return NGX_MAIL_PARSE_INVALID_COMMAND;
+ /* skip invalid command till LF */
+
+ for ( /* void */ ; p < s->buffer->last; p++) {
+ if (*p == LF) {
+ s->state = sw_start;
+ s->buffer->pos = p + 1;
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+ }
+
+ s->buffer->pos = p;
+
+ return NGX_AGAIN;
}
ngx_int_t
ngx_mail_imap_parse_command(ngx_mail_session_t *s)
{
- u_char ch, *p, *c;
+ u_char ch, *p, *c, *dst, *src, *end;
ngx_str_t *arg;
enum {
sw_start = 0,
+ sw_tag,
+ sw_invalid,
sw_spaces_before_command,
sw_command,
sw_spaces_before_argument,
@@ -241,31 +255,45 @@ ngx_mail_imap_parse_command(ngx_mail_session_t *s)
/* IMAP tag */
case sw_start:
+ s->tag_start = p;
+ state = sw_tag;
+
+ /* fall through */
+
+ case sw_tag:
switch (ch) {
case ' ':
- s->tag.len = p - s->buffer->start + 1;
- s->tag.data = s->buffer->start;
+ s->tag.len = p - s->tag_start + 1;
+ s->tag.data = s->tag_start;
state = sw_spaces_before_command;
break;
case CR:
- s->state = sw_start;
- return NGX_MAIL_PARSE_INVALID_COMMAND;
case LF:
- s->state = sw_start;
- return NGX_MAIL_PARSE_INVALID_COMMAND;
+ goto invalid;
+ default:
+ if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')
+ && (ch < '0' || ch > '9') && ch != '-' && ch != '.'
+ && ch != '_')
+ {
+ goto invalid;
+ }
+ if (p - s->tag_start > 31) {
+ goto invalid;
+ }
+ break;
}
break;
+ case sw_invalid:
+ goto invalid;
+
case sw_spaces_before_command:
switch (ch) {
case ' ':
break;
case CR:
- s->state = sw_start;
- return NGX_MAIL_PARSE_INVALID_COMMAND;
case LF:
- s->state = sw_start;
- return NGX_MAIL_PARSE_INVALID_COMMAND;
+ goto invalid;
default:
s->cmd_start = p;
state = sw_command;
@@ -385,6 +413,9 @@ ngx_mail_imap_parse_command(ngx_mail_session_t *s)
goto invalid;
}
+ s->cmd.data = s->cmd_start;
+ s->cmd.len = p - s->cmd_start;
+
switch (ch) {
case ' ':
state = sw_spaces_before_argument;
@@ -410,10 +441,8 @@ ngx_mail_imap_parse_command(ngx_mail_session_t *s)
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) {
@@ -460,6 +489,22 @@ ngx_mail_imap_parse_command(ngx_mail_session_t *s)
}
arg->len = p - s->arg_start;
arg->data = s->arg_start;
+
+ if (s->backslash) {
+ dst = s->arg_start;
+ end = p;
+
+ for (src = dst; src < end; dst++) {
+ *dst = *src;
+ if (*src++ == '\\') {
+ *dst = *src++;
+ }
+ }
+
+ arg->len = dst - s->arg_start;
+ s->backslash = 0;
+ }
+
s->arg_start = NULL;
switch (ch) {
@@ -588,34 +633,46 @@ ngx_mail_imap_parse_command(ngx_mail_session_t *s)
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->no_sync_literal = 0;
- s->literal_len = 0;
- }
-
s->state = (s->command != NGX_IMAP_AUTHENTICATE) ? sw_start : sw_argument;
return NGX_OK;
invalid:
- s->state = sw_start;
+ s->state = sw_invalid;
s->quoted = 0;
+ s->backslash = 0;
s->no_sync_literal = 0;
s->literal_len = 0;
- return NGX_MAIL_PARSE_INVALID_COMMAND;
+ /* skip invalid command till LF */
+
+ for ( /* void */ ; p < s->buffer->last; p++) {
+ if (*p == LF) {
+ s->state = sw_start;
+ s->buffer->pos = p + 1;
+
+ /* detect non-synchronizing literals */
+
+ if ((size_t) (p - s->buffer->start) > sizeof("{1+}") - 1) {
+ p--;
+
+ if (*p == CR) {
+ p--;
+ }
+
+ if (*p == '}' && *(p - 1) == '+') {
+ s->quit = 1;
+ }
+ }
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+ }
+
+ s->buffer->pos = p;
+
+ return NGX_AGAIN;
}
@@ -758,10 +815,8 @@ ngx_mail_smtp_parse_command(ngx_mail_session_t *s)
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 <= 10) {
@@ -821,17 +876,6 @@ ngx_mail_smtp_parse_command(ngx_mail_session_t *s)
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 = (s->command != NGX_SMTP_AUTH) ? sw_start : sw_argument;
return NGX_OK;
@@ -839,21 +883,20 @@ done:
invalid:
s->state = sw_invalid;
- s->arg_start = NULL;
/* skip invalid command till LF */
- for (p = s->buffer->pos; p < s->buffer->last; p++) {
+ for ( /* void */ ; p < s->buffer->last; p++) {
if (*p == LF) {
s->state = sw_start;
- p++;
- break;
+ s->buffer->pos = p + 1;
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
}
}
s->buffer->pos = p;
- return NGX_MAIL_PARSE_INVALID_COMMAND;
+ return NGX_AGAIN;
}
diff --git a/src/mail/ngx_mail_pop3_handler.c b/src/mail/ngx_mail_pop3_handler.c
index edfd98681..226e7419b 100644
--- a/src/mail/ngx_mail_pop3_handler.c
+++ b/src/mail/ngx_mail_pop3_handler.c
@@ -262,6 +262,10 @@ ngx_mail_pop3_auth_state(ngx_event_t *rev)
}
}
+ if (s->buffer->pos < s->buffer->last) {
+ s->blocked = 1;
+ }
+
switch (rc) {
case NGX_DONE:
@@ -283,11 +287,14 @@ ngx_mail_pop3_auth_state(ngx_event_t *rev)
case NGX_OK:
s->args.nelts = 0;
- s->buffer->pos = s->buffer->start;
- s->buffer->last = s->buffer->start;
+
+ if (s->buffer->pos == s->buffer->last) {
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
+ }
if (s->state) {
- s->arg_start = s->buffer->start;
+ s->arg_start = s->buffer->pos;
}
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
@@ -400,6 +407,8 @@ ngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c)
if (c->ssl == NULL) {
sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
if (sslcf->starttls) {
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
c->read->handler = ngx_mail_starttls_handler;
return NGX_OK;
}
diff --git a/src/mail/ngx_mail_proxy_module.c b/src/mail/ngx_mail_proxy_module.c
index 66aa0ba09..a7ab0776e 100644
--- a/src/mail/ngx_mail_proxy_module.c
+++ b/src/mail/ngx_mail_proxy_module.c
@@ -327,6 +327,10 @@ ngx_mail_proxy_pop3_handler(ngx_event_t *rev)
c->log->action = NULL;
ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");
+ if (s->buffer->pos < s->buffer->last) {
+ ngx_post_event(c->write, &ngx_posted_events);
+ }
+
ngx_mail_proxy_handler(s->connection->write);
return;
@@ -482,6 +486,10 @@ ngx_mail_proxy_imap_handler(ngx_event_t *rev)
c->log->action = NULL;
ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");
+ if (s->buffer->pos < s->buffer->last) {
+ ngx_post_event(c->write, &ngx_posted_events);
+ }
+
ngx_mail_proxy_handler(s->connection->write);
return;
@@ -813,13 +821,12 @@ ngx_mail_proxy_smtp_handler(ngx_event_t *rev)
c->log->action = NULL;
ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");
- if (s->buffer->pos == s->buffer->last) {
- ngx_mail_proxy_handler(s->connection->write);
-
- } else {
- ngx_mail_proxy_handler(c->write);
+ if (s->buffer->pos < s->buffer->last) {
+ ngx_post_event(c->write, &ngx_posted_events);
}
+ ngx_mail_proxy_handler(s->connection->write);
+
return;
default: