Igor Sysoev | 0d9da9b | 2004-09-08 05:18:51 +0000 | [diff] [blame] | 1 | |
| 2 | #include <ngx_config.h> |
| 3 | #include <ngx_core.h> |
| 4 | #include <ngx_event.h> |
Igor Sysoev | 2e6ba93 | 2004-09-09 15:40:48 +0000 | [diff] [blame^] | 5 | #include <ngx_event_connect.h> |
Igor Sysoev | 0d9da9b | 2004-09-08 05:18:51 +0000 | [diff] [blame] | 6 | #include <ngx_imap.h> |
| 7 | |
| 8 | |
Igor Sysoev | 2e6ba93 | 2004-09-09 15:40:48 +0000 | [diff] [blame^] | 9 | static void ngx_imap_proxy_block_read(ngx_event_t *rev); |
| 10 | static void ngx_imap_proxy_greeting_handler(ngx_event_t *rev); |
| 11 | static void ngx_imap_proxy_init_handler(ngx_event_t *wev); |
| 12 | static void ngx_imap_proxy_dummy_handler(ngx_event_t *ev); |
| 13 | static ngx_int_t ngx_imap_proxy_read_response(ngx_imap_session_t *s); |
| 14 | static void ngx_imap_proxy_handler(ngx_event_t *ev); |
Igor Sysoev | 0d9da9b | 2004-09-08 05:18:51 +0000 | [diff] [blame] | 15 | static void ngx_imap_proxy_close_session(ngx_imap_session_t *s); |
| 16 | |
| 17 | |
Igor Sysoev | 2e6ba93 | 2004-09-09 15:40:48 +0000 | [diff] [blame^] | 18 | void ngx_imap_proxy_init(ngx_imap_session_t *s) |
Igor Sysoev | 0d9da9b | 2004-09-08 05:18:51 +0000 | [diff] [blame] | 19 | { |
Igor Sysoev | 2e6ba93 | 2004-09-09 15:40:48 +0000 | [diff] [blame^] | 20 | ngx_int_t rc; |
| 21 | ngx_peers_t *peers; |
| 22 | ngx_imap_proxy_ctx_t *p; |
| 23 | |
| 24 | if (!(p = ngx_pcalloc(s->connection->pool, sizeof(ngx_imap_proxy_ctx_t)))) { |
| 25 | ngx_imap_close_connection(s->connection); |
| 26 | return; |
| 27 | } |
| 28 | |
| 29 | s->proxy = p; |
| 30 | |
| 31 | /**/ |
| 32 | |
| 33 | if (!(peers = ngx_pcalloc(s->connection->pool, sizeof(ngx_peers_t)))) { |
| 34 | ngx_imap_close_connection(s->connection); |
| 35 | return; |
| 36 | } |
| 37 | |
| 38 | p->upstream.peers = peers; |
| 39 | p->upstream.log = s->connection->log; |
| 40 | p->upstream.log_error = NGX_ERROR_ERR; |
| 41 | |
| 42 | peers->number = 1; |
| 43 | peers->max_fails = 1; |
| 44 | peers->peers[0].addr = inet_addr("81.19.69.70"); |
| 45 | peers->peers[0].addr_port_text.len = sizeof("81.19.69.70:110") - 1; |
| 46 | peers->peers[0].addr_port_text.data = "81.19.69.70:110"; |
| 47 | peers->peers[0].port = htons(110); |
| 48 | |
| 49 | rc = ngx_event_connect_peer(&p->upstream); |
| 50 | |
| 51 | if (rc == NGX_ERROR) { |
| 52 | ngx_imap_proxy_close_session(s); |
| 53 | return; |
| 54 | } |
| 55 | |
| 56 | p->upstream.connection->data = s; |
| 57 | p->upstream.connection->pool = s->connection->pool; |
| 58 | |
| 59 | s->connection->read->event_handler = ngx_imap_proxy_block_read; |
| 60 | p->upstream.connection->read->event_handler = |
| 61 | ngx_imap_proxy_greeting_handler; |
| 62 | p->upstream.connection->write->event_handler = ngx_imap_proxy_dummy_handler; |
| 63 | } |
| 64 | |
| 65 | |
| 66 | static void ngx_imap_proxy_block_read(ngx_event_t *rev) |
| 67 | { |
| 68 | ngx_connection_t *c; |
| 69 | ngx_imap_session_t *s; |
| 70 | |
| 71 | ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy block read"); |
| 72 | |
| 73 | if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { |
| 74 | c = rev->data; |
| 75 | s = c->data; |
| 76 | |
| 77 | ngx_imap_proxy_close_session(s); |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | |
| 82 | static void ngx_imap_proxy_greeting_handler(ngx_event_t *rev) |
| 83 | { |
| 84 | ngx_int_t rc; |
| 85 | ngx_buf_t *b; |
| 86 | ngx_connection_t *c; |
| 87 | ngx_imap_session_t *s; |
| 88 | |
| 89 | ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, |
| 90 | "imap proxy greeting handler"); |
| 91 | |
| 92 | c = rev->data; |
| 93 | s = c->data; |
| 94 | |
| 95 | if (s->proxy->buffer == NULL) { |
| 96 | s->proxy->buffer = ngx_create_temp_buf(c->pool, /* STUB */ 4096); |
| 97 | if (s->proxy->buffer == NULL) { |
| 98 | ngx_imap_proxy_close_session(s); |
| 99 | return; |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | rc = ngx_imap_proxy_read_response(s); |
| 104 | |
| 105 | if (rc == NGX_AGAIN) { |
| 106 | return; |
| 107 | } |
| 108 | |
| 109 | if (rc == NGX_OK) { |
| 110 | s->connection->read->event_handler = ngx_imap_proxy_handler; |
| 111 | s->connection->write->event_handler = ngx_imap_proxy_handler; |
| 112 | rev->event_handler = ngx_imap_proxy_handler; |
| 113 | c->write->event_handler = ngx_imap_proxy_handler; |
| 114 | |
| 115 | b = s->proxy->buffer; |
| 116 | b->pos = b->start; |
| 117 | b->last = b->start; |
| 118 | |
| 119 | if (ngx_handle_read_event(s->connection->read, 0) == NGX_ERROR) { |
| 120 | ngx_imap_proxy_close_session(s); |
| 121 | return; |
| 122 | } |
| 123 | |
| 124 | if (s->connection->read->ready) { |
| 125 | ngx_imap_proxy_handler(s->connection->read); |
| 126 | } |
| 127 | |
| 128 | return; |
| 129 | } |
| 130 | |
| 131 | /* TODO: ngx_imap_proxy_finalize_session(s, NGX_IMAP_INTERNAL_ERROR) */ |
| 132 | ngx_imap_proxy_close_session(s); |
| 133 | } |
| 134 | |
| 135 | |
| 136 | static void ngx_imap_proxy_dummy_handler(ngx_event_t *ev) |
| 137 | { |
| 138 | ngx_log_debug0(NGX_LOG_DEBUG_IMAP, ev->log, 0, "imap proxy dummy handler"); |
| 139 | } |
| 140 | |
| 141 | |
| 142 | static ngx_int_t ngx_imap_proxy_read_response(ngx_imap_session_t *s) |
| 143 | { |
| 144 | u_char *p; |
| 145 | ssize_t n; |
| 146 | ngx_buf_t *b; |
| 147 | |
| 148 | b = s->proxy->buffer; |
| 149 | |
| 150 | n = ngx_recv(s->proxy->upstream.connection, b->last, b->end - b->last); |
| 151 | |
| 152 | if (n == NGX_ERROR || n == 0) { |
| 153 | return NGX_ERROR; |
| 154 | } |
| 155 | |
| 156 | if (n == NGX_AGAIN) { |
| 157 | return NGX_AGAIN; |
| 158 | } |
| 159 | |
| 160 | b->last += n; |
| 161 | |
| 162 | if (b->last - b->pos < 5) { |
| 163 | return NGX_AGAIN; |
| 164 | } |
| 165 | |
| 166 | if (*(b->last - 2) != CR || *(b->last - 1) != LF) { |
| 167 | if (b->last == b->end) { |
| 168 | *(b->last - 1) = '\0'; |
| 169 | ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, |
| 170 | "upstream sent too long response line: \"%s\"", |
| 171 | b->pos); |
| 172 | return NGX_IMAP_PROXY_INVALID; |
| 173 | } |
| 174 | |
| 175 | return NGX_AGAIN; |
| 176 | } |
| 177 | |
| 178 | p = b->pos; |
| 179 | |
| 180 | if (p[0] == '+' && p[1] == 'O' && p[2] == 'K') { |
| 181 | return NGX_OK; |
| 182 | } |
| 183 | |
| 184 | if (p[0] == '-' && p[1] == 'E' && p[2] == 'R' && p[3] == 'R') { |
| 185 | return NGX_IMAP_PROXY_ERROR; |
| 186 | } |
| 187 | |
| 188 | *(b->last - 2) = '\0'; |
| 189 | ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, |
| 190 | "upstream sent invalid greeting line: \"%s\"", p); |
| 191 | |
| 192 | return NGX_IMAP_PROXY_INVALID; |
| 193 | } |
| 194 | |
| 195 | |
| 196 | static void ngx_imap_proxy_handler(ngx_event_t *ev) |
| 197 | { |
| 198 | size_t size; |
Igor Sysoev | 0d9da9b | 2004-09-08 05:18:51 +0000 | [diff] [blame] | 199 | ssize_t n; |
| 200 | ngx_buf_t *b; |
| 201 | ngx_uint_t data, do_write; |
| 202 | ngx_connection_t *c, *src, *dst; |
| 203 | ngx_imap_session_t *s; |
| 204 | |
| 205 | c = ev->data; |
| 206 | s = c->data; |
| 207 | |
| 208 | if (c == s->connection) { |
| 209 | src = c; |
Igor Sysoev | 2e6ba93 | 2004-09-09 15:40:48 +0000 | [diff] [blame^] | 210 | dst = s->proxy->upstream.connection; |
| 211 | b = s->buffer; |
Igor Sysoev | 0d9da9b | 2004-09-08 05:18:51 +0000 | [diff] [blame] | 212 | |
| 213 | } else { |
Igor Sysoev | 2e6ba93 | 2004-09-09 15:40:48 +0000 | [diff] [blame^] | 214 | src = c; |
| 215 | dst = s->connection; |
| 216 | b = s->proxy->buffer; |
Igor Sysoev | 0d9da9b | 2004-09-08 05:18:51 +0000 | [diff] [blame] | 217 | } |
| 218 | |
| 219 | do_write = ev->write ? 1 : 0; |
| 220 | |
Igor Sysoev | 2e6ba93 | 2004-09-09 15:40:48 +0000 | [diff] [blame^] | 221 | ngx_log_debug3(NGX_LOG_DEBUG_IMAP, ev->log, 0, |
| 222 | "imap proxy handler: %d, #%d > #%d", |
| 223 | do_write, src->fd, dst->fd); |
| 224 | |
Igor Sysoev | 0d9da9b | 2004-09-08 05:18:51 +0000 | [diff] [blame] | 225 | do { |
| 226 | data = 0; |
| 227 | |
| 228 | if (do_write == 1) { |
Igor Sysoev | 2e6ba93 | 2004-09-09 15:40:48 +0000 | [diff] [blame^] | 229 | |
| 230 | size = b->last - b->pos; |
| 231 | |
| 232 | if (dst->write->ready && size) { |
| 233 | n = ngx_send(dst, b->pos, size); |
Igor Sysoev | 0d9da9b | 2004-09-08 05:18:51 +0000 | [diff] [blame] | 234 | |
| 235 | if (n == NGX_ERROR) { |
| 236 | ngx_imap_proxy_close_session(s); |
| 237 | return; |
| 238 | } |
| 239 | |
| 240 | if (n > 0) { |
| 241 | data = 1; |
| 242 | b->pos += n; |
| 243 | |
| 244 | if (b->pos == b->last) { |
| 245 | b->pos = b->start; |
| 246 | b->last = b->start; |
| 247 | } |
| 248 | } |
Igor Sysoev | 2e6ba93 | 2004-09-09 15:40:48 +0000 | [diff] [blame^] | 249 | |
| 250 | if (n == NGX_AGAIN || n < (ssize_t) size) { |
| 251 | dst->write->available = 0; |
| 252 | if (ngx_handle_write_event(dst->write, NGX_LOWAT_EVENT) |
| 253 | == NGX_ERROR) |
| 254 | { |
| 255 | ngx_imap_proxy_close_session(s); |
| 256 | return; |
| 257 | } |
| 258 | } |
Igor Sysoev | 0d9da9b | 2004-09-08 05:18:51 +0000 | [diff] [blame] | 259 | } |
| 260 | } |
| 261 | |
Igor Sysoev | 2e6ba93 | 2004-09-09 15:40:48 +0000 | [diff] [blame^] | 262 | size = b->end - b->last; |
| 263 | |
| 264 | if (src->read->ready && size) { |
| 265 | n = ngx_recv(src, b->last, size); |
Igor Sysoev | 0d9da9b | 2004-09-08 05:18:51 +0000 | [diff] [blame] | 266 | |
| 267 | if (n == NGX_ERROR || n == 0) { |
| 268 | ngx_imap_proxy_close_session(s); |
| 269 | return; |
| 270 | } |
| 271 | |
| 272 | if (n > 0) { |
| 273 | data = 1; |
| 274 | do_write = 1; |
| 275 | b->last += n; |
| 276 | } |
Igor Sysoev | 2e6ba93 | 2004-09-09 15:40:48 +0000 | [diff] [blame^] | 277 | |
| 278 | if (n == NGX_AGAIN || n < (ssize_t) size) { |
| 279 | if (ngx_handle_read_event(src->read, 0) == NGX_ERROR) { |
| 280 | ngx_imap_proxy_close_session(s); |
| 281 | return; |
| 282 | } |
| 283 | } |
Igor Sysoev | 0d9da9b | 2004-09-08 05:18:51 +0000 | [diff] [blame] | 284 | } |
| 285 | |
| 286 | } while (data); |
| 287 | } |
| 288 | |
| 289 | |
| 290 | static void ngx_imap_proxy_close_session(ngx_imap_session_t *s) |
| 291 | { |
Igor Sysoev | 2e6ba93 | 2004-09-09 15:40:48 +0000 | [diff] [blame^] | 292 | if (ngx_close_socket(s->proxy->upstream.connection->fd) == -1) { |
| 293 | ngx_log_error(NGX_LOG_ALERT, s->connection->log, ngx_socket_errno, |
| 294 | ngx_close_socket_n " failed"); |
| 295 | } |
| 296 | |
| 297 | ngx_imap_close_connection(s->connection); |
Igor Sysoev | 0d9da9b | 2004-09-08 05:18:51 +0000 | [diff] [blame] | 298 | } |