Annotation of embedaddon/nginx/src/mail/ngx_mail_proxy_module.c, revision 1.1
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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>