summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaxim Dounin <mdounin@mdounin.ru>2021-05-19 03:13:28 +0300
committerMaxim Dounin <mdounin@mdounin.ru>2021-05-19 03:13:28 +0300
commit5015209054f68141cd4f5f61e874d4497d4ef49c (patch)
tree7e3caab12367ee7d3be8b2f33b5bb1c0ecec9c95
parent4617dd64b863df111e33b1b395709f4c2f427350 (diff)
downloadnginx-5015209054f68141cd4f5f61e874d4497d4ef49c.tar.gz
Mail: IMAP pipelining support.
The change is mostly the same as the SMTP one (04e43d03e153 and 3f5d0af4e40a), and ensures that nginx is able to properly handle or reject multiple IMAP commands. The s->cmd field is not really used and set for consistency. Non-synchronizing literals handling in invalid/unknown commands is limited, so when a non-synchronizing literal is detected at the end of a discarded line, the connection is closed.
-rw-r--r--src/mail/ngx_mail.h1
-rw-r--r--src/mail/ngx_mail_imap_handler.c17
-rw-r--r--src/mail/ngx_mail_parse.c61
-rw-r--r--src/mail/ngx_mail_proxy_module.c4
4 files changed, 65 insertions, 18 deletions
diff --git a/src/mail/ngx_mail.h b/src/mail/ngx_mail.h
index 0a0bde290..07104df68 100644
--- a/src/mail/ngx_mail.h
+++ b/src/mail/ngx_mail.h
@@ -236,6 +236,7 @@ typedef struct {
/* used to parse POP3/IMAP/SMTP command */
ngx_uint_t state;
+ u_char *tag_start;
u_char *cmd_start;
u_char *arg_start;
ngx_uint_t literal_len;
diff --git a/src/mail/ngx_mail_imap_handler.c b/src/mail/ngx_mail_imap_handler.c
index ba66b898c..291e87a4d 100644
--- a/src/mail/ngx_mail_imap_handler.c
+++ b/src/mail/ngx_mail_imap_handler.c
@@ -226,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:
@@ -275,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;
}
}
@@ -459,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 47c9e3a90..4db1f18d3 100644
--- a/src/mail/ngx_mail_parse.c
+++ b/src/mail/ngx_mail_parse.c
@@ -231,6 +231,8 @@ ngx_mail_imap_parse_command(ngx_mail_session_t *s)
ngx_str_t *arg;
enum {
sw_start = 0,
+ sw_tag,
+ sw_invalid,
sw_spaces_before_command,
sw_command,
sw_spaces_before_argument,
@@ -253,18 +255,21 @@ 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 != '.'
@@ -272,23 +277,23 @@ ngx_mail_imap_parse_command(ngx_mail_session_t *s)
{
goto invalid;
}
- if (p - s->buffer->start > 31) {
+ 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;
@@ -408,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;
@@ -631,13 +639,40 @@ done:
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;
}
diff --git a/src/mail/ngx_mail_proxy_module.c b/src/mail/ngx_mail_proxy_module.c
index 0f56d299e..a7ab0776e 100644
--- a/src/mail/ngx_mail_proxy_module.c
+++ b/src/mail/ngx_mail_proxy_module.c
@@ -486,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;