diff options
Diffstat (limited to 'src/imap')
-rw-r--r-- | src/imap/ngx_imap.c | 1 | ||||
-rw-r--r-- | src/imap/ngx_imap.h | 59 | ||||
-rw-r--r-- | src/imap/ngx_imap_handler.c | 48 | ||||
-rw-r--r-- | src/imap/ngx_imap_parse.c | 152 | ||||
-rw-r--r-- | src/imap/ngx_imap_proxy.c | 238 |
5 files changed, 439 insertions, 59 deletions
diff --git a/src/imap/ngx_imap.c b/src/imap/ngx_imap.c index 13855f17d..eb12b6790 100644 --- a/src/imap/ngx_imap.c +++ b/src/imap/ngx_imap.c @@ -50,6 +50,7 @@ static char *ngx_imap_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } ls->backlog = -1; + ls->addr_ntop = 1; ls->handler = ngx_imap_init_connection; ls->pool_size = 16384; /* ls->post_accept_timeout = 0; */ diff --git a/src/imap/ngx_imap.h b/src/imap/ngx_imap.h index 6f5307d3d..d5adc184e 100644 --- a/src/imap/ngx_imap.h +++ b/src/imap/ngx_imap.h @@ -4,40 +4,63 @@ #include <ngx_config.h> #include <ngx_core.h> +#include <ngx_event.h> +#include <ngx_event_connect.h> typedef struct { - ngx_connection_t *connection; + ngx_peer_connection_t upstream; - ngx_buf_t *downstream_buffer; - ngx_buf_t *upstream_buffer; + ngx_buf_t *buffer; } ngx_imap_proxy_ctx_t; typedef struct { - uint32_t signature; /* "IMAP" */ + uint32_t signature; /* "IMAP" */ - ngx_connection_t *connection; - ngx_imap_proxy_ctx_t *proxy; + ngx_connection_t *connection; + ngx_buf_t *buffer; + + ngx_imap_proxy_ctx_t *proxy; + + ngx_uint_t command; + ngx_array_t args; + + /* used to parse IMAP/POP3 command */ + + ngx_uint_t state; + u_char *arg_start; + u_char *arg_end; } ngx_imap_session_t; -#define NGX_POP3_USER 1 -#define NGX_POP3_PASS 2 -#define NGX_POP3_APOP 3 -#define NGX_POP3_STAT 4 -#define NGX_POP3_LIST 5 -#define NGX_POP3_RETR 6 -#define NGX_POP3_DELE 7 -#define NGX_POP3_NOOP 8 -#define NGX_POP3_RSET 9 -#define NGX_POP3_TOP 10 -#define NGX_POP3_UIDL 11 -#define NGX_POP3_QUIT 12 +#define NGX_POP3_USER 1 +#define NGX_POP3_PASS 2 +#define NGX_POP3_APOP 3 +#define NGX_POP3_STAT 4 +#define NGX_POP3_LIST 5 +#define NGX_POP3_RETR 6 +#define NGX_POP3_DELE 7 +#define NGX_POP3_NOOP 8 +#define NGX_POP3_RSET 9 +#define NGX_POP3_TOP 10 +#define NGX_POP3_UIDL 11 +#define NGX_POP3_QUIT 12 + + +#define NGX_IMAP_PARSE_INVALID_COMMAND 10 + + +#define NGX_IMAP_PROXY_INVALID 10 +#define NGX_IMAP_PROXY_ERROR 11 void ngx_imap_init_connection(ngx_connection_t *c); void ngx_imap_close_connection(ngx_connection_t *c); +void ngx_imap_proxy_init(ngx_imap_session_t *s); + +ngx_int_t ngx_pop3_parse_command(ngx_imap_session_t *s); + #endif /* _NGX_IMAP_H_INCLUDED_ */ diff --git a/src/imap/ngx_imap_handler.c b/src/imap/ngx_imap_handler.c index 61c59c98d..55808fac4 100644 --- a/src/imap/ngx_imap_handler.c +++ b/src/imap/ngx_imap_handler.c @@ -6,7 +6,7 @@ #include <nginx.h> -static void ngx_imap_auth_state(ngx_event_t *rev); +static void ngx_imap_init_session(ngx_event_t *rev); static char pop3_greeting[] = "+OK " NGINX_VER " ready" CRLF; @@ -15,36 +15,66 @@ static char imap_greeting[] = "* OK " NGINX_VER " ready" CRLF; void ngx_imap_init_connection(ngx_connection_t *c) { - ngx_int_t n; + char *greeting; + ssize_t size; + ngx_int_t n; ngx_log_debug0(NGX_LOG_DEBUG_IMAP, c->log, 0, "imap init connection"); c->log_error = NGX_ERROR_INFO; - n = ngx_send(c, pop3_greeting, sizeof(pop3_greeting) - 1); + greeting = pop3_greeting; + size = sizeof(pop3_greeting) - 1; - if (n == NGX_ERROR) { + n = ngx_send(c, greeting, size); + + if (n < size) { + /* + * we treat the incomplete sending as NGX_ERROR + * because it is very strange here + */ ngx_imap_close_connection(c); return; } - c->read->event_handler = ngx_imap_auth_state; + c->read->event_handler = ngx_imap_init_session; + + ngx_add_timer(c->read, /* STUB */ 60000); if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { ngx_imap_close_connection(c); - return; } } -static void ngx_imap_auth_state(ngx_event_t *rev) +static void ngx_imap_init_session(ngx_event_t *rev) { - ngx_connection_t *c; + ngx_connection_t *c; + ngx_imap_session_t *s; c = rev->data; - ngx_imap_close_connection(c); + if (!(s = ngx_pcalloc(c->pool, sizeof(ngx_imap_session_t)))) { + ngx_imap_close_connection(c); + return; + } + + c->data = s; + s->connection = c; + + if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) == NGX_ERROR) { + ngx_imap_close_connection(s->connection); + return; + } + + s->buffer = ngx_create_temp_buf(s->connection->pool, /* STUB */ 4096); + if (s->buffer == NULL) { + ngx_imap_close_connection(s->connection); + return; + } + + ngx_imap_proxy_init(s); } diff --git a/src/imap/ngx_imap_parse.c b/src/imap/ngx_imap_parse.c index 08ff86355..e29791d73 100644 --- a/src/imap/ngx_imap_parse.c +++ b/src/imap/ngx_imap_parse.c @@ -5,49 +5,75 @@ #include <ngx_imap.h> -ngx_int_t ngx_pop3_parse_command(ngx_imap_request_t *r) +ngx_int_t ngx_pop3_parse_command(ngx_imap_session_t *s) { - u_char ch, *p, *c; + u_char ch, *p, *c; + ngx_str_t *arg; enum { sw_start = 0, + sw_spaces_before_argument, + sw_argument, + sw_almost_done, sw_done } state; - while (p < r->buf->last && state < sw_done) { + state = s->state; + p = s->buffer->pos; + + while (p < s->buffer->last && state < sw_done) { ch = *p++; switch (state) { - /* POP3 commands */ + /* POP3 command */ + case sw_start: - if (ch == ' ') { - c = r->buf->start; + if (ch == ' ' || ch == CR || ch == LF) { + c = s->buffer->start; - if (p - 1 - m == 4) { + if (p - 1 - c == 4) { - if (*c == 'U' && *(c + 1) == 'S' - && *(c + 2) == 'E' && *(c + 3) == 'R') + if (c[0] == 'U' && c[1] == 'S' + && c[2] == 'E' && c[3] == 'R') { - r->command = NGX_POP3_USER; + s->command = NGX_POP3_USER; - } else if (*c == 'P' && *(c + 1) == 'A' - && *(c + 2) == 'A' && *(c + 3) == 'S') + } else if (c[0] == 'P' && c[1] == 'A' + && c[2] == 'A' && c[3] == 'S') { - r->method = NGX_POP3_PASS; + s->command = NGX_POP3_PASS; - } else if (*c == 'Q' && *(c + 1) == 'U' - && *(c + 2) == 'I' && *(c + 3) == 'T') + } else if (c[0] == 'Q' && c[1] == 'U' + && c[2] == 'I' && c[3] == 'T') { - r->method = NGX_POP3_QUIT; + s->command = NGX_POP3_QUIT; - } else if (*c == 'N' && *(c + 1) == 'O' - && *(c + 2) == 'O' && *(c + 3) == 'P') +#if 0 + } else if (c[0] == 'N' && c[1] == 'O' + && c[2] == 'O' && c[3] == 'P') { - r->method = NGX_POP3_NOOP; + s->command = NGX_POP3_NOOP; +#endif + + } else { + return NGX_IMAP_PARSE_INVALID_COMMAND; } + + } else { + return NGX_IMAP_PARSE_INVALID_COMMAND; } - state = sw_spaces_before_arg; + switch (ch) { + case ' ': + state = sw_spaces_before_argument; + break; + case CR: + state = sw_almost_done; + break; + case LF: + state = sw_done; + break; + } break; } @@ -56,7 +82,72 @@ ngx_int_t ngx_pop3_parse_command(ngx_imap_request_t *r) } break; - } + + /* the spaces before the argument */ + case sw_spaces_before_argument: + switch (ch) { + case ' ': + break; + case CR: + state = sw_almost_done; + s->arg_end = p - 1; + break; + case LF: + state = sw_done; + s->arg_end = p - 1; + break; + default: + if (s->args.nelts > 2) { + return NGX_IMAP_PARSE_INVALID_COMMAND; + } + + state = sw_argument; + s->arg_start = p - 1; + break; + } + break; + + /* the argument */ + case sw_argument: + switch (ch) { + case ' ': + case CR: + case LF: + if (!(arg = ngx_array_push(&s->args))) { + return NGX_ERROR; + } + arg->len = p - s->arg_start; + arg->data = s->arg_start; + s->arg_start = NULL; + + switch (ch) { + case ' ': + state = sw_spaces_before_argument; + break; + case CR: + state = sw_almost_done; + break; + case LF: + state = sw_done; + break; + } + break; + + default: + break; + } + break; + + /* end of request line */ + case sw_almost_done: + switch (ch) { + case LF: + state = sw_done; + break; + default: + return NGX_IMAP_PARSE_INVALID_COMMAND; + } + break; /* suppress warning */ case sw_done: @@ -64,5 +155,22 @@ ngx_int_t ngx_pop3_parse_command(ngx_imap_request_t *r) } } - return NGX_OK; + s->buffer->pos = p; + + if (state == sw_done) { + if (s->arg_start) { + if (!(arg = ngx_array_push(&s->args))) { + return NGX_ERROR; + } + arg->len = s->arg_end - s->arg_start; + arg->data = s->arg_start; + s->arg_start = NULL; + } + + return NGX_OK; + + } else { + s->state = state; + return NGX_AGAIN; + } } diff --git a/src/imap/ngx_imap_proxy.c b/src/imap/ngx_imap_proxy.c index f856159ff..6a915f701 100644 --- a/src/imap/ngx_imap_proxy.c +++ b/src/imap/ngx_imap_proxy.c @@ -2,14 +2,200 @@ #include <ngx_config.h> #include <ngx_core.h> #include <ngx_event.h> +#include <ngx_event_connect.h> #include <ngx_imap.h> +static void ngx_imap_proxy_block_read(ngx_event_t *rev); +static void ngx_imap_proxy_greeting_handler(ngx_event_t *rev); +static void ngx_imap_proxy_init_handler(ngx_event_t *wev); +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 void ngx_imap_proxy_handler(ngx_event_t *ev); static void ngx_imap_proxy_close_session(ngx_imap_session_t *s); -void ngx_imap_proxy_handler(ngx_event_t *ev) +void ngx_imap_proxy_init(ngx_imap_session_t *s) { + ngx_int_t rc; + ngx_peers_t *peers; + ngx_imap_proxy_ctx_t *p; + + if (!(p = ngx_pcalloc(s->connection->pool, sizeof(ngx_imap_proxy_ctx_t)))) { + ngx_imap_close_connection(s->connection); + return; + } + + s->proxy = p; + + /**/ + + if (!(peers = ngx_pcalloc(s->connection->pool, sizeof(ngx_peers_t)))) { + ngx_imap_close_connection(s->connection); + return; + } + + p->upstream.peers = peers; + p->upstream.log = s->connection->log; + p->upstream.log_error = NGX_ERROR_ERR; + + peers->number = 1; + peers->max_fails = 1; + peers->peers[0].addr = inet_addr("81.19.69.70"); + peers->peers[0].addr_port_text.len = sizeof("81.19.69.70:110") - 1; + peers->peers[0].addr_port_text.data = "81.19.69.70:110"; + peers->peers[0].port = htons(110); + + rc = ngx_event_connect_peer(&p->upstream); + + if (rc == NGX_ERROR) { + ngx_imap_proxy_close_session(s); + return; + } + + p->upstream.connection->data = s; + p->upstream.connection->pool = s->connection->pool; + + s->connection->read->event_handler = ngx_imap_proxy_block_read; + p->upstream.connection->read->event_handler = + ngx_imap_proxy_greeting_handler; + p->upstream.connection->write->event_handler = ngx_imap_proxy_dummy_handler; +} + + +static void ngx_imap_proxy_block_read(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_imap_session_t *s; + + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy block read"); + + if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + c = rev->data; + s = c->data; + + ngx_imap_proxy_close_session(s); + } +} + + +static void ngx_imap_proxy_greeting_handler(ngx_event_t *rev) +{ + ngx_int_t rc; + ngx_buf_t *b; + ngx_connection_t *c; + ngx_imap_session_t *s; + + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, + "imap proxy greeting handler"); + + c = rev->data; + s = c->data; + + if (s->proxy->buffer == NULL) { + s->proxy->buffer = ngx_create_temp_buf(c->pool, /* STUB */ 4096); + if (s->proxy->buffer == NULL) { + ngx_imap_proxy_close_session(s); + return; + } + } + + rc = ngx_imap_proxy_read_response(s); + + if (rc == NGX_AGAIN) { + return; + } + + if (rc == NGX_OK) { + s->connection->read->event_handler = ngx_imap_proxy_handler; + s->connection->write->event_handler = ngx_imap_proxy_handler; + rev->event_handler = ngx_imap_proxy_handler; + c->write->event_handler = ngx_imap_proxy_handler; + + b = s->proxy->buffer; + b->pos = b->start; + b->last = b->start; + + if (ngx_handle_read_event(s->connection->read, 0) == NGX_ERROR) { + ngx_imap_proxy_close_session(s); + return; + } + + if (s->connection->read->ready) { + ngx_imap_proxy_handler(s->connection->read); + } + + return; + } + + /* TODO: ngx_imap_proxy_finalize_session(s, NGX_IMAP_INTERNAL_ERROR) */ + ngx_imap_proxy_close_session(s); +} + + +static void ngx_imap_proxy_dummy_handler(ngx_event_t *ev) +{ + ngx_log_debug0(NGX_LOG_DEBUG_IMAP, ev->log, 0, "imap proxy dummy handler"); +} + + +static ngx_int_t ngx_imap_proxy_read_response(ngx_imap_session_t *s) +{ + u_char *p; + ssize_t n; + ngx_buf_t *b; + + b = s->proxy->buffer; + + n = ngx_recv(s->proxy->upstream.connection, b->last, b->end - b->last); + + if (n == NGX_ERROR || n == 0) { + return NGX_ERROR; + } + + if (n == NGX_AGAIN) { + return NGX_AGAIN; + } + + b->last += n; + + if (b->last - b->pos < 5) { + return NGX_AGAIN; + } + + if (*(b->last - 2) != CR || *(b->last - 1) != LF) { + if (b->last == b->end) { + *(b->last - 1) = '\0'; + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "upstream sent too long response line: \"%s\"", + b->pos); + return NGX_IMAP_PROXY_INVALID; + } + + return NGX_AGAIN; + } + + p = b->pos; + + 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; + } + + *(b->last - 2) = '\0'; + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "upstream sent invalid greeting line: \"%s\"", p); + + return NGX_IMAP_PROXY_INVALID; +} + + +static void ngx_imap_proxy_handler(ngx_event_t *ev) +{ + size_t size; ssize_t n; ngx_buf_t *b; ngx_uint_t data, do_write; @@ -21,23 +207,30 @@ void ngx_imap_proxy_handler(ngx_event_t *ev) if (c == s->connection) { src = c; - dst = s->proxy->connection; - b = s->proxy->downstream_buffer; + dst = s->proxy->upstream.connection; + b = s->buffer; } else { - src = s->proxy->connection; - dst = c; - b = s->proxy->upstream_buffer; + src = c; + dst = s->connection; + b = s->proxy->buffer; } do_write = ev->write ? 1 : 0; + ngx_log_debug3(NGX_LOG_DEBUG_IMAP, ev->log, 0, + "imap proxy handler: %d, #%d > #%d", + do_write, src->fd, dst->fd); + do { data = 0; if (do_write == 1) { - if (dst->write->ready && b->pos < b->last) { - n = ngx_send(dst, b->pos, b->last - b->pos); + + size = b->last - b->pos; + + if (dst->write->ready && size) { + n = ngx_send(dst, b->pos, size); if (n == NGX_ERROR) { ngx_imap_proxy_close_session(s); @@ -53,11 +246,23 @@ void ngx_imap_proxy_handler(ngx_event_t *ev) b->last = b->start; } } + + if (n == NGX_AGAIN || n < (ssize_t) size) { + dst->write->available = 0; + if (ngx_handle_write_event(dst->write, NGX_LOWAT_EVENT) + == NGX_ERROR) + { + ngx_imap_proxy_close_session(s); + return; + } + } } } - if (src->read->ready && b->last < b->end) { - n = ngx_recv(src, b->last, b->end - b->last); + size = b->end - b->last; + + if (src->read->ready && size) { + n = ngx_recv(src, b->last, size); if (n == NGX_ERROR || n == 0) { ngx_imap_proxy_close_session(s); @@ -69,6 +274,13 @@ void ngx_imap_proxy_handler(ngx_event_t *ev) do_write = 1; b->last += n; } + + if (n == NGX_AGAIN || n < (ssize_t) size) { + if (ngx_handle_read_event(src->read, 0) == NGX_ERROR) { + ngx_imap_proxy_close_session(s); + return; + } + } } } while (data); @@ -77,4 +289,10 @@ void ngx_imap_proxy_handler(ngx_event_t *ev) static void ngx_imap_proxy_close_session(ngx_imap_session_t *s) { + if (ngx_close_socket(s->proxy->upstream.connection->fd) == -1) { + ngx_log_error(NGX_LOG_ALERT, s->connection->log, ngx_socket_errno, + ngx_close_socket_n " failed"); + } + + ngx_imap_close_connection(s->connection); } |