changeset 436:9b19e26b2660

Mail: smtp pipelining support. Basically, this does the following two changes (and corresponding modifications of related code): 1. Does not reset session buffer unless it's reached it's end, and always wait for LF to terminate command (even if we detected invalid command). 2. Record command name as the first argument to make it available for handlers (since now we can't assume that command starts from s->buffer->start).
author Maxim Dounin <mdounin@mdounin.ru>
date Thu, 11 Sep 2008 15:26:25 +0400
parents e2df123bbbe2
children 35409f39a096
files src/mail/ngx_mail_handler.c src/mail/ngx_mail_parse.c src/mail/ngx_mail_proxy_module.c src/mail/ngx_mail_smtp_handler.c
diffstat 4 files changed, 92 insertions(+), 73 deletions(-) [+]
line wrap: on
line diff
--- a/src/mail/ngx_mail_handler.c
+++ b/src/mail/ngx_mail_handler.c
@@ -584,7 +584,9 @@ ngx_mail_read_command(ngx_mail_session_t
             return NGX_ERROR;
         }
 
-        return NGX_AGAIN;
+        if (s->buffer->pos == s->buffer->last) { 
+            return NGX_AGAIN;
+        }
     }
 
     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
@@ -625,8 +627,12 @@ void
 ngx_mail_auth(ngx_mail_session_t *s, ngx_connection_t *c)
 {
     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;
+    }
+
     s->state = 0;
 
     if (c->read->timer_set) {
--- a/src/mail/ngx_mail_parse.c
+++ b/src/mail/ngx_mail_parse.c
@@ -622,6 +622,8 @@ ngx_mail_smtp_parse_command(ngx_mail_ses
     ngx_str_t  *arg;
     enum {
         sw_start = 0,
+        sw_command,
+        sw_invalid,
         sw_spaces_before_argument,
         sw_argument,
         sw_almost_done
@@ -636,8 +638,14 @@ ngx_mail_smtp_parse_command(ngx_mail_ses
 
         /* SMTP command */
         case sw_start:
+            s->arg_start = p;
+            state = sw_command;
+
+            /* fall through */
+
+        case sw_command:
             if (ch == ' ' || ch == CR || ch == LF) {
-                c = s->buffer->start;
+                c = s->arg_start;
 
                 if (p - c == 4) {
 
@@ -715,6 +723,14 @@ ngx_mail_smtp_parse_command(ngx_mail_ses
                     goto invalid;
                 }
 
+                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;
@@ -734,6 +750,9 @@ ngx_mail_smtp_parse_command(ngx_mail_ses
 
             break;
 
+        case sw_invalid:
+            goto invalid;
+
         case sw_spaces_before_argument:
             switch (ch) {
             case ' ':
@@ -820,9 +839,21 @@ done:
 
 invalid:
 
-    s->state = sw_start;
+    s->state = sw_invalid;
     s->arg_start = NULL;
 
+    /* skip invalid command till LF */
+
+    for (p = s->buffer->pos; p < s->buffer->last; p++) {
+        if (*p == LF) {
+            s->state = sw_start;
+            p++;
+            break;
+        }
+    }
+
+    s->buffer->pos = p;
+
     return NGX_MAIL_PARSE_INVALID_COMMAND;
 }
 
@@ -831,6 +862,7 @@ ngx_int_t
 ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c)
 {
     ngx_str_t                 *arg;
+    ngx_uint_t                 nelts;
 
 #if (NGX_MAIL_SSL)
     if (ngx_mail_starttls_only(s, c)) {
@@ -839,12 +871,18 @@ ngx_mail_auth_parse(ngx_mail_session_t *
 #endif
 
     arg = s->args.elts;
+    nelts = s->args.nelts;
+
+    if (s->protocol == NGX_MAIL_SMTP_PROTOCOL) {
+        arg++;
+        nelts--;
+    }
 
     if (arg[0].len == 5) {
 
         if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5) == 0) {
 
-            if (s->args.nelts == 1) {
+            if (nelts == 1) {
                 return NGX_MAIL_AUTH_LOGIN;
             }
 
@@ -853,12 +891,13 @@ ngx_mail_auth_parse(ngx_mail_session_t *
 
         if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN", 5) == 0) {
 
-            if (s->args.nelts == 1) {
+            if (nelts == 1) {
                 return NGX_MAIL_AUTH_PLAIN;
             }
 
-            if (s->args.nelts == 2) {
-                return ngx_mail_auth_plain(s, c, 1);
+            if (nelts == 2) {
+                return ngx_mail_auth_plain(s, c,
+                              (s->protocol == NGX_MAIL_SMTP_PROTOCOL) ? 2 : 1);
             }
         }
 
@@ -867,7 +906,7 @@ ngx_mail_auth_parse(ngx_mail_session_t *
 
     if (arg[0].len == 8) {
 
-        if (s->args.nelts != 1) {
+        if (nelts != 1) {
             return NGX_MAIL_PARSE_INVALID_COMMAND;
         }
 
--- a/src/mail/ngx_mail_proxy_module.c
+++ b/src/mail/ngx_mail_proxy_module.c
@@ -623,7 +623,12 @@ ngx_mail_proxy_smtp_handler(ngx_event_t 
         c->log->action = NULL;
         ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");
 
-        ngx_mail_proxy_handler(s->connection->write);
+        if (s->buffer->pos == s->buffer->last) {
+            ngx_mail_proxy_handler(s->connection->write);
+
+        } else {
+            ngx_mail_proxy_handler(c->write);
+        }
 
         return;
 
--- a/src/mail/ngx_mail_smtp_handler.c
+++ b/src/mail/ngx_mail_smtp_handler.c
@@ -467,6 +467,10 @@ ngx_mail_smtp_auth_state(ngx_event_t *re
         }
     }
 
+    if (s->buffer->pos < s->buffer->last) {
+        s->blocked = 1;
+    }
+
     switch (rc) {
 
     case NGX_DONE:
@@ -488,11 +492,14 @@ ngx_mail_smtp_auth_state(ngx_event_t *re
 
     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;
         }
 
         ngx_mail_send(c->write);
@@ -506,7 +513,7 @@ ngx_mail_smtp_helo(ngx_mail_session_t *s
     ngx_str_t                 *arg;
     ngx_mail_smtp_srv_conf_t  *sscf;
 
-    if (s->args.nelts != 1) {
+    if (s->args.nelts != 2) {
         s->out.len = sizeof(smtp_invalid_argument) - 1;
         s->out.data = smtp_invalid_argument;
         s->state = 0;
@@ -515,14 +522,14 @@ ngx_mail_smtp_helo(ngx_mail_session_t *s
 
     arg = s->args.elts;
 
-    s->smtp_helo.len = arg[0].len;
+    s->smtp_helo.len = arg[1].len;
 
-    s->smtp_helo.data = ngx_pnalloc(c->pool, arg[0].len);
+    s->smtp_helo.data = ngx_pnalloc(c->pool, arg[1].len);
     if (s->smtp_helo.data == NULL) {
         return NGX_ERROR;
     }
 
-    ngx_memcpy(s->smtp_helo.data, arg[0].data, arg[0].len);
+    ngx_memcpy(s->smtp_helo.data, arg[1].data, arg[1].len);
 
     s->smtp_from.len = 0;
     s->smtp_from.data = NULL;
@@ -576,7 +583,7 @@ ngx_mail_smtp_auth(ngx_mail_session_t *s
     }
 #endif
 
-    if (s->args.nelts == 0) {
+    if (s->args.nelts < 2) {
         s->out.len = sizeof(smtp_invalid_argument) - 1;
         s->out.data = smtp_invalid_argument;
         s->state = 0;
@@ -634,9 +641,7 @@ 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)
 {
-    u_char                     ch;
-    ngx_str_t                  l;
-    ngx_uint_t                 i;
+    ngx_str_t                 *arg, *end, cmd;
     ngx_mail_smtp_srv_conf_t  *sscf;
 
     sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);
@@ -659,37 +664,20 @@ ngx_mail_smtp_mail(ngx_mail_session_t *s
         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] = ' ';
-    }
+    arg = s->args.elts;
+    end = arg + s->args.nelts - 1;
 
-    while (i) {
-        if (l.data[i - 1] != ' ') {
-            break;
-        }
+    cmd.len = end->data + end->len - arg->data;
+    cmd.data = arg->data;
 
-        i--;
-    }
+    s->smtp_from.len = cmd.len;
 
-    l.len = i;
-
-    s->smtp_from.len = l.len;
-
-    s->smtp_from.data = ngx_palloc(c->pool, l.len);
+    s->smtp_from.data = ngx_palloc(c->pool, cmd.len);
     if (s->smtp_from.data == NULL) {
         return NGX_ERROR;
     }
 
-    ngx_memcpy(s->smtp_from.data, l.data, l.len);
+    ngx_memcpy(s->smtp_from.data, cmd.data, cmd.len);
 
     s->out.len = sizeof(smtp_ok) - 1;
     s->out.data = smtp_ok;
@@ -701,9 +689,7 @@ ngx_mail_smtp_mail(ngx_mail_session_t *s
 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;
+    ngx_str_t  *arg, *end, cmd;
 
     if (s->smtp_from.len == 0) {
         s->out.len = sizeof(smtp_bad_sequence) - 1;
@@ -711,37 +697,20 @@ ngx_mail_smtp_rcpt(ngx_mail_session_t *s
         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] = ' ';
-    }
+    arg = s->args.elts;
+    end = arg + s->args.nelts - 1;
 
-    while (i) {
-        if (l.data[i - 1] != ' ') {
-            break;
-        }
+    cmd.len = end->data + end->len - arg->data;
+    cmd.data = arg->data;
 
-        i--;
-    }
+    s->smtp_to.len = cmd.len;
 
-    l.len = i;
-
-    s->smtp_to.len = l.len;
-
-    s->smtp_to.data = ngx_palloc(c->pool, l.len);
+    s->smtp_to.data = ngx_palloc(c->pool, cmd.len);
     if (s->smtp_to.data == NULL) {
         return NGX_ERROR;
     }
 
-    ngx_memcpy(s->smtp_to.data, l.data, l.len);
+    ngx_memcpy(s->smtp_to.data, cmd.data, cmd.len);
 
     s->auth_method = NGX_MAIL_AUTH_NONE;