Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.15.11 ]​[ nginx-1.14.2 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_event.h>
0011 #include <ngx_event_connect.h>
0012 #include <ngx_mail.h>
0013 
0014 
0015 typedef struct {
0016     ngx_flag_t  enable;
0017     ngx_flag_t  pass_error_message;
0018     ngx_flag_t  xclient;
0019     size_t      buffer_size;
0020     ngx_msec_t  timeout;
0021 } ngx_mail_proxy_conf_t;
0022 
0023 
0024 static void ngx_mail_proxy_block_read(ngx_event_t *rev);
0025 static void ngx_mail_proxy_pop3_handler(ngx_event_t *rev);
0026 static void ngx_mail_proxy_imap_handler(ngx_event_t *rev);
0027 static void ngx_mail_proxy_smtp_handler(ngx_event_t *rev);
0028 static void ngx_mail_proxy_dummy_handler(ngx_event_t *ev);
0029 static ngx_int_t ngx_mail_proxy_read_response(ngx_mail_session_t *s,
0030     ngx_uint_t state);
0031 static void ngx_mail_proxy_handler(ngx_event_t *ev);
0032 static void ngx_mail_proxy_upstream_error(ngx_mail_session_t *s);
0033 static void ngx_mail_proxy_internal_server_error(ngx_mail_session_t *s);
0034 static void ngx_mail_proxy_close_session(ngx_mail_session_t *s);
0035 static void *ngx_mail_proxy_create_conf(ngx_conf_t *cf);
0036 static char *ngx_mail_proxy_merge_conf(ngx_conf_t *cf, void *parent,
0037     void *child);
0038 
0039 
0040 static ngx_command_t  ngx_mail_proxy_commands[] = {
0041 
0042     { ngx_string("proxy"),
0043       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
0044       ngx_conf_set_flag_slot,
0045       NGX_MAIL_SRV_CONF_OFFSET,
0046       offsetof(ngx_mail_proxy_conf_t, enable),
0047       NULL },
0048 
0049     { ngx_string("proxy_buffer"),
0050       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
0051       ngx_conf_set_size_slot,
0052       NGX_MAIL_SRV_CONF_OFFSET,
0053       offsetof(ngx_mail_proxy_conf_t, buffer_size),
0054       NULL },
0055 
0056     { ngx_string("proxy_timeout"),
0057       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
0058       ngx_conf_set_msec_slot,
0059       NGX_MAIL_SRV_CONF_OFFSET,
0060       offsetof(ngx_mail_proxy_conf_t, timeout),
0061       NULL },
0062 
0063     { ngx_string("proxy_pass_error_message"),
0064       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
0065       ngx_conf_set_flag_slot,
0066       NGX_MAIL_SRV_CONF_OFFSET,
0067       offsetof(ngx_mail_proxy_conf_t, pass_error_message),
0068       NULL },
0069 
0070     { ngx_string("xclient"),
0071       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
0072       ngx_conf_set_flag_slot,
0073       NGX_MAIL_SRV_CONF_OFFSET,
0074       offsetof(ngx_mail_proxy_conf_t, xclient),
0075       NULL },
0076 
0077       ngx_null_command
0078 };
0079 
0080 
0081 static ngx_mail_module_t  ngx_mail_proxy_module_ctx = {
0082     NULL,                                  /* protocol */
0083 
0084     NULL,                                  /* create main configuration */
0085     NULL,                                  /* init main configuration */
0086 
0087     ngx_mail_proxy_create_conf,            /* create server configuration */
0088     ngx_mail_proxy_merge_conf              /* merge server configuration */
0089 };
0090 
0091 
0092 ngx_module_t  ngx_mail_proxy_module = {
0093     NGX_MODULE_V1,
0094     &ngx_mail_proxy_module_ctx,            /* module context */
0095     ngx_mail_proxy_commands,               /* module directives */
0096     NGX_MAIL_MODULE,                       /* module type */
0097     NULL,                                  /* init master */
0098     NULL,                                  /* init module */
0099     NULL,                                  /* init process */
0100     NULL,                                  /* init thread */
0101     NULL,                                  /* exit thread */
0102     NULL,                                  /* exit process */
0103     NULL,                                  /* exit master */
0104     NGX_MODULE_V1_PADDING
0105 };
0106 
0107 
0108 static u_char  smtp_auth_ok[] = "235 2.0.0 OK" CRLF;
0109 
0110 
0111 void
0112 ngx_mail_proxy_init(ngx_mail_session_t *s, ngx_addr_t *peer)
0113 {
0114     ngx_int_t                  rc;
0115     ngx_mail_proxy_ctx_t      *p;
0116     ngx_mail_proxy_conf_t     *pcf;
0117     ngx_mail_core_srv_conf_t  *cscf;
0118 
0119     s->connection->log->action = "connecting to upstream";
0120 
0121     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
0122 
0123     p = ngx_pcalloc(s->connection->pool, sizeof(ngx_mail_proxy_ctx_t));
0124     if (p == NULL) {
0125         ngx_mail_session_internal_server_error(s);
0126         return;
0127     }
0128 
0129     s->proxy = p;
0130 
0131     p->upstream.sockaddr = peer->sockaddr;
0132     p->upstream.socklen = peer->socklen;
0133     p->upstream.name = &peer->name;
0134     p->upstream.get = ngx_event_get_peer;
0135     p->upstream.log = s->connection->log;
0136     p->upstream.log_error = NGX_ERROR_ERR;
0137 
0138     rc = ngx_event_connect_peer(&p->upstream);
0139 
0140     if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
0141         ngx_mail_proxy_internal_server_error(s);
0142         return;
0143     }
0144 
0145     ngx_add_timer(p->upstream.connection->read, cscf->timeout);
0146 
0147     p->upstream.connection->data = s;
0148     p->upstream.connection->pool = s->connection->pool;
0149 
0150     s->connection->read->handler = ngx_mail_proxy_block_read;
0151     p->upstream.connection->write->handler = ngx_mail_proxy_dummy_handler;
0152 
0153     pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
0154 
0155     s->proxy->buffer = ngx_create_temp_buf(s->connection->pool,
0156                                            pcf->buffer_size);
0157     if (s->proxy->buffer == NULL) {
0158         ngx_mail_proxy_internal_server_error(s);
0159         return;
0160     }
0161 
0162     s->out.len = 0;
0163 
0164     switch (s->protocol) {
0165 
0166     case NGX_MAIL_POP3_PROTOCOL:
0167         p->upstream.connection->read->handler = ngx_mail_proxy_pop3_handler;
0168         s->mail_state = ngx_pop3_start;
0169         break;
0170 
0171     case NGX_MAIL_IMAP_PROTOCOL:
0172         p->upstream.connection->read->handler = ngx_mail_proxy_imap_handler;
0173         s->mail_state = ngx_imap_start;
0174         break;
0175 
0176     default: /* NGX_MAIL_SMTP_PROTOCOL */
0177         p->upstream.connection->read->handler = ngx_mail_proxy_smtp_handler;
0178         s->mail_state = ngx_smtp_start;
0179         break;
0180     }
0181 }
0182 
0183 
0184 static void
0185 ngx_mail_proxy_block_read(ngx_event_t *rev)
0186 {
0187     ngx_connection_t    *c;
0188     ngx_mail_session_t  *s;
0189 
0190     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy block read");
0191 
0192     if (ngx_handle_read_event(rev, 0) != NGX_OK) {
0193         c = rev->data;
0194         s = c->data;
0195 
0196         ngx_mail_proxy_close_session(s);
0197     }
0198 }
0199 
0200 
0201 static void
0202 ngx_mail_proxy_pop3_handler(ngx_event_t *rev)
0203 {
0204     u_char                 *p;
0205     ngx_int_t               rc;
0206     ngx_str_t               line;
0207     ngx_connection_t       *c;
0208     ngx_mail_session_t     *s;
0209     ngx_mail_proxy_conf_t  *pcf;
0210 
0211     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
0212                    "mail proxy pop3 auth handler");
0213 
0214     c = rev->data;
0215     s = c->data;
0216 
0217     if (rev->timedout) {
0218         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
0219                       "upstream timed out");
0220         c->timedout = 1;
0221         ngx_mail_proxy_internal_server_error(s);
0222         return;
0223     }
0224 
0225     rc = ngx_mail_proxy_read_response(s, 0);
0226 
0227     if (rc == NGX_AGAIN) {
0228         return;
0229     }
0230 
0231     if (rc == NGX_ERROR) {
0232         ngx_mail_proxy_upstream_error(s);
0233         return;
0234     }
0235 
0236     switch (s->mail_state) {
0237 
0238     case ngx_pop3_start:
0239         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send user");
0240 
0241         s->connection->log->action = "sending user name to upstream";
0242 
0243         line.len = sizeof("USER ")  - 1 + s->login.len + 2;
0244         line.data = ngx_pnalloc(c->pool, line.len);
0245         if (line.data == NULL) {
0246             ngx_mail_proxy_internal_server_error(s);
0247             return;
0248         }
0249 
0250         p = ngx_cpymem(line.data, "USER ", sizeof("USER ") - 1);
0251         p = ngx_cpymem(p, s->login.data, s->login.len);
0252         *p++ = CR; *p = LF;
0253 
0254         s->mail_state = ngx_pop3_user;
0255         break;
0256 
0257     case ngx_pop3_user:
0258         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send pass");
0259 
0260         s->connection->log->action = "sending password to upstream";
0261 
0262         line.len = sizeof("PASS ")  - 1 + s->passwd.len + 2;
0263         line.data = ngx_pnalloc(c->pool, line.len);
0264         if (line.data == NULL) {
0265             ngx_mail_proxy_internal_server_error(s);
0266             return;
0267         }
0268 
0269         p = ngx_cpymem(line.data, "PASS ", sizeof("PASS ") - 1);
0270         p = ngx_cpymem(p, s->passwd.data, s->passwd.len);
0271         *p++ = CR; *p = LF;
0272 
0273         s->mail_state = ngx_pop3_passwd;
0274         break;
0275 
0276     case ngx_pop3_passwd:
0277         s->connection->read->handler = ngx_mail_proxy_handler;
0278         s->connection->write->handler = ngx_mail_proxy_handler;
0279         rev->handler = ngx_mail_proxy_handler;
0280         c->write->handler = ngx_mail_proxy_handler;
0281 
0282         pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
0283         ngx_add_timer(s->connection->read, pcf->timeout);
0284         ngx_del_timer(c->read);
0285 
0286         c->log->action = NULL;
0287         ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");
0288 
0289         ngx_mail_proxy_handler(s->connection->write);
0290 
0291         return;
0292 
0293     default:
0294 #if (NGX_SUPPRESS_WARN)
0295         ngx_str_null(&line);
0296 #endif
0297         break;
0298     }
0299 
0300     if (c->send(c, line.data, line.len) < (ssize_t) line.len) {
0301         /*
0302          * we treat the incomplete sending as NGX_ERROR
0303          * because it is very strange here
0304          */
0305         ngx_mail_proxy_internal_server_error(s);
0306         return;
0307     }
0308 
0309     s->proxy->buffer->pos = s->proxy->buffer->start;
0310     s->proxy->buffer->last = s->proxy->buffer->start;
0311 }
0312 
0313 
0314 static void
0315 ngx_mail_proxy_imap_handler(ngx_event_t *rev)
0316 {
0317     u_char                 *p;
0318     ngx_int_t               rc;
0319     ngx_str_t               line;
0320     ngx_connection_t       *c;
0321     ngx_mail_session_t     *s;
0322     ngx_mail_proxy_conf_t  *pcf;
0323 
0324     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
0325                    "mail proxy imap auth handler");
0326 
0327     c = rev->data;
0328     s = c->data;
0329 
0330     if (rev->timedout) {
0331         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
0332                       "upstream timed out");
0333         c->timedout = 1;
0334         ngx_mail_proxy_internal_server_error(s);
0335         return;
0336     }
0337 
0338     rc = ngx_mail_proxy_read_response(s, s->mail_state);
0339 
0340     if (rc == NGX_AGAIN) {
0341         return;
0342     }
0343 
0344     if (rc == NGX_ERROR) {
0345         ngx_mail_proxy_upstream_error(s);
0346         return;
0347     }
0348 
0349     switch (s->mail_state) {
0350 
0351     case ngx_imap_start:
0352         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
0353                        "mail proxy send login");
0354 
0355         s->connection->log->action = "sending LOGIN command to upstream";
0356 
0357         line.len = s->tag.len + sizeof("LOGIN ") - 1
0358                    + 1 + NGX_SIZE_T_LEN + 1 + 2;
0359         line.data = ngx_pnalloc(c->pool, line.len);
0360         if (line.data == NULL) {
0361             ngx_mail_proxy_internal_server_error(s);
0362             return;
0363         }
0364 
0365         line.len = ngx_sprintf(line.data, "%VLOGIN {%uz}" CRLF,
0366                                &s->tag, s->login.len)
0367                    - line.data;
0368 
0369         s->mail_state = ngx_imap_login;
0370         break;
0371 
0372     case ngx_imap_login:
0373         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send user");
0374 
0375         s->connection->log->action = "sending user name to upstream";
0376 
0377         line.len = s->login.len + 1 + 1 + NGX_SIZE_T_LEN + 1 + 2;
0378         line.data = ngx_pnalloc(c->pool, line.len);
0379         if (line.data == NULL) {
0380             ngx_mail_proxy_internal_server_error(s);
0381             return;
0382         }
0383 
0384         line.len = ngx_sprintf(line.data, "%V {%uz}" CRLF,
0385                                &s->login, s->passwd.len)
0386                    - line.data;
0387 
0388         s->mail_state = ngx_imap_user;
0389         break;
0390 
0391     case ngx_imap_user:
0392         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
0393                        "mail proxy send passwd");
0394 
0395         s->connection->log->action = "sending password to upstream";
0396 
0397         line.len = s->passwd.len + 2;
0398         line.data = ngx_pnalloc(c->pool, line.len);
0399         if (line.data == NULL) {
0400             ngx_mail_proxy_internal_server_error(s);
0401             return;
0402         }
0403 
0404         p = ngx_cpymem(line.data, s->passwd.data, s->passwd.len);
0405         *p++ = CR; *p = LF;
0406 
0407         s->mail_state = ngx_imap_passwd;
0408         break;
0409 
0410     case ngx_imap_passwd:
0411         s->connection->read->handler = ngx_mail_proxy_handler;
0412         s->connection->write->handler = ngx_mail_proxy_handler;
0413         rev->handler = ngx_mail_proxy_handler;
0414         c->write->handler = ngx_mail_proxy_handler;
0415 
0416         pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
0417         ngx_add_timer(s->connection->read, pcf->timeout);
0418         ngx_del_timer(c->read);
0419 
0420         c->log->action = NULL;
0421         ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");
0422 
0423         ngx_mail_proxy_handler(s->connection->write);
0424 
0425         return;
0426 
0427     default:
0428 #if (NGX_SUPPRESS_WARN)
0429         ngx_str_null(&line);
0430 #endif
0431         break;
0432     }
0433 
0434     if (c->send(c, line.data, line.len) < (ssize_t) line.len) {
0435         /*
0436          * we treat the incomplete sending as NGX_ERROR
0437          * because it is very strange here
0438          */
0439         ngx_mail_proxy_internal_server_error(s);
0440         return;
0441     }
0442 
0443     s->proxy->buffer->pos = s->proxy->buffer->start;
0444     s->proxy->buffer->last = s->proxy->buffer->start;
0445 }
0446 
0447 
0448 static void
0449 ngx_mail_proxy_smtp_handler(ngx_event_t *rev)
0450 {
0451     u_char                    *p;
0452     ngx_int_t                  rc;
0453     ngx_str_t                  line;
0454     ngx_buf_t                 *b;
0455     ngx_connection_t          *c;
0456     ngx_mail_session_t        *s;
0457     ngx_mail_proxy_conf_t     *pcf;
0458     ngx_mail_core_srv_conf_t  *cscf;
0459 
0460     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
0461                    "mail proxy smtp auth handler");
0462 
0463     c = rev->data;
0464     s = c->data;
0465 
0466     if (rev->timedout) {
0467         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
0468                       "upstream timed out");
0469         c->timedout = 1;
0470         ngx_mail_proxy_internal_server_error(s);
0471         return;
0472     }
0473 
0474     rc = ngx_mail_proxy_read_response(s, s->mail_state);
0475 
0476     if (rc == NGX_AGAIN) {
0477         return;
0478     }
0479 
0480     if (rc == NGX_ERROR) {
0481         ngx_mail_proxy_upstream_error(s);
0482         return;
0483     }
0484 
0485     switch (s->mail_state) {
0486 
0487     case ngx_smtp_start:
0488         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send ehlo");
0489 
0490         s->connection->log->action = "sending HELO/EHLO to upstream";
0491 
0492         cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
0493 
0494         line.len = sizeof("HELO ")  - 1 + cscf->server_name.len + 2;
0495         line.data = ngx_pnalloc(c->pool, line.len);
0496         if (line.data == NULL) {
0497             ngx_mail_proxy_internal_server_error(s);
0498             return;
0499         }
0500 
0501         pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
0502 
0503         p = ngx_cpymem(line.data,
0504                        ((s->esmtp || pcf->xclient) ? "EHLO " : "HELO "),
0505                        sizeof("HELO ") - 1);
0506 
0507         p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);
0508         *p++ = CR; *p = LF;
0509 
0510         if (pcf->xclient) {
0511             s->mail_state = ngx_smtp_helo_xclient;
0512 
0513         } else if (s->auth_method == NGX_MAIL_AUTH_NONE) {
0514             s->mail_state = ngx_smtp_helo_from;
0515 
0516         } else {
0517             s->mail_state = ngx_smtp_helo;
0518         }
0519 
0520         break;
0521 
0522     case ngx_smtp_helo_xclient:
0523         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
0524                        "mail proxy send xclient");
0525 
0526         s->connection->log->action = "sending XCLIENT to upstream";
0527 
0528         line.len = sizeof("XCLIENT ADDR= LOGIN= NAME="
0529                           CRLF) - 1
0530                    + s->connection->addr_text.len + s->login.len + s->host.len;
0531 
0532 #if (NGX_HAVE_INET6)
0533         if (s->connection->sockaddr->sa_family == AF_INET6) {
0534             line.len += sizeof("IPV6:") - 1;
0535         }
0536 #endif
0537 
0538         line.data = ngx_pnalloc(c->pool, line.len);
0539         if (line.data == NULL) {
0540             ngx_mail_proxy_internal_server_error(s);
0541             return;
0542         }
0543 
0544         p = ngx_cpymem(line.data, "XCLIENT ADDR=", sizeof("XCLIENT ADDR=") - 1);
0545 
0546 #if (NGX_HAVE_INET6)
0547         if (s->connection->sockaddr->sa_family == AF_INET6) {
0548             p = ngx_cpymem(p, "IPV6:", sizeof("IPV6:") - 1);
0549         }
0550 #endif
0551 
0552         p = ngx_copy(p, s->connection->addr_text.data,
0553                      s->connection->addr_text.len);
0554 
0555         if (s->login.len) {
0556             p = ngx_cpymem(p, " LOGIN=", sizeof(" LOGIN=") - 1);
0557             p = ngx_copy(p, s->login.data, s->login.len);
0558         }
0559 
0560         p = ngx_cpymem(p, " NAME=", sizeof(" NAME=") - 1);
0561         p = ngx_copy(p, s->host.data, s->host.len);
0562 
0563         *p++ = CR; *p++ = LF;
0564 
0565         line.len = p - line.data;
0566 
0567         if (s->smtp_helo.len) {
0568             s->mail_state = ngx_smtp_xclient_helo;
0569 
0570         } else if (s->auth_method == NGX_MAIL_AUTH_NONE) {
0571             s->mail_state = ngx_smtp_xclient_from;
0572 
0573         } else {
0574             s->mail_state = ngx_smtp_xclient;
0575         }
0576 
0577         break;
0578 
0579     case ngx_smtp_xclient_helo:
0580         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
0581                        "mail proxy send client ehlo");
0582 
0583         s->connection->log->action = "sending client HELO/EHLO to upstream";
0584 
0585         line.len = sizeof("HELO " CRLF) - 1 + s->smtp_helo.len;
0586 
0587         line.data = ngx_pnalloc(c->pool, line.len);
0588         if (line.data == NULL) {
0589             ngx_mail_proxy_internal_server_error(s);
0590             return;
0591         }
0592 
0593         line.len = ngx_sprintf(line.data,
0594                        ((s->esmtp) ? "EHLO %V" CRLF : "HELO %V" CRLF),
0595                        &s->smtp_helo)
0596                    - line.data;
0597 
0598         s->mail_state = (s->auth_method == NGX_MAIL_AUTH_NONE) ?
0599                             ngx_smtp_helo_from : ngx_smtp_helo;
0600 
0601         break;
0602 
0603     case ngx_smtp_helo_from:
0604     case ngx_smtp_xclient_from:
0605         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
0606                        "mail proxy send mail from");
0607 
0608         s->connection->log->action = "sending MAIL FROM to upstream";
0609 
0610         line.len = s->smtp_from.len + sizeof(CRLF) - 1;
0611         line.data = ngx_pnalloc(c->pool, line.len);
0612         if (line.data == NULL) {
0613             ngx_mail_proxy_internal_server_error(s);
0614             return;
0615         }
0616 
0617         p = ngx_cpymem(line.data, s->smtp_from.data, s->smtp_from.len);
0618         *p++ = CR; *p = LF;
0619 
0620         s->mail_state = ngx_smtp_from;
0621 
0622         break;
0623 
0624     case ngx_smtp_from:
0625         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
0626                        "mail proxy send rcpt to");
0627 
0628         s->connection->log->action = "sending RCPT TO to upstream";
0629 
0630         line.len = s->smtp_to.len + sizeof(CRLF) - 1;
0631         line.data = ngx_pnalloc(c->pool, line.len);
0632         if (line.data == NULL) {
0633             ngx_mail_proxy_internal_server_error(s);
0634             return;
0635         }
0636 
0637         p = ngx_cpymem(line.data, s->smtp_to.data, s->smtp_to.len);
0638         *p++ = CR; *p = LF;
0639 
0640         s->mail_state = ngx_smtp_to;
0641 
0642         break;
0643 
0644     case ngx_smtp_helo:
0645     case ngx_smtp_xclient:
0646     case ngx_smtp_to:
0647 
0648         b = s->proxy->buffer;
0649 
0650         if (s->auth_method == NGX_MAIL_AUTH_NONE) {
0651             b->pos = b->start;
0652 
0653         } else {
0654             ngx_memcpy(b->start, smtp_auth_ok, sizeof(smtp_auth_ok) - 1);
0655             b->last = b->start + sizeof(smtp_auth_ok) - 1;
0656         }
0657 
0658         s->connection->read->handler = ngx_mail_proxy_handler;
0659         s->connection->write->handler = ngx_mail_proxy_handler;
0660         rev->handler = ngx_mail_proxy_handler;
0661         c->write->handler = ngx_mail_proxy_handler;
0662 
0663         pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
0664         ngx_add_timer(s->connection->read, pcf->timeout);
0665         ngx_del_timer(c->read);
0666 
0667         c->log->action = NULL;
0668         ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");
0669 
0670         if (s->buffer->pos == s->buffer->last) {
0671             ngx_mail_proxy_handler(s->connection->write);
0672 
0673         } else {
0674             ngx_mail_proxy_handler(c->write);
0675         }
0676 
0677         return;
0678 
0679     default:
0680 #if (NGX_SUPPRESS_WARN)
0681         ngx_str_null(&line);
0682 #endif
0683         break;
0684     }
0685 
0686     if (c->send(c, line.data, line.len) < (ssize_t) line.len) {
0687         /*
0688          * we treat the incomplete sending as NGX_ERROR
0689          * because it is very strange here
0690          */
0691         ngx_mail_proxy_internal_server_error(s);
0692         return;
0693     }
0694 
0695     s->proxy->buffer->pos = s->proxy->buffer->start;
0696     s->proxy->buffer->last = s->proxy->buffer->start;
0697 }
0698 
0699 
0700 static void
0701 ngx_mail_proxy_dummy_handler(ngx_event_t *wev)
0702 {
0703     ngx_connection_t    *c;
0704     ngx_mail_session_t  *s;
0705 
0706     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0, "mail proxy dummy handler");
0707 
0708     if (ngx_handle_write_event(wev, 0) != NGX_OK) {
0709         c = wev->data;
0710         s = c->data;
0711 
0712         ngx_mail_proxy_close_session(s);
0713     }
0714 }
0715 
0716 
0717 static ngx_int_t
0718 ngx_mail_proxy_read_response(ngx_mail_session_t *s, ngx_uint_t state)
0719 {
0720     u_char                 *p, *m;
0721     ssize_t                 n;
0722     ngx_buf_t              *b;
0723     ngx_mail_proxy_conf_t  *pcf;
0724 
0725     s->connection->log->action = "reading response from upstream";
0726 
0727     b = s->proxy->buffer;
0728 
0729     n = s->proxy->upstream.connection->recv(s->proxy->upstream.connection,
0730                                             b->last, b->end - b->last);
0731 
0732     if (n == NGX_ERROR || n == 0) {
0733         return NGX_ERROR;
0734     }
0735 
0736     if (n == NGX_AGAIN) {
0737         return NGX_AGAIN;
0738     }
0739 
0740     b->last += n;
0741 
0742     if (b->last - b->pos < 4) {
0743         return NGX_AGAIN;
0744     }
0745 
0746     if (*(b->last - 2) != CR || *(b->last - 1) != LF) {
0747         if (b->last == b->end) {
0748             *(b->last - 1) = '\0';
0749             ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
0750                           "upstream sent too long response line: \"%s\"",
0751                           b->pos);
0752             return NGX_ERROR;
0753         }
0754 
0755         return NGX_AGAIN;
0756     }
0757 
0758     p = b->pos;
0759 
0760     switch (s->protocol) {
0761 
0762     case NGX_MAIL_POP3_PROTOCOL:
0763         if (p[0] == '+' && p[1] == 'O' && p[2] == 'K') {
0764             return NGX_OK;
0765         }
0766         break;
0767 
0768     case NGX_MAIL_IMAP_PROTOCOL:
0769         switch (state) {
0770 
0771         case ngx_imap_start:
0772             if (p[0] == '*' && p[1] == ' ' && p[2] == 'O' && p[3] == 'K') {
0773                 return NGX_OK;
0774             }
0775             break;
0776 
0777         case ngx_imap_login:
0778         case ngx_imap_user:
0779             if (p[0] == '+') {
0780                 return NGX_OK;
0781             }
0782             break;
0783 
0784         case ngx_imap_passwd:
0785             if (ngx_strncmp(p, s->tag.data, s->tag.len) == 0) {
0786                 p += s->tag.len;
0787                 if (p[0] == 'O' && p[1] == 'K') {
0788                     return NGX_OK;
0789                 }
0790             }
0791             break;
0792         }
0793 
0794         break;
0795 
0796     default: /* NGX_MAIL_SMTP_PROTOCOL */
0797 
0798         if (p[3] == '-') {
0799             /* multiline reply, check if we got last line */
0800 
0801             m = b->last - (sizeof(CRLF "200" CRLF) - 1);
0802 
0803             while (m > p) {
0804                 if (m[0] == CR && m[1] == LF) {
0805                     break;
0806                 }
0807 
0808                 m--;
0809             }
0810 
0811             if (m <= p || m[5] == '-') {
0812                 return NGX_AGAIN;
0813             }
0814         }
0815 
0816         switch (state) {
0817 
0818         case ngx_smtp_start:
0819             if (p[0] == '2' && p[1] == '2' && p[2] == '0') {
0820                 return NGX_OK;
0821             }
0822             break;
0823 
0824         case ngx_smtp_helo:
0825         case ngx_smtp_helo_xclient:
0826         case ngx_smtp_helo_from:
0827         case ngx_smtp_from:
0828             if (p[0] == '2' && p[1] == '5' && p[2] == '0') {
0829                 return NGX_OK;
0830             }
0831             break;
0832 
0833         case ngx_smtp_xclient:
0834         case ngx_smtp_xclient_from:
0835         case ngx_smtp_xclient_helo:
0836             if (p[0] == '2' && (p[1] == '2' || p[1] == '5') && p[2] == '0') {
0837                 return NGX_OK;
0838             }
0839             break;
0840 
0841         case ngx_smtp_to:
0842             return NGX_OK;
0843         }
0844 
0845         break;
0846     }
0847 
0848     pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
0849 
0850     if (pcf->pass_error_message == 0) {
0851         *(b->last - 2) = '\0';
0852         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
0853                       "upstream sent invalid response: \"%s\"", p);
0854         return NGX_ERROR;
0855     }
0856 
0857     s->out.len = b->last - p - 2;
0858     s->out.data = p;
0859 
0860     ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
0861                   "upstream sent invalid response: \"%V\"", &s->out);
0862 
0863     s->out.len = b->last - b->pos;
0864     s->out.data = b->pos;
0865 
0866     return NGX_ERROR;
0867 }
0868 
0869 
0870 static void
0871 ngx_mail_proxy_handler(ngx_event_t *ev)
0872 {
0873     char                   *action, *recv_action, *send_action;
0874     size_t                  size;
0875     ssize_t                 n;
0876     ngx_buf_t              *b;
0877     ngx_uint_t              do_write;
0878     ngx_connection_t       *c, *src, *dst;
0879     ngx_mail_session_t     *s;
0880     ngx_mail_proxy_conf_t  *pcf;
0881 
0882     c = ev->data;
0883     s = c->data;
0884 
0885     if (ev->timedout || c->close) {
0886         c->log->action = "proxying";
0887 
0888         if (c->close) {
0889             ngx_log_error(NGX_LOG_INFO, c->log, 0, "shutdown timeout");
0890 
0891         } else if (c == s->connection) {
0892             ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
0893                           "client timed out");
0894             c->timedout = 1;
0895 
0896         } else {
0897             ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
0898                           "upstream timed out");
0899         }
0900 
0901         ngx_mail_proxy_close_session(s);
0902         return;
0903     }
0904 
0905     if (c == s->connection) {
0906         if (ev->write) {
0907             recv_action = "proxying and reading from upstream";
0908             send_action = "proxying and sending to client";
0909             src = s->proxy->upstream.connection;
0910             dst = c;
0911             b = s->proxy->buffer;
0912 
0913         } else {
0914             recv_action = "proxying and reading from client";
0915             send_action = "proxying and sending to upstream";
0916             src = c;
0917             dst = s->proxy->upstream.connection;
0918             b = s->buffer;
0919         }
0920 
0921     } else {
0922         if (ev->write) {
0923             recv_action = "proxying and reading from client";
0924             send_action = "proxying and sending to upstream";
0925             src = s->connection;
0926             dst = c;
0927             b = s->buffer;
0928 
0929         } else {
0930             recv_action = "proxying and reading from upstream";
0931             send_action = "proxying and sending to client";
0932             src = c;
0933             dst = s->connection;
0934             b = s->proxy->buffer;
0935         }
0936     }
0937 
0938     do_write = ev->write ? 1 : 0;
0939 
0940     ngx_log_debug3(NGX_LOG_DEBUG_MAIL, ev->log, 0,
0941                    "mail proxy handler: %ui, #%d > #%d",
0942                    do_write, src->fd, dst->fd);
0943 
0944     for ( ;; ) {
0945 
0946         if (do_write) {
0947 
0948             size = b->last - b->pos;
0949 
0950             if (size && dst->write->ready) {
0951                 c->log->action = send_action;
0952 
0953                 n = dst->send(dst, b->pos, size);
0954 
0955                 if (n == NGX_ERROR) {
0956                     ngx_mail_proxy_close_session(s);
0957                     return;
0958                 }
0959 
0960                 if (n > 0) {
0961                     b->pos += n;
0962 
0963                     if (b->pos == b->last) {
0964                         b->pos = b->start;
0965                         b->last = b->start;
0966                     }
0967                 }
0968             }
0969         }
0970 
0971         size = b->end - b->last;
0972 
0973         if (size && src->read->ready) {
0974             c->log->action = recv_action;
0975 
0976             n = src->recv(src, b->last, size);
0977 
0978             if (n == NGX_AGAIN || n == 0) {
0979                 break;
0980             }
0981 
0982             if (n > 0) {
0983                 do_write = 1;
0984                 b->last += n;
0985 
0986                 continue;
0987             }
0988 
0989             if (n == NGX_ERROR) {
0990                 src->read->eof = 1;
0991             }
0992         }
0993 
0994         break;
0995     }
0996 
0997     c->log->action = "proxying";
0998 
0999     if ((s->connection->read->eof && s->buffer->pos == s->buffer->last)
1000         || (s->proxy->upstream.connection->read->eof
1001             && s->proxy->buffer->pos == s->proxy->buffer->last)
1002         || (s->connection->read->eof
1003             && s->proxy->upstream.connection->read->eof))
1004     {
1005         action = c->log->action;
1006         c->log->action = NULL;
1007         ngx_log_error(NGX_LOG_INFO, c->log, 0, "proxied session done");
1008         c->log->action = action;
1009 
1010         ngx_mail_proxy_close_session(s);
1011         return;
1012     }
1013 
1014     if (ngx_handle_write_event(dst->write, 0) != NGX_OK) {
1015         ngx_mail_proxy_close_session(s);
1016         return;
1017     }
1018 
1019     if (ngx_handle_read_event(dst->read, 0) != NGX_OK) {
1020         ngx_mail_proxy_close_session(s);
1021         return;
1022     }
1023 
1024     if (ngx_handle_write_event(src->write, 0) != NGX_OK) {
1025         ngx_mail_proxy_close_session(s);
1026         return;
1027     }
1028 
1029     if (ngx_handle_read_event(src->read, 0) != NGX_OK) {
1030         ngx_mail_proxy_close_session(s);
1031         return;
1032     }
1033 
1034     if (c == s->connection) {
1035         pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
1036         ngx_add_timer(c->read, pcf->timeout);
1037     }
1038 }
1039 
1040 
1041 static void
1042 ngx_mail_proxy_upstream_error(ngx_mail_session_t *s)
1043 {
1044     if (s->proxy->upstream.connection) {
1045         ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
1046                        "close mail proxy connection: %d",
1047                        s->proxy->upstream.connection->fd);
1048 
1049         ngx_close_connection(s->proxy->upstream.connection);
1050     }
1051 
1052     if (s->out.len == 0) {
1053         ngx_mail_session_internal_server_error(s);
1054         return;
1055     }
1056 
1057     s->quit = 1;
1058     ngx_mail_send(s->connection->write);
1059 }
1060 
1061 
1062 static void
1063 ngx_mail_proxy_internal_server_error(ngx_mail_session_t *s)
1064 {
1065     if (s->proxy->upstream.connection) {
1066         ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
1067                        "close mail proxy connection: %d",
1068                        s->proxy->upstream.connection->fd);
1069 
1070         ngx_close_connection(s->proxy->upstream.connection);
1071     }
1072 
1073     ngx_mail_session_internal_server_error(s);
1074 }
1075 
1076 
1077 static void
1078 ngx_mail_proxy_close_session(ngx_mail_session_t *s)
1079 {
1080     if (s->proxy->upstream.connection) {
1081         ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
1082                        "close mail proxy connection: %d",
1083                        s->proxy->upstream.connection->fd);
1084 
1085         ngx_close_connection(s->proxy->upstream.connection);
1086     }
1087 
1088     ngx_mail_close_connection(s->connection);
1089 }
1090 
1091 
1092 static void *
1093 ngx_mail_proxy_create_conf(ngx_conf_t *cf)
1094 {
1095     ngx_mail_proxy_conf_t  *pcf;
1096 
1097     pcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_proxy_conf_t));
1098     if (pcf == NULL) {
1099         return NULL;
1100     }
1101 
1102     pcf->enable = NGX_CONF_UNSET;
1103     pcf->pass_error_message = NGX_CONF_UNSET;
1104     pcf->xclient = NGX_CONF_UNSET;
1105     pcf->buffer_size = NGX_CONF_UNSET_SIZE;
1106     pcf->timeout = NGX_CONF_UNSET_MSEC;
1107 
1108     return pcf;
1109 }
1110 
1111 
1112 static char *
1113 ngx_mail_proxy_merge_conf(ngx_conf_t *cf, void *parent, void *child)
1114 {
1115     ngx_mail_proxy_conf_t *prev = parent;
1116     ngx_mail_proxy_conf_t *conf = child;
1117 
1118     ngx_conf_merge_value(conf->enable, prev->enable, 0);
1119     ngx_conf_merge_value(conf->pass_error_message, prev->pass_error_message, 0);
1120     ngx_conf_merge_value(conf->xclient, prev->xclient, 1);
1121     ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
1122                               (size_t) ngx_pagesize);
1123     ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 24 * 60 * 60000);
1124 
1125     return NGX_CONF_OK;
1126 }