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

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, *pos, *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.     pos = 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++ - pos;
  403.     s->login.data = pos;

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

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

  410.     return NGX_DONE;
  411. }


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

  417.     arg = s->args.elts;

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

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

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

  429.     s->login = login;

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

  432.     return NGX_OK;
  433. }


  434. ngx_int_t
  435. ngx_mail_auth_login_password(ngx_mail_session_t *s, ngx_connection_t *c)
  436. {
  437.     ngx_str_t  *arg, passwd;

  438.     arg = s->args.elts;

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

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

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

  452.     s->passwd = passwd;

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

  457.     return NGX_DONE;
  458. }


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

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

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

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

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

  476.     s->out.len = n;
  477.     s->out.data = p;

  478.     return NGX_OK;
  479. }


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

  485.     arg = s->args.elts;

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

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

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

  497.     s->login = login;

  498.     p = s->login.data;
  499.     last = p + s->login.len;

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

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

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

  515.     s->auth_method = NGX_MAIL_AUTH_CRAM_MD5;

  516.     return NGX_DONE;
  517. }


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

  523.     arg = s->args.elts;

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

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

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

  535.     s->login.len = external.len;
  536.     s->login.data = external.data;

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

  539.     s->auth_method = NGX_MAIL_AUTH_EXTERNAL;

  540.     return NGX_DONE;
  541. }


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

  549.     c = wev->data;
  550.     s = c->data;

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

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

  561.         return;
  562.     }

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

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

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

  570.         if (wev->timer_set) {
  571.             ngx_del_timer(wev);
  572.         }

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

  577.         if (s->blocked) {
  578.             c->read->handler(c->read);
  579.         }

  580.         return;
  581.     }

  582.     if (n == NGX_ERROR) {
  583.         ngx_mail_close_connection(c);
  584.         return;
  585.     }

  586.     /* n == NGX_AGAIN */

  587. again:

  588.     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

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

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


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

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

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

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

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

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

  617.     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

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

  619.     if (rc == NGX_AGAIN) {

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

  623.         l.len = s->buffer->last - s->buffer->start;
  624.         l.data = s->buffer->start;

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

  627.         s->quit = 1;

  628.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  629.     }

  630.     if (rc == NGX_MAIL_PARSE_INVALID_COMMAND) {

  631.         s->errors++;

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

  637.         return rc;
  638.     }

  639.     if (rc == NGX_IMAP_NEXT) {
  640.         return rc;
  641.     }

  642.     if (rc == NGX_ERROR) {
  643.         ngx_mail_close_connection(c);
  644.         return NGX_ERROR;
  645.     }

  646.     return NGX_OK;
  647. }


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

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

  656.     s->state = 0;

  657.     if (c->read->timer_set) {
  658.         ngx_del_timer(c->read);
  659.     }

  660.     s->login_attempt++;

  661.     ngx_mail_auth_http_init(s);
  662. }


  663. void
  664. ngx_mail_session_internal_server_error(ngx_mail_session_t *s)
  665. {
  666.     ngx_mail_core_srv_conf_t  *cscf;

  667.     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

  668.     s->out = cscf->protocol->internal_server_error;
  669.     s->quit = 1;

  670.     ngx_mail_send(s->connection->write);
  671. }


  672. void
  673. ngx_mail_close_connection(ngx_connection_t *c)
  674. {
  675.     ngx_pool_t  *pool;

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

  678. #if (NGX_MAIL_SSL)

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

  685. #endif

  686. #if (NGX_STAT_STUB)
  687.     (void) ngx_atomic_fetch_add(ngx_stat_active, -1);
  688. #endif

  689.     c->destroyed = 1;

  690.     pool = c->pool;

  691.     ngx_close_connection(c);

  692.     ngx_destroy_pool(pool);
  693. }


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

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

  705.     ctx = log->data;

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

  709.     s = ctx->session;

  710.     if (s == NULL) {
  711.         return p;
  712.     }

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

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

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

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

  727.     return p;
  728. }