diff options
Diffstat (limited to 'src/imap/ngx_imap_proxy_module.c')
-rw-r--r-- | src/imap/ngx_imap_proxy_module.c | 296 |
1 files changed, 240 insertions, 56 deletions
diff --git a/src/imap/ngx_imap_proxy_module.c b/src/imap/ngx_imap_proxy_module.c index b3bedae58..5177e289f 100644 --- a/src/imap/ngx_imap_proxy_module.c +++ b/src/imap/ngx_imap_proxy_module.c @@ -17,16 +17,23 @@ typedef struct { static void ngx_imap_proxy_block_read(ngx_event_t *rev); -static void ngx_imap_proxy_auth_handler(ngx_event_t *rev); +static void ngx_imap_proxy_imap_handler(ngx_event_t *rev); +static void ngx_imap_proxy_pop3_handler(ngx_event_t *rev); static void ngx_imap_proxy_dummy_handler(ngx_event_t *ev); -static ngx_int_t ngx_imap_proxy_read_response(ngx_imap_session_t *s); +static ngx_int_t ngx_imap_proxy_read_response(ngx_imap_session_t *s, + ngx_uint_t what); static void ngx_imap_proxy_handler(ngx_event_t *ev); +static void ngx_imap_proxy_internal_server_error(ngx_imap_session_t *s); static void ngx_imap_proxy_close_session(ngx_imap_session_t *s); static void *ngx_imap_proxy_create_conf(ngx_conf_t *cf); static char *ngx_imap_proxy_merge_conf(ngx_conf_t *cf, void *parent, void *child); +#define NGX_IMAP_WAIT_OK 0 +#define NGX_IMAP_WAIT_NEXT 1 + + static ngx_command_t ngx_imap_proxy_commands[] = { { ngx_string("proxy"), NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_FLAG, @@ -61,12 +68,13 @@ ngx_module_t ngx_imap_proxy_module = { void ngx_imap_proxy_init(ngx_imap_session_t *s, ngx_peers_t *peers) { - ngx_int_t rc; - ngx_imap_proxy_ctx_t *p; + ngx_int_t rc; + ngx_imap_proxy_ctx_t *p; + ngx_imap_core_srv_conf_t *cscf; p = ngx_pcalloc(s->connection->pool, sizeof(ngx_imap_proxy_ctx_t)); if (p == NULL) { - ngx_imap_close_connection(s->connection); + ngx_imap_session_internal_server_error(s); return; } @@ -79,16 +87,27 @@ ngx_imap_proxy_init(ngx_imap_session_t *s, ngx_peers_t *peers) rc = ngx_event_connect_peer(&p->upstream); if (rc == NGX_ERROR) { - ngx_imap_proxy_close_session(s); + ngx_imap_session_internal_server_error(s); return; } + cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module); + ngx_add_timer(p->upstream.connection->read, cscf->timeout); + p->upstream.connection->data = s; p->upstream.connection->pool = s->connection->pool; s->connection->read->handler = ngx_imap_proxy_block_read; - p->upstream.connection->read->handler = ngx_imap_proxy_auth_handler; p->upstream.connection->write->handler = ngx_imap_proxy_dummy_handler; + + if (s->protocol == NGX_IMAP_POP3_PROTOCOL) { + p->upstream.connection->read->handler = ngx_imap_proxy_pop3_handler; + s->imap_state = ngx_pop3_start; + + } else { + p->upstream.connection->read->handler = ngx_imap_proxy_imap_handler; + s->imap_state = ngx_imap_start; + } } @@ -110,106 +129,242 @@ ngx_imap_proxy_block_read(ngx_event_t *rev) static void -ngx_imap_proxy_auth_handler(ngx_event_t *rev) +ngx_imap_proxy_imap_handler(ngx_event_t *rev) { - u_char *p; - ngx_int_t rc; - ngx_str_t line; - ngx_connection_t *c; - ngx_imap_session_t *s; + u_char *p; + ngx_int_t rc; + ngx_str_t line; + ngx_connection_t *c; + ngx_imap_session_t *s; + ngx_imap_core_srv_conf_t *cscf; - ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy auth handler"); + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, + "imap proxy imap auth handler"); c = rev->data; s = c->data; if (rev->timedout) { - ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); - ngx_imap_proxy_close_session(s); + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, + "upstream timed out"); + ngx_imap_proxy_internal_server_error(s); return; } if (s->proxy->buffer == NULL) { - s->proxy->buffer = ngx_create_temp_buf(c->pool, /* STUB */ 4096); + cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module); + + s->proxy->buffer = ngx_create_temp_buf(c->pool, + cscf->proxy_buffer_size); if (s->proxy->buffer == NULL) { - ngx_imap_proxy_close_session(s); + ngx_imap_proxy_internal_server_error(s); return; } } - rc = ngx_imap_proxy_read_response(s); + rc = ngx_imap_proxy_read_response(s, s->imap_state == ngx_imap_start ? + NGX_IMAP_WAIT_OK : NGX_IMAP_WAIT_NEXT); if (rc == NGX_AGAIN) { return; } if (rc == NGX_ERROR) { - /* TODO: ngx_imap_proxy_finalize_session(s, NGX_IMAP_INTERNAL_ERROR) */ - ngx_imap_proxy_close_session(s); + ngx_imap_proxy_internal_server_error(s); return; } - if (s->imap_state == ngx_pop3_start) { + switch (s->imap_state) { + + case ngx_imap_start: + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, + "imap proxy send login"); + line.len = s->tag.len + sizeof("LOGIN ") - 1 + + 1 + NGX_SIZE_T_LEN + 1 + 2; + line.data = ngx_palloc(c->pool, line.len); + if (line.data == NULL) { + ngx_imap_proxy_internal_server_error(s); + return; + } + + line.len = ngx_sprintf(line.data, "%VLOGIN {%uz}" CRLF, + &s->tag, s->login.len) + - line.data; + + s->imap_state = ngx_imap_login; + break; + + case ngx_imap_login: ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy send user"); - line.len = sizeof("USER ") + s->login.len - 1 + 2; + line.len = s->login.len + 1 + NGX_SIZE_T_LEN + 1 + 2; line.data = ngx_palloc(c->pool, line.len); if (line.data == NULL) { - ngx_imap_proxy_close_session(s); + ngx_imap_proxy_internal_server_error(s); return; } - p = ngx_cpymem(line.data, "USER ", sizeof("USER ") - 1); - p = ngx_cpymem(p, s->login.data, s->login.len); - *p++ = CR; *p = LF; + line.len = ngx_sprintf(line.data, "%V{%uz}" CRLF, + &s->login, s->passwd.len) + - line.data; + + s->imap_state = ngx_imap_user; + break; + + case ngx_imap_user: + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, + "imap proxy send passwd"); - if (ngx_send(c, line.data, line.len) < (ssize_t) line.len) { - /* - * we treat the incomplete sending as NGX_ERROR - * because it is very strange here - */ - ngx_imap_close_connection(c); + line.len = s->passwd.len + 2; + line.data = ngx_palloc(c->pool, line.len); + if (line.data == NULL) { + ngx_imap_proxy_internal_server_error(s); return; } - s->imap_state = ngx_pop3_user; + p = ngx_cpymem(line.data, s->passwd.data, s->passwd.len); + *p++ = CR; *p = LF; + + s->imap_state = ngx_imap_passwd; + break; - s->proxy->buffer->pos = s->proxy->buffer->start; - s->proxy->buffer->last = s->proxy->buffer->start; + default: +#if (NGX_SUPPRESS_WARN) + line.len = 0; + line.data = NULL; +#endif + break; + } + if (ngx_send(c, line.data, line.len) < (ssize_t) line.len) { + /* + * we treat the incomplete sending as NGX_ERROR + * because it is very strange here + */ + ngx_imap_proxy_internal_server_error(s); return; } - ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy send pass"); + s->proxy->buffer->pos = s->proxy->buffer->start; + s->proxy->buffer->last = s->proxy->buffer->start; - line.len = sizeof("PASS ") + s->passwd.len - 1 + 2; - line.data = ngx_palloc(c->pool, line.len); - if (line.data == NULL) { - ngx_imap_proxy_close_session(s); + if (s->imap_state == ngx_imap_passwd) { + s->connection->read->handler = ngx_imap_proxy_handler; + s->connection->write->handler = ngx_imap_proxy_handler; + rev->handler = ngx_imap_proxy_handler; + c->write->handler = ngx_imap_proxy_handler; + } +} + + +static void +ngx_imap_proxy_pop3_handler(ngx_event_t *rev) +{ + u_char *p; + ngx_int_t rc; + ngx_str_t line; + ngx_connection_t *c; + ngx_imap_session_t *s; + ngx_imap_core_srv_conf_t *cscf; + + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, + "imap proxy pop3 auth handler"); + + c = rev->data; + s = c->data; + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, + "upstream timed out"); + ngx_imap_proxy_internal_server_error(s); + return; + } + + if (s->proxy->buffer == NULL) { + cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module); + + s->proxy->buffer = ngx_create_temp_buf(c->pool, + cscf->proxy_buffer_size); + if (s->proxy->buffer == NULL) { + ngx_imap_proxy_internal_server_error(s); + return; + } + } + + rc = ngx_imap_proxy_read_response(s, NGX_IMAP_WAIT_OK); + + if (rc == NGX_AGAIN) { return; } - p = ngx_cpymem(line.data, "PASS ", sizeof("PASS ") - 1); - p = ngx_cpymem(p, s->passwd.data, s->passwd.len); - *p++ = CR; *p = LF; + if (rc == NGX_ERROR) { + ngx_imap_proxy_internal_server_error(s); + return; + } + + switch (s->imap_state) { + + case ngx_pop3_start: + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy send user"); + + line.len = sizeof("USER ") - 1 + s->login.len + 2; + line.data = ngx_palloc(c->pool, line.len); + if (line.data == NULL) { + ngx_imap_proxy_internal_server_error(s); + return; + } + + p = ngx_cpymem(line.data, "USER ", sizeof("USER ") - 1); + p = ngx_cpymem(p, s->login.data, s->login.len); + *p++ = CR; *p = LF; + + s->imap_state = ngx_pop3_user; + break; + + case ngx_pop3_user: + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy send pass"); + + line.len = sizeof("PASS ") - 1 + s->passwd.len + 2; + line.data = ngx_palloc(c->pool, line.len); + if (line.data == NULL) { + ngx_imap_proxy_internal_server_error(s); + return; + } + + p = ngx_cpymem(line.data, "PASS ", sizeof("PASS ") - 1); + p = ngx_cpymem(p, s->passwd.data, s->passwd.len); + *p++ = CR; *p = LF; + + s->imap_state = ngx_pop3_passwd; + break; + + default: +#if (NGX_SUPPRESS_WARN) + line.len = 0; + line.data = NULL; +#endif + break; + } if (ngx_send(c, line.data, line.len) < (ssize_t) line.len) { /* * we treat the incomplete sending as NGX_ERROR * because it is very strange here */ - ngx_imap_close_connection(c); + ngx_imap_proxy_internal_server_error(s); return; } s->proxy->buffer->pos = s->proxy->buffer->start; s->proxy->buffer->last = s->proxy->buffer->start; - s->connection->read->handler = ngx_imap_proxy_handler; - s->connection->write->handler = ngx_imap_proxy_handler; - rev->handler = ngx_imap_proxy_handler; - c->write->handler = ngx_imap_proxy_handler; + if (s->imap_state == ngx_pop3_passwd) { + s->connection->read->handler = ngx_imap_proxy_handler; + s->connection->write->handler = ngx_imap_proxy_handler; + rev->handler = ngx_imap_proxy_handler; + c->write->handler = ngx_imap_proxy_handler; + } } @@ -221,7 +376,7 @@ ngx_imap_proxy_dummy_handler(ngx_event_t *ev) static ngx_int_t -ngx_imap_proxy_read_response(ngx_imap_session_t *s) +ngx_imap_proxy_read_response(ngx_imap_session_t *s, ngx_uint_t what) { u_char *p; ssize_t n; @@ -259,17 +414,31 @@ ngx_imap_proxy_read_response(ngx_imap_session_t *s) p = b->pos; - if (p[0] == '+' && p[1] == 'O' && p[2] == 'K') { - return NGX_OK; - } + if (s->protocol == NGX_IMAP_POP3_PROTOCOL) { + if (p[0] == '+' && p[1] == 'O' && p[2] == 'K') { + return NGX_OK; + } - if (p[0] == '-' && p[1] == 'E' && p[2] == 'R' && p[3] == 'R') { - return NGX_IMAP_PROXY_ERROR; + if (p[0] == '-' && p[1] == 'E' && p[2] == 'R' && p[3] == 'R') { + return NGX_IMAP_PROXY_ERROR; + } + + } else { + if (what == NGX_IMAP_WAIT_OK) { + if (p[0] == '*' && p[1] == ' ' && p[2] == 'O' && p[3] == 'K') { + return NGX_OK; + } + + } else { + if (p[0] == '+' && p[1] == ' ' && p[2] == 'O' && p[3] == 'K') { + return NGX_OK; + } + } } *(b->last - 2) = '\0'; ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, - "upstream sent invalid greeting line: \"%s\"", p); + "upstream sent invalid response: \"%s\"", p); return NGX_IMAP_PROXY_INVALID; } @@ -397,6 +566,21 @@ ngx_imap_proxy_handler(ngx_event_t *ev) static void +ngx_imap_proxy_internal_server_error(ngx_imap_session_t *s) +{ + if (s->proxy->upstream.connection) { + ngx_log_debug1(NGX_LOG_DEBUG_IMAP, s->connection->log, 0, + "close imap proxy connection: %d", + s->proxy->upstream.connection->fd); + + ngx_close_connection(s->proxy->upstream.connection); + } + + ngx_imap_session_internal_server_error(s); +} + + +static void ngx_imap_proxy_close_session(ngx_imap_session_t *s) { if (s->proxy->upstream.connection) { |