Mercurial > hg > nginx-vendor-0-8
view src/imap/ngx_imap_proxy_module.c @ 196:8759b346e431 NGINX_0_3_45
nginx 0.3.45
*) Feature: the "ssl_verify_client", "ssl_verify_depth", and
"ssl_client_certificate" directives.
*) Change: the $request_method variable now returns the main request
method.
*) Change: the ° symbol codes were changed in koi-win conversion
table.
*) Feature: the euro É N symbols were added to koi-win conversion table.
*) Bugfix: if nginx distributed the requests among several backends and
some backend failed, then requests intended for this backend was
directed to one live backend only instead of being distributed among
the rest.
author | Igor Sysoev <http://sysoev.ru> |
---|---|
date | Sat, 06 May 2006 00:00:00 +0400 |
parents | 8e6d4d96ec4c |
children | 56688ed172c8 |
line wrap: on
line source
/* * Copyright (C) Igor Sysoev */ #include <ngx_config.h> #include <ngx_core.h> #include <ngx_event.h> #include <ngx_event_connect.h> #include <ngx_imap.h> typedef struct { ngx_flag_t enable; size_t buffer_size; ngx_msec_t timeout; } ngx_imap_proxy_conf_t; static void ngx_imap_proxy_block_read(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, 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, ngx_conf_set_flag_slot, NGX_IMAP_SRV_CONF_OFFSET, offsetof(ngx_imap_proxy_conf_t, enable), NULL }, { ngx_string("proxy_buffer"), NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, NGX_IMAP_SRV_CONF_OFFSET, offsetof(ngx_imap_proxy_conf_t, buffer_size), NULL }, { ngx_string("proxy_timeout"), NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_msec_slot, NGX_IMAP_SRV_CONF_OFFSET, offsetof(ngx_imap_proxy_conf_t, timeout), NULL }, ngx_null_command }; static ngx_imap_module_t ngx_imap_proxy_module_ctx = { NULL, /* create main configuration */ NULL, /* init main configuration */ ngx_imap_proxy_create_conf, /* create server configuration */ ngx_imap_proxy_merge_conf /* merge server configuration */ }; ngx_module_t ngx_imap_proxy_module = { NGX_MODULE_V1, &ngx_imap_proxy_module_ctx, /* module context */ ngx_imap_proxy_commands, /* module directives */ NGX_IMAP_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ NULL, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING }; void ngx_imap_proxy_init(ngx_imap_session_t *s, ngx_peers_t *peers) { int keepalive; ngx_int_t rc; ngx_imap_proxy_ctx_t *p; ngx_imap_core_srv_conf_t *cscf; s->connection->log->action = "connecting to upstream"; cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module); if (cscf->so_keepalive) { keepalive = 1; if (setsockopt(s->connection->fd, SOL_SOCKET, SO_KEEPALIVE, (const void *) &keepalive, sizeof(int)) == -1) { ngx_log_error(NGX_LOG_ALERT, s->connection->log, ngx_socket_errno, "setsockopt(SO_KEEPALIVE) failed"); } } p = ngx_pcalloc(s->connection->pool, sizeof(ngx_imap_proxy_ctx_t)); if (p == NULL) { ngx_imap_session_internal_server_error(s); return; } s->proxy = p; p->upstream.peers = peers; p->upstream.log = s->connection->log; p->upstream.log_error = NGX_ERROR_ERR; rc = ngx_event_connect_peer(&p->upstream); if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) { ngx_imap_proxy_internal_server_error(s); return; } 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->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; } } 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_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; ngx_imap_proxy_conf_t *pcf; 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, "upstream timed out"); c->timedout = 1; ngx_imap_proxy_internal_server_error(s); return; } if (s->proxy->buffer == NULL) { pcf = ngx_imap_get_module_srv_conf(s, ngx_imap_proxy_module); s->proxy->buffer = ngx_create_temp_buf(c->pool, pcf->buffer_size); if (s->proxy->buffer == NULL) { ngx_imap_proxy_internal_server_error(s); return; } } 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) { ngx_imap_proxy_internal_server_error(s); return; } switch (s->imap_state) { case ngx_imap_start: ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy send login"); s->connection->log->action = "sending LOGIN command to upstream"; 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"); s->connection->log->action = "sending user name to upstream"; line.len = s->login.len + 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, "%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"); s->connection->log->action = "sending password to upstream"; 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; } p = ngx_cpymem(line.data, s->passwd.data, s->passwd.len); *p++ = CR; *p = LF; s->imap_state = ngx_imap_passwd; break; default: #if (NGX_SUPPRESS_WARN) line.len = 0; line.data = NULL; #endif break; } if (c->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; } s->proxy->buffer->pos = s->proxy->buffer->start; s->proxy->buffer->last = s->proxy->buffer->start; 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; pcf = ngx_imap_get_module_srv_conf(s, ngx_imap_proxy_module); ngx_add_timer(s->connection->read, pcf->timeout); ngx_del_timer(c->read); c->log->action = NULL; ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in"); } } 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_proxy_conf_t *pcf; 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"); c->timedout = 1; ngx_imap_proxy_internal_server_error(s); return; } if (s->proxy->buffer == NULL) { pcf = ngx_imap_get_module_srv_conf(s, ngx_imap_proxy_module); s->proxy->buffer = ngx_create_temp_buf(c->pool, pcf->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; } 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"); s->connection->log->action = "sending user name to upstream"; 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"); s->connection->log->action = "sending password to upstream"; 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 (c->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; } s->proxy->buffer->pos = s->proxy->buffer->start; s->proxy->buffer->last = s->proxy->buffer->start; 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; pcf = ngx_imap_get_module_srv_conf(s, ngx_imap_proxy_module); ngx_add_timer(s->connection->read, pcf->timeout); ngx_del_timer(c->read); c->log->action = NULL; ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in"); } } static void ngx_imap_proxy_dummy_handler(ngx_event_t *wev) { ngx_connection_t *c; ngx_imap_session_t *s; ngx_log_debug0(NGX_LOG_DEBUG_IMAP, wev->log, 0, "imap proxy dummy handler"); if (ngx_handle_write_event(wev, 0) == NGX_ERROR) { c = wev->data; s = c->data; ngx_imap_proxy_close_session(s); } } static ngx_int_t ngx_imap_proxy_read_response(ngx_imap_session_t *s, ngx_uint_t what) { u_char *p; ssize_t n; ngx_buf_t *b; s->connection->log->action = "reading response from upstream"; b = s->proxy->buffer; n = s->proxy->upstream.connection->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_ERROR; } return NGX_AGAIN; } p = b->pos; if (s->protocol == NGX_IMAP_POP3_PROTOCOL) { if (p[0] == '+' && p[1] == 'O' && p[2] == 'K') { return NGX_OK; } } 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] == '+') { return NGX_OK; } } } *(b->last - 2) = '\0'; ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "upstream sent invalid response: \"%s\"", p); return NGX_ERROR; } static void ngx_imap_proxy_handler(ngx_event_t *ev) { char *action, *recv_action, *send_action; size_t size; ssize_t n; ngx_buf_t *b; ngx_uint_t do_write; ngx_connection_t *c, *src, *dst; ngx_imap_session_t *s; ngx_imap_proxy_conf_t *pcf; c = ev->data; s = c->data; if (ev->timedout) { c->log->action = "proxying"; if (c == s->connection) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); c->timedout = 1; } else { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "upstream timed out"); } ngx_imap_proxy_close_session(s); return; } if (c == s->connection) { if (ev->write) { recv_action = "proxying and reading from upstream"; send_action = "proxying and sending to client"; src = s->proxy->upstream.connection; dst = c; b = s->proxy->buffer; } else { recv_action = "proxying and reading from client"; send_action = "proxying and sending to upstream"; src = c; dst = s->proxy->upstream.connection; b = s->buffer; } } else { if (ev->write) { recv_action = "proxying and reading from client"; send_action = "proxying and sending to upstream"; src = s->connection; dst = c; b = s->buffer; } else { recv_action = "proxying and reading from upstream"; send_action = "proxying and sending to client"; 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); for ( ;; ) { if (do_write) { size = b->last - b->pos; if (size && dst->write->ready) { c->log->action = send_action; n = dst->send(dst, b->pos, size); if (n == NGX_ERROR) { ngx_imap_proxy_close_session(s); return; } if (n > 0) { b->pos += n; if (b->pos == b->last) { b->pos = b->start; b->last = b->start; } } } } size = b->end - b->last; if (size && src->read->ready) { c->log->action = recv_action; n = src->recv(src, b->last, size); if (n == NGX_AGAIN || n == 0) { break; } if (n > 0) { do_write = 1; b->last += n; continue; } if (n == NGX_ERROR) { src->read->eof = 1; } } break; } c->log->action = "proxying"; if ((s->connection->read->eof || s->proxy->upstream.connection->read->eof) && s->buffer->pos == s->buffer->last && s->proxy->buffer->pos == s->proxy->buffer->last) { action = c->log->action; c->log->action = NULL; ngx_log_error(NGX_LOG_INFO, c->log, 0, "proxied session done"); c->log->action = action; ngx_imap_proxy_close_session(s); return; } if (ngx_handle_write_event(dst->write, 0) == NGX_ERROR) { ngx_imap_proxy_close_session(s); return; } if (ngx_handle_read_event(dst->read, 0) == NGX_ERROR) { ngx_imap_proxy_close_session(s); return; } if (ngx_handle_write_event(src->write, 0) == NGX_ERROR) { ngx_imap_proxy_close_session(s); return; } if (ngx_handle_read_event(src->read, 0) == NGX_ERROR) { ngx_imap_proxy_close_session(s); return; } if (c == s->connection) { pcf = ngx_imap_get_module_srv_conf(s, ngx_imap_proxy_module); ngx_add_timer(c->read, pcf->timeout); } } 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) { 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_close_connection(s->connection); } static void * ngx_imap_proxy_create_conf(ngx_conf_t *cf) { ngx_imap_proxy_conf_t *pcf; pcf = ngx_pcalloc(cf->pool, sizeof(ngx_imap_proxy_conf_t)); if (pcf == NULL) { return NGX_CONF_ERROR; } pcf->enable = NGX_CONF_UNSET; pcf->buffer_size = NGX_CONF_UNSET_SIZE; pcf->timeout = NGX_CONF_UNSET_MSEC; return pcf; } static char * ngx_imap_proxy_merge_conf(ngx_conf_t *cf, void *parent, void *child) { ngx_imap_proxy_conf_t *prev = parent; ngx_imap_proxy_conf_t *conf = child; ngx_conf_merge_value(conf->enable, prev->enable, 0); ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, (size_t) ngx_pagesize); ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 24 * 60 * 60000); return NGX_CONF_OK; }