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