src/mail/ngx_mail_handler.c - nginx source code

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.          * AcceptEx() already gave this address.
  46.          */

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

  51.         sa = c->local_sockaddr;

  52.         switch (sa->sa_family) {

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

  56.             addr6 = port->addrs;

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

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

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

  64.             break;
  65. #endif

  66.         default: /* AF_INET */
  67.             sin = (struct sockaddr_in *) sa;

  68.             addr = port->addrs;

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

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

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

  76.             break;
  77.         }

  78.     } else {
  79.         switch (c->local_sockaddr->sa_family) {

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

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

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

  97.     s->signature = NGX_MAIL_MODULE;

  98.     s->main_conf = addr_conf->ctx->main_conf;
  99.     s->srv_conf = addr_conf->ctx->srv_conf;

  100. #if (NGX_MAIL_SSL)
  101.     s->ssl = addr_conf->ssl;
  102. #endif

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

  104.     c->data = s;
  105.     s->connection = c;

  106.     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

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

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

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

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

  116.     ctx->client = &c->addr_text;
  117.     ctx->session = s;

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

  122.     c->log_error = NGX_ERROR_INFO;

  123.     rev = c->read;
  124.     rev->handler = ngx_mail_init_session_handler;

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

  127.         rev->handler = ngx_mail_proxy_protocol_handler;

  128.         if (!rev->ready) {
  129.             ngx_add_timer(rev, cscf->timeout);

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

  133.             return;
  134.         }
  135.     }

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

  140.     rev->handler(rev);
  141. }


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

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

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

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

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

  163.     err = ngx_socket_errno;

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

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

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

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

  175.             return;
  176.         }

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

  178.         ngx_mail_close_connection(c);
  179.         return;
  180.     }

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

  182.     if (p == NULL) {
  183.         ngx_mail_close_connection(c);
  184.         return;
  185.     }

  186.     size = p - buf;

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

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

  195.     ngx_mail_init_session_handler(rev);
  196. }


  197. static void
  198. ngx_mail_init_session_handler(ngx_event_t *rev)
  199. {
  200.     ngx_connection_t  *c;

  201.     c = rev->data;

  202. #if (NGX_MAIL_SSL)
  203.     {
  204.     ngx_mail_session_t   *s;
  205.     ngx_mail_ssl_conf_t  *sslcf;

  206.     s = c->data;

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

  209.         sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);

  210.         ngx_mail_ssl_init_connection(&sslcf->ssl, c);
  211.         return;
  212.     }

  213.     }
  214. #endif

  215.     ngx_mail_init_session(c);
  216. }


  217. #if (NGX_MAIL_SSL)

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

  224.     c = rev->data;
  225.     s = c->data;
  226.     s->starttls = 1;

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

  228.     sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);

  229.     ngx_mail_ssl_init_connection(&sslcf->ssl, c);
  230. }


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

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

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

  241.         s = c->data;

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

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

  247.         return;
  248.     }

  249.     ngx_mail_ssl_handshake_handler(c);
  250. }


  251. static void
  252. ngx_mail_ssl_handshake_handler(ngx_connection_t *c)
  253. {
  254.     ngx_mail_session_t        *s;
  255.     ngx_mail_core_srv_conf_t  *cscf;

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

  257.         s = c->data;

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

  261.         if (s->starttls) {
  262.             cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

  263.             c->read->handler = cscf->protocol->init_protocol;
  264.             c->write->handler = ngx_mail_send;

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

  266.             return;
  267.         }

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

  269.         ngx_mail_init_session(c);
  270.         return;
  271.     }

  272.     ngx_mail_close_connection(c);
  273. }


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

  281.     sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);

  282.     if (!sslcf->verify) {
  283.         return NGX_OK;
  284.     }

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

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

  292.         ngx_ssl_remove_cached_session(c->ssl->session_ctx,
  293.                                       (SSL_get0_session(c->ssl->connection)));

  294.         cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

  295.         s->out = cscf->protocol->cert_error;
  296.         s->quit = 1;

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

  298.         ngx_mail_send(s->connection->write);
  299.         return NGX_ERROR;
  300.     }

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

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

  306.             ngx_ssl_remove_cached_session(c->ssl->session_ctx,
  307.                                        (SSL_get0_session(c->ssl->connection)));

  308.             cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

  309.             s->out = cscf->protocol->no_cert;
  310.             s->quit = 1;

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

  312.             ngx_mail_send(s->connection->write);
  313.             return NGX_ERROR;
  314.         }

  315.         X509_free(cert);
  316.     }

  317.     return NGX_OK;
  318. }

  319. #endif


  320. static void
  321. ngx_mail_init_session(ngx_connection_t *c)
  322. {
  323.     ngx_mail_session_t        *s;
  324.     ngx_mail_core_srv_conf_t  *cscf;

  325.     s = c->data;

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

  327.     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

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

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

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

  335.     cscf->protocol->init_session(s, c);
  336. }


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

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

  351.     return NGX_OK;
  352. }


  353. #if (NGX_MAIL_SSL)

  354. ngx_int_t
  355. ngx_mail_starttls_only(ngx_mail_session_t *s, ngx_connection_t *c)
  356. {
  357.     ngx_mail_ssl_conf_t  *sslcf;

  358.     if (c->ssl) {
  359.         return 0;
  360.     }

  361.     sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);

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

  365.     return 0;
  366. }

  367. #endif


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

  373.     arg = s->args.elts;

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

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

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

  387.     p = plain.data;
  388.     last = p + plain.len;

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

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

  395.     s->login.data = p;

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

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

  402.     s->login.len = p++ - s->login.data;

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

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

  409.     return NGX_DONE;
  410. }


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

  416.     arg = s->args.elts;

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

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

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

  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;

  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.     s->passwd.data = ngx_pnalloc(c->pool,
  442.                                  ngx_base64_decoded_length(arg[0].len));
  443.     if (s->passwd.data == NULL) {
  444.         return NGX_ERROR;
  445.     }

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

  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;

  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.     s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len));
  487.     if (s->login.data == NULL) {
  488.         return NGX_ERROR;
  489.     }

  490.     if (ngx_decode_base64(&s->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.     p = s->login.data;
  496.     last = p + s->login.len;

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

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

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

  512.     s->auth_method = NGX_MAIL_AUTH_CRAM_MD5;

  513.     return NGX_DONE;
  514. }


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

  520.     arg = s->args.elts;

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

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

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

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

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

  536.     s->auth_method = NGX_MAIL_AUTH_EXTERNAL;

  537.     return NGX_DONE;
  538. }


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

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

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

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

  558.         return;
  559.     }

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

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

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

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

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

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

  577.         return;
  578.     }

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

  583.     /* n == NGX_AGAIN */

  584. again:

  585.     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

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

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


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

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

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

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

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

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

  614.     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

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

  616.     if (rc == NGX_AGAIN) {

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

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

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

  624.         s->quit = 1;

  625.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  626.     }

  627.     if (rc == NGX_MAIL_PARSE_INVALID_COMMAND) {

  628.         s->errors++;

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

  634.         return rc;
  635.     }

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

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

  643.     return NGX_OK;
  644. }


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

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

  653.     s->state = 0;

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

  657.     s->login_attempt++;

  658.     ngx_mail_auth_http_init(s);
  659. }


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

  664.     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

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

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


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

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

  675. #if (NGX_MAIL_SSL)

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

  682. #endif

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

  686.     c->destroyed = 1;

  687.     pool = c->pool;

  688.     ngx_close_connection(c);

  689.     ngx_destroy_pool(pool);
  690. }


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

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

  702.     ctx = log->data;

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

  706.     s = ctx->session;

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

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

  715.     if (s->login.len == 0) {
  716.         return p;
  717.     }

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

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