src/mail/ngx_mail_smtp_handler.c - nginx-1.7.10

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 void ngx_mail_smtp_resolve_name(ngx_event_t *rev);
  12. static void ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx);
  13. static void ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c);
  14. static void ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev);
  15. static ngx_int_t ngx_mail_smtp_create_buffer(ngx_mail_session_t *s,
  16.     ngx_connection_t *c);

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

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


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


  40. static ngx_str_t  smtp_unavailable = ngx_string("[UNAVAILABLE]");
  41. static ngx_str_t  smtp_tempunavail = ngx_string("[TEMPUNAVAIL]");


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

  47.     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

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

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

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

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

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

  71.     if (ngx_resolve_addr(ctx) != NGX_OK) {
  72.         ngx_mail_close_connection(c);
  73.     }
  74. }


  75. static void
  76. ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx)
  77. {
  78.     ngx_connection_t    *c;
  79.     ngx_mail_session_t  *s;

  80.     s = ctx->data;
  81.     c = s->connection;

  82.     if (ctx->state) {
  83.         ngx_log_error(NGX_LOG_ERR, c->log, 0,
  84.                       "%V could not be resolved (%i: %s)",
  85.                       &c->addr_text, ctx->state,
  86.                       ngx_resolver_strerror(ctx->state));

  87.         if (ctx->state == NGX_RESOLVE_NXDOMAIN) {
  88.             s->host = smtp_unavailable;

  89.         } else {
  90.             s->host = smtp_tempunavail;
  91.         }

  92.         ngx_resolve_addr_done(ctx);

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

  94.         return;
  95.     }

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

  97.     s->host.data = ngx_pstrdup(c->pool, &ctx->name);
  98.     if (s->host.data == NULL) {
  99.         ngx_resolve_addr_done(ctx);
  100.         ngx_mail_close_connection(c);
  101.         return;
  102.     }

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

  104.     ngx_resolve_addr_done(ctx);

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

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

  108.     ngx_post_event(c->read, &ngx_posted_events);
  109. }


  110. static void
  111. ngx_mail_smtp_resolve_name(ngx_event_t *rev)
  112. {
  113.     ngx_connection_t          *c;
  114.     ngx_mail_session_t        *s;
  115.     ngx_resolver_ctx_t        *ctx;
  116.     ngx_mail_core_srv_conf_t  *cscf;

  117.     c = rev->data;
  118.     s = c->data;

  119.     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

  120.     ctx = ngx_resolve_start(cscf->resolver, NULL);
  121.     if (ctx == NULL) {
  122.         ngx_mail_close_connection(c);
  123.         return;
  124.     }

  125.     ctx->name = s->host;
  126.     ctx->handler = ngx_mail_smtp_resolve_name_handler;
  127.     ctx->data = s;
  128.     ctx->timeout = cscf->resolver_timeout;

  129.     if (ngx_resolve_name(ctx) != NGX_OK) {
  130.         ngx_mail_close_connection(c);
  131.     }
  132. }


  133. static void
  134. ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx)
  135. {
  136.     ngx_uint_t           i;
  137.     ngx_connection_t    *c;
  138.     ngx_mail_session_t  *s;

  139.     s = ctx->data;
  140.     c = s->connection;

  141.     if (ctx->state) {
  142.         ngx_log_error(NGX_LOG_ERR, c->log, 0,
  143.                       "\"%V\" could not be resolved (%i: %s)",
  144.                       &ctx->name, ctx->state,
  145.                       ngx_resolver_strerror(ctx->state));

  146.         if (ctx->state == NGX_RESOLVE_NXDOMAIN) {
  147.             s->host = smtp_unavailable;

  148.         } else {
  149.             s->host = smtp_tempunavail;
  150.         }

  151.     } else {

  152. #if (NGX_DEBUG)
  153.         {
  154.         u_char     text[NGX_SOCKADDR_STRLEN];
  155.         ngx_str_t  addr;

  156.         addr.data = text;

  157.         for (i = 0; i < ctx->naddrs; i++) {
  158.             addr.len = ngx_sock_ntop(ctx->addrs[i].sockaddr,
  159.                                      ctx->addrs[i].socklen,
  160.                                      text, NGX_SOCKADDR_STRLEN, 0);

  161.             ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
  162.                            "name was resolved to %V", &addr);
  163.         }
  164.         }
  165. #endif

  166.         for (i = 0; i < ctx->naddrs; i++) {
  167.             if (ngx_cmp_sockaddr(ctx->addrs[i].sockaddr, ctx->addrs[i].socklen,
  168.                                  c->sockaddr, c->socklen, 0)
  169.                 == NGX_OK)
  170.             {
  171.                 goto found;
  172.             }
  173.         }

  174.         s->host = smtp_unavailable;
  175.     }

  176. found:

  177.     ngx_resolve_name_done(ctx);

  178.     ngx_mail_smtp_greeting(s, c);
  179. }


  180. static void
  181. ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c)
  182. {
  183.     ngx_msec_t                 timeout;
  184.     ngx_mail_core_srv_conf_t  *cscf;
  185.     ngx_mail_smtp_srv_conf_t  *sscf;

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

  188.     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
  189.     sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

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

  192.     if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
  193.         ngx_mail_close_connection(c);
  194.     }

  195.     if (sscf->greeting_delay) {
  196.          c->read->handler = ngx_mail_smtp_invalid_pipelining;
  197.          return;
  198.     }

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

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

  201.     ngx_mail_send(c->write);
  202. }


  203. static void
  204. ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev)
  205. {
  206.     ngx_connection_t          *c;
  207.     ngx_mail_session_t        *s;
  208.     ngx_mail_core_srv_conf_t  *cscf;
  209.     ngx_mail_smtp_srv_conf_t  *sscf;

  210.     c = rev->data;
  211.     s = c->data;

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

  213.     if (rev->timedout) {

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

  215.         rev->timedout = 0;

  216.         cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

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

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

  219.         if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
  220.             ngx_mail_close_connection(c);
  221.             return;
  222.         }

  223.         sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

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

  225.     } else {

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

  227.         if (s->buffer == NULL) {
  228.             if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) {
  229.                 return;
  230.             }
  231.         }

  232.         if (ngx_mail_smtp_discard_command(s, c,
  233.                                 "client was rejected before greeting: \"%V\"")
  234.             != NGX_OK)
  235.         {
  236.             return;
  237.         }

  238.         ngx_str_set(&s->out, smtp_invalid_pipelining);
  239.         s->quit = 1;
  240.     }

  241.     ngx_mail_send(c->write);
  242. }


  243. void
  244. ngx_mail_smtp_init_protocol(ngx_event_t *rev)
  245. {
  246.     ngx_connection_t    *c;
  247.     ngx_mail_session_t  *s;

  248.     c = rev->data;

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

  250.     if (rev->timedout) {
  251.         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
  252.         c->timedout = 1;
  253.         ngx_mail_close_connection(c);
  254.         return;
  255.     }

  256.     s = c->data;

  257.     if (s->buffer == NULL) {
  258.         if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) {
  259.             return;
  260.         }
  261.     }

  262.     s->mail_state = ngx_smtp_start;
  263.     c->read->handler = ngx_mail_smtp_auth_state;

  264.     ngx_mail_smtp_auth_state(rev);
  265. }


  266. static ngx_int_t
  267. ngx_mail_smtp_create_buffer(ngx_mail_session_t *s, ngx_connection_t *c)
  268. {
  269.     ngx_mail_smtp_srv_conf_t  *sscf;

  270.     if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) == NGX_ERROR) {
  271.         ngx_mail_session_internal_server_error(s);
  272.         return NGX_ERROR;
  273.     }

  274.     sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

  275.     s->buffer = ngx_create_temp_buf(c->pool, sscf->client_buffer_size);
  276.     if (s->buffer == NULL) {
  277.         ngx_mail_session_internal_server_error(s);
  278.         return NGX_ERROR;
  279.     }

  280.     return NGX_OK;
  281. }


  282. void
  283. ngx_mail_smtp_auth_state(ngx_event_t *rev)
  284. {
  285.     ngx_int_t            rc;
  286.     ngx_connection_t    *c;
  287.     ngx_mail_session_t  *s;

  288.     c = rev->data;
  289.     s = c->data;

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

  291.     if (rev->timedout) {
  292.         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
  293.         c->timedout = 1;
  294.         ngx_mail_close_connection(c);
  295.         return;
  296.     }

  297.     if (s->out.len) {
  298.         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp send handler busy");
  299.         s->blocked = 1;
  300.         return;
  301.     }

  302.     s->blocked = 0;

  303.     rc = ngx_mail_read_command(s, c);

  304.     if (rc == NGX_AGAIN || rc == NGX_ERROR) {
  305.         return;
  306.     }

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

  308.     if (rc == NGX_OK) {
  309.         switch (s->mail_state) {

  310.         case ngx_smtp_start:

  311.             switch (s->command) {

  312.             case NGX_SMTP_HELO:
  313.             case NGX_SMTP_EHLO:
  314.                 rc = ngx_mail_smtp_helo(s, c);
  315.                 break;

  316.             case NGX_SMTP_AUTH:
  317.                 rc = ngx_mail_smtp_auth(s, c);
  318.                 break;

  319.             case NGX_SMTP_QUIT:
  320.                 s->quit = 1;
  321.                 ngx_str_set(&s->out, smtp_bye);
  322.                 break;

  323.             case NGX_SMTP_MAIL:
  324.                 rc = ngx_mail_smtp_mail(s, c);
  325.                 break;

  326.             case NGX_SMTP_RCPT:
  327.                 rc = ngx_mail_smtp_rcpt(s, c);
  328.                 break;

  329.             case NGX_SMTP_RSET:
  330.                 rc = ngx_mail_smtp_rset(s, c);
  331.                 break;

  332.             case NGX_SMTP_NOOP:
  333.                 break;

  334.             case NGX_SMTP_STARTTLS:
  335.                 rc = ngx_mail_smtp_starttls(s, c);
  336.                 ngx_str_set(&s->out, smtp_starttls);
  337.                 break;

  338.             default:
  339.                 rc = NGX_MAIL_PARSE_INVALID_COMMAND;
  340.                 break;
  341.             }

  342.             break;

  343.         case ngx_smtp_auth_login_username:
  344.             rc = ngx_mail_auth_login_username(s, c, 0);

  345.             ngx_str_set(&s->out, smtp_password);
  346.             s->mail_state = ngx_smtp_auth_login_password;
  347.             break;

  348.         case ngx_smtp_auth_login_password:
  349.             rc = ngx_mail_auth_login_password(s, c);
  350.             break;

  351.         case ngx_smtp_auth_plain:
  352.             rc = ngx_mail_auth_plain(s, c, 0);
  353.             break;

  354.         case ngx_smtp_auth_cram_md5:
  355.             rc = ngx_mail_auth_cram_md5(s, c);
  356.             break;
  357.         }
  358.     }

  359.     if (s->buffer->pos < s->buffer->last) {
  360.         s->blocked = 1;
  361.     }

  362.     switch (rc) {

  363.     case NGX_DONE:
  364.         ngx_mail_auth(s, c);
  365.         return;

  366.     case NGX_ERROR:
  367.         ngx_mail_session_internal_server_error(s);
  368.         return;

  369.     case NGX_MAIL_PARSE_INVALID_COMMAND:
  370.         s->mail_state = ngx_smtp_start;
  371.         s->state = 0;
  372.         ngx_str_set(&s->out, smtp_invalid_command);

  373.         /* fall through */

  374.     case NGX_OK:
  375.         s->args.nelts = 0;

  376.         if (s->buffer->pos == s->buffer->last) {
  377.             s->buffer->pos = s->buffer->start;
  378.             s->buffer->last = s->buffer->start;
  379.         }

  380.         if (s->state) {
  381.             s->arg_start = s->buffer->pos;
  382.         }

  383.         ngx_mail_send(c->write);
  384.     }
  385. }


  386. static ngx_int_t
  387. ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c)
  388. {
  389.     ngx_str_t                 *arg;
  390.     ngx_mail_smtp_srv_conf_t  *sscf;

  391.     if (s->args.nelts != 1) {
  392.         ngx_str_set(&s->out, smtp_invalid_argument);
  393.         s->state = 0;
  394.         return NGX_OK;
  395.     }

  396.     arg = s->args.elts;

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

  398.     s->smtp_helo.data = ngx_pnalloc(c->pool, arg[0].len);
  399.     if (s->smtp_helo.data == NULL) {
  400.         return NGX_ERROR;
  401.     }

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

  403.     ngx_str_null(&s->smtp_from);
  404.     ngx_str_null(&s->smtp_to);

  405.     sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

  406.     if (s->command == NGX_SMTP_HELO) {
  407.         s->out = sscf->server_name;

  408.     } else {
  409.         s->esmtp = 1;

  410. #if (NGX_MAIL_SSL)

  411.         if (c->ssl == NULL) {
  412.             ngx_mail_ssl_conf_t  *sslcf;

  413.             sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);

  414.             if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {
  415.                 s->out = sscf->starttls_capability;
  416.                 return NGX_OK;
  417.             }

  418.             if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
  419.                 s->out = sscf->starttls_only_capability;
  420.                 return NGX_OK;
  421.             }
  422.         }
  423. #endif

  424.         s->out = sscf->capability;
  425.     }

  426.     return NGX_OK;
  427. }


  428. static ngx_int_t
  429. ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c)
  430. {
  431.     ngx_int_t                  rc;
  432.     ngx_mail_core_srv_conf_t  *cscf;
  433.     ngx_mail_smtp_srv_conf_t  *sscf;

  434. #if (NGX_MAIL_SSL)
  435.     if (ngx_mail_starttls_only(s, c)) {
  436.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  437.     }
  438. #endif

  439.     if (s->args.nelts == 0) {
  440.         ngx_str_set(&s->out, smtp_invalid_argument);
  441.         s->state = 0;
  442.         return NGX_OK;
  443.     }

  444.     rc = ngx_mail_auth_parse(s, c);

  445.     switch (rc) {

  446.     case NGX_MAIL_AUTH_LOGIN:

  447.         ngx_str_set(&s->out, smtp_username);
  448.         s->mail_state = ngx_smtp_auth_login_username;

  449.         return NGX_OK;

  450.     case NGX_MAIL_AUTH_LOGIN_USERNAME:

  451.         ngx_str_set(&s->out, smtp_password);
  452.         s->mail_state = ngx_smtp_auth_login_password;

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

  454.     case NGX_MAIL_AUTH_PLAIN:

  455.         ngx_str_set(&s->out, smtp_next);
  456.         s->mail_state = ngx_smtp_auth_plain;

  457.         return NGX_OK;

  458.     case NGX_MAIL_AUTH_CRAM_MD5:

  459.         sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

  460.         if (!(sscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) {
  461.             return NGX_MAIL_PARSE_INVALID_COMMAND;
  462.         }

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

  465.             if (ngx_mail_salt(s, c, cscf) != NGX_OK) {
  466.                 return NGX_ERROR;
  467.             }
  468.         }

  469.         if (ngx_mail_auth_cram_md5_salt(s, c, "334 ", 4) == NGX_OK) {
  470.             s->mail_state = ngx_smtp_auth_cram_md5;
  471.             return NGX_OK;
  472.         }

  473.         return NGX_ERROR;
  474.     }

  475.     return rc;
  476. }


  477. static ngx_int_t
  478. ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c)
  479. {
  480.     ngx_str_t                 *arg, cmd;
  481.     ngx_mail_smtp_srv_conf_t  *sscf;

  482.     sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

  483.     if (!(sscf->auth_methods & NGX_MAIL_AUTH_NONE_ENABLED)) {
  484.         ngx_mail_smtp_log_rejected_command(s, c, "client was rejected: \"%V\"");
  485.         ngx_str_set(&s->out, smtp_auth_required);
  486.         return NGX_OK;
  487.     }

  488.     /* auth none */

  489.     if (s->smtp_from.len) {
  490.         ngx_str_set(&s->out, smtp_bad_sequence);
  491.         return NGX_OK;
  492.     }

  493.     if (s->args.nelts == 0) {
  494.         ngx_str_set(&s->out, smtp_invalid_argument);
  495.         return NGX_OK;
  496.     }

  497.     arg = s->args.elts;
  498.     arg += s->args.nelts - 1;

  499.     cmd.len = arg->data + arg->len - s->cmd.data;
  500.     cmd.data = s->cmd.data;

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

  502.     s->smtp_from.data = ngx_pnalloc(c->pool, cmd.len);
  503.     if (s->smtp_from.data == NULL) {
  504.         return NGX_ERROR;
  505.     }

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

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

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

  510.     return NGX_OK;
  511. }


  512. static ngx_int_t
  513. ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c)
  514. {
  515.     ngx_str_t  *arg, cmd;

  516.     if (s->smtp_from.len == 0) {
  517.         ngx_str_set(&s->out, smtp_bad_sequence);
  518.         return NGX_OK;
  519.     }

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

  524.     arg = s->args.elts;
  525.     arg += s->args.nelts - 1;

  526.     cmd.len = arg->data + arg->len - s->cmd.data;
  527.     cmd.data = s->cmd.data;

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

  529.     s->smtp_to.data = ngx_pnalloc(c->pool, cmd.len);
  530.     if (s->smtp_to.data == NULL) {
  531.         return NGX_ERROR;
  532.     }

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

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

  536.     s->auth_method = NGX_MAIL_AUTH_NONE;

  537.     return NGX_DONE;
  538. }


  539. static ngx_int_t
  540. ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c)
  541. {
  542.     ngx_str_null(&s->smtp_from);
  543.     ngx_str_null(&s->smtp_to);
  544.     ngx_str_set(&s->out, smtp_ok);

  545.     return NGX_OK;
  546. }


  547. static ngx_int_t
  548. ngx_mail_smtp_starttls(ngx_mail_session_t *s, ngx_connection_t *c)
  549. {
  550. #if (NGX_MAIL_SSL)
  551.     ngx_mail_ssl_conf_t  *sslcf;

  552.     if (c->ssl == NULL) {
  553.         sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
  554.         if (sslcf->starttls) {

  555.             /*
  556.              * RFC3207 requires us to discard any knowledge
  557.              * obtained from client before STARTTLS.
  558.              */

  559.             ngx_str_null(&s->smtp_helo);
  560.             ngx_str_null(&s->smtp_from);
  561.             ngx_str_null(&s->smtp_to);

  562.             s->buffer->pos = s->buffer->start;
  563.             s->buffer->last = s->buffer->start;

  564.             c->read->handler = ngx_mail_starttls_handler;
  565.             return NGX_OK;
  566.         }
  567.     }

  568. #endif

  569.     return NGX_MAIL_PARSE_INVALID_COMMAND;
  570. }


  571. static ngx_int_t
  572. ngx_mail_smtp_discard_command(ngx_mail_session_t *s, ngx_connection_t *c,
  573.     char *err)
  574. {
  575.     ssize_t    n;

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

  577.     if (n == NGX_ERROR || n == 0) {
  578.         ngx_mail_close_connection(c);
  579.         return NGX_ERROR;
  580.     }

  581.     if (n > 0) {
  582.         s->buffer->last += n;
  583.     }

  584.     if (n == NGX_AGAIN) {
  585.         if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
  586.             ngx_mail_session_internal_server_error(s);
  587.             return NGX_ERROR;
  588.         }

  589.         return NGX_AGAIN;
  590.     }

  591.     ngx_mail_smtp_log_rejected_command(s, c, err);

  592.     s->buffer->pos = s->buffer->start;
  593.     s->buffer->last = s->buffer->start;

  594.     return NGX_OK;
  595. }


  596. static void
  597. ngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s, ngx_connection_t *c,
  598.     char *err)
  599. {
  600.     u_char      ch;
  601.     ngx_str_t   cmd;
  602.     ngx_uint_t  i;

  603.     if (c->log->log_level < NGX_LOG_INFO) {
  604.         return;
  605.     }

  606.     cmd.len = s->buffer->last - s->buffer->start;
  607.     cmd.data = s->buffer->start;

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

  610.         if (ch != CR && ch != LF) {
  611.             continue;
  612.         }

  613.         cmd.data[i] = '_';
  614.     }

  615.     cmd.len = i;

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