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