]> git.kaiwu.me - nginx.git/commitdiff
Mail: IMAP pipelining support.
authorMaxim Dounin <mdounin@mdounin.ru>
Wed, 19 May 2021 00:13:28 +0000 (03:13 +0300)
committerMaxim Dounin <mdounin@mdounin.ru>
Wed, 19 May 2021 00:13:28 +0000 (03:13 +0300)
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.

src/mail/ngx_mail.h
src/mail/ngx_mail_imap_handler.c
src/mail/ngx_mail_parse.c
src/mail/ngx_mail_proxy_module.c

index 0a0bde2908e10dcb4aebf1f86c5de5836e079856..07104df68556eacc27946f3e2282acebf77e0ca4 100644 (file)
@@ -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;
index ba66b898c42f151493a34416a6de80c373fbe74f..291e87a4deba134ba0faad127704200389ef1a53 100644 (file)
@@ -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;
         }
index 47c9e3a90747589cb7adc3bbebccc30153e81985..4db1f18d30e3482ac944a9709741c1804c033dc0 100644 (file)
@@ -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;
 }
 
 
index 0f56d299e2abe1a3fd71dca990d7b8617268667b..a7ab0776e0febb1267ea39623c372f8e06f2c74c 100644 (file)
@@ -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;