src/mail/ngx_mail_proxy_module.c - nginx-1.7.10

Global variables defined

Data types 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_event_connect.h>
  9. #include <ngx_mail.h>


  10. typedef struct {
  11.     ngx_flag_t  enable;
  12.     ngx_flag_t  pass_error_message;
  13.     ngx_flag_t  xclient;
  14.     size_t      buffer_size;
  15.     ngx_msec_t  timeout;
  16. } ngx_mail_proxy_conf_t;


  17. static void ngx_mail_proxy_block_read(ngx_event_t *rev);
  18. static void ngx_mail_proxy_pop3_handler(ngx_event_t *rev);
  19. static void ngx_mail_proxy_imap_handler(ngx_event_t *rev);
  20. static void ngx_mail_proxy_smtp_handler(ngx_event_t *rev);
  21. static void ngx_mail_proxy_dummy_handler(ngx_event_t *ev);
  22. static ngx_int_t ngx_mail_proxy_read_response(ngx_mail_session_t *s,
  23.     ngx_uint_t state);
  24. static void ngx_mail_proxy_handler(ngx_event_t *ev);
  25. static void ngx_mail_proxy_upstream_error(ngx_mail_session_t *s);
  26. static void ngx_mail_proxy_internal_server_error(ngx_mail_session_t *s);
  27. static void ngx_mail_proxy_close_session(ngx_mail_session_t *s);
  28. static void *ngx_mail_proxy_create_conf(ngx_conf_t *cf);
  29. static char *ngx_mail_proxy_merge_conf(ngx_conf_t *cf, void *parent,
  30.     void *child);


  31. static ngx_command_t  ngx_mail_proxy_commands[] = {

  32.     { ngx_string("proxy"),
  33.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
  34.       ngx_conf_set_flag_slot,
  35.       NGX_MAIL_SRV_CONF_OFFSET,
  36.       offsetof(ngx_mail_proxy_conf_t, enable),
  37.       NULL },

  38.     { ngx_string("proxy_buffer"),
  39.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
  40.       ngx_conf_set_size_slot,
  41.       NGX_MAIL_SRV_CONF_OFFSET,
  42.       offsetof(ngx_mail_proxy_conf_t, buffer_size),
  43.       NULL },

  44.     { ngx_string("proxy_timeout"),
  45.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
  46.       ngx_conf_set_msec_slot,
  47.       NGX_MAIL_SRV_CONF_OFFSET,
  48.       offsetof(ngx_mail_proxy_conf_t, timeout),
  49.       NULL },

  50.     { ngx_string("proxy_pass_error_message"),
  51.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
  52.       ngx_conf_set_flag_slot,
  53.       NGX_MAIL_SRV_CONF_OFFSET,
  54.       offsetof(ngx_mail_proxy_conf_t, pass_error_message),
  55.       NULL },

  56.     { ngx_string("xclient"),
  57.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
  58.       ngx_conf_set_flag_slot,
  59.       NGX_MAIL_SRV_CONF_OFFSET,
  60.       offsetof(ngx_mail_proxy_conf_t, xclient),
  61.       NULL },

  62.       ngx_null_command
  63. };


  64. static ngx_mail_module_t  ngx_mail_proxy_module_ctx = {
  65.     NULL,                                  /* protocol */

  66.     NULL,                                  /* create main configuration */
  67.     NULL,                                  /* init main configuration */

  68.     ngx_mail_proxy_create_conf,            /* create server configuration */
  69.     ngx_mail_proxy_merge_conf              /* merge server configuration */
  70. };


  71. ngx_module_t  ngx_mail_proxy_module = {
  72.     NGX_MODULE_V1,
  73.     &ngx_mail_proxy_module_ctx,            /* module context */
  74.     ngx_mail_proxy_commands,               /* module directives */
  75.     NGX_MAIL_MODULE,                       /* module type */
  76.     NULL,                                  /* init master */
  77.     NULL,                                  /* init module */
  78.     NULL,                                  /* init process */
  79.     NULL,                                  /* init thread */
  80.     NULL,                                  /* exit thread */
  81.     NULL,                                  /* exit process */
  82.     NULL,                                  /* exit master */
  83.     NGX_MODULE_V1_PADDING
  84. };


  85. static u_char  smtp_auth_ok[] = "235 2.0.0 OK" CRLF;


  86. void
  87. ngx_mail_proxy_init(ngx_mail_session_t *s, ngx_addr_t *peer)
  88. {
  89.     int                        keepalive;
  90.     ngx_int_t                  rc;
  91.     ngx_mail_proxy_ctx_t      *p;
  92.     ngx_mail_proxy_conf_t     *pcf;
  93.     ngx_mail_core_srv_conf_t  *cscf;

  94.     s->connection->log->action = "connecting to upstream";

  95.     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

  96.     if (cscf->so_keepalive) {
  97.         keepalive = 1;

  98.         if (setsockopt(s->connection->fd, SOL_SOCKET, SO_KEEPALIVE,
  99.                        (const void *) &keepalive, sizeof(int))
  100.                 == -1)
  101.         {
  102.             ngx_log_error(NGX_LOG_ALERT, s->connection->log, ngx_socket_errno,
  103.                           "setsockopt(SO_KEEPALIVE) failed");
  104.         }
  105.     }

  106.     p = ngx_pcalloc(s->connection->pool, sizeof(ngx_mail_proxy_ctx_t));
  107.     if (p == NULL) {
  108.         ngx_mail_session_internal_server_error(s);
  109.         return;
  110.     }

  111.     s->proxy = p;

  112.     p->upstream.sockaddr = peer->sockaddr;
  113.     p->upstream.socklen = peer->socklen;
  114.     p->upstream.name = &peer->name;
  115.     p->upstream.get = ngx_event_get_peer;
  116.     p->upstream.log = s->connection->log;
  117.     p->upstream.log_error = NGX_ERROR_ERR;

  118.     rc = ngx_event_connect_peer(&p->upstream);

  119.     if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
  120.         ngx_mail_proxy_internal_server_error(s);
  121.         return;
  122.     }

  123.     ngx_add_timer(p->upstream.connection->read, cscf->timeout);

  124.     p->upstream.connection->data = s;
  125.     p->upstream.connection->pool = s->connection->pool;

  126.     s->connection->read->handler = ngx_mail_proxy_block_read;
  127.     p->upstream.connection->write->handler = ngx_mail_proxy_dummy_handler;

  128.     pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);

  129.     s->proxy->buffer = ngx_create_temp_buf(s->connection->pool,
  130.                                            pcf->buffer_size);
  131.     if (s->proxy->buffer == NULL) {
  132.         ngx_mail_proxy_internal_server_error(s);
  133.         return;
  134.     }

  135.     s->out.len = 0;

  136.     switch (s->protocol) {

  137.     case NGX_MAIL_POP3_PROTOCOL:
  138.         p->upstream.connection->read->handler = ngx_mail_proxy_pop3_handler;
  139.         s->mail_state = ngx_pop3_start;
  140.         break;

  141.     case NGX_MAIL_IMAP_PROTOCOL:
  142.         p->upstream.connection->read->handler = ngx_mail_proxy_imap_handler;
  143.         s->mail_state = ngx_imap_start;
  144.         break;

  145.     default: /* NGX_MAIL_SMTP_PROTOCOL */
  146.         p->upstream.connection->read->handler = ngx_mail_proxy_smtp_handler;
  147.         s->mail_state = ngx_smtp_start;
  148.         break;
  149.     }
  150. }


  151. static void
  152. ngx_mail_proxy_block_read(ngx_event_t *rev)
  153. {
  154.     ngx_connection_t    *c;
  155.     ngx_mail_session_t  *s;

  156.     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy block read");

  157.     if (ngx_handle_read_event(rev, 0) != NGX_OK) {
  158.         c = rev->data;
  159.         s = c->data;

  160.         ngx_mail_proxy_close_session(s);
  161.     }
  162. }


  163. static void
  164. ngx_mail_proxy_pop3_handler(ngx_event_t *rev)
  165. {
  166.     u_char                 *p;
  167.     ngx_int_t               rc;
  168.     ngx_str_t               line;
  169.     ngx_connection_t       *c;
  170.     ngx_mail_session_t     *s;
  171.     ngx_mail_proxy_conf_t  *pcf;

  172.     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
  173.                    "mail proxy pop3 auth handler");

  174.     c = rev->data;
  175.     s = c->data;

  176.     if (rev->timedout) {
  177.         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
  178.                       "upstream timed out");
  179.         c->timedout = 1;
  180.         ngx_mail_proxy_internal_server_error(s);
  181.         return;
  182.     }

  183.     rc = ngx_mail_proxy_read_response(s, 0);

  184.     if (rc == NGX_AGAIN) {
  185.         return;
  186.     }

  187.     if (rc == NGX_ERROR) {
  188.         ngx_mail_proxy_upstream_error(s);
  189.         return;
  190.     }

  191.     switch (s->mail_state) {

  192.     case ngx_pop3_start:
  193.         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send user");

  194.         s->connection->log->action = "sending user name to upstream";

  195.         line.len = sizeof("USER ")  - 1 + s->login.len + 2;
  196.         line.data = ngx_pnalloc(c->pool, line.len);
  197.         if (line.data == NULL) {
  198.             ngx_mail_proxy_internal_server_error(s);
  199.             return;
  200.         }

  201.         p = ngx_cpymem(line.data, "USER ", sizeof("USER ") - 1);
  202.         p = ngx_cpymem(p, s->login.data, s->login.len);
  203.         *p++ = CR; *p = LF;

  204.         s->mail_state = ngx_pop3_user;
  205.         break;

  206.     case ngx_pop3_user:
  207.         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send pass");

  208.         s->connection->log->action = "sending password to upstream";

  209.         line.len = sizeof("PASS ")  - 1 + s->passwd.len + 2;
  210.         line.data = ngx_pnalloc(c->pool, line.len);
  211.         if (line.data == NULL) {
  212.             ngx_mail_proxy_internal_server_error(s);
  213.             return;
  214.         }

  215.         p = ngx_cpymem(line.data, "PASS ", sizeof("PASS ") - 1);
  216.         p = ngx_cpymem(p, s->passwd.data, s->passwd.len);
  217.         *p++ = CR; *p = LF;

  218.         s->mail_state = ngx_pop3_passwd;
  219.         break;

  220.     case ngx_pop3_passwd:
  221.         s->connection->read->handler = ngx_mail_proxy_handler;
  222.         s->connection->write->handler = ngx_mail_proxy_handler;
  223.         rev->handler = ngx_mail_proxy_handler;
  224.         c->write->handler = ngx_mail_proxy_handler;

  225.         pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
  226.         ngx_add_timer(s->connection->read, pcf->timeout);
  227.         ngx_del_timer(c->read);

  228.         c->log->action = NULL;
  229.         ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");

  230.         ngx_mail_proxy_handler(s->connection->write);

  231.         return;

  232.     default:
  233. #if (NGX_SUPPRESS_WARN)
  234.         ngx_str_null(&line);
  235. #endif
  236.         break;
  237.     }

  238.     if (c->send(c, line.data, line.len) < (ssize_t) line.len) {
  239.         /*
  240.          * we treat the incomplete sending as NGX_ERROR
  241.          * because it is very strange here
  242.          */
  243.         ngx_mail_proxy_internal_server_error(s);
  244.         return;
  245.     }

  246.     s->proxy->buffer->pos = s->proxy->buffer->start;
  247.     s->proxy->buffer->last = s->proxy->buffer->start;
  248. }


  249. static void
  250. ngx_mail_proxy_imap_handler(ngx_event_t *rev)
  251. {
  252.     u_char                 *p;
  253.     ngx_int_t               rc;
  254.     ngx_str_t               line;
  255.     ngx_connection_t       *c;
  256.     ngx_mail_session_t     *s;
  257.     ngx_mail_proxy_conf_t  *pcf;

  258.     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
  259.                    "mail proxy imap auth handler");

  260.     c = rev->data;
  261.     s = c->data;

  262.     if (rev->timedout) {
  263.         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
  264.                       "upstream timed out");
  265.         c->timedout = 1;
  266.         ngx_mail_proxy_internal_server_error(s);
  267.         return;
  268.     }

  269.     rc = ngx_mail_proxy_read_response(s, s->mail_state);

  270.     if (rc == NGX_AGAIN) {
  271.         return;
  272.     }

  273.     if (rc == NGX_ERROR) {
  274.         ngx_mail_proxy_upstream_error(s);
  275.         return;
  276.     }

  277.     switch (s->mail_state) {

  278.     case ngx_imap_start:
  279.         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
  280.                        "mail proxy send login");

  281.         s->connection->log->action = "sending LOGIN command to upstream";

  282.         line.len = s->tag.len + sizeof("LOGIN ") - 1
  283.                    + 1 + NGX_SIZE_T_LEN + 1 + 2;
  284.         line.data = ngx_pnalloc(c->pool, line.len);
  285.         if (line.data == NULL) {
  286.             ngx_mail_proxy_internal_server_error(s);
  287.             return;
  288.         }

  289.         line.len = ngx_sprintf(line.data, "%VLOGIN {%uz}" CRLF,
  290.                                &s->tag, s->login.len)
  291.                    - line.data;

  292.         s->mail_state = ngx_imap_login;
  293.         break;

  294.     case ngx_imap_login:
  295.         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send user");

  296.         s->connection->log->action = "sending user name to upstream";

  297.         line.len = s->login.len + 1 + 1 + NGX_SIZE_T_LEN + 1 + 2;
  298.         line.data = ngx_pnalloc(c->pool, line.len);
  299.         if (line.data == NULL) {
  300.             ngx_mail_proxy_internal_server_error(s);
  301.             return;
  302.         }

  303.         line.len = ngx_sprintf(line.data, "%V {%uz}" CRLF,
  304.                                &s->login, s->passwd.len)
  305.                    - line.data;

  306.         s->mail_state = ngx_imap_user;
  307.         break;

  308.     case ngx_imap_user:
  309.         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
  310.                        "mail proxy send passwd");

  311.         s->connection->log->action = "sending password to upstream";

  312.         line.len = s->passwd.len + 2;
  313.         line.data = ngx_pnalloc(c->pool, line.len);
  314.         if (line.data == NULL) {
  315.             ngx_mail_proxy_internal_server_error(s);
  316.             return;
  317.         }

  318.         p = ngx_cpymem(line.data, s->passwd.data, s->passwd.len);
  319.         *p++ = CR; *p = LF;

  320.         s->mail_state = ngx_imap_passwd;
  321.         break;

  322.     case ngx_imap_passwd:
  323.         s->connection->read->handler = ngx_mail_proxy_handler;
  324.         s->connection->write->handler = ngx_mail_proxy_handler;
  325.         rev->handler = ngx_mail_proxy_handler;
  326.         c->write->handler = ngx_mail_proxy_handler;

  327.         pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
  328.         ngx_add_timer(s->connection->read, pcf->timeout);
  329.         ngx_del_timer(c->read);

  330.         c->log->action = NULL;
  331.         ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");

  332.         ngx_mail_proxy_handler(s->connection->write);

  333.         return;

  334.     default:
  335. #if (NGX_SUPPRESS_WARN)
  336.         ngx_str_null(&line);
  337. #endif
  338.         break;
  339.     }

  340.     if (c->send(c, line.data, line.len) < (ssize_t) line.len) {
  341.         /*
  342.          * we treat the incomplete sending as NGX_ERROR
  343.          * because it is very strange here
  344.          */
  345.         ngx_mail_proxy_internal_server_error(s);
  346.         return;
  347.     }

  348.     s->proxy->buffer->pos = s->proxy->buffer->start;
  349.     s->proxy->buffer->last = s->proxy->buffer->start;
  350. }


  351. static void
  352. ngx_mail_proxy_smtp_handler(ngx_event_t *rev)
  353. {
  354.     u_char                    *p;
  355.     ngx_int_t                  rc;
  356.     ngx_str_t                  line;
  357.     ngx_buf_t                 *b;
  358.     ngx_connection_t          *c;
  359.     ngx_mail_session_t        *s;
  360.     ngx_mail_proxy_conf_t     *pcf;
  361.     ngx_mail_core_srv_conf_t  *cscf;

  362.     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
  363.                    "mail proxy smtp auth handler");

  364.     c = rev->data;
  365.     s = c->data;

  366.     if (rev->timedout) {
  367.         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
  368.                       "upstream timed out");
  369.         c->timedout = 1;
  370.         ngx_mail_proxy_internal_server_error(s);
  371.         return;
  372.     }

  373.     rc = ngx_mail_proxy_read_response(s, s->mail_state);

  374.     if (rc == NGX_AGAIN) {
  375.         return;
  376.     }

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

  381.     switch (s->mail_state) {

  382.     case ngx_smtp_start:
  383.         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send ehlo");

  384.         s->connection->log->action = "sending HELO/EHLO to upstream";

  385.         cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

  386.         line.len = sizeof("HELO ")  - 1 + cscf->server_name.len + 2;
  387.         line.data = ngx_pnalloc(c->pool, line.len);
  388.         if (line.data == NULL) {
  389.             ngx_mail_proxy_internal_server_error(s);
  390.             return;
  391.         }

  392.         pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);

  393.         p = ngx_cpymem(line.data,
  394.                        ((s->esmtp || pcf->xclient) ? "EHLO " : "HELO "),
  395.                        sizeof("HELO ") - 1);

  396.         p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);
  397.         *p++ = CR; *p = LF;

  398.         if (pcf->xclient) {
  399.             s->mail_state = ngx_smtp_helo_xclient;

  400.         } else if (s->auth_method == NGX_MAIL_AUTH_NONE) {
  401.             s->mail_state = ngx_smtp_helo_from;

  402.         } else {
  403.             s->mail_state = ngx_smtp_helo;
  404.         }

  405.         break;

  406.     case ngx_smtp_helo_xclient:
  407.         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
  408.                        "mail proxy send xclient");

  409.         s->connection->log->action = "sending XCLIENT to upstream";

  410.         line.len = sizeof("XCLIENT ADDR= LOGIN= NAME="
  411.                           CRLF) - 1
  412.                    + s->connection->addr_text.len + s->login.len + s->host.len;

  413. #if (NGX_HAVE_INET6)
  414.         if (s->connection->sockaddr->sa_family == AF_INET6) {
  415.             line.len += sizeof("IPV6:") - 1;
  416.         }
  417. #endif

  418.         line.data = ngx_pnalloc(c->pool, line.len);
  419.         if (line.data == NULL) {
  420.             ngx_mail_proxy_internal_server_error(s);
  421.             return;
  422.         }

  423.         p = ngx_cpymem(line.data, "XCLIENT ADDR=", sizeof("XCLIENT ADDR=") - 1);

  424. #if (NGX_HAVE_INET6)
  425.         if (s->connection->sockaddr->sa_family == AF_INET6) {
  426.             p = ngx_cpymem(p, "IPV6:", sizeof("IPV6:") - 1);
  427.         }
  428. #endif

  429.         p = ngx_copy(p, s->connection->addr_text.data,
  430.                      s->connection->addr_text.len);

  431.         if (s->login.len) {
  432.             p = ngx_cpymem(p, " LOGIN=", sizeof(" LOGIN=") - 1);
  433.             p = ngx_copy(p, s->login.data, s->login.len);
  434.         }

  435.         p = ngx_cpymem(p, " NAME=", sizeof(" NAME=") - 1);
  436.         p = ngx_copy(p, s->host.data, s->host.len);

  437.         *p++ = CR; *p++ = LF;

  438.         line.len = p - line.data;

  439.         if (s->smtp_helo.len) {
  440.             s->mail_state = ngx_smtp_xclient_helo;

  441.         } else if (s->auth_method == NGX_MAIL_AUTH_NONE) {
  442.             s->mail_state = ngx_smtp_xclient_from;

  443.         } else {
  444.             s->mail_state = ngx_smtp_xclient;
  445.         }

  446.         break;

  447.     case ngx_smtp_xclient_helo:
  448.         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
  449.                        "mail proxy send client ehlo");

  450.         s->connection->log->action = "sending client HELO/EHLO to upstream";

  451.         line.len = sizeof("HELO " CRLF) - 1 + s->smtp_helo.len;

  452.         line.data = ngx_pnalloc(c->pool, line.len);
  453.         if (line.data == NULL) {
  454.             ngx_mail_proxy_internal_server_error(s);
  455.             return;
  456.         }

  457.         line.len = ngx_sprintf(line.data,
  458.                        ((s->esmtp) ? "EHLO %V" CRLF : "HELO %V" CRLF),
  459.                        &s->smtp_helo)
  460.                    - line.data;

  461.         s->mail_state = (s->auth_method == NGX_MAIL_AUTH_NONE) ?
  462.                             ngx_smtp_helo_from : ngx_smtp_helo;

  463.         break;

  464.     case ngx_smtp_helo_from:
  465.     case ngx_smtp_xclient_from:
  466.         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
  467.                        "mail proxy send mail from");

  468.         s->connection->log->action = "sending MAIL FROM to upstream";

  469.         line.len = s->smtp_from.len + sizeof(CRLF) - 1;
  470.         line.data = ngx_pnalloc(c->pool, line.len);
  471.         if (line.data == NULL) {
  472.             ngx_mail_proxy_internal_server_error(s);
  473.             return;
  474.         }

  475.         p = ngx_cpymem(line.data, s->smtp_from.data, s->smtp_from.len);
  476.         *p++ = CR; *p = LF;

  477.         s->mail_state = ngx_smtp_from;

  478.         break;

  479.     case ngx_smtp_from:
  480.         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
  481.                        "mail proxy send rcpt to");

  482.         s->connection->log->action = "sending RCPT TO to upstream";

  483.         line.len = s->smtp_to.len + sizeof(CRLF) - 1;
  484.         line.data = ngx_pnalloc(c->pool, line.len);
  485.         if (line.data == NULL) {
  486.             ngx_mail_proxy_internal_server_error(s);
  487.             return;
  488.         }

  489.         p = ngx_cpymem(line.data, s->smtp_to.data, s->smtp_to.len);
  490.         *p++ = CR; *p = LF;

  491.         s->mail_state = ngx_smtp_to;

  492.         break;

  493.     case ngx_smtp_helo:
  494.     case ngx_smtp_xclient:
  495.     case ngx_smtp_to:

  496.         b = s->proxy->buffer;

  497.         if (s->auth_method == NGX_MAIL_AUTH_NONE) {
  498.             b->pos = b->start;

  499.         } else {
  500.             ngx_memcpy(b->start, smtp_auth_ok, sizeof(smtp_auth_ok) - 1);
  501.             b->last = b->start + sizeof(smtp_auth_ok) - 1;
  502.         }

  503.         s->connection->read->handler = ngx_mail_proxy_handler;
  504.         s->connection->write->handler = ngx_mail_proxy_handler;
  505.         rev->handler = ngx_mail_proxy_handler;
  506.         c->write->handler = ngx_mail_proxy_handler;

  507.         pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
  508.         ngx_add_timer(s->connection->read, pcf->timeout);
  509.         ngx_del_timer(c->read);

  510.         c->log->action = NULL;
  511.         ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");

  512.         if (s->buffer->pos == s->buffer->last) {
  513.             ngx_mail_proxy_handler(s->connection->write);

  514.         } else {
  515.             ngx_mail_proxy_handler(c->write);
  516.         }

  517.         return;

  518.     default:
  519. #if (NGX_SUPPRESS_WARN)
  520.         ngx_str_null(&line);
  521. #endif
  522.         break;
  523.     }

  524.     if (c->send(c, line.data, line.len) < (ssize_t) line.len) {
  525.         /*
  526.          * we treat the incomplete sending as NGX_ERROR
  527.          * because it is very strange here
  528.          */
  529.         ngx_mail_proxy_internal_server_error(s);
  530.         return;
  531.     }

  532.     s->proxy->buffer->pos = s->proxy->buffer->start;
  533.     s->proxy->buffer->last = s->proxy->buffer->start;
  534. }


  535. static void
  536. ngx_mail_proxy_dummy_handler(ngx_event_t *wev)
  537. {
  538.     ngx_connection_t    *c;
  539.     ngx_mail_session_t  *s;

  540.     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0, "mail proxy dummy handler");

  541.     if (ngx_handle_write_event(wev, 0) != NGX_OK) {
  542.         c = wev->data;
  543.         s = c->data;

  544.         ngx_mail_proxy_close_session(s);
  545.     }
  546. }


  547. static ngx_int_t
  548. ngx_mail_proxy_read_response(ngx_mail_session_t *s, ngx_uint_t state)
  549. {
  550.     u_char                 *p, *m;
  551.     ssize_t                 n;
  552.     ngx_buf_t              *b;
  553.     ngx_mail_proxy_conf_t  *pcf;

  554.     s->connection->log->action = "reading response from upstream";

  555.     b = s->proxy->buffer;

  556.     n = s->proxy->upstream.connection->recv(s->proxy->upstream.connection,
  557.                                             b->last, b->end - b->last);

  558.     if (n == NGX_ERROR || n == 0) {
  559.         return NGX_ERROR;
  560.     }

  561.     if (n == NGX_AGAIN) {
  562.         return NGX_AGAIN;
  563.     }

  564.     b->last += n;

  565.     if (b->last - b->pos < 4) {
  566.         return NGX_AGAIN;
  567.     }

  568.     if (*(b->last - 2) != CR || *(b->last - 1) != LF) {
  569.         if (b->last == b->end) {
  570.             *(b->last - 1) = '\0';
  571.             ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
  572.                           "upstream sent too long response line: \"%s\"",
  573.                           b->pos);
  574.             return NGX_ERROR;
  575.         }

  576.         return NGX_AGAIN;
  577.     }

  578.     p = b->pos;

  579.     switch (s->protocol) {

  580.     case NGX_MAIL_POP3_PROTOCOL:
  581.         if (p[0] == '+' && p[1] == 'O' && p[2] == 'K') {
  582.             return NGX_OK;
  583.         }
  584.         break;

  585.     case NGX_MAIL_IMAP_PROTOCOL:
  586.         switch (state) {

  587.         case ngx_imap_start:
  588.             if (p[0] == '*' && p[1] == ' ' && p[2] == 'O' && p[3] == 'K') {
  589.                 return NGX_OK;
  590.             }
  591.             break;

  592.         case ngx_imap_login:
  593.         case ngx_imap_user:
  594.             if (p[0] == '+') {
  595.                 return NGX_OK;
  596.             }
  597.             break;

  598.         case ngx_imap_passwd:
  599.             if (ngx_strncmp(p, s->tag.data, s->tag.len) == 0) {
  600.                 p += s->tag.len;
  601.                 if (p[0] == 'O' && p[1] == 'K') {
  602.                     return NGX_OK;
  603.                 }
  604.             }
  605.             break;
  606.         }

  607.         break;

  608.     default: /* NGX_MAIL_SMTP_PROTOCOL */

  609.         if (p[3] == '-') {
  610.             /* multiline reply, check if we got last line */

  611.             m = b->last - (sizeof(CRLF "200" CRLF) - 1);

  612.             while (m > p) {
  613.                 if (m[0] == CR && m[1] == LF) {
  614.                     break;
  615.                 }

  616.                 m--;
  617.             }

  618.             if (m <= p || m[5] == '-') {
  619.                 return NGX_AGAIN;
  620.             }
  621.         }

  622.         switch (state) {

  623.         case ngx_smtp_start:
  624.             if (p[0] == '2' && p[1] == '2' && p[2] == '0') {
  625.                 return NGX_OK;
  626.             }
  627.             break;

  628.         case ngx_smtp_helo:
  629.         case ngx_smtp_helo_xclient:
  630.         case ngx_smtp_helo_from:
  631.         case ngx_smtp_from:
  632.             if (p[0] == '2' && p[1] == '5' && p[2] == '0') {
  633.                 return NGX_OK;
  634.             }
  635.             break;

  636.         case ngx_smtp_xclient:
  637.         case ngx_smtp_xclient_from:
  638.         case ngx_smtp_xclient_helo:
  639.             if (p[0] == '2' && (p[1] == '2' || p[1] == '5') && p[2] == '0') {
  640.                 return NGX_OK;
  641.             }
  642.             break;

  643.         case ngx_smtp_to:
  644.             return NGX_OK;
  645.         }

  646.         break;
  647.     }

  648.     pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);

  649.     if (pcf->pass_error_message == 0) {
  650.         *(b->last - 2) = '\0';
  651.         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
  652.                       "upstream sent invalid response: \"%s\"", p);
  653.         return NGX_ERROR;
  654.     }

  655.     s->out.len = b->last - p - 2;
  656.     s->out.data = p;

  657.     ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
  658.                   "upstream sent invalid response: \"%V\"", &s->out);

  659.     s->out.len = b->last - b->pos;
  660.     s->out.data = b->pos;

  661.     return NGX_ERROR;
  662. }


  663. static void
  664. ngx_mail_proxy_handler(ngx_event_t *ev)
  665. {
  666.     char                   *action, *recv_action, *send_action;
  667.     size_t                  size;
  668.     ssize_t                 n;
  669.     ngx_buf_t              *b;
  670.     ngx_uint_t              do_write;
  671.     ngx_connection_t       *c, *src, *dst;
  672.     ngx_mail_session_t     *s;
  673.     ngx_mail_proxy_conf_t  *pcf;

  674.     c = ev->data;
  675.     s = c->data;

  676.     if (ev->timedout) {
  677.         c->log->action = "proxying";

  678.         if (c == s->connection) {
  679.             ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
  680.                           "client timed out");
  681.             c->timedout = 1;

  682.         } else {
  683.             ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
  684.                           "upstream timed out");
  685.         }

  686.         ngx_mail_proxy_close_session(s);
  687.         return;
  688.     }

  689.     if (c == s->connection) {
  690.         if (ev->write) {
  691.             recv_action = "proxying and reading from upstream";
  692.             send_action = "proxying and sending to client";
  693.             src = s->proxy->upstream.connection;
  694.             dst = c;
  695.             b = s->proxy->buffer;

  696.         } else {
  697.             recv_action = "proxying and reading from client";
  698.             send_action = "proxying and sending to upstream";
  699.             src = c;
  700.             dst = s->proxy->upstream.connection;
  701.             b = s->buffer;
  702.         }

  703.     } else {
  704.         if (ev->write) {
  705.             recv_action = "proxying and reading from client";
  706.             send_action = "proxying and sending to upstream";
  707.             src = s->connection;
  708.             dst = c;
  709.             b = s->buffer;

  710.         } else {
  711.             recv_action = "proxying and reading from upstream";
  712.             send_action = "proxying and sending to client";
  713.             src = c;
  714.             dst = s->connection;
  715.             b = s->proxy->buffer;
  716.         }
  717.     }

  718.     do_write = ev->write ? 1 : 0;

  719.     ngx_log_debug3(NGX_LOG_DEBUG_MAIL, ev->log, 0,
  720.                    "mail proxy handler: %d, #%d > #%d",
  721.                    do_write, src->fd, dst->fd);

  722.     for ( ;; ) {

  723.         if (do_write) {

  724.             size = b->last - b->pos;

  725.             if (size && dst->write->ready) {
  726.                 c->log->action = send_action;

  727.                 n = dst->send(dst, b->pos, size);

  728.                 if (n == NGX_ERROR) {
  729.                     ngx_mail_proxy_close_session(s);
  730.                     return;
  731.                 }

  732.                 if (n > 0) {
  733.                     b->pos += n;

  734.                     if (b->pos == b->last) {
  735.                         b->pos = b->start;
  736.                         b->last = b->start;
  737.                     }
  738.                 }
  739.             }
  740.         }

  741.         size = b->end - b->last;

  742.         if (size && src->read->ready) {
  743.             c->log->action = recv_action;

  744.             n = src->recv(src, b->last, size);

  745.             if (n == NGX_AGAIN || n == 0) {
  746.                 break;
  747.             }

  748.             if (n > 0) {
  749.                 do_write = 1;
  750.                 b->last += n;

  751.                 continue;
  752.             }

  753.             if (n == NGX_ERROR) {
  754.                 src->read->eof = 1;
  755.             }
  756.         }

  757.         break;
  758.     }

  759.     c->log->action = "proxying";

  760.     if ((s->connection->read->eof && s->buffer->pos == s->buffer->last)
  761.         || (s->proxy->upstream.connection->read->eof
  762.             && s->proxy->buffer->pos == s->proxy->buffer->last)
  763.         || (s->connection->read->eof
  764.             && s->proxy->upstream.connection->read->eof))
  765.     {
  766.         action = c->log->action;
  767.         c->log->action = NULL;
  768.         ngx_log_error(NGX_LOG_INFO, c->log, 0, "proxied session done");
  769.         c->log->action = action;

  770.         ngx_mail_proxy_close_session(s);
  771.         return;
  772.     }

  773.     if (ngx_handle_write_event(dst->write, 0) != NGX_OK) {
  774.         ngx_mail_proxy_close_session(s);
  775.         return;
  776.     }

  777.     if (ngx_handle_read_event(dst->read, 0) != NGX_OK) {
  778.         ngx_mail_proxy_close_session(s);
  779.         return;
  780.     }

  781.     if (ngx_handle_write_event(src->write, 0) != NGX_OK) {
  782.         ngx_mail_proxy_close_session(s);
  783.         return;
  784.     }

  785.     if (ngx_handle_read_event(src->read, 0) != NGX_OK) {
  786.         ngx_mail_proxy_close_session(s);
  787.         return;
  788.     }

  789.     if (c == s->connection) {
  790.         pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
  791.         ngx_add_timer(c->read, pcf->timeout);
  792.     }
  793. }


  794. static void
  795. ngx_mail_proxy_upstream_error(ngx_mail_session_t *s)
  796. {
  797.     if (s->proxy->upstream.connection) {
  798.         ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
  799.                        "close mail proxy connection: %d",
  800.                        s->proxy->upstream.connection->fd);

  801.         ngx_close_connection(s->proxy->upstream.connection);
  802.     }

  803.     if (s->out.len == 0) {
  804.         ngx_mail_session_internal_server_error(s);
  805.         return;
  806.     }

  807.     s->quit = 1;
  808.     ngx_mail_send(s->connection->write);
  809. }


  810. static void
  811. ngx_mail_proxy_internal_server_error(ngx_mail_session_t *s)
  812. {
  813.     if (s->proxy->upstream.connection) {
  814.         ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
  815.                        "close mail proxy connection: %d",
  816.                        s->proxy->upstream.connection->fd);

  817.         ngx_close_connection(s->proxy->upstream.connection);
  818.     }

  819.     ngx_mail_session_internal_server_error(s);
  820. }


  821. static void
  822. ngx_mail_proxy_close_session(ngx_mail_session_t *s)
  823. {
  824.     if (s->proxy->upstream.connection) {
  825.         ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
  826.                        "close mail proxy connection: %d",
  827.                        s->proxy->upstream.connection->fd);

  828.         ngx_close_connection(s->proxy->upstream.connection);
  829.     }

  830.     ngx_mail_close_connection(s->connection);
  831. }


  832. static void *
  833. ngx_mail_proxy_create_conf(ngx_conf_t *cf)
  834. {
  835.     ngx_mail_proxy_conf_t  *pcf;

  836.     pcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_proxy_conf_t));
  837.     if (pcf == NULL) {
  838.         return NULL;
  839.     }

  840.     pcf->enable = NGX_CONF_UNSET;
  841.     pcf->pass_error_message = NGX_CONF_UNSET;
  842.     pcf->xclient = NGX_CONF_UNSET;
  843.     pcf->buffer_size = NGX_CONF_UNSET_SIZE;
  844.     pcf->timeout = NGX_CONF_UNSET_MSEC;

  845.     return pcf;
  846. }


  847. static char *
  848. ngx_mail_proxy_merge_conf(ngx_conf_t *cf, void *parent, void *child)
  849. {
  850.     ngx_mail_proxy_conf_t *prev = parent;
  851.     ngx_mail_proxy_conf_t *conf = child;

  852.     ngx_conf_merge_value(conf->enable, prev->enable, 0);
  853.     ngx_conf_merge_value(conf->pass_error_message, prev->pass_error_message, 0);
  854.     ngx_conf_merge_value(conf->xclient, prev->xclient, 1);
  855.     ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
  856.                               (size_t) ngx_pagesize);
  857.     ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 24 * 60 * 60000);

  858.     return NGX_CONF_OK;
  859. }