comparison src/mail/ngx_mail_proxy_module.c @ 1136:68f30ab68bb7

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