src/mail/ngx_mail_handler.c - nginx-1.31.9 nginx/ @ 2a3a19c6f

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. static void ngx_mail_proxy_protocol_handler(ngx_event_t *rev);
  10. static void ngx_mail_init_session_handler(ngx_event_t *rev);
  11. static void ngx_mail_init_session(ngx_connection_t *c);

  12. #if (NGX_MAIL_SSL)
  13. static void ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c);
  14. static void ngx_mail_ssl_handshake_handler(ngx_connection_t *c);
  15. static ngx_int_t ngx_mail_verify_cert(ngx_mail_session_t *s,
  16.     ngx_connection_t *c);
  17. #endif


  18. void
  19. ‌ngx_mail_init_connection(ngx_connection_t *c)
  20. {
  21.     size_t                     len;
  22.     ngx_uint_t                 i;
  23.     ngx_event_t               *rev;
  24.     ngx_mail_port_t           *port;
  25.     struct sockaddr           *sa;
  26.     struct sockaddr_in        *sin;
  27.     ngx_mail_log_ctx_t        *ctx;
  28.     ngx_mail_in_addr_t        *addr;
  29.     ngx_mail_session_t        *s;
  30.     ngx_mail_addr_conf_t      *addr_conf;
  31.     ngx_mail_core_srv_conf_t  *cscf;
  32.     u_char                     text[NGX_SOCKADDR_STRLEN];
  33. #if (NGX_HAVE_INET6)
  34.     struct sockaddr_in6       *sin6;
  35.     ngx_mail_in6_addr_t       *addr6;
  36. #endif


  37.     /* find the server configuration for the address:port */

  38.     port = c->listening->servers;

  39.     if (port->naddrs > 1) {

  40.         /*
  41.          * There are several addresses on this port and one of them
  42.          * is the "*:port" wildcard so getsockname() is needed to determine
  43.          * the server address.
  44.          */

  45.         if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
  46.             ngx_mail_close_connection(c);
  47.             return;
  48.         }

  49.         sa = c->local_sockaddr;

  50.         switch (sa->sa_family) {

  51. #if (NGX_HAVE_INET6)
  52.         case AF_INET6:
  53.             sin6 = (struct sockaddr_in6 *) sa;

  54.             addr6 = port->addrs;

  55.             /* the last address is "*" */

  56.             for (i = 0; i < port->naddrs - 1; i++) {
  57.                 if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {
  58.                     break;
  59.                 }
  60.             }

  61.             addr_conf = &addr6[i].conf;

  62.             break;
  63. #endif

  64.         default: /* AF_INET */
  65.             sin = (struct sockaddr_in *) sa;

  66.             addr = port->addrs;

  67.             /* the last address is "*" */

  68.             for (i = 0; i < port->naddrs - 1; i++) {
  69.                 if (addr[i].addr == sin->sin_addr.s_addr) {
  70.                     break;
  71.                 }
  72.             }

  73.             addr_conf = &addr[i].conf;

  74.             break;
  75.         }

  76.     } else {
  77.         switch (c->local_sockaddr->sa_family) {

  78. #if (NGX_HAVE_INET6)
  79.         case AF_INET6:
  80.             addr6 = port->addrs;
  81.             addr_conf = &addr6[0].conf;
  82.             break;
  83. #endif

  84.         default: /* AF_INET */
  85.             addr = port->addrs;
  86.             addr_conf = &addr[0].conf;
  87.             break;
  88.         }
  89.     }

  90.     s = ngx_pcalloc(c->pool, sizeof(ngx_mail_session_t));
  91.     if (s == NULL) {
  92.         ngx_mail_close_connection(c);
  93.         return;
  94.     }

  95.     s->signature = NGX_MAIL_MODULE;

  96.     s->main_conf = addr_conf->ctx->main_conf;
  97.     s->srv_conf = addr_conf->ctx->srv_conf;

  98. #if (NGX_MAIL_SSL)
  99.     s->ssl = addr_conf->ssl;
  100. #endif

  101.     s->addr_text = &addr_conf->addr_text;

  102.     c->data = s;
  103.     s->connection = c;

  104.     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

  105.     ngx_set_connection_log(c, cscf->error_log);

  106.     len = ngx_sock_ntop(c->sockaddr, c->socklen, text, NGX_SOCKADDR_STRLEN, 1);

  107.     ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%uA client %*s connected to %V",
  108.                   c->number, len, text, s->addr_text);

  109.     ctx = ngx_palloc(c->pool, sizeof(ngx_mail_log_ctx_t));
  110.     if (ctx == NULL) {
  111.         ngx_mail_close_connection(c);
  112.         return;
  113.     }

  114.     ctx->client = &c->addr_text;
  115.     ctx->session = s;

  116.     c->log->connection = c->number;
  117.     c->log->handler = ngx_mail_log_error;
  118.     c->log->data = ctx;
  119.     c->log->action = "sending client greeting line";

  120.     c->log_error = NGX_ERROR_INFO;

  121.     rev = c->read;
  122.     rev->handler = ngx_mail_init_session_handler;

  123.     if (addr_conf->proxy_protocol) {
  124.         c->log->action = "reading PROXY protocol";

  125.         rev->handler = ngx_mail_proxy_protocol_handler;

  126.         if (!rev->ready) {
  127.             ngx_add_timer(rev, cscf->timeout);

  128.             if (ngx_handle_read_event(rev, 0) != NGX_OK) {
  129.                 ngx_mail_close_connection(c);
  130.             }

  131.             return;
  132.         }
  133.     }

  134.     if (ngx_use_accept_mutex) {
  135.         ngx_post_event(rev, &ngx_posted_events);
  136.         return;
  137.     }

  138.     rev->handler(rev);
  139. }


  140. static void
  141. ‌ngx_mail_proxy_protocol_handler(ngx_event_t *rev)
  142. {
  143.     u_char                    *p, buf[NGX_PROXY_PROTOCOL_MAX_HEADER];
  144.     size_t                     size;
  145.     ssize_t                    n;
  146.     ngx_err_t                  err;
  147.     ngx_connection_t          *c;
  148.     ngx_mail_session_t        *s;
  149.     ngx_mail_core_srv_conf_t  *cscf;

  150.     c = rev->data;
  151.     s = c->data;

  152.     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0,
  153.                    "mail PROXY protocol handler");

  154.     if (rev->timedout) {
  155.         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
  156.         c->timedout = 1;
  157.         ngx_mail_close_connection(c);
  158.         return;
  159.     }

  160.     n = recv(c->fd, (char *) buf, sizeof(buf), MSG_PEEK);

  161.     err = ngx_socket_errno;

  162.     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "recv(): %z", n);

  163.     if (n == -1) {
  164.         if (err == NGX_EAGAIN) {
  165.             rev->ready = 0;

  166.             if (!rev->timer_set) {
  167.                 cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
  168.                 ngx_add_timer(rev, cscf->timeout);
  169.             }

  170.             if (ngx_handle_read_event(rev, 0) != NGX_OK) {
  171.                 ngx_mail_close_connection(c);
  172.             }

  173.             return;
  174.         }

  175.         ngx_connection_error(c, err, "recv() failed");

  176.         ngx_mail_close_connection(c);
  177.         return;
  178.     }

  179.     p = ngx_proxy_protocol_read(c, buf, buf + n);

  180.     if (p == NULL) {
  181.         ngx_mail_close_connection(c);
  182.         return;
  183.     }

  184.     size = p - buf;

  185.     if (c->recv(c, buf, size) != (ssize_t) size) {
  186.         ngx_mail_close_connection(c);
  187.         return;
  188.     }

  189.     if (ngx_mail_realip_handler(s) != NGX_OK) {
  190.         ngx_mail_close_connection(c);
  191.         return;
  192.     }

  193.     ngx_mail_init_session_handler(rev);
  194. }


  195. static void
  196. ‌ngx_mail_init_session_handler(ngx_event_t *rev)
  197. {
  198.     ngx_connection_t  *c;

  199.     c = rev->data;

  200. #if (NGX_MAIL_SSL)
  201.     {
  202.     ngx_mail_session_t   *s;
  203.     ngx_mail_ssl_conf_t  *sslcf;

  204.     s = c->data;

  205.     if (s->ssl) {
  206.         c->log->action = "SSL handshaking";

  207.         sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);

  208.         ngx_mail_ssl_init_connection(&sslcf->ssl, c);
  209.         return;
  210.     }

  211.     }
  212. #endif

  213.     ngx_mail_init_session(c);
  214. }


  215. #if (NGX_MAIL_SSL)

  216. void
  217. ‌ngx_mail_starttls_handler(ngx_event_t *rev)
  218. {
  219.     ngx_connection_t     *c;
  220.     ngx_mail_session_t   *s;
  221.     ngx_mail_ssl_conf_t  *sslcf;

  222.     c = rev->data;
  223.     s = c->data;
  224.     s->starttls = 1;

  225.     c->log->action = "in starttls state";

  226.     sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);

  227.     ngx_mail_ssl_init_connection(&sslcf->ssl, c);
  228. }


  229. static void
  230. ‌ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c)
  231. {
  232.     ngx_mail_session_t        *s;
  233.     ngx_mail_core_srv_conf_t  *cscf;

  234.     if (ngx_ssl_create_connection(ssl, c, 0) != NGX_OK) {
  235.         ngx_mail_close_connection(c);
  236.         return;
  237.     }

  238.     if (ngx_ssl_handshake(c) == NGX_AGAIN) {

  239.         s = c->data;

  240.         if (!c->read->timer_set) {
  241.             cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
  242.             ngx_add_timer(c->read, cscf->timeout);
  243.         }

  244.         c->ssl->handler = ngx_mail_ssl_handshake_handler;

  245.         return;
  246.     }

  247.     ngx_mail_ssl_handshake_handler(c);
  248. }


  249. static void
  250. ‌ngx_mail_ssl_handshake_handler(ngx_connection_t *c)
  251. {
  252.     ngx_mail_session_t        *s;
  253.     ngx_mail_core_srv_conf_t  *cscf;

  254.     if (c->ssl->handshaked) {

  255.         s = c->data;

  256.         if (ngx_mail_verify_cert(s, c) != NGX_OK) {
  257.             return;
  258.         }

  259.         if (s->starttls) {
  260.             cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

  261.             c->read->handler = cscf->protocol->init_protocol;
  262.             c->write->handler = ngx_mail_send;

  263.             cscf->protocol->init_protocol(c->read);

  264.             return;
  265.         }

  266.         c->read->ready = 0;

  267.         ngx_mail_init_session(c);
  268.         return;
  269.     }

  270.     ngx_mail_close_connection(c);
  271. }


  272. static ngx_int_t
  273. ‌ngx_mail_verify_cert(ngx_mail_session_t *s, ngx_connection_t *c)
  274. {
  275.     long                       rc;
  276.     X509                      *cert;
  277.     ngx_mail_ssl_conf_t       *sslcf;
  278.     ngx_mail_core_srv_conf_t  *cscf;

  279.     sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);

  280.     if (!sslcf->verify) {
  281.         return NGX_OK;
  282.     }

  283.     rc = SSL_get_verify_result(c->ssl->connection);

  284.     if (rc != X509_V_OK
  285.         && (sslcf->verify != 3 || !ngx_ssl_verify_error_optional(rc)))
  286.     {
  287.         ngx_log_error(NGX_LOG_INFO, c->log, 0,
  288.                       "client SSL certificate verify error: (%l:%s)",
  289.                       rc, X509_verify_cert_error_string(rc));

  290.         ngx_ssl_remove_cached_session(c->ssl->session_ctx,
  291.                                       (SSL_get0_session(c->ssl->connection)));

  292.         cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

  293.         s->out = cscf->protocol->cert_error;
  294.         s->quit = 1;

  295.         c->write->handler = ngx_mail_send;

  296.         ngx_mail_send(s->connection->write);
  297.         return NGX_ERROR;
  298.     }

  299.     if (sslcf->verify == 1) {
  300.         cert = SSL_get_peer_certificate(c->ssl->connection);

  301.         if (cert == NULL) {
  302.             ngx_log_error(NGX_LOG_INFO, c->log, 0,
  303.                           "client sent no required SSL certificate");

  304.             ngx_ssl_remove_cached_session(c->ssl->session_ctx,
  305.                                        (SSL_get0_session(c->ssl->connection)));

  306.             cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

  307.             s->out = cscf->protocol->no_cert;
  308.             s->quit = 1;

  309.             c->write->handler = ngx_mail_send;

  310.             ngx_mail_send(s->connection->write);
  311.             return NGX_ERROR;
  312.         }

  313.         X509_free(cert);
  314.     }

  315.     return NGX_OK;
  316. }

  317. #endif


  318. static void
  319. ‌ngx_mail_init_session(ngx_connection_t *c)
  320. {
  321.     ngx_mail_session_t        *s;
  322.     ngx_mail_core_srv_conf_t  *cscf;

  323.     s = c->data;

  324.     c->log->action = "sending client greeting line";

  325.     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

  326.     s->protocol = cscf->protocol->type;

  327.     s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_mail_max_module);
  328.     if (s->ctx == NULL) {
  329.         ngx_mail_session_internal_server_error(s);
  330.         return;
  331.     }

  332.     c->write->handler = ngx_mail_send;

  333.     cscf->protocol->init_session(s, c);
  334. }


  335. ngx_int_t
  336. ‌ngx_mail_salt(ngx_mail_session_t *s, ngx_connection_t *c,
  337.     ngx_mail_core_srv_conf_t *cscf)
  338. {
  339.     s->salt.data = ngx_pnalloc(c->pool,
  340.                                sizeof(" <18446744073709551616.@>" CRLF) - 1
  341.                                + NGX_TIME_T_LEN
  342.                                + cscf->server_name.len);
  343.     if (s->salt.data == NULL) {
  344.         return NGX_ERROR;
  345.     }

  346.     s->salt.len = ngx_sprintf(s->salt.data, "<%ul.%T@%V>" CRLF,
  347.                               ngx_random(), ngx_time(), &cscf->server_name)
  348.                   - s->salt.data;

  349.     return NGX_OK;
  350. }


  351. #if (NGX_MAIL_SSL)

  352. ngx_int_t
  353. ‌ngx_mail_starttls_only(ngx_mail_session_t *s, ngx_connection_t *c)
  354. {
  355.     ngx_mail_ssl_conf_t  *sslcf;

  356.     if (c->ssl) {
  357.         return 0;
  358.     }

  359.     sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);

  360.     if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
  361.         return 1;
  362.     }

  363.     return 0;
  364. }

  365. #endif


  366. ngx_int_t
  367. ‌ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n)
  368. {
  369.     u_char     *p, *pos, *last;
  370.     ngx_str_t  *arg, plain;

  371.     arg = s->args.elts;

  372. #if (NGX_DEBUG_MAIL_PASSWD)
  373.     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
  374.                    "mail auth plain: \"%V\"", &arg[n]);
  375. #endif

  376.     plain.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
  377.     if (plain.data == NULL) {
  378.         return NGX_ERROR;
  379.     }

  380.     if (ngx_decode_base64(&plain, &arg[n]) != NGX_OK) {
  381.         ngx_log_error(NGX_LOG_INFO, c->log, 0,
  382.             "client sent invalid base64 encoding in AUTH PLAIN command");
  383.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  384.     }

  385.     p = plain.data;
  386.     last = p + plain.len;

  387.     while (p < last && *p++) { /* void */ }

  388.     if (p == last) {
  389.         ngx_log_error(NGX_LOG_INFO, c->log, 0,
  390.                       "client sent invalid login in AUTH PLAIN command");
  391.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  392.     }

  393.     pos = p;

  394.     while (p < last && *p) { p++; }

  395.     if (p == last) {
  396.         ngx_log_error(NGX_LOG_INFO, c->log, 0,
  397.                       "client sent invalid password in AUTH PLAIN command");
  398.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  399.     }

  400.     s->login.len = p++ - pos;
  401.     s->login.data = pos;

  402.     s->passwd.len = last - p;
  403.     s->passwd.data = p;

  404. #if (NGX_DEBUG_MAIL_PASSWD)
  405.     ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
  406.                    "mail auth plain: \"%V\" \"%V\"", &s->login, &s->passwd);
  407. #endif

  408.     return NGX_DONE;
  409. }


  410. ngx_int_t
  411. ‌ngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c,
  412.     ngx_uint_t n)
  413. {
  414.     ngx_str_t  *arg, login;

  415.     arg = s->args.elts;

  416.     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
  417.                    "mail auth login username: \"%V\"", &arg[n]);

  418.     login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
  419.     if (login.data == NULL) {
  420.         return NGX_ERROR;
  421.     }

  422.     if (ngx_decode_base64(&login, &arg[n]) != NGX_OK) {
  423.         ngx_log_error(NGX_LOG_INFO, c->log, 0,
  424.             "client sent invalid base64 encoding in AUTH LOGIN command");
  425.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  426.     }

  427.     s->login = login;

  428.     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
  429.                    "mail auth login username: \"%V\"", &s->login);

  430.     return NGX_OK;
  431. }


  432. ngx_int_t
  433. ‌ngx_mail_auth_login_password(ngx_mail_session_t *s, ngx_connection_t *c)
  434. {
  435.     ngx_str_t  *arg, passwd;

  436.     arg = s->args.elts;

  437. #if (NGX_DEBUG_MAIL_PASSWD)
  438.     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
  439.                    "mail auth login password: \"%V\"", &arg[0]);
  440. #endif

  441.     passwd.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len));
  442.     if (passwd.data == NULL) {
  443.         return NGX_ERROR;
  444.     }

  445.     if (ngx_decode_base64(&passwd, &arg[0]) != NGX_OK) {
  446.         ngx_log_error(NGX_LOG_INFO, c->log, 0,
  447.             "client sent invalid base64 encoding in AUTH LOGIN command");
  448.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  449.     }

  450.     s->passwd = passwd;

  451. #if (NGX_DEBUG_MAIL_PASSWD)
  452.     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
  453.                    "mail auth login password: \"%V\"", &s->passwd);
  454. #endif

  455.     return NGX_DONE;
  456. }


  457. ngx_int_t
  458. ‌ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s, ngx_connection_t *c,
  459.     char *prefix, size_t len)
  460. {
  461.     u_char      *p;
  462.     ngx_str_t    salt;
  463.     ngx_uint_t   n;

  464.     p = ngx_pnalloc(c->pool, len + ngx_base64_encoded_length(s->salt.len) + 2);
  465.     if (p == NULL) {
  466.         return NGX_ERROR;
  467.     }

  468.     salt.data = ngx_cpymem(p, prefix, len);
  469.     s->salt.len -= 2;

  470.     ngx_encode_base64(&salt, &s->salt);

  471.     s->salt.len += 2;
  472.     n = len + salt.len;
  473.     p[n++] = CR; p[n++] = LF;

  474.     s->out.len = n;
  475.     s->out.data = p;

  476.     return NGX_OK;
  477. }


  478. ngx_int_t
  479. ‌ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c)
  480. {
  481.     u_char     *p, *last;
  482.     ngx_str_t  *arg, login;

  483.     arg = s->args.elts;

  484.     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
  485.                    "mail auth cram-md5: \"%V\"", &arg[0]);

  486.     login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len));
  487.     if (login.data == NULL) {
  488.         return NGX_ERROR;
  489.     }

  490.     if (ngx_decode_base64(&login, &arg[0]) != NGX_OK) {
  491.         ngx_log_error(NGX_LOG_INFO, c->log, 0,
  492.             "client sent invalid base64 encoding in AUTH CRAM-MD5 command");
  493.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  494.     }

  495.     s->login = login;

  496.     p = s->login.data;
  497.     last = p + s->login.len;

  498.     while (p < last) {
  499.         if (*p++ == ' ') {
  500.             s->login.len = p - s->login.data - 1;
  501.             s->passwd.len = last - p;
  502.             s->passwd.data = p;
  503.             break;
  504.         }
  505.     }

  506.     if (s->passwd.len != 32) {
  507.         ngx_log_error(NGX_LOG_INFO, c->log, 0,
  508.             "client sent invalid CRAM-MD5 hash in AUTH CRAM-MD5 command");
  509.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  510.     }

  511.     ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
  512.                    "mail auth cram-md5: \"%V\" \"%V\"", &s->login, &s->passwd);

  513.     s->auth_method = NGX_MAIL_AUTH_CRAM_MD5;

  514.     return NGX_DONE;
  515. }


  516. ngx_int_t
  517. ‌ngx_mail_auth_external(ngx_mail_session_t *s, ngx_connection_t *c,
  518.     ngx_uint_t n)
  519. {
  520.     ngx_str_t  *arg, external;

  521.     arg = s->args.elts;

  522.     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
  523.                    "mail auth external: \"%V\"", &arg[n]);

  524.     external.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
  525.     if (external.data == NULL) {
  526.         return NGX_ERROR;
  527.     }

  528.     if (ngx_decode_base64(&external, &arg[n]) != NGX_OK) {
  529.         ngx_log_error(NGX_LOG_INFO, c->log, 0,
  530.             "client sent invalid base64 encoding in AUTH EXTERNAL command");
  531.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  532.     }

  533.     s->login.len = external.len;
  534.     s->login.data = external.data;

  535.     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
  536.                    "mail auth external: \"%V\"", &s->login);

  537.     s->auth_method = NGX_MAIL_AUTH_EXTERNAL;

  538.     return NGX_DONE;
  539. }


  540. void
  541. ‌ngx_mail_send(ngx_event_t *wev)
  542. {
  543.     ngx_int_t                  n;
  544.     ngx_connection_t          *c;
  545.     ngx_mail_session_t        *s;
  546.     ngx_mail_core_srv_conf_t  *cscf;

  547.     c = wev->data;
  548.     s = c->data;

  549.     if (wev->timedout) {
  550.         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
  551.         c->timedout = 1;
  552.         ngx_mail_close_connection(c);
  553.         return;
  554.     }

  555.     if (s->out.len == 0) {
  556.         if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
  557.             ngx_mail_close_connection(c);
  558.         }

  559.         return;
  560.     }

  561.     n = c->send(c, s->out.data, s->out.len);

  562.     if (n > 0) {
  563.         s->out.data += n;
  564.         s->out.len -= n;

  565.         if (s->out.len != 0) {
  566.             goto again;
  567.         }

  568.         if (wev->timer_set) {
  569.             ngx_del_timer(wev);
  570.         }

  571.         if (s->quit) {
  572.             ngx_mail_close_connection(c);
  573.             return;
  574.         }

  575.         if (s->blocked) {
  576.             c->read->handler(c->read);
  577.         }

  578.         return;
  579.     }

  580.     if (n == NGX_ERROR) {
  581.         ngx_mail_close_connection(c);
  582.         return;
  583.     }

  584.     /* n == NGX_AGAIN */

  585. again:

  586.     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

  587.     ngx_add_timer(c->write, cscf->timeout);

  588.     if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
  589.         ngx_mail_close_connection(c);
  590.         return;
  591.     }
  592. }


  593. ngx_int_t
  594. ‌ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c)
  595. {
  596.     ssize_t                    n;
  597.     ngx_int_t                  rc;
  598.     ngx_str_t                  l;
  599.     ngx_mail_core_srv_conf_t  *cscf;

  600.     if (s->buffer->last < s->buffer->end) {

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

  602.         if (n == NGX_ERROR || n == 0) {
  603.             ngx_mail_close_connection(c);
  604.             return NGX_ERROR;
  605.         }

  606.         if (n > 0) {
  607.             s->buffer->last += n;
  608.         }

  609.         if (n == NGX_AGAIN) {
  610.             if (s->buffer->pos == s->buffer->last) {
  611.                 return NGX_AGAIN;
  612.             }
  613.         }
  614.     }

  615.     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

  616.     rc = cscf->protocol->parse_command(s);

  617.     if (rc == NGX_AGAIN) {

  618.         if (s->buffer->last < s->buffer->end) {
  619.             return rc;
  620.         }

  621.         l.len = s->buffer->last - s->buffer->start;
  622.         l.data = s->buffer->start;

  623.         ngx_log_error(NGX_LOG_INFO, c->log, 0,
  624.                       "client sent too long command \"%V\"", &l);

  625.         s->quit = 1;

  626.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  627.     }

  628.     if (rc == NGX_MAIL_PARSE_INVALID_COMMAND) {

  629.         s->errors++;

  630.         if (s->errors >= cscf->max_errors) {
  631.             ngx_log_error(NGX_LOG_INFO, c->log, 0,
  632.                           "client sent too many invalid commands");
  633.             s->quit = 1;
  634.         }

  635.         return rc;
  636.     }

  637.     if (rc == NGX_IMAP_NEXT) {
  638.         return rc;
  639.     }

  640.     if (rc == NGX_ERROR) {
  641.         ngx_mail_close_connection(c);
  642.         return NGX_ERROR;
  643.     }

  644.     return NGX_OK;
  645. }


  646. void
  647. ‌ngx_mail_auth(ngx_mail_session_t *s, ngx_connection_t *c)
  648. {
  649.     s->args.nelts = 0;

  650.     if (s->buffer->pos == s->buffer->last) {
  651.         s->buffer->pos = s->buffer->start;
  652.         s->buffer->last = s->buffer->start;
  653.     }

  654.     s->state = 0;

  655.     if (c->read->timer_set) {
  656.         ngx_del_timer(c->read);
  657.     }

  658.     s->login_attempt++;

  659.     ngx_mail_auth_http_init(s);
  660. }


  661. void
  662. ‌ngx_mail_session_internal_server_error(ngx_mail_session_t *s)
  663. {
  664.     ngx_mail_core_srv_conf_t  *cscf;

  665.     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

  666.     s->out = cscf->protocol->internal_server_error;
  667.     s->quit = 1;

  668.     ngx_mail_send(s->connection->write);
  669. }


  670. void
  671. ‌ngx_mail_close_connection(ngx_connection_t *c)
  672. {
  673.     ngx_pool_t  *pool;

  674.     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
  675.                    "close mail connection: %d", c->fd);

  676. #if (NGX_MAIL_SSL)

  677.     if (c->ssl) {
  678.         if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
  679.             c->ssl->handler = ngx_mail_close_connection;
  680.             return;
  681.         }
  682.     }

  683. #endif

  684. #if (NGX_STAT_STUB)
  685.     (void) ngx_atomic_fetch_add(ngx_stat_active, -1);
  686. #endif

  687.     c->destroyed = 1;

  688.     pool = c->pool;

  689.     ngx_close_connection(c);

  690.     ngx_destroy_pool(pool);
  691. }


  692. u_char *
  693. ‌ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len)
  694. {
  695.     u_char              *p;
  696.     ngx_mail_session_t  *s;
  697.     ngx_mail_log_ctx_t  *ctx;

  698.     if (log->action) {
  699.         p = ngx_snprintf(buf, len, " while %s", log->action);
  700.         len -= p - buf;
  701.         buf = p;
  702.     }

  703.     ctx = log->data;

  704.     p = ngx_snprintf(buf, len, ", client: %V", ctx->client);
  705.     len -= p - buf;
  706.     buf = p;

  707.     s = ctx->session;

  708.     if (s == NULL) {
  709.         return p;
  710.     }

  711.     p = ngx_snprintf(buf, len, "%s, server: %V",
  712.                      s->starttls ? " using starttls" : "",
  713.                      s->addr_text);
  714.     len -= p - buf;
  715.     buf = p;

  716.     if (s->login.len) {
  717.         p = ngx_snprintf(buf, len, ", login: \"%V\"", &s->login);
  718.         len -= p - buf;
  719.         buf = p;
  720.     }

  721.     if (s->proxy == NULL) {
  722.         return p;
  723.     }

  724.     p = ngx_snprintf(buf, len, ", upstream: %V", s->proxy->upstream.name);

  725.     return p;
  726. }