src/mail/ngx_mail_smtp_handler.c - nginx

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.     }

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

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

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

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

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


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

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

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

  275.     if (rev->timedout) {

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

  277.         rev->timedout = 0;

  278.         cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

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

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

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

  285.         sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

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

  287.     } else {

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

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

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

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

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


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

  310.     c = rev->data;

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

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

  318.     s = c->data;

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

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

  326.     ngx_mail_smtp_auth_state(rev);
  327. }


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

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

  336.     sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

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

  342.     return NGX_OK;
  343. }


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

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

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

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

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

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

  366.         return;
  367.     }

  368.     s->blocked = 0;

  369.     rc = ngx_mail_read_command(s, c);

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

  375.         return;
  376.     }

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

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

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

  383.         case ngx_smtp_start:

  384.             switch (s->command) {

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

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

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

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

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

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

  405.             case NGX_SMTP_NOOP:
  406.                 break;

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

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

  415.             break;

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

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

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

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

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

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

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

  438.     switch (rc) {

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

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

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

  449.         /* fall through */

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

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

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

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

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


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

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

  476.     arg = s->args.elts;

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

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

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

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

  485.     sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

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

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

  490. #if (NGX_MAIL_SSL)

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

  493.             sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);

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

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

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

  506.     return NGX_OK;
  507. }


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

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

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

  524.     sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

  525.     rc = ngx_mail_auth_parse(s, c);

  526.     switch (rc) {

  527.     case NGX_MAIL_AUTH_LOGIN:

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

  530.         return NGX_OK;

  531.     case NGX_MAIL_AUTH_LOGIN_USERNAME:

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

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

  535.     case NGX_MAIL_AUTH_PLAIN:

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

  538.         return NGX_OK;

  539.     case NGX_MAIL_AUTH_CRAM_MD5:

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

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

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

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

  553.         return NGX_ERROR;

  554.     case NGX_MAIL_AUTH_EXTERNAL:

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

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

  560.         return NGX_OK;
  561.     }

  562.     return rc;
  563. }


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

  569.     sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

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

  575.     /* auth none */

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

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

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

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

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

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

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

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

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

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

  599.     return NGX_OK;
  600. }


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

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

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

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

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

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

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

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

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

  625.     s->auth_method = NGX_MAIL_AUTH_NONE;

  626.     return NGX_DONE;
  627. }


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

  634.     return NGX_OK;
  635. }


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

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

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

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

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

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

  657. #endif

  658.     return NGX_MAIL_PARSE_INVALID_COMMAND;
  659. }


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

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

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

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

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

  678.         return NGX_AGAIN;
  679.     }

  680.     ngx_mail_smtp_log_rejected_command(s, c, err);

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

  683.     return NGX_OK;
  684. }


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

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

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

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

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

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

  704.     cmd.len = i;

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