comparison src/imap/ngx_imap_proxy_module.c @ 76:da9a3b14312d NGINX_0_1_38

nginx 0.1.38 *) Feature: the "limit_rate" directive is supported in in proxy and FastCGI mode. *) Feature: the "X-Accel-Limit-Rate" response header line is supported in proxy and FastCGI mode. *) Feature: the "break" directive. *) Feature: the "log_not_found" directive. *) Bugfix: the response status code was not changed when request was redirected by the ""X-Accel-Redirect" header line. *) Bugfix: the variables set by the "set" directive could not be used in SSI. *) Bugfix: the segmentation fault may occurred if the SSI page has more than one remote subrequest. *) Bugfix: nginx treated the backend response as invalid if the status line in the header was transferred in two packets; bug appeared in 0.1.29. *) Feature: the "ssi_types" directive. *) Feature: the "autoindex_exact_size" directive. *) Bugfix: the ngx_http_autoindex_module did not support the long file names in UTF-8. *) Feature: the IMAP/POP3 proxy.
author Igor Sysoev <http://sysoev.ru>
date Fri, 08 Jul 2005 00:00:00 +0400
parents
children 9db7e0b5b27f
comparison
equal deleted inserted replaced
75:985847bb65f9 76:da9a3b14312d
1
2 /*
3 * Copyright (C) Igor Sysoev
4 */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_event.h>
10 #include <ngx_event_connect.h>
11 #include <ngx_imap.h>
12
13
14 typedef struct {
15 ngx_flag_t enable;
16 } ngx_imap_proxy_conf_t;
17
18
19 static void ngx_imap_proxy_block_read(ngx_event_t *rev);
20 static void ngx_imap_proxy_imap_handler(ngx_event_t *rev);
21 static void ngx_imap_proxy_pop3_handler(ngx_event_t *rev);
22 static void ngx_imap_proxy_dummy_handler(ngx_event_t *ev);
23 static ngx_int_t ngx_imap_proxy_read_response(ngx_imap_session_t *s,
24 ngx_uint_t what);
25 static void ngx_imap_proxy_handler(ngx_event_t *ev);
26 static void ngx_imap_proxy_internal_server_error(ngx_imap_session_t *s);
27 static void ngx_imap_proxy_close_session(ngx_imap_session_t *s);
28 static void *ngx_imap_proxy_create_conf(ngx_conf_t *cf);
29 static char *ngx_imap_proxy_merge_conf(ngx_conf_t *cf, void *parent,
30 void *child);
31
32
33 #define NGX_IMAP_WAIT_OK 0
34 #define NGX_IMAP_WAIT_NEXT 1
35
36
37 static ngx_command_t ngx_imap_proxy_commands[] = {
38 { ngx_string("proxy"),
39 NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_FLAG,
40 ngx_conf_set_flag_slot,
41 NGX_IMAP_SRV_CONF_OFFSET,
42 offsetof(ngx_imap_proxy_conf_t, enable),
43 NULL },
44
45 ngx_null_command
46 };
47
48
49 static ngx_imap_module_t ngx_imap_proxy_module_ctx = {
50 NULL, /* create main configuration */
51 NULL, /* init main configuration */
52
53 ngx_imap_proxy_create_conf, /* create server configuration */
54 ngx_imap_proxy_merge_conf /* merge server configuration */
55 };
56
57
58 ngx_module_t ngx_imap_proxy_module = {
59 NGX_MODULE_V1,
60 &ngx_imap_proxy_module_ctx, /* module context */
61 ngx_imap_proxy_commands, /* module directives */
62 NGX_IMAP_MODULE, /* module type */
63 NULL, /* init module */
64 NULL /* init process */
65 };
66
67
68 void
69 ngx_imap_proxy_init(ngx_imap_session_t *s, ngx_peers_t *peers)
70 {
71 ngx_int_t rc;
72 ngx_imap_proxy_ctx_t *p;
73 ngx_imap_core_srv_conf_t *cscf;
74
75 p = ngx_pcalloc(s->connection->pool, sizeof(ngx_imap_proxy_ctx_t));
76 if (p == NULL) {
77 ngx_imap_session_internal_server_error(s);
78 return;
79 }
80
81 s->proxy = p;
82
83 p->upstream.peers = peers;
84 p->upstream.log = s->connection->log;
85 p->upstream.log_error = NGX_ERROR_ERR;
86
87 rc = ngx_event_connect_peer(&p->upstream);
88
89 if (rc == NGX_ERROR) {
90 ngx_imap_session_internal_server_error(s);
91 return;
92 }
93
94 cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
95 ngx_add_timer(p->upstream.connection->read, cscf->timeout);
96
97 p->upstream.connection->data = s;
98 p->upstream.connection->pool = s->connection->pool;
99
100 s->connection->read->handler = ngx_imap_proxy_block_read;
101 p->upstream.connection->write->handler = ngx_imap_proxy_dummy_handler;
102
103 if (s->protocol == NGX_IMAP_POP3_PROTOCOL) {
104 p->upstream.connection->read->handler = ngx_imap_proxy_pop3_handler;
105 s->imap_state = ngx_pop3_start;
106
107 } else {
108 p->upstream.connection->read->handler = ngx_imap_proxy_imap_handler;
109 s->imap_state = ngx_imap_start;
110 }
111 }
112
113
114 static void
115 ngx_imap_proxy_block_read(ngx_event_t *rev)
116 {
117 ngx_connection_t *c;
118 ngx_imap_session_t *s;
119
120 ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy block read");
121
122 if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
123 c = rev->data;
124 s = c->data;
125
126 ngx_imap_proxy_close_session(s);
127 }
128 }
129
130
131 static void
132 ngx_imap_proxy_imap_handler(ngx_event_t *rev)
133 {
134 u_char *p;
135 ngx_int_t rc;
136 ngx_str_t line;
137 ngx_connection_t *c;
138 ngx_imap_session_t *s;
139 ngx_imap_core_srv_conf_t *cscf;
140
141 ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0,
142 "imap proxy imap auth handler");
143
144 c = rev->data;
145 s = c->data;
146
147 if (rev->timedout) {
148 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
149 "upstream timed out");
150 ngx_imap_proxy_internal_server_error(s);
151 return;
152 }
153
154 if (s->proxy->buffer == NULL) {
155 cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
156
157 s->proxy->buffer = ngx_create_temp_buf(c->pool,
158 cscf->proxy_buffer_size);
159 if (s->proxy->buffer == NULL) {
160 ngx_imap_proxy_internal_server_error(s);
161 return;
162 }
163 }
164
165 rc = ngx_imap_proxy_read_response(s, s->imap_state == ngx_imap_start ?
166 NGX_IMAP_WAIT_OK : NGX_IMAP_WAIT_NEXT);
167
168 if (rc == NGX_AGAIN) {
169 return;
170 }
171
172 if (rc == NGX_ERROR) {
173 ngx_imap_proxy_internal_server_error(s);
174 return;
175 }
176
177 switch (s->imap_state) {
178
179 case ngx_imap_start:
180 ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0,
181 "imap proxy send login");
182
183 line.len = s->tag.len + sizeof("LOGIN ") - 1
184 + 1 + NGX_SIZE_T_LEN + 1 + 2;
185 line.data = ngx_palloc(c->pool, line.len);
186 if (line.data == NULL) {
187 ngx_imap_proxy_internal_server_error(s);
188 return;
189 }
190
191 line.len = ngx_sprintf(line.data, "%VLOGIN {%uz}" CRLF,
192 &s->tag, s->login.len)
193 - line.data;
194
195 s->imap_state = ngx_imap_login;
196 break;
197
198 case ngx_imap_login:
199 ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy send user");
200
201 line.len = s->login.len + 1 + NGX_SIZE_T_LEN + 1 + 2;
202 line.data = ngx_palloc(c->pool, line.len);
203 if (line.data == NULL) {
204 ngx_imap_proxy_internal_server_error(s);
205 return;
206 }
207
208 line.len = ngx_sprintf(line.data, "%V{%uz}" CRLF,
209 &s->login, s->passwd.len)
210 - line.data;
211
212 s->imap_state = ngx_imap_user;
213 break;
214
215 case ngx_imap_user:
216 ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0,
217 "imap proxy send passwd");
218
219 line.len = s->passwd.len + 2;
220 line.data = ngx_palloc(c->pool, line.len);
221 if (line.data == NULL) {
222 ngx_imap_proxy_internal_server_error(s);
223 return;
224 }
225
226 p = ngx_cpymem(line.data, s->passwd.data, s->passwd.len);
227 *p++ = CR; *p = LF;
228
229 s->imap_state = ngx_imap_passwd;
230 break;
231
232 default:
233 #if (NGX_SUPPRESS_WARN)
234 line.len = 0;
235 line.data = NULL;
236 #endif
237 break;
238 }
239
240 if (ngx_send(c, line.data, line.len) < (ssize_t) line.len) {
241 /*
242 * we treat the incomplete sending as NGX_ERROR
243 * because it is very strange here
244 */
245 ngx_imap_proxy_internal_server_error(s);
246 return;
247 }
248
249 s->proxy->buffer->pos = s->proxy->buffer->start;
250 s->proxy->buffer->last = s->proxy->buffer->start;
251
252 if (s->imap_state == ngx_imap_passwd) {
253 s->connection->read->handler = ngx_imap_proxy_handler;
254 s->connection->write->handler = ngx_imap_proxy_handler;
255 rev->handler = ngx_imap_proxy_handler;
256 c->write->handler = ngx_imap_proxy_handler;
257 }
258 }
259
260
261 static void
262 ngx_imap_proxy_pop3_handler(ngx_event_t *rev)
263 {
264 u_char *p;
265 ngx_int_t rc;
266 ngx_str_t line;
267 ngx_connection_t *c;
268 ngx_imap_session_t *s;
269 ngx_imap_core_srv_conf_t *cscf;
270
271 ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0,
272 "imap proxy pop3 auth handler");
273
274 c = rev->data;
275 s = c->data;
276
277 if (rev->timedout) {
278 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
279 "upstream timed out");
280 ngx_imap_proxy_internal_server_error(s);
281 return;
282 }
283
284 if (s->proxy->buffer == NULL) {
285 cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
286
287 s->proxy->buffer = ngx_create_temp_buf(c->pool,
288 cscf->proxy_buffer_size);
289 if (s->proxy->buffer == NULL) {
290 ngx_imap_proxy_internal_server_error(s);
291 return;
292 }
293 }
294
295 rc = ngx_imap_proxy_read_response(s, NGX_IMAP_WAIT_OK);
296
297 if (rc == NGX_AGAIN) {
298 return;
299 }
300
301 if (rc == NGX_ERROR) {
302 ngx_imap_proxy_internal_server_error(s);
303 return;
304 }
305
306 switch (s->imap_state) {
307
308 case ngx_pop3_start:
309 ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy send user");
310
311 line.len = sizeof("USER ") - 1 + s->login.len + 2;
312 line.data = ngx_palloc(c->pool, line.len);
313 if (line.data == NULL) {
314 ngx_imap_proxy_internal_server_error(s);
315 return;
316 }
317
318 p = ngx_cpymem(line.data, "USER ", sizeof("USER ") - 1);
319 p = ngx_cpymem(p, s->login.data, s->login.len);
320 *p++ = CR; *p = LF;
321
322 s->imap_state = ngx_pop3_user;
323 break;
324
325 case ngx_pop3_user:
326 ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy send pass");
327
328 line.len = sizeof("PASS ") - 1 + s->passwd.len + 2;
329 line.data = ngx_palloc(c->pool, line.len);
330 if (line.data == NULL) {
331 ngx_imap_proxy_internal_server_error(s);
332 return;
333 }
334
335 p = ngx_cpymem(line.data, "PASS ", sizeof("PASS ") - 1);
336 p = ngx_cpymem(p, s->passwd.data, s->passwd.len);
337 *p++ = CR; *p = LF;
338
339 s->imap_state = ngx_pop3_passwd;
340 break;
341
342 default:
343 #if (NGX_SUPPRESS_WARN)
344 line.len = 0;
345 line.data = NULL;
346 #endif
347 break;
348 }
349
350 if (ngx_send(c, line.data, line.len) < (ssize_t) line.len) {
351 /*
352 * we treat the incomplete sending as NGX_ERROR
353 * because it is very strange here
354 */
355 ngx_imap_proxy_internal_server_error(s);
356 return;
357 }
358
359 s->proxy->buffer->pos = s->proxy->buffer->start;
360 s->proxy->buffer->last = s->proxy->buffer->start;
361
362 if (s->imap_state == ngx_pop3_passwd) {
363 s->connection->read->handler = ngx_imap_proxy_handler;
364 s->connection->write->handler = ngx_imap_proxy_handler;
365 rev->handler = ngx_imap_proxy_handler;
366 c->write->handler = ngx_imap_proxy_handler;
367 }
368 }
369
370
371 static void
372 ngx_imap_proxy_dummy_handler(ngx_event_t *ev)
373 {
374 ngx_log_debug0(NGX_LOG_DEBUG_IMAP, ev->log, 0, "imap proxy dummy handler");
375 }
376
377
378 static ngx_int_t
379 ngx_imap_proxy_read_response(ngx_imap_session_t *s, ngx_uint_t what)
380 {
381 u_char *p;
382 ssize_t n;
383 ngx_buf_t *b;
384
385 b = s->proxy->buffer;
386
387 n = ngx_recv(s->proxy->upstream.connection, b->last, b->end - b->last);
388
389 if (n == NGX_ERROR || n == 0) {
390 return NGX_ERROR;
391 }
392
393 if (n == NGX_AGAIN) {
394 return NGX_AGAIN;
395 }
396
397 b->last += n;
398
399 if (b->last - b->pos < 5) {
400 return NGX_AGAIN;
401 }
402
403 if (*(b->last - 2) != CR || *(b->last - 1) != LF) {
404 if (b->last == b->end) {
405 *(b->last - 1) = '\0';
406 ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
407 "upstream sent too long response line: \"%s\"",
408 b->pos);
409 return NGX_IMAP_PROXY_INVALID;
410 }
411
412 return NGX_AGAIN;
413 }
414
415 p = b->pos;
416
417 if (s->protocol == NGX_IMAP_POP3_PROTOCOL) {
418 if (p[0] == '+' && p[1] == 'O' && p[2] == 'K') {
419 return NGX_OK;
420 }
421
422 if (p[0] == '-' && p[1] == 'E' && p[2] == 'R' && p[3] == 'R') {
423 return NGX_IMAP_PROXY_ERROR;
424 }
425
426 } else {
427 if (what == NGX_IMAP_WAIT_OK) {
428 if (p[0] == '*' && p[1] == ' ' && p[2] == 'O' && p[3] == 'K') {
429 return NGX_OK;
430 }
431
432 } else {
433 if (p[0] == '+' && p[1] == ' ' && p[2] == 'O' && p[3] == 'K') {
434 return NGX_OK;
435 }
436 }
437 }
438
439 *(b->last - 2) = '\0';
440 ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
441 "upstream sent invalid response: \"%s\"", p);
442
443 return NGX_IMAP_PROXY_INVALID;
444 }
445
446
447 static void
448 ngx_imap_proxy_handler(ngx_event_t *ev)
449 {
450 size_t size;
451 ssize_t n;
452 ngx_buf_t *b;
453 ngx_uint_t again, do_write;
454 ngx_connection_t *c, *src, *dst;
455 ngx_imap_session_t *s;
456
457 c = ev->data;
458 s = c->data;
459
460 if (ev->timedout) {
461 if (c == s->connection) {
462 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
463 "client timed out");
464 } else {
465 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
466 "upstream timed out");
467 }
468
469 ngx_imap_proxy_close_session(s);
470 return;
471 }
472
473 if (c == s->connection) {
474 if (ev->write) {
475 src = s->proxy->upstream.connection;
476 dst = c;
477 b = s->proxy->buffer;
478
479 } else {
480 src = c;
481 dst = s->proxy->upstream.connection;
482 b = s->buffer;
483 }
484
485 } else {
486 if (ev->write) {
487 src = s->connection;
488 dst = c;
489 b = s->buffer;
490
491 } else {
492 src = c;
493 dst = s->connection;
494 b = s->proxy->buffer;
495 }
496 }
497
498 do_write = ev->write ? 1 : 0;
499
500 ngx_log_debug3(NGX_LOG_DEBUG_IMAP, ev->log, 0,
501 "imap proxy handler: %d, #%d > #%d",
502 do_write, src->fd, dst->fd);
503
504 do {
505 again = 0;
506
507 if (do_write == 1) {
508
509 size = b->last - b->pos;
510
511 if (size && dst->write->ready) {
512 n = ngx_send(dst, b->pos, size);
513
514 if (n == NGX_ERROR) {
515 ngx_imap_proxy_close_session(s);
516 return;
517 }
518
519 if (n > 0) {
520 again = 1;
521 b->pos += n;
522
523 if (b->pos == b->last) {
524 b->pos = b->start;
525 b->last = b->start;
526 }
527 }
528
529 if (n == NGX_AGAIN || n < (ssize_t) size) {
530 if (ngx_handle_write_event(dst->write, /* TODO: LOWAT */ 0)
531 == NGX_ERROR)
532 {
533 ngx_imap_proxy_close_session(s);
534 return;
535 }
536 }
537 }
538 }
539
540 size = b->end - b->last;
541
542 if (size && src->read->ready) {
543 n = ngx_recv(src, b->last, size);
544
545 if (n == NGX_ERROR || n == 0) {
546 ngx_imap_proxy_close_session(s);
547 return;
548 }
549
550 if (n > 0) {
551 again = 1;
552 do_write = 1;
553 b->last += n;
554 }
555
556 if (n == NGX_AGAIN || n < (ssize_t) size) {
557 if (ngx_handle_read_event(src->read, 0) == NGX_ERROR) {
558 ngx_imap_proxy_close_session(s);
559 return;
560 }
561 }
562 }
563
564 } while (again);
565 }
566
567
568 static void
569 ngx_imap_proxy_internal_server_error(ngx_imap_session_t *s)
570 {
571 if (s->proxy->upstream.connection) {
572 ngx_log_debug1(NGX_LOG_DEBUG_IMAP, s->connection->log, 0,
573 "close imap proxy connection: %d",
574 s->proxy->upstream.connection->fd);
575
576 ngx_close_connection(s->proxy->upstream.connection);
577 }
578
579 ngx_imap_session_internal_server_error(s);
580 }
581
582
583 static void
584 ngx_imap_proxy_close_session(ngx_imap_session_t *s)
585 {
586 if (s->proxy->upstream.connection) {
587 ngx_log_debug1(NGX_LOG_DEBUG_IMAP, s->connection->log, 0,
588 "close imap proxy connection: %d",
589 s->proxy->upstream.connection->fd);
590
591 ngx_close_connection(s->proxy->upstream.connection);
592 }
593
594 ngx_imap_close_connection(s->connection);
595 }
596
597
598 static void *
599 ngx_imap_proxy_create_conf(ngx_conf_t *cf)
600 {
601 ngx_imap_proxy_conf_t *pcf;
602
603 pcf = ngx_pcalloc(cf->pool, sizeof(ngx_imap_proxy_conf_t));
604 if (pcf == NULL) {
605 return NGX_CONF_ERROR;
606 }
607
608 pcf->enable = NGX_CONF_UNSET;
609
610 return pcf;
611 }
612
613
614 static char *
615 ngx_imap_proxy_merge_conf(ngx_conf_t *cf, void *parent, void *child)
616 {
617 ngx_imap_proxy_conf_t *prev = parent;
618 ngx_imap_proxy_conf_t *conf = child;
619
620 ngx_conf_merge_msec_value(conf->enable, prev->enable, 0);
621
622 return NGX_CONF_OK;
623 }