src/mail/ngx_mail_smtp_handler.c - nginx-1.31.3 nginx/ @ 42f8df65b

Global variables defined

Functions defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_event.h>
  8. #include <ngx_mail.h>
  9. #include <ngx_mail_smtp_module.h>


  10. static void ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx);
  11. static ngx_int_t ngx_mail_smtp_validate_host(ngx_str_t *name);
  12. static void ngx_mail_smtp_resolve_name(ngx_event_t *rev);
  13. static void ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx);
  14. static void ngx_mail_smtp_block_reading(ngx_event_t *rev);
  15. static void ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c);
  16. static void ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev);
  17. static ngx_int_t ngx_mail_smtp_create_buffer(ngx_mail_session_t *s,
  18.     ngx_connection_t *c);

  19. static ngx_int_t ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c);
  20. static ngx_int_t ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c);
  21. static ngx_int_t ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c);
  22. static ngx_int_t ngx_mail_smtp_starttls(ngx_mail_session_t *s,
  23.     ngx_connection_t *c);
  24. static ngx_int_t ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c);
  25. static ngx_int_t ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c);

  26. static ngx_int_t ngx_mail_smtp_discard_command(ngx_mail_session_t *s,
  27.     ngx_connection_t *c, char *err);
  28. static void ngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s,
  29.     ngx_connection_t *c, char *err);


  30. static u_char  smtp_ok[] = "250 2.0.0 OK" CRLF;
  31. static u_char  smtp_bye[] = "221 2.0.0 Bye" CRLF;
  32. static u_char  smtp_starttls[] = "220 2.0.0 Start TLS" CRLF;
  33. static u_char  smtp_next[] = "334 " CRLF;
  34. static u_char  smtp_username[] = "334 VXNlcm5hbWU6" CRLF;
  35. static u_char  smtp_password[] = "334 UGFzc3dvcmQ6" CRLF;
  36. static u_char  smtp_invalid_command[] = "500 5.5.1 Invalid command" CRLF;
  37. static u_char  smtp_invalid_pipelining[] =
  38.     "503 5.5.0 Improper use of SMTP command pipelining" CRLF;
  39. static u_char  smtp_invalid_argument[] = "501 5.5.4 Invalid argument" CRLF;
  40. static u_char  smtp_auth_required[] = "530 5.7.1 Authentication required" CRLF;
  41. static u_char  smtp_bad_sequence[] = "503 5.5.1 Bad sequence of commands" CRLF;


  42. static ngx_str_t  smtp_unavailable = ngx_string("[UNAVAILABLE]");
  43. static ngx_str_t  smtp_tempunavail = ngx_string("[TEMPUNAVAIL]");


  44. void
  45. ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c)
  46. {
  47.     ngx_resolver_ctx_t        *ctx;
  48.     ngx_mail_core_srv_conf_t  *cscf;

  49.     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

  50.     if (cscf->resolver == NULL) {
  51.         s->host = smtp_unavailable;
  52.         ngx_mail_smtp_greeting(s, c);
  53.         return;
  54.     }

  55. #if (NGX_HAVE_UNIX_DOMAIN)
  56.     if (c->sockaddr->sa_family == AF_UNIX) {
  57.         s->host = smtp_tempunavail;
  58.         ngx_mail_smtp_greeting(s, c);
  59.         return;
  60.     }
  61. #endif

  62.     c->log->action = "in resolving client address";

  63.     ctx = ngx_resolve_start(cscf->resolver, NULL);
  64.     if (ctx == NULL) {
  65.         ngx_mail_close_connection(c);
  66.         return;
  67.     }

  68.     ctx->addr.sockaddr = c->sockaddr;
  69.     ctx->addr.socklen = c->socklen;
  70.     ctx->handler = ngx_mail_smtp_resolve_addr_handler;
  71.     ctx->data = s;
  72.     ctx->timeout = cscf->resolver_timeout;

  73.     s->resolver_ctx = ctx;
  74.     c->read->handler = ngx_mail_smtp_block_reading;

  75.     if (ngx_resolve_addr(ctx) != NGX_OK) {
  76.         ngx_mail_close_connection(c);
  77.     }
  78. }


  79. static void
  80. ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx)
  81. {
  82.     ngx_connection_t    *c;
  83.     ngx_mail_session_t  *s;

  84.     s = ctx->data;
  85.     c = s->connection;

  86.     if (ctx->state) {
  87.         ngx_log_error(NGX_LOG_ERR, c->log, 0,
  88.                       "%V could not be resolved (%i: %s)",
  89.                       &c->addr_text, ctx->state,
  90.                       ngx_resolver_strerror(ctx->state));

  91.         if (ctx->state == NGX_RESOLVE_NXDOMAIN) {
  92.             s->host = smtp_unavailable;

  93.         } else {
  94.             s->host = smtp_tempunavail;
  95.         }

  96.         ngx_resolve_addr_done(ctx);

  97.         ngx_mail_smtp_greeting(s, s->connection);

  98.         return;
  99.     }

  100.     if (ngx_mail_smtp_validate_host(&ctx->name) != NGX_OK) {
  101.         ngx_log_error(NGX_LOG_ERR, c->log, 0,
  102.                       "%V resolved to invalid host name \"%V\"",
  103.                       &c->addr_text, &ctx->name);

  104.         s->host = smtp_tempunavail;

  105.         ngx_resolve_addr_done(ctx);

  106.         ngx_mail_smtp_greeting(s, s->connection);

  107.         return;
  108.     }

  109.     c->log->action = "in resolving client hostname";

  110.     s->host.data = ngx_pstrdup(c->pool, &ctx->name);
  111.     if (s->host.data == NULL) {
  112.         ngx_resolve_addr_done(ctx);
  113.         ngx_mail_close_connection(c);
  114.         return;
  115.     }

  116.     s->host.len = ctx->name.len;

  117.     ngx_resolve_addr_done(ctx);

  118.     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
  119.                    "address resolved: %V", &s->host);

  120.     c->read->handler = ngx_mail_smtp_resolve_name;

  121.     ngx_post_event(c->read, &ngx_posted_events);
  122. }


  123. static ngx_int_t
  124. ngx_mail_smtp_validate_host(ngx_str_t *name)
  125. {
  126.     u_char      ch;
  127.     ngx_uint_t  i;

  128.     if (name->len == 0) {
  129.         return NGX_DECLINED;
  130.     }

  131.     for (i = 0; i < name->len; i++) {
  132.         ch = name->data[i];

  133.         /* allow only characters from RFC 1034, Section 3.5 */

  134.         if ((ch >= 'a' && ch <= 'z')
  135.             || (ch >= 'A' && ch <= 'Z')
  136.             || (ch >= '0' && ch <= '9')
  137.             || ch == '-' || ch == '.')
  138.         {
  139.             continue;
  140.         }

  141.         return NGX_DECLINED;
  142.     }

  143.     return NGX_OK;
  144. }


  145. static void
  146. ngx_mail_smtp_resolve_name(ngx_event_t *rev)
  147. {
  148.     ngx_connection_t          *c;
  149.     ngx_mail_session_t        *s;
  150.     ngx_resolver_ctx_t        *ctx;
  151.     ngx_mail_core_srv_conf_t  *cscf;

  152.     c = rev->data;
  153.     s = c->data;

  154.     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

  155.     ctx = ngx_resolve_start(cscf->resolver, NULL);
  156.     if (ctx == NULL) {
  157.         ngx_mail_close_connection(c);
  158.         return;
  159.     }

  160.     ctx->name = s->host;
  161.     ctx->handler = ngx_mail_smtp_resolve_name_handler;
  162.     ctx->data = s;
  163.     ctx->timeout = cscf->resolver_timeout;

  164.     s->resolver_ctx = ctx;
  165.     c->read->handler = ngx_mail_smtp_block_reading;

  166.     if (ngx_resolve_name(ctx) != NGX_OK) {
  167.         ngx_mail_close_connection(c);
  168.     }
  169. }


  170. static void
  171. ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx)
  172. {
  173.     ngx_uint_t           i;
  174.     ngx_connection_t    *c;
  175.     ngx_mail_session_t  *s;

  176.     s = ctx->data;
  177.     c = s->connection;

  178.     if (ctx->state) {
  179.         ngx_log_error(NGX_LOG_ERR, c->log, 0,
  180.                       "\"%V\" could not be resolved (%i: %s)",
  181.                       &ctx->name, ctx->state,
  182.                       ngx_resolver_strerror(ctx->state));

  183.         if (ctx->state == NGX_RESOLVE_NXDOMAIN) {
  184.             s->host = smtp_unavailable;

  185.         } else {
  186.             s->host = smtp_tempunavail;
  187.         }

  188.     } else {

  189. #if (NGX_DEBUG)
  190.         {
  191.         u_char     text[NGX_SOCKADDR_STRLEN];
  192.         ngx_str_t  addr;

  193.         addr.data = text;

  194.         for (i = 0; i < ctx->naddrs; i++) {
  195.             addr.len = ngx_sock_ntop(ctx->addrs[i].sockaddr,
  196.                                      ctx->addrs[i].socklen,
  197.                                      text, NGX_SOCKADDR_STRLEN, 0);

  198.             ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
  199.                            "name was resolved to %V", &addr);
  200.         }
  201.         }
  202. #endif

  203.         for (i = 0; i < ctx->naddrs; i++) {
  204.             if (ngx_cmp_sockaddr(ctx->addrs[i].sockaddr, ctx->addrs[i].socklen,
  205.                                  c->sockaddr, c->socklen, 0)
  206.                 == NGX_OK)
  207.             {
  208.                 goto found;
  209.             }
  210.         }

  211.         s->host = smtp_unavailable;
  212.     }

  213. found:

  214.     ngx_resolve_name_done(ctx);

  215.     ngx_mail_smtp_greeting(s, c);
  216. }


  217. static void
  218. ngx_mail_smtp_block_reading(ngx_event_t *rev)
  219. {
  220.     ngx_connection_t    *c;
  221.     ngx_mail_session_t  *s;
  222.     ngx_resolver_ctx_t  *ctx;

  223.     c = rev->data;
  224.     s = c->data;

  225.     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp reading blocked");

  226.     if (ngx_handle_read_event(rev, 0) != NGX_OK) {

  227.         if (s->resolver_ctx) {
  228.             ctx = s->resolver_ctx;

  229.             if (ctx->handler == ngx_mail_smtp_resolve_addr_handler) {
  230.                 ngx_resolve_addr_done(ctx);

  231.             } else if (ctx->handler == ngx_mail_smtp_resolve_name_handler) {
  232.                 ngx_resolve_name_done(ctx);
  233.             }

  234.             s->resolver_ctx = NULL;
  235.         }

  236.         ngx_mail_close_connection(c);
  237.     }
  238. }


  239. static void
  240. ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c)
  241. {
  242.     ngx_msec_t                 timeout;
  243.     ngx_mail_core_srv_conf_t  *cscf;
  244.     ngx_mail_smtp_srv_conf_t  *sscf;

  245.     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
  246.                    "smtp greeting for \"%V\"", &s->host);

  247.     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
  248.     sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

  249.     timeout = sscf->greeting_delay ? sscf->greeting_delay : cscf->timeout;
  250.     ngx_add_timer(c->read, timeout);

  251.     if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
  252.         ngx_mail_close_connection(c);
  253.         return;
  254.     }

  255.     if (c->read->ready) {
  256.         ngx_post_event(c->read, &ngx_posted_events);
  257.     }

  258.     if (sscf->greeting_delay) {
  259.         c->read->handler = ngx_mail_smtp_invalid_pipelining;
  260.         return;
  261.     }

  262.     c->read->handler = ngx_mail_smtp_init_protocol;

  263.     s->out = sscf->greeting;

  264.     ngx_mail_send(c->write);
  265. }


  266. static void
  267. ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev)
  268. {
  269.     ngx_connection_t          *c;
  270.     ngx_mail_session_t        *s;
  271.     ngx_mail_core_srv_conf_t  *cscf;
  272.     ngx_mail_smtp_srv_conf_t  *sscf;

  273.     c = rev->data;
  274.     s = c->data;

  275.     c->log->action = "in delay pipelining state";

  276.     if (rev->timedout) {

  277.         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "delay greeting");

  278.         rev->timedout = 0;

  279.         cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

  280.         c->read->handler = ngx_mail_smtp_init_protocol;

  281.         ngx_add_timer(c->read, cscf->timeout);

  282.         if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
  283.             ngx_mail_close_connection(c);
  284.             return;
  285.         }

  286.         sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

  287.         s->out = sscf->greeting;

  288.     } else {

  289.         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "invalid pipelining");

  290.         if (s->buffer == NULL) {
  291.             if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) {
  292.                 return;
  293.             }
  294.         }

  295.         if (ngx_mail_smtp_discard_command(s, c,
  296.                                 "client was rejected before greeting: \"%V\"")
  297.             != NGX_OK)
  298.         {
  299.             return;
  300.         }

  301.         ngx_str_set(&s->out, smtp_invalid_pipelining);
  302.         s->quit = 1;
  303.     }

  304.     ngx_mail_send(c->write);
  305. }


  306. void
  307. ngx_mail_smtp_init_protocol(ngx_event_t *rev)
  308. {
  309.     ngx_connection_t    *c;
  310.     ngx_mail_session_t  *s;

  311.     c = rev->data;

  312.     c->log->action = "in auth state";

  313.     if (rev->timedout) {
  314.         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
  315.         c->timedout = 1;
  316.         ngx_mail_close_connection(c);
  317.         return;
  318.     }

  319.     s = c->data;

  320.     if (s->buffer == NULL) {
  321.         if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) {
  322.             return;
  323.         }
  324.     }

  325.     s->mail_state = ngx_smtp_start;
  326.     c->read->handler = ngx_mail_smtp_auth_state;

  327.     ngx_mail_smtp_auth_state(rev);
  328. }


  329. static ngx_int_t
  330. ngx_mail_smtp_create_buffer(ngx_mail_session_t *s, ngx_connection_t *c)
  331. {
  332.     ngx_mail_smtp_srv_conf_t  *sscf;

  333.     if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) == NGX_ERROR) {
  334.         ngx_mail_session_internal_server_error(s);
  335.         return NGX_ERROR;
  336.     }

  337.     sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

  338.     s->buffer = ngx_create_temp_buf(c->pool, sscf->client_buffer_size);
  339.     if (s->buffer == NULL) {
  340.         ngx_mail_session_internal_server_error(s);
  341.         return NGX_ERROR;
  342.     }

  343.     return NGX_OK;
  344. }


  345. void
  346. ngx_mail_smtp_auth_state(ngx_event_t *rev)
  347. {
  348.     ngx_int_t            rc;
  349.     ngx_connection_t    *c;
  350.     ngx_mail_session_t  *s;

  351.     c = rev->data;
  352.     s = c->data;

  353.     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp auth state");

  354.     if (rev->timedout) {
  355.         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
  356.         c->timedout = 1;
  357.         ngx_mail_close_connection(c);
  358.         return;
  359.     }

  360.     if (s->out.len) {
  361.         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp send handler busy");
  362.         s->blocked = 1;

  363.         if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
  364.             ngx_mail_close_connection(c);
  365.             return;
  366.         }

  367.         return;
  368.     }

  369.     s->blocked = 0;

  370.     rc = ngx_mail_read_command(s, c);

  371.     if (rc == NGX_AGAIN) {
  372.         if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
  373.             ngx_mail_session_internal_server_error(s);
  374.             return;
  375.         }

  376.         return;
  377.     }

  378.     if (rc == NGX_ERROR) {
  379.         return;
  380.     }

  381.     ngx_str_set(&s->out, smtp_ok);

  382.     if (rc == NGX_OK) {
  383.         switch (s->mail_state) {

  384.         case ngx_smtp_start:

  385.             switch (s->command) {

  386.             case NGX_SMTP_HELO:
  387.             case NGX_SMTP_EHLO:
  388.                 rc = ngx_mail_smtp_helo(s, c);
  389.                 break;

  390.             case NGX_SMTP_AUTH:
  391.                 rc = ngx_mail_smtp_auth(s, c);
  392.                 break;

  393.             case NGX_SMTP_QUIT:
  394.                 s->quit = 1;
  395.                 ngx_str_set(&s->out, smtp_bye);
  396.                 break;

  397.             case NGX_SMTP_MAIL:
  398.                 rc = ngx_mail_smtp_mail(s, c);
  399.                 break;

  400.             case NGX_SMTP_RCPT:
  401.                 rc = ngx_mail_smtp_rcpt(s, c);
  402.                 break;

  403.             case NGX_SMTP_RSET:
  404.                 rc = ngx_mail_smtp_rset(s, c);
  405.                 break;

  406.             case NGX_SMTP_NOOP:
  407.                 break;

  408.             case NGX_SMTP_STARTTLS:
  409.                 rc = ngx_mail_smtp_starttls(s, c);
  410.                 ngx_str_set(&s->out, smtp_starttls);
  411.                 break;

  412.             default:
  413.                 rc = NGX_MAIL_PARSE_INVALID_COMMAND;
  414.                 break;
  415.             }

  416.             break;

  417.         case ngx_smtp_auth_login_username:
  418.             rc = ngx_mail_auth_login_username(s, c, 0);

  419.             ngx_str_set(&s->out, smtp_password);
  420.             s->mail_state = ngx_smtp_auth_login_password;
  421.             break;

  422.         case ngx_smtp_auth_login_password:
  423.             rc = ngx_mail_auth_login_password(s, c);
  424.             break;

  425.         case ngx_smtp_auth_plain:
  426.             rc = ngx_mail_auth_plain(s, c, 0);
  427.             break;

  428.         case ngx_smtp_auth_cram_md5:
  429.             rc = ngx_mail_auth_cram_md5(s, c);
  430.             break;

  431.         case ngx_smtp_auth_external:
  432.             rc = ngx_mail_auth_external(s, c, 0);
  433.             break;
  434.         }
  435.     }

  436.     if (s->buffer->pos < s->buffer->last) {
  437.         s->blocked = 1;
  438.     }

  439.     switch (rc) {

  440.     case NGX_DONE:
  441.         ngx_mail_auth(s, c);
  442.         return;

  443.     case NGX_ERROR:
  444.         ngx_mail_session_internal_server_error(s);
  445.         return;

  446.     case NGX_MAIL_PARSE_INVALID_COMMAND:
  447.         s->mail_state = ngx_smtp_start;
  448.         s->state = 0;
  449.         ngx_str_set(&s->out, smtp_invalid_command);

  450.         /* fall through */

  451.     case NGX_OK:
  452.         s->args.nelts = 0;

  453.         if (s->buffer->pos == s->buffer->last) {
  454.             s->buffer->pos = s->buffer->start;
  455.             s->buffer->last = s->buffer->start;
  456.         }

  457.         if (s->state) {
  458.             s->arg_start = s->buffer->pos;
  459.         }

  460.         if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
  461.             ngx_mail_session_internal_server_error(s);
  462.             return;
  463.         }

  464.         ngx_mail_send(c->write);
  465.     }
  466. }


  467. static ngx_int_t
  468. ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c)
  469. {
  470.     ngx_str_t                 *arg;
  471.     ngx_mail_smtp_srv_conf_t  *sscf;

  472.     if (s->args.nelts != 1) {
  473.         ngx_str_set(&s->out, smtp_invalid_argument);
  474.         s->state = 0;
  475.         return NGX_OK;
  476.     }

  477.     arg = s->args.elts;

  478.     s->smtp_helo.len = arg[0].len;

  479.     s->smtp_helo.data = ngx_pnalloc(c->pool, arg[0].len);
  480.     if (s->smtp_helo.data == NULL) {
  481.         return NGX_ERROR;
  482.     }

  483.     ngx_memcpy(s->smtp_helo.data, arg[0].data, arg[0].len);

  484.     ngx_str_null(&s->smtp_from);
  485.     ngx_str_null(&s->smtp_to);

  486.     sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

  487.     if (s->command == NGX_SMTP_HELO) {
  488.         s->out = sscf->server_name;

  489.     } else {
  490.         s->esmtp = 1;

  491. #if (NGX_MAIL_SSL)

  492.         if (c->ssl == NULL) {
  493.             ngx_mail_ssl_conf_t  *sslcf;

  494.             sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);

  495.             if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {
  496.                 s->out = sscf->starttls_capability;
  497.                 return NGX_OK;
  498.             }

  499.             if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
  500.                 s->out = sscf->starttls_only_capability;
  501.                 return NGX_OK;
  502.             }
  503.         }
  504. #endif

  505.         s->out = sscf->capability;
  506.     }

  507.     return NGX_OK;
  508. }


  509. static ngx_int_t
  510. ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c)
  511. {
  512.     ngx_int_t                  rc;
  513.     ngx_mail_core_srv_conf_t  *cscf;
  514.     ngx_mail_smtp_srv_conf_t  *sscf;

  515. #if (NGX_MAIL_SSL)
  516.     if (ngx_mail_starttls_only(s, c)) {
  517.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  518.     }
  519. #endif

  520.     if (s->args.nelts == 0) {
  521.         ngx_str_set(&s->out, smtp_invalid_argument);
  522.         s->state = 0;
  523.         return NGX_OK;
  524.     }

  525.     sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

  526.     rc = ngx_mail_auth_parse(s, c);

  527.     switch (rc) {

  528.     case NGX_MAIL_AUTH_LOGIN:

  529.         ngx_str_set(&s->out, smtp_username);
  530.         s->mail_state = ngx_smtp_auth_login_username;

  531.         return NGX_OK;

  532.     case NGX_MAIL_AUTH_LOGIN_USERNAME:

  533.         ngx_str_set(&s->out, smtp_password);
  534.         s->mail_state = ngx_smtp_auth_login_password;

  535.         return ngx_mail_auth_login_username(s, c, 1);

  536.     case NGX_MAIL_AUTH_PLAIN:

  537.         ngx_str_set(&s->out, smtp_next);
  538.         s->mail_state = ngx_smtp_auth_plain;

  539.         return NGX_OK;

  540.     case NGX_MAIL_AUTH_CRAM_MD5:

  541.         if (!(sscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) {
  542.             return NGX_MAIL_PARSE_INVALID_COMMAND;
  543.         }

  544.         if (s->salt.data == NULL) {
  545.             cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

  546.             if (ngx_mail_salt(s, c, cscf) != NGX_OK) {
  547.                 return NGX_ERROR;
  548.             }
  549.         }

  550.         if (ngx_mail_auth_cram_md5_salt(s, c, "334 ", 4) == NGX_OK) {
  551.             s->mail_state = ngx_smtp_auth_cram_md5;
  552.             return NGX_OK;
  553.         }

  554.         return NGX_ERROR;

  555.     case NGX_MAIL_AUTH_EXTERNAL:

  556.         if (!(sscf->auth_methods & NGX_MAIL_AUTH_EXTERNAL_ENABLED)) {
  557.             return NGX_MAIL_PARSE_INVALID_COMMAND;
  558.         }

  559.         ngx_str_set(&s->out, smtp_username);
  560.         s->mail_state = ngx_smtp_auth_external;

  561.         return NGX_OK;
  562.     }

  563.     return rc;
  564. }


  565. static ngx_int_t
  566. ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c)
  567. {
  568.     ngx_str_t                 *arg, cmd;
  569.     ngx_mail_smtp_srv_conf_t  *sscf;

  570.     sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

  571.     if (!(sscf->auth_methods & NGX_MAIL_AUTH_NONE_ENABLED)) {
  572.         ngx_mail_smtp_log_rejected_command(s, c, "client was rejected: \"%V\"");
  573.         ngx_str_set(&s->out, smtp_auth_required);
  574.         return NGX_OK;
  575.     }

  576.     /* auth none */

  577.     if (s->smtp_from.len) {
  578.         ngx_str_set(&s->out, smtp_bad_sequence);
  579.         return NGX_OK;
  580.     }

  581.     if (s->args.nelts == 0) {
  582.         ngx_str_set(&s->out, smtp_invalid_argument);
  583.         return NGX_OK;
  584.     }

  585.     arg = s->args.elts;
  586.     arg += s->args.nelts - 1;

  587.     cmd.len = arg->data + arg->len - s->cmd.data;
  588.     cmd.data = s->cmd.data;

  589.     s->smtp_from.len = cmd.len;

  590.     s->smtp_from.data = ngx_pnalloc(c->pool, cmd.len);
  591.     if (s->smtp_from.data == NULL) {
  592.         return NGX_ERROR;
  593.     }

  594.     ngx_memcpy(s->smtp_from.data, cmd.data, cmd.len);

  595.     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
  596.                    "smtp mail from:\"%V\"", &s->smtp_from);

  597.     ngx_str_set(&s->out, smtp_ok);

  598.     ngx_str_null(&s->login);
  599.     ngx_str_null(&s->passwd);

  600.     return NGX_OK;
  601. }


  602. static ngx_int_t
  603. ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c)
  604. {
  605.     ngx_str_t  *arg, cmd;

  606.     if (s->smtp_from.len == 0) {
  607.         ngx_str_set(&s->out, smtp_bad_sequence);
  608.         return NGX_OK;
  609.     }

  610.     if (s->args.nelts == 0) {
  611.         ngx_str_set(&s->out, smtp_invalid_argument);
  612.         return NGX_OK;
  613.     }

  614.     arg = s->args.elts;
  615.     arg += s->args.nelts - 1;

  616.     cmd.len = arg->data + arg->len - s->cmd.data;
  617.     cmd.data = s->cmd.data;

  618.     s->smtp_to.len = cmd.len;

  619.     s->smtp_to.data = ngx_pnalloc(c->pool, cmd.len);
  620.     if (s->smtp_to.data == NULL) {
  621.         return NGX_ERROR;
  622.     }

  623.     ngx_memcpy(s->smtp_to.data, cmd.data, cmd.len);

  624.     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
  625.                    "smtp rcpt to:\"%V\"", &s->smtp_to);

  626.     s->auth_method = NGX_MAIL_AUTH_NONE;

  627.     return NGX_DONE;
  628. }


  629. static ngx_int_t
  630. ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c)
  631. {
  632.     ngx_str_null(&s->smtp_from);
  633.     ngx_str_null(&s->smtp_to);
  634.     ngx_str_set(&s->out, smtp_ok);

  635.     return NGX_OK;
  636. }


  637. static ngx_int_t
  638. ngx_mail_smtp_starttls(ngx_mail_session_t *s, ngx_connection_t *c)
  639. {
  640. #if (NGX_MAIL_SSL)
  641.     ngx_mail_ssl_conf_t  *sslcf;

  642.     if (c->ssl == NULL) {
  643.         sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
  644.         if (sslcf->starttls) {

  645.             /*
  646.              * RFC3207 requires us to discard any knowledge
  647.              * obtained from client before STARTTLS.
  648.              */

  649.             ngx_str_null(&s->smtp_helo);
  650.             ngx_str_null(&s->smtp_from);
  651.             ngx_str_null(&s->smtp_to);

  652.             s->buffer->pos = s->buffer->start;
  653.             s->buffer->last = s->buffer->start;

  654.             c->read->handler = ngx_mail_starttls_handler;
  655.             return NGX_OK;
  656.         }
  657.     }

  658. #endif

  659.     return NGX_MAIL_PARSE_INVALID_COMMAND;
  660. }


  661. static ngx_int_t
  662. ngx_mail_smtp_discard_command(ngx_mail_session_t *s, ngx_connection_t *c,
  663.     char *err)
  664. {
  665.     ssize_t    n;

  666.     n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last);

  667.     if (n == NGX_ERROR || n == 0) {
  668.         ngx_mail_close_connection(c);
  669.         return NGX_ERROR;
  670.     }

  671.     if (n > 0) {
  672.         s->buffer->last += n;
  673.     }

  674.     if (n == NGX_AGAIN) {
  675.         if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
  676.             ngx_mail_session_internal_server_error(s);
  677.             return NGX_ERROR;
  678.         }

  679.         return NGX_AGAIN;
  680.     }

  681.     ngx_mail_smtp_log_rejected_command(s, c, err);

  682.     s->buffer->pos = s->buffer->start;
  683.     s->buffer->last = s->buffer->start;

  684.     return NGX_OK;
  685. }


  686. static void
  687. ngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s, ngx_connection_t *c,
  688.     char *err)
  689. {
  690.     u_char      ch;
  691.     ngx_str_t   cmd;
  692.     ngx_uint_t  i;

  693.     if (c->log->log_level < NGX_LOG_INFO) {
  694.         return;
  695.     }

  696.     cmd.len = s->buffer->last - s->buffer->start;
  697.     cmd.data = s->buffer->start;

  698.     for (i = 0; i < cmd.len; i++) {
  699.         ch = cmd.data[i];

  700.         if (ch != CR && ch != LF) {
  701.             continue;
  702.         }

  703.         cmd.data[i] = '_';
  704.     }

  705.     cmd.len = i;

  706.     ngx_log_error(NGX_LOG_INFO, c->log, 0, err, &cmd);
  707. }