1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) Nginx, Inc.
5  */
6 
7 
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_event.h>
11 #include <ngx_event_connect.h>
12 #include <ngx_mail.h>
13 
14 
15 typedef struct {
16     ngx_flag_t  enable;
17     ngx_flag_t  pass_error_message;
18     ngx_flag_t  xclient;
19     size_t      buffer_size;
20     ngx_msec_t  timeout;
21 
22 #if (NGX_HAVE_FSTACK)
23     ngx_flag_t  kernel_network_stack;
24 #endif
25 } ngx_mail_proxy_conf_t;
26 
27 
28 static void ngx_mail_proxy_block_read(ngx_event_t *rev);
29 static void ngx_mail_proxy_pop3_handler(ngx_event_t *rev);
30 static void ngx_mail_proxy_imap_handler(ngx_event_t *rev);
31 static void ngx_mail_proxy_smtp_handler(ngx_event_t *rev);
32 static void ngx_mail_proxy_dummy_handler(ngx_event_t *ev);
33 static ngx_int_t ngx_mail_proxy_read_response(ngx_mail_session_t *s,
34     ngx_uint_t state);
35 static void ngx_mail_proxy_handler(ngx_event_t *ev);
36 static void ngx_mail_proxy_upstream_error(ngx_mail_session_t *s);
37 static void ngx_mail_proxy_internal_server_error(ngx_mail_session_t *s);
38 static void ngx_mail_proxy_close_session(ngx_mail_session_t *s);
39 static void *ngx_mail_proxy_create_conf(ngx_conf_t *cf);
40 static char *ngx_mail_proxy_merge_conf(ngx_conf_t *cf, void *parent,
41     void *child);
42 
43 
44 static ngx_command_t  ngx_mail_proxy_commands[] = {
45 
46     { ngx_string("proxy"),
47       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
48       ngx_conf_set_flag_slot,
49       NGX_MAIL_SRV_CONF_OFFSET,
50       offsetof(ngx_mail_proxy_conf_t, enable),
51       NULL },
52 
53     { ngx_string("proxy_buffer"),
54       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
55       ngx_conf_set_size_slot,
56       NGX_MAIL_SRV_CONF_OFFSET,
57       offsetof(ngx_mail_proxy_conf_t, buffer_size),
58       NULL },
59 
60     { ngx_string("proxy_timeout"),
61       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
62       ngx_conf_set_msec_slot,
63       NGX_MAIL_SRV_CONF_OFFSET,
64       offsetof(ngx_mail_proxy_conf_t, timeout),
65       NULL },
66 
67     { ngx_string("proxy_pass_error_message"),
68       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
69       ngx_conf_set_flag_slot,
70       NGX_MAIL_SRV_CONF_OFFSET,
71       offsetof(ngx_mail_proxy_conf_t, pass_error_message),
72       NULL },
73 
74     { ngx_string("xclient"),
75       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
76       ngx_conf_set_flag_slot,
77       NGX_MAIL_SRV_CONF_OFFSET,
78       offsetof(ngx_mail_proxy_conf_t, xclient),
79       NULL },
80 
81       ngx_null_command
82 };
83 
84 
85 static ngx_mail_module_t  ngx_mail_proxy_module_ctx = {
86     NULL,                                  /* protocol */
87 
88     NULL,                                  /* create main configuration */
89     NULL,                                  /* init main configuration */
90 
91     ngx_mail_proxy_create_conf,            /* create server configuration */
92     ngx_mail_proxy_merge_conf              /* merge server configuration */
93 };
94 
95 
96 ngx_module_t  ngx_mail_proxy_module = {
97     NGX_MODULE_V1,
98     &ngx_mail_proxy_module_ctx,            /* module context */
99     ngx_mail_proxy_commands,               /* module directives */
100     NGX_MAIL_MODULE,                       /* module type */
101     NULL,                                  /* init master */
102     NULL,                                  /* init module */
103     NULL,                                  /* init process */
104     NULL,                                  /* init thread */
105     NULL,                                  /* exit thread */
106     NULL,                                  /* exit process */
107     NULL,                                  /* exit master */
108     NGX_MODULE_V1_PADDING
109 };
110 
111 
112 static u_char  smtp_auth_ok[] = "235 2.0.0 OK" CRLF;
113 
114 
115 void
ngx_mail_proxy_init(ngx_mail_session_t * s,ngx_addr_t * peer)116 ngx_mail_proxy_init(ngx_mail_session_t *s, ngx_addr_t *peer)
117 {
118     ngx_int_t                  rc;
119     ngx_mail_proxy_ctx_t      *p;
120     ngx_mail_proxy_conf_t     *pcf;
121     ngx_mail_core_srv_conf_t  *cscf;
122 
123     s->connection->log->action = "connecting to upstream";
124 
125     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
126 
127     p = ngx_pcalloc(s->connection->pool, sizeof(ngx_mail_proxy_ctx_t));
128     if (p == NULL) {
129         ngx_mail_session_internal_server_error(s);
130         return;
131     }
132 
133     s->proxy = p;
134 
135     p->upstream.sockaddr = peer->sockaddr;
136     p->upstream.socklen = peer->socklen;
137     p->upstream.name = &peer->name;
138     p->upstream.get = ngx_event_get_peer;
139     p->upstream.log = s->connection->log;
140     p->upstream.log_error = NGX_ERROR_ERR;
141 
142     rc = ngx_event_connect_peer(&p->upstream);
143 
144     if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
145         ngx_mail_proxy_internal_server_error(s);
146         return;
147     }
148 
149     ngx_add_timer(p->upstream.connection->read, cscf->timeout);
150 
151     p->upstream.connection->data = s;
152     p->upstream.connection->pool = s->connection->pool;
153 
154     s->connection->read->handler = ngx_mail_proxy_block_read;
155     p->upstream.connection->write->handler = ngx_mail_proxy_dummy_handler;
156 
157     pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
158 
159     s->proxy->buffer = ngx_create_temp_buf(s->connection->pool,
160                                            pcf->buffer_size);
161     if (s->proxy->buffer == NULL) {
162         ngx_mail_proxy_internal_server_error(s);
163         return;
164     }
165 
166     s->out.len = 0;
167 
168     switch (s->protocol) {
169 
170     case NGX_MAIL_POP3_PROTOCOL:
171         p->upstream.connection->read->handler = ngx_mail_proxy_pop3_handler;
172         s->mail_state = ngx_pop3_start;
173         break;
174 
175     case NGX_MAIL_IMAP_PROTOCOL:
176         p->upstream.connection->read->handler = ngx_mail_proxy_imap_handler;
177         s->mail_state = ngx_imap_start;
178         break;
179 
180     default: /* NGX_MAIL_SMTP_PROTOCOL */
181         p->upstream.connection->read->handler = ngx_mail_proxy_smtp_handler;
182         s->mail_state = ngx_smtp_start;
183         break;
184     }
185 }
186 
187 
188 static void
ngx_mail_proxy_block_read(ngx_event_t * rev)189 ngx_mail_proxy_block_read(ngx_event_t *rev)
190 {
191     ngx_connection_t    *c;
192     ngx_mail_session_t  *s;
193 
194     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy block read");
195 
196     if (ngx_handle_read_event(rev, 0) != NGX_OK) {
197         c = rev->data;
198         s = c->data;
199 
200         ngx_mail_proxy_close_session(s);
201     }
202 }
203 
204 
205 static void
ngx_mail_proxy_pop3_handler(ngx_event_t * rev)206 ngx_mail_proxy_pop3_handler(ngx_event_t *rev)
207 {
208     u_char                 *p;
209     ngx_int_t               rc;
210     ngx_str_t               line;
211     ngx_connection_t       *c;
212     ngx_mail_session_t     *s;
213     ngx_mail_proxy_conf_t  *pcf;
214 
215     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
216                    "mail proxy pop3 auth handler");
217 
218     c = rev->data;
219     s = c->data;
220 
221     if (rev->timedout) {
222         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
223                       "upstream timed out");
224         c->timedout = 1;
225         ngx_mail_proxy_internal_server_error(s);
226         return;
227     }
228 
229     rc = ngx_mail_proxy_read_response(s, 0);
230 
231     if (rc == NGX_AGAIN) {
232         return;
233     }
234 
235     if (rc == NGX_ERROR) {
236         ngx_mail_proxy_upstream_error(s);
237         return;
238     }
239 
240     switch (s->mail_state) {
241 
242     case ngx_pop3_start:
243         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send user");
244 
245         s->connection->log->action = "sending user name to upstream";
246 
247         line.len = sizeof("USER ")  - 1 + s->login.len + 2;
248         line.data = ngx_pnalloc(c->pool, line.len);
249         if (line.data == NULL) {
250             ngx_mail_proxy_internal_server_error(s);
251             return;
252         }
253 
254         p = ngx_cpymem(line.data, "USER ", sizeof("USER ") - 1);
255         p = ngx_cpymem(p, s->login.data, s->login.len);
256         *p++ = CR; *p = LF;
257 
258         s->mail_state = ngx_pop3_user;
259         break;
260 
261     case ngx_pop3_user:
262         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send pass");
263 
264         s->connection->log->action = "sending password to upstream";
265 
266         line.len = sizeof("PASS ")  - 1 + s->passwd.len + 2;
267         line.data = ngx_pnalloc(c->pool, line.len);
268         if (line.data == NULL) {
269             ngx_mail_proxy_internal_server_error(s);
270             return;
271         }
272 
273         p = ngx_cpymem(line.data, "PASS ", sizeof("PASS ") - 1);
274         p = ngx_cpymem(p, s->passwd.data, s->passwd.len);
275         *p++ = CR; *p = LF;
276 
277         s->mail_state = ngx_pop3_passwd;
278         break;
279 
280     case ngx_pop3_passwd:
281         s->connection->read->handler = ngx_mail_proxy_handler;
282         s->connection->write->handler = ngx_mail_proxy_handler;
283         rev->handler = ngx_mail_proxy_handler;
284         c->write->handler = ngx_mail_proxy_handler;
285 
286         pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
287         ngx_add_timer(s->connection->read, pcf->timeout);
288         ngx_del_timer(c->read);
289 
290         c->log->action = NULL;
291         ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");
292 
293         ngx_mail_proxy_handler(s->connection->write);
294 
295         return;
296 
297     default:
298 #if (NGX_SUPPRESS_WARN)
299         ngx_str_null(&line);
300 #endif
301         break;
302     }
303 
304     if (c->send(c, line.data, line.len) < (ssize_t) line.len) {
305         /*
306          * we treat the incomplete sending as NGX_ERROR
307          * because it is very strange here
308          */
309         ngx_mail_proxy_internal_server_error(s);
310         return;
311     }
312 
313     s->proxy->buffer->pos = s->proxy->buffer->start;
314     s->proxy->buffer->last = s->proxy->buffer->start;
315 }
316 
317 
318 static void
ngx_mail_proxy_imap_handler(ngx_event_t * rev)319 ngx_mail_proxy_imap_handler(ngx_event_t *rev)
320 {
321     u_char                 *p;
322     ngx_int_t               rc;
323     ngx_str_t               line;
324     ngx_connection_t       *c;
325     ngx_mail_session_t     *s;
326     ngx_mail_proxy_conf_t  *pcf;
327 
328     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
329                    "mail proxy imap auth handler");
330 
331     c = rev->data;
332     s = c->data;
333 
334     if (rev->timedout) {
335         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
336                       "upstream timed out");
337         c->timedout = 1;
338         ngx_mail_proxy_internal_server_error(s);
339         return;
340     }
341 
342     rc = ngx_mail_proxy_read_response(s, s->mail_state);
343 
344     if (rc == NGX_AGAIN) {
345         return;
346     }
347 
348     if (rc == NGX_ERROR) {
349         ngx_mail_proxy_upstream_error(s);
350         return;
351     }
352 
353     switch (s->mail_state) {
354 
355     case ngx_imap_start:
356         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
357                        "mail proxy send login");
358 
359         s->connection->log->action = "sending LOGIN command to upstream";
360 
361         line.len = s->tag.len + sizeof("LOGIN ") - 1
362                    + 1 + NGX_SIZE_T_LEN + 1 + 2;
363         line.data = ngx_pnalloc(c->pool, line.len);
364         if (line.data == NULL) {
365             ngx_mail_proxy_internal_server_error(s);
366             return;
367         }
368 
369         line.len = ngx_sprintf(line.data, "%VLOGIN {%uz}" CRLF,
370                                &s->tag, s->login.len)
371                    - line.data;
372 
373         s->mail_state = ngx_imap_login;
374         break;
375 
376     case ngx_imap_login:
377         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send user");
378 
379         s->connection->log->action = "sending user name to upstream";
380 
381         line.len = s->login.len + 1 + 1 + NGX_SIZE_T_LEN + 1 + 2;
382         line.data = ngx_pnalloc(c->pool, line.len);
383         if (line.data == NULL) {
384             ngx_mail_proxy_internal_server_error(s);
385             return;
386         }
387 
388         line.len = ngx_sprintf(line.data, "%V {%uz}" CRLF,
389                                &s->login, s->passwd.len)
390                    - line.data;
391 
392         s->mail_state = ngx_imap_user;
393         break;
394 
395     case ngx_imap_user:
396         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
397                        "mail proxy send passwd");
398 
399         s->connection->log->action = "sending password to upstream";
400 
401         line.len = s->passwd.len + 2;
402         line.data = ngx_pnalloc(c->pool, line.len);
403         if (line.data == NULL) {
404             ngx_mail_proxy_internal_server_error(s);
405             return;
406         }
407 
408         p = ngx_cpymem(line.data, s->passwd.data, s->passwd.len);
409         *p++ = CR; *p = LF;
410 
411         s->mail_state = ngx_imap_passwd;
412         break;
413 
414     case ngx_imap_passwd:
415         s->connection->read->handler = ngx_mail_proxy_handler;
416         s->connection->write->handler = ngx_mail_proxy_handler;
417         rev->handler = ngx_mail_proxy_handler;
418         c->write->handler = ngx_mail_proxy_handler;
419 
420         pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
421         ngx_add_timer(s->connection->read, pcf->timeout);
422         ngx_del_timer(c->read);
423 
424         c->log->action = NULL;
425         ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");
426 
427         ngx_mail_proxy_handler(s->connection->write);
428 
429         return;
430 
431     default:
432 #if (NGX_SUPPRESS_WARN)
433         ngx_str_null(&line);
434 #endif
435         break;
436     }
437 
438     if (c->send(c, line.data, line.len) < (ssize_t) line.len) {
439         /*
440          * we treat the incomplete sending as NGX_ERROR
441          * because it is very strange here
442          */
443         ngx_mail_proxy_internal_server_error(s);
444         return;
445     }
446 
447     s->proxy->buffer->pos = s->proxy->buffer->start;
448     s->proxy->buffer->last = s->proxy->buffer->start;
449 }
450 
451 
452 static void
ngx_mail_proxy_smtp_handler(ngx_event_t * rev)453 ngx_mail_proxy_smtp_handler(ngx_event_t *rev)
454 {
455     u_char                    *p;
456     ngx_int_t                  rc;
457     ngx_str_t                  line;
458     ngx_buf_t                 *b;
459     ngx_connection_t          *c;
460     ngx_mail_session_t        *s;
461     ngx_mail_proxy_conf_t     *pcf;
462     ngx_mail_core_srv_conf_t  *cscf;
463 
464     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
465                    "mail proxy smtp auth handler");
466 
467     c = rev->data;
468     s = c->data;
469 
470     if (rev->timedout) {
471         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
472                       "upstream timed out");
473         c->timedout = 1;
474         ngx_mail_proxy_internal_server_error(s);
475         return;
476     }
477 
478     rc = ngx_mail_proxy_read_response(s, s->mail_state);
479 
480     if (rc == NGX_AGAIN) {
481         return;
482     }
483 
484     if (rc == NGX_ERROR) {
485         ngx_mail_proxy_upstream_error(s);
486         return;
487     }
488 
489     switch (s->mail_state) {
490 
491     case ngx_smtp_start:
492         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send ehlo");
493 
494         s->connection->log->action = "sending HELO/EHLO to upstream";
495 
496         cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
497 
498         line.len = sizeof("HELO ")  - 1 + cscf->server_name.len + 2;
499         line.data = ngx_pnalloc(c->pool, line.len);
500         if (line.data == NULL) {
501             ngx_mail_proxy_internal_server_error(s);
502             return;
503         }
504 
505         pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
506 
507         p = ngx_cpymem(line.data,
508                        ((s->esmtp || pcf->xclient) ? "EHLO " : "HELO "),
509                        sizeof("HELO ") - 1);
510 
511         p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);
512         *p++ = CR; *p = LF;
513 
514         if (pcf->xclient) {
515             s->mail_state = ngx_smtp_helo_xclient;
516 
517         } else if (s->auth_method == NGX_MAIL_AUTH_NONE) {
518             s->mail_state = ngx_smtp_helo_from;
519 
520         } else {
521             s->mail_state = ngx_smtp_helo;
522         }
523 
524         break;
525 
526     case ngx_smtp_helo_xclient:
527         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
528                        "mail proxy send xclient");
529 
530         s->connection->log->action = "sending XCLIENT to upstream";
531 
532         line.len = sizeof("XCLIENT ADDR= LOGIN= NAME="
533                           CRLF) - 1
534                    + s->connection->addr_text.len + s->login.len + s->host.len;
535 
536 #if (NGX_HAVE_INET6)
537         if (s->connection->sockaddr->sa_family == AF_INET6) {
538             line.len += sizeof("IPV6:") - 1;
539         }
540 #endif
541 
542         line.data = ngx_pnalloc(c->pool, line.len);
543         if (line.data == NULL) {
544             ngx_mail_proxy_internal_server_error(s);
545             return;
546         }
547 
548         p = ngx_cpymem(line.data, "XCLIENT ADDR=", sizeof("XCLIENT ADDR=") - 1);
549 
550 #if (NGX_HAVE_INET6)
551         if (s->connection->sockaddr->sa_family == AF_INET6) {
552             p = ngx_cpymem(p, "IPV6:", sizeof("IPV6:") - 1);
553         }
554 #endif
555 
556         p = ngx_copy(p, s->connection->addr_text.data,
557                      s->connection->addr_text.len);
558 
559         if (s->login.len) {
560             p = ngx_cpymem(p, " LOGIN=", sizeof(" LOGIN=") - 1);
561             p = ngx_copy(p, s->login.data, s->login.len);
562         }
563 
564         p = ngx_cpymem(p, " NAME=", sizeof(" NAME=") - 1);
565         p = ngx_copy(p, s->host.data, s->host.len);
566 
567         *p++ = CR; *p++ = LF;
568 
569         line.len = p - line.data;
570 
571         if (s->smtp_helo.len) {
572             s->mail_state = ngx_smtp_xclient_helo;
573 
574         } else if (s->auth_method == NGX_MAIL_AUTH_NONE) {
575             s->mail_state = ngx_smtp_xclient_from;
576 
577         } else {
578             s->mail_state = ngx_smtp_xclient;
579         }
580 
581         break;
582 
583     case ngx_smtp_xclient_helo:
584         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
585                        "mail proxy send client ehlo");
586 
587         s->connection->log->action = "sending client HELO/EHLO to upstream";
588 
589         line.len = sizeof("HELO " CRLF) - 1 + s->smtp_helo.len;
590 
591         line.data = ngx_pnalloc(c->pool, line.len);
592         if (line.data == NULL) {
593             ngx_mail_proxy_internal_server_error(s);
594             return;
595         }
596 
597         line.len = ngx_sprintf(line.data,
598                        ((s->esmtp) ? "EHLO %V" CRLF : "HELO %V" CRLF),
599                        &s->smtp_helo)
600                    - line.data;
601 
602         s->mail_state = (s->auth_method == NGX_MAIL_AUTH_NONE) ?
603                             ngx_smtp_helo_from : ngx_smtp_helo;
604 
605         break;
606 
607     case ngx_smtp_helo_from:
608     case ngx_smtp_xclient_from:
609         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
610                        "mail proxy send mail from");
611 
612         s->connection->log->action = "sending MAIL FROM to upstream";
613 
614         line.len = s->smtp_from.len + sizeof(CRLF) - 1;
615         line.data = ngx_pnalloc(c->pool, line.len);
616         if (line.data == NULL) {
617             ngx_mail_proxy_internal_server_error(s);
618             return;
619         }
620 
621         p = ngx_cpymem(line.data, s->smtp_from.data, s->smtp_from.len);
622         *p++ = CR; *p = LF;
623 
624         s->mail_state = ngx_smtp_from;
625 
626         break;
627 
628     case ngx_smtp_from:
629         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
630                        "mail proxy send rcpt to");
631 
632         s->connection->log->action = "sending RCPT TO to upstream";
633 
634         line.len = s->smtp_to.len + sizeof(CRLF) - 1;
635         line.data = ngx_pnalloc(c->pool, line.len);
636         if (line.data == NULL) {
637             ngx_mail_proxy_internal_server_error(s);
638             return;
639         }
640 
641         p = ngx_cpymem(line.data, s->smtp_to.data, s->smtp_to.len);
642         *p++ = CR; *p = LF;
643 
644         s->mail_state = ngx_smtp_to;
645 
646         break;
647 
648     case ngx_smtp_helo:
649     case ngx_smtp_xclient:
650     case ngx_smtp_to:
651 
652         b = s->proxy->buffer;
653 
654         if (s->auth_method == NGX_MAIL_AUTH_NONE) {
655             b->pos = b->start;
656 
657         } else {
658             ngx_memcpy(b->start, smtp_auth_ok, sizeof(smtp_auth_ok) - 1);
659             b->last = b->start + sizeof(smtp_auth_ok) - 1;
660         }
661 
662         s->connection->read->handler = ngx_mail_proxy_handler;
663         s->connection->write->handler = ngx_mail_proxy_handler;
664         rev->handler = ngx_mail_proxy_handler;
665         c->write->handler = ngx_mail_proxy_handler;
666 
667         pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
668         ngx_add_timer(s->connection->read, pcf->timeout);
669         ngx_del_timer(c->read);
670 
671         c->log->action = NULL;
672         ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");
673 
674         if (s->buffer->pos == s->buffer->last) {
675             ngx_mail_proxy_handler(s->connection->write);
676 
677         } else {
678             ngx_mail_proxy_handler(c->write);
679         }
680 
681         return;
682 
683     default:
684 #if (NGX_SUPPRESS_WARN)
685         ngx_str_null(&line);
686 #endif
687         break;
688     }
689 
690     if (c->send(c, line.data, line.len) < (ssize_t) line.len) {
691         /*
692          * we treat the incomplete sending as NGX_ERROR
693          * because it is very strange here
694          */
695         ngx_mail_proxy_internal_server_error(s);
696         return;
697     }
698 
699     s->proxy->buffer->pos = s->proxy->buffer->start;
700     s->proxy->buffer->last = s->proxy->buffer->start;
701 }
702 
703 
704 static void
ngx_mail_proxy_dummy_handler(ngx_event_t * wev)705 ngx_mail_proxy_dummy_handler(ngx_event_t *wev)
706 {
707     ngx_connection_t    *c;
708     ngx_mail_session_t  *s;
709 
710     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0, "mail proxy dummy handler");
711 
712     if (ngx_handle_write_event(wev, 0) != NGX_OK) {
713         c = wev->data;
714         s = c->data;
715 
716         ngx_mail_proxy_close_session(s);
717     }
718 }
719 
720 
721 static ngx_int_t
ngx_mail_proxy_read_response(ngx_mail_session_t * s,ngx_uint_t state)722 ngx_mail_proxy_read_response(ngx_mail_session_t *s, ngx_uint_t state)
723 {
724     u_char                 *p, *m;
725     ssize_t                 n;
726     ngx_buf_t              *b;
727     ngx_mail_proxy_conf_t  *pcf;
728 
729     s->connection->log->action = "reading response from upstream";
730 
731     b = s->proxy->buffer;
732 
733     n = s->proxy->upstream.connection->recv(s->proxy->upstream.connection,
734                                             b->last, b->end - b->last);
735 
736     if (n == NGX_ERROR || n == 0) {
737         return NGX_ERROR;
738     }
739 
740     if (n == NGX_AGAIN) {
741         return NGX_AGAIN;
742     }
743 
744     b->last += n;
745 
746     if (b->last - b->pos < 4) {
747         return NGX_AGAIN;
748     }
749 
750     if (*(b->last - 2) != CR || *(b->last - 1) != LF) {
751         if (b->last == b->end) {
752             *(b->last - 1) = '\0';
753             ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
754                           "upstream sent too long response line: \"%s\"",
755                           b->pos);
756             return NGX_ERROR;
757         }
758 
759         return NGX_AGAIN;
760     }
761 
762     p = b->pos;
763 
764     switch (s->protocol) {
765 
766     case NGX_MAIL_POP3_PROTOCOL:
767         if (p[0] == '+' && p[1] == 'O' && p[2] == 'K') {
768             return NGX_OK;
769         }
770         break;
771 
772     case NGX_MAIL_IMAP_PROTOCOL:
773         switch (state) {
774 
775         case ngx_imap_start:
776             if (p[0] == '*' && p[1] == ' ' && p[2] == 'O' && p[3] == 'K') {
777                 return NGX_OK;
778             }
779             break;
780 
781         case ngx_imap_login:
782         case ngx_imap_user:
783             if (p[0] == '+') {
784                 return NGX_OK;
785             }
786             break;
787 
788         case ngx_imap_passwd:
789             if (ngx_strncmp(p, s->tag.data, s->tag.len) == 0) {
790                 p += s->tag.len;
791                 if (p[0] == 'O' && p[1] == 'K') {
792                     return NGX_OK;
793                 }
794             }
795             break;
796         }
797 
798         break;
799 
800     default: /* NGX_MAIL_SMTP_PROTOCOL */
801 
802         if (p[3] == '-') {
803             /* multiline reply, check if we got last line */
804 
805             m = b->last - (sizeof(CRLF "200" CRLF) - 1);
806 
807             while (m > p) {
808                 if (m[0] == CR && m[1] == LF) {
809                     break;
810                 }
811 
812                 m--;
813             }
814 
815             if (m <= p || m[5] == '-') {
816                 return NGX_AGAIN;
817             }
818         }
819 
820         switch (state) {
821 
822         case ngx_smtp_start:
823             if (p[0] == '2' && p[1] == '2' && p[2] == '0') {
824                 return NGX_OK;
825             }
826             break;
827 
828         case ngx_smtp_helo:
829         case ngx_smtp_helo_xclient:
830         case ngx_smtp_helo_from:
831         case ngx_smtp_from:
832             if (p[0] == '2' && p[1] == '5' && p[2] == '0') {
833                 return NGX_OK;
834             }
835             break;
836 
837         case ngx_smtp_xclient:
838         case ngx_smtp_xclient_from:
839         case ngx_smtp_xclient_helo:
840             if (p[0] == '2' && (p[1] == '2' || p[1] == '5') && p[2] == '0') {
841                 return NGX_OK;
842             }
843             break;
844 
845         case ngx_smtp_to:
846             return NGX_OK;
847         }
848 
849         break;
850     }
851 
852     pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
853 
854     if (pcf->pass_error_message == 0) {
855         *(b->last - 2) = '\0';
856         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
857                       "upstream sent invalid response: \"%s\"", p);
858         return NGX_ERROR;
859     }
860 
861     s->out.len = b->last - p - 2;
862     s->out.data = p;
863 
864     ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
865                   "upstream sent invalid response: \"%V\"", &s->out);
866 
867     s->out.len = b->last - b->pos;
868     s->out.data = b->pos;
869 
870     return NGX_ERROR;
871 }
872 
873 
874 static void
ngx_mail_proxy_handler(ngx_event_t * ev)875 ngx_mail_proxy_handler(ngx_event_t *ev)
876 {
877     char                   *action, *recv_action, *send_action;
878     size_t                  size;
879     ssize_t                 n;
880     ngx_buf_t              *b;
881     ngx_uint_t              do_write;
882     ngx_connection_t       *c, *src, *dst;
883     ngx_mail_session_t     *s;
884     ngx_mail_proxy_conf_t  *pcf;
885 
886     c = ev->data;
887     s = c->data;
888 
889     if (ev->timedout || c->close) {
890         c->log->action = "proxying";
891 
892         if (c->close) {
893             ngx_log_error(NGX_LOG_INFO, c->log, 0, "shutdown timeout");
894 
895         } else if (c == s->connection) {
896             ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
897                           "client timed out");
898             c->timedout = 1;
899 
900         } else {
901             ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
902                           "upstream timed out");
903         }
904 
905         ngx_mail_proxy_close_session(s);
906         return;
907     }
908 
909     if (c == s->connection) {
910         if (ev->write) {
911             recv_action = "proxying and reading from upstream";
912             send_action = "proxying and sending to client";
913             src = s->proxy->upstream.connection;
914             dst = c;
915             b = s->proxy->buffer;
916 
917         } else {
918             recv_action = "proxying and reading from client";
919             send_action = "proxying and sending to upstream";
920             src = c;
921             dst = s->proxy->upstream.connection;
922             b = s->buffer;
923         }
924 
925     } else {
926         if (ev->write) {
927             recv_action = "proxying and reading from client";
928             send_action = "proxying and sending to upstream";
929             src = s->connection;
930             dst = c;
931             b = s->buffer;
932 
933         } else {
934             recv_action = "proxying and reading from upstream";
935             send_action = "proxying and sending to client";
936             src = c;
937             dst = s->connection;
938             b = s->proxy->buffer;
939         }
940     }
941 
942     do_write = ev->write ? 1 : 0;
943 
944     ngx_log_debug3(NGX_LOG_DEBUG_MAIL, ev->log, 0,
945                    "mail proxy handler: %ui, #%d > #%d",
946                    do_write, src->fd, dst->fd);
947 
948     for ( ;; ) {
949 
950         if (do_write) {
951 
952             size = b->last - b->pos;
953 
954             if (size && dst->write->ready) {
955                 c->log->action = send_action;
956 
957                 n = dst->send(dst, b->pos, size);
958 
959                 if (n == NGX_ERROR) {
960                     ngx_mail_proxy_close_session(s);
961                     return;
962                 }
963 
964                 if (n > 0) {
965                     b->pos += n;
966 
967                     if (b->pos == b->last) {
968                         b->pos = b->start;
969                         b->last = b->start;
970                     }
971                 }
972             }
973         }
974 
975         size = b->end - b->last;
976 
977         if (size && src->read->ready) {
978             c->log->action = recv_action;
979 
980             n = src->recv(src, b->last, size);
981 
982             if (n == NGX_AGAIN || n == 0) {
983                 break;
984             }
985 
986             if (n > 0) {
987                 do_write = 1;
988                 b->last += n;
989 
990                 continue;
991             }
992 
993             if (n == NGX_ERROR) {
994                 src->read->eof = 1;
995             }
996         }
997 
998         break;
999     }
1000 
1001     c->log->action = "proxying";
1002 
1003     if ((s->connection->read->eof && s->buffer->pos == s->buffer->last)
1004         || (s->proxy->upstream.connection->read->eof
1005             && s->proxy->buffer->pos == s->proxy->buffer->last)
1006         || (s->connection->read->eof
1007             && s->proxy->upstream.connection->read->eof))
1008     {
1009         action = c->log->action;
1010         c->log->action = NULL;
1011         ngx_log_error(NGX_LOG_INFO, c->log, 0, "proxied session done");
1012         c->log->action = action;
1013 
1014         ngx_mail_proxy_close_session(s);
1015         return;
1016     }
1017 
1018     if (ngx_handle_write_event(dst->write, 0) != NGX_OK) {
1019         ngx_mail_proxy_close_session(s);
1020         return;
1021     }
1022 
1023     if (ngx_handle_read_event(dst->read, 0) != NGX_OK) {
1024         ngx_mail_proxy_close_session(s);
1025         return;
1026     }
1027 
1028     if (ngx_handle_write_event(src->write, 0) != NGX_OK) {
1029         ngx_mail_proxy_close_session(s);
1030         return;
1031     }
1032 
1033     if (ngx_handle_read_event(src->read, 0) != NGX_OK) {
1034         ngx_mail_proxy_close_session(s);
1035         return;
1036     }
1037 
1038     if (c == s->connection) {
1039         pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
1040         ngx_add_timer(c->read, pcf->timeout);
1041     }
1042 }
1043 
1044 
1045 static void
ngx_mail_proxy_upstream_error(ngx_mail_session_t * s)1046 ngx_mail_proxy_upstream_error(ngx_mail_session_t *s)
1047 {
1048     if (s->proxy->upstream.connection) {
1049         ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
1050                        "close mail proxy connection: %d",
1051                        s->proxy->upstream.connection->fd);
1052 
1053         ngx_close_connection(s->proxy->upstream.connection);
1054     }
1055 
1056     if (s->out.len == 0) {
1057         ngx_mail_session_internal_server_error(s);
1058         return;
1059     }
1060 
1061     s->quit = 1;
1062     ngx_mail_send(s->connection->write);
1063 }
1064 
1065 
1066 static void
ngx_mail_proxy_internal_server_error(ngx_mail_session_t * s)1067 ngx_mail_proxy_internal_server_error(ngx_mail_session_t *s)
1068 {
1069     if (s->proxy->upstream.connection) {
1070         ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
1071                        "close mail proxy connection: %d",
1072                        s->proxy->upstream.connection->fd);
1073 
1074         ngx_close_connection(s->proxy->upstream.connection);
1075     }
1076 
1077     ngx_mail_session_internal_server_error(s);
1078 }
1079 
1080 
1081 static void
ngx_mail_proxy_close_session(ngx_mail_session_t * s)1082 ngx_mail_proxy_close_session(ngx_mail_session_t *s)
1083 {
1084     if (s->proxy->upstream.connection) {
1085         ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
1086                        "close mail proxy connection: %d",
1087                        s->proxy->upstream.connection->fd);
1088 
1089         ngx_close_connection(s->proxy->upstream.connection);
1090     }
1091 
1092     ngx_mail_close_connection(s->connection);
1093 }
1094 
1095 
1096 static void *
ngx_mail_proxy_create_conf(ngx_conf_t * cf)1097 ngx_mail_proxy_create_conf(ngx_conf_t *cf)
1098 {
1099     ngx_mail_proxy_conf_t  *pcf;
1100 
1101     pcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_proxy_conf_t));
1102     if (pcf == NULL) {
1103         return NULL;
1104     }
1105 
1106     pcf->enable = NGX_CONF_UNSET;
1107     pcf->pass_error_message = NGX_CONF_UNSET;
1108     pcf->xclient = NGX_CONF_UNSET;
1109     pcf->buffer_size = NGX_CONF_UNSET_SIZE;
1110     pcf->timeout = NGX_CONF_UNSET_MSEC;
1111 
1112     return pcf;
1113 }
1114 
1115 
1116 static char *
ngx_mail_proxy_merge_conf(ngx_conf_t * cf,void * parent,void * child)1117 ngx_mail_proxy_merge_conf(ngx_conf_t *cf, void *parent, void *child)
1118 {
1119     ngx_mail_proxy_conf_t *prev = parent;
1120     ngx_mail_proxy_conf_t *conf = child;
1121 
1122     ngx_conf_merge_value(conf->enable, prev->enable, 0);
1123     ngx_conf_merge_value(conf->pass_error_message, prev->pass_error_message, 0);
1124     ngx_conf_merge_value(conf->xclient, prev->xclient, 1);
1125     ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
1126                               (size_t) ngx_pagesize);
1127     ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 24 * 60 * 60000);
1128 
1129     return NGX_CONF_OK;
1130 }
1131