76
|
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_imap.h>
|
|
11
|
|
12
|
|
13 static void ngx_imap_init_session(ngx_event_t *rev);
|
|
14 static ngx_int_t ngx_imap_read_command(ngx_imap_session_t *s);
|
|
15
|
|
16
|
|
17 static ngx_str_t greetings[] = {
|
|
18 ngx_string("+OK POP3 ready" CRLF),
|
|
19 ngx_string("* OK IMAP ready" CRLF)
|
|
20 };
|
|
21
|
|
22 static ngx_str_t internal_server_errors[] = {
|
|
23 ngx_string("-ERR internal server error" CRLF),
|
|
24 ngx_string("* BAD internal server error" CRLF),
|
|
25 };
|
|
26
|
|
27 static u_char pop3_ok[] = "+OK" CRLF;
|
|
28 static u_char pop3_invalid_command[] = "-ERR invalid command" CRLF;
|
|
29
|
|
30 static u_char imap_ok[] = "OK" CRLF;
|
|
31 static u_char imap_next[] = "+ OK" CRLF;
|
|
32 static u_char imap_bye[] = "* BYE" CRLF;
|
|
33 static u_char imap_invalid_command[] = "BAD invalid command" CRLF;
|
|
34
|
|
35
|
|
36 void
|
|
37 ngx_imap_init_connection(ngx_connection_t *c)
|
|
38 {
|
|
39 ssize_t size;
|
|
40 ngx_imap_conf_ctx_t *ctx;
|
|
41 ngx_imap_core_srv_conf_t *cscf;
|
|
42
|
|
43 ngx_log_debug0(NGX_LOG_DEBUG_IMAP, c->log, 0, "imap init connection");
|
|
44
|
|
45 c->log_error = NGX_ERROR_INFO;
|
|
46
|
|
47 ctx = c->ctx;
|
|
48 cscf = ngx_imap_get_module_srv_conf(ctx, ngx_imap_core_module);
|
|
49
|
|
50 size = greetings[cscf->protocol].len;
|
|
51
|
|
52 if (ngx_send(c, greetings[cscf->protocol].data, size) < size) {
|
|
53 /*
|
|
54 * we treat the incomplete sending as NGX_ERROR
|
|
55 * because it is very strange here
|
|
56 */
|
|
57 ngx_imap_close_connection(c);
|
|
58 return;
|
|
59 }
|
|
60
|
|
61 c->read->handler = ngx_imap_init_session;
|
|
62
|
|
63 ngx_add_timer(c->read, cscf->timeout);
|
|
64
|
|
65 if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
|
|
66 ngx_imap_close_connection(c);
|
|
67 }
|
|
68 }
|
|
69
|
|
70
|
|
71 static void
|
|
72 ngx_imap_init_session(ngx_event_t *rev)
|
|
73 {
|
|
74 size_t size;
|
|
75 ngx_connection_t *c;
|
|
76 ngx_imap_session_t *s;
|
|
77 ngx_imap_conf_ctx_t *ctx;
|
|
78 ngx_imap_core_srv_conf_t *cscf;
|
|
79
|
|
80 c = rev->data;
|
|
81
|
|
82 if (rev->timedout) {
|
|
83 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
|
|
84 ngx_imap_close_connection(c);
|
|
85 return;
|
|
86 }
|
|
87
|
|
88 s = ngx_pcalloc(c->pool, sizeof(ngx_imap_session_t));
|
|
89 if (s == NULL) {
|
|
90 ngx_imap_session_internal_server_error(s);
|
|
91 return;
|
|
92 }
|
|
93
|
|
94 c->data = s;
|
|
95 s->connection = c;
|
|
96
|
|
97 s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_imap_max_module);
|
|
98 if (s->ctx == NULL) {
|
|
99 ngx_imap_session_internal_server_error(s);
|
|
100 return;
|
|
101 }
|
|
102
|
|
103 ctx = c->ctx;
|
|
104 s->main_conf = ctx->main_conf;
|
|
105 s->srv_conf = ctx->srv_conf;
|
|
106
|
|
107 if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) == NGX_ERROR) {
|
|
108 ngx_imap_session_internal_server_error(s);
|
|
109 return;
|
|
110 }
|
|
111
|
|
112 cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
|
|
113
|
|
114 s->protocol = cscf->protocol;
|
|
115
|
|
116 if (cscf->protocol == NGX_IMAP_POP3_PROTOCOL) {
|
|
117 size = 128;
|
|
118 s->imap_state = ngx_pop3_start;
|
|
119 c->read->handler = ngx_pop3_auth_state;
|
|
120
|
|
121 } else {
|
|
122 size = cscf->imap_client_buffer_size;
|
|
123 s->imap_state = ngx_imap_start;
|
|
124 c->read->handler = ngx_imap_auth_state;
|
|
125 }
|
|
126
|
|
127 s->buffer = ngx_create_temp_buf(c->pool, size);
|
|
128 if (s->buffer == NULL) {
|
|
129 ngx_imap_session_internal_server_error(s);
|
|
130 return;
|
|
131 }
|
|
132
|
|
133 c->read->handler(rev);
|
|
134 }
|
|
135
|
|
136
|
|
137 void
|
|
138 ngx_imap_auth_state(ngx_event_t *rev)
|
|
139 {
|
|
140 u_char *text, *last, *out, *p;
|
|
141 ssize_t size, text_len, last_len;
|
|
142 ngx_str_t *arg;
|
|
143 ngx_int_t rc;
|
|
144 ngx_uint_t quit, tag;
|
|
145 ngx_connection_t *c;
|
|
146 ngx_imap_session_t *s;
|
|
147 ngx_imap_core_srv_conf_t *cscf;
|
|
148
|
|
149 c = rev->data;
|
|
150 s = c->data;
|
|
151
|
|
152 ngx_log_debug0(NGX_LOG_DEBUG_IMAP, c->log, 0, "imap auth state");
|
|
153
|
|
154 if (rev->timedout) {
|
|
155 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
|
|
156 ngx_imap_close_connection(c);
|
|
157 return;
|
|
158 }
|
|
159
|
|
160 rc = ngx_imap_read_command(s);
|
|
161
|
|
162 ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0, "imap auth: %i", rc);
|
|
163
|
|
164 if (rc == NGX_AGAIN || rc == NGX_ERROR) {
|
|
165 return;
|
|
166 }
|
|
167
|
|
168 quit = 0;
|
|
169 tag = 1;
|
|
170
|
|
171 text = NULL;
|
|
172 text_len = 0;
|
|
173
|
|
174 last = imap_ok;
|
|
175 last_len = sizeof(imap_ok) - 1;
|
|
176
|
|
177 if (rc == NGX_OK) {
|
|
178
|
|
179 ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0, "imap auth command: %i",
|
|
180 s->command);
|
|
181
|
|
182 switch (s->command) {
|
|
183
|
|
184 case NGX_IMAP_LOGIN:
|
|
185 if (s->args.nelts == 2) {
|
|
186
|
|
187 arg = s->args.elts;
|
|
188
|
|
189 s->login.len = arg[0].len;
|
|
190 s->login.data = ngx_palloc(c->pool, s->login.len);
|
|
191 if (s->login.data == NULL) {
|
|
192 ngx_imap_session_internal_server_error(s);
|
|
193 return;
|
|
194 }
|
|
195
|
|
196 ngx_memcpy(s->login.data, arg[0].data, s->login.len);
|
|
197
|
|
198 s->passwd.len = arg[1].len;
|
|
199 s->passwd.data = ngx_palloc(c->pool, s->passwd.len);
|
|
200 if (s->passwd.data == NULL) {
|
|
201 ngx_imap_session_internal_server_error(s);
|
|
202 return;
|
|
203 }
|
|
204
|
|
205 ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len);
|
|
206
|
|
207 ngx_log_debug2(NGX_LOG_DEBUG_IMAP, c->log, 0,
|
|
208 "imap login:\"%V\" passwd:\"%V\"",
|
|
209 &s->login, &s->passwd);
|
|
210
|
|
211 s->args.nelts = 0;
|
|
212 s->buffer->pos = s->buffer->start;
|
|
213 s->buffer->last = s->buffer->start;
|
|
214
|
|
215 if (rev->timer_set) {
|
|
216 ngx_del_timer(rev);
|
|
217 }
|
|
218
|
|
219 s->login_attempt++;
|
|
220
|
|
221 ngx_imap_auth_http_init(s);
|
|
222
|
|
223 return;
|
|
224
|
|
225 } else {
|
|
226 rc = NGX_IMAP_PARSE_INVALID_COMMAND;
|
|
227 }
|
|
228
|
|
229 break;
|
|
230
|
|
231 case NGX_IMAP_CAPABILITY:
|
|
232 cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
|
|
233 text = cscf->imap_capability->pos;
|
|
234 text_len = cscf->imap_capability->last - cscf->imap_capability->pos;
|
|
235 break;
|
|
236
|
|
237 case NGX_IMAP_LOGOUT:
|
|
238 text = imap_bye;
|
|
239 text_len = sizeof(imap_bye) - 1;
|
|
240 quit = 1;
|
|
241 break;
|
|
242
|
|
243 case NGX_IMAP_NOOP:
|
|
244 break;
|
|
245
|
|
246 default:
|
|
247 rc = NGX_IMAP_PARSE_INVALID_COMMAND;
|
|
248 break;
|
|
249 }
|
|
250
|
|
251 } else if (rc == NGX_IMAP_NEXT) {
|
|
252 last = imap_next;
|
|
253 last_len = sizeof(imap_next) - 1;
|
|
254 tag = 0;
|
|
255 }
|
|
256
|
|
257 if (rc == NGX_IMAP_PARSE_INVALID_COMMAND) {
|
|
258 last = imap_invalid_command;
|
|
259 last_len = sizeof(imap_invalid_command) - 1;
|
|
260 }
|
|
261
|
|
262 if (tag) {
|
|
263 if (s->out.len < text_len + s->tag.len + last_len) {
|
|
264
|
|
265 s->out.len = text_len + s->tag.len + last_len;
|
|
266 s->out.data = ngx_palloc(c->pool, s->out.len);
|
|
267 if (s->out.data == NULL) {
|
|
268 ngx_imap_close_connection(c);
|
|
269 return;
|
|
270 }
|
|
271 }
|
|
272
|
|
273 out = s->out.data;
|
|
274 p = out;
|
|
275
|
|
276 if (text) {
|
|
277 p = ngx_cpymem(p, text, text_len);
|
|
278 }
|
|
279 p = ngx_cpymem(p, s->tag.data, s->tag.len);
|
|
280 ngx_memcpy(p, last, last_len);
|
|
281
|
|
282 size = text_len + s->tag.len + last_len;
|
|
283
|
|
284 } else {
|
|
285 out = last;
|
|
286 size = last_len;
|
|
287 }
|
|
288
|
|
289 if (ngx_send(c, out, size) < size) {
|
|
290 /*
|
|
291 * we treat the incomplete sending as NGX_ERROR
|
|
292 * because it is very strange here
|
|
293 */
|
|
294 ngx_imap_close_connection(c);
|
|
295 return;
|
|
296 }
|
|
297
|
|
298 if (rc == NGX_IMAP_NEXT) {
|
|
299 return;
|
|
300 }
|
|
301
|
|
302 if (quit) {
|
|
303 ngx_imap_close_connection(c);
|
|
304 return;
|
|
305 }
|
|
306
|
|
307 s->args.nelts = 0;
|
|
308 s->buffer->pos = s->buffer->start;
|
|
309 s->buffer->last = s->buffer->start;
|
|
310 s->tag.len = 0;
|
|
311 }
|
|
312
|
|
313
|
|
314 void
|
|
315 ngx_pop3_auth_state(ngx_event_t *rev)
|
|
316 {
|
|
317 u_char *text;
|
|
318 ssize_t size;
|
|
319 ngx_int_t rc;
|
|
320 ngx_uint_t quit;
|
|
321 ngx_str_t *arg;
|
|
322 ngx_connection_t *c;
|
|
323 ngx_imap_session_t *s;
|
|
324 ngx_imap_core_srv_conf_t *cscf;
|
|
325
|
|
326 c = rev->data;
|
|
327 s = c->data;
|
|
328
|
|
329 ngx_log_debug0(NGX_LOG_DEBUG_IMAP, c->log, 0, "pop3 auth state");
|
|
330
|
|
331 if (rev->timedout) {
|
|
332 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
|
|
333 ngx_imap_close_connection(c);
|
|
334 return;
|
|
335 }
|
|
336
|
|
337 rc = ngx_imap_read_command(s);
|
|
338
|
|
339 if (rc == NGX_AGAIN || rc == NGX_ERROR) {
|
|
340 return;
|
|
341 }
|
|
342
|
|
343 quit = 0;
|
|
344 text = pop3_ok;
|
|
345 size = sizeof(pop3_ok) - 1;
|
|
346
|
|
347 if (rc == NGX_OK) {
|
|
348 switch (s->imap_state) {
|
|
349
|
|
350 case ngx_pop3_start:
|
|
351
|
|
352 switch (s->command) {
|
|
353
|
|
354 case NGX_POP3_USER:
|
|
355 if (s->args.nelts == 1) {
|
|
356 s->imap_state = ngx_pop3_user;
|
|
357
|
|
358 arg = s->args.elts;
|
|
359 s->login.len = arg[0].len;
|
|
360 s->login.data = ngx_palloc(c->pool, s->login.len);
|
|
361 if (s->login.data == NULL) {
|
|
362 ngx_imap_session_internal_server_error(s);
|
|
363 return;
|
|
364 }
|
|
365
|
|
366 ngx_memcpy(s->login.data, arg[0].data, s->login.len);
|
|
367
|
|
368 ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0,
|
|
369 "pop3 login: \"%V\"", &s->login);
|
|
370
|
|
371 } else {
|
|
372 rc = NGX_IMAP_PARSE_INVALID_COMMAND;
|
|
373 }
|
|
374
|
|
375 break;
|
|
376
|
|
377 case NGX_POP3_CAPA:
|
|
378 cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
|
|
379 text = cscf->pop3_capability->pos;
|
|
380 size = cscf->pop3_capability->last - cscf->pop3_capability->pos;
|
|
381 break;
|
|
382
|
|
383 case NGX_POP3_QUIT:
|
|
384 quit = 1;
|
|
385 break;
|
|
386
|
|
387 case NGX_POP3_NOOP:
|
|
388 break;
|
|
389
|
|
390 default:
|
|
391 s->imap_state = ngx_pop3_start;
|
|
392 rc = NGX_IMAP_PARSE_INVALID_COMMAND;
|
|
393 break;
|
|
394 }
|
|
395
|
|
396 break;
|
|
397
|
|
398 case ngx_pop3_user:
|
|
399
|
|
400 switch (s->command) {
|
|
401
|
|
402 case NGX_POP3_PASS:
|
|
403 if (s->args.nelts == 1) {
|
|
404 /* STUB */ s->imap_state = ngx_pop3_start;
|
|
405
|
|
406 arg = s->args.elts;
|
|
407 s->passwd.len = arg[0].len;
|
|
408 s->passwd.data = ngx_palloc(c->pool, s->passwd.len);
|
|
409 if (s->passwd.data == NULL) {
|
|
410 ngx_imap_session_internal_server_error(s);
|
|
411 return;
|
|
412 }
|
|
413
|
|
414 ngx_memcpy(s->passwd.data, arg[0].data, s->passwd.len);
|
|
415
|
|
416 ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0,
|
|
417 "pop3 passwd: \"%V\"", &s->passwd);
|
|
418
|
|
419 s->args.nelts = 0;
|
|
420 s->buffer->pos = s->buffer->start;
|
|
421 s->buffer->last = s->buffer->start;
|
|
422
|
|
423 if (rev->timer_set) {
|
|
424 ngx_del_timer(rev);
|
|
425 }
|
|
426
|
|
427 ngx_imap_auth_http_init(s);
|
|
428
|
|
429 return;
|
|
430
|
|
431 } else {
|
|
432 rc = NGX_IMAP_PARSE_INVALID_COMMAND;
|
|
433 }
|
|
434
|
|
435 break;
|
|
436
|
|
437 case NGX_POP3_CAPA:
|
|
438 cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
|
|
439 text = cscf->pop3_capability->pos;
|
|
440 size = cscf->pop3_capability->last - cscf->pop3_capability->pos;
|
|
441 break;
|
|
442
|
|
443 case NGX_POP3_QUIT:
|
|
444 quit = 1;
|
|
445 break;
|
|
446
|
|
447 case NGX_POP3_NOOP:
|
|
448 break;
|
|
449
|
|
450 default:
|
|
451 s->imap_state = ngx_pop3_start;
|
|
452 rc = NGX_IMAP_PARSE_INVALID_COMMAND;
|
|
453 break;
|
|
454 }
|
|
455
|
|
456 break;
|
|
457
|
|
458 /* suppress warinings */
|
|
459 case ngx_pop3_passwd:
|
|
460 break;
|
|
461 }
|
|
462 }
|
|
463
|
|
464 if (rc == NGX_IMAP_PARSE_INVALID_COMMAND) {
|
|
465 text = pop3_invalid_command;
|
|
466 size = sizeof(pop3_invalid_command) - 1;
|
|
467 }
|
|
468
|
|
469 if (ngx_send(c, text, size) < size) {
|
|
470 /*
|
|
471 * we treat the incomplete sending as NGX_ERROR
|
|
472 * because it is very strange here
|
|
473 */
|
|
474 ngx_imap_close_connection(c);
|
|
475 return;
|
|
476 }
|
|
477
|
|
478 if (quit) {
|
|
479 ngx_imap_close_connection(c);
|
|
480 return;
|
|
481 }
|
|
482
|
|
483 s->args.nelts = 0;
|
|
484 s->buffer->pos = s->buffer->start;
|
|
485 s->buffer->last = s->buffer->start;
|
|
486 }
|
|
487
|
|
488
|
|
489 static ngx_int_t
|
|
490 ngx_imap_read_command(ngx_imap_session_t *s)
|
|
491 {
|
|
492 ssize_t n;
|
|
493 ngx_int_t rc;
|
|
494
|
|
495 n = ngx_recv(s->connection, s->buffer->last,
|
|
496 s->buffer->end - s->buffer->last);
|
|
497
|
|
498 if (n == NGX_ERROR || n == 0) {
|
|
499 ngx_imap_close_connection(s->connection);
|
|
500 return NGX_ERROR;
|
|
501 }
|
|
502
|
|
503 if (n > 0) {
|
|
504 s->buffer->last += n;
|
|
505 }
|
|
506
|
|
507 if (n == NGX_AGAIN) {
|
|
508 if (ngx_handle_read_event(s->connection->read, 0) == NGX_ERROR) {
|
|
509 ngx_imap_session_internal_server_error(s);
|
|
510 return NGX_ERROR;
|
|
511 }
|
|
512
|
|
513 return NGX_AGAIN;
|
|
514 }
|
|
515
|
|
516 if (s->protocol == NGX_IMAP_POP3_PROTOCOL) {
|
|
517 rc = ngx_pop3_parse_command(s);
|
|
518 } else {
|
|
519 rc = ngx_imap_parse_command(s);
|
|
520 }
|
|
521
|
|
522 if (rc == NGX_AGAIN
|
|
523 || rc == NGX_IMAP_NEXT
|
|
524 || rc == NGX_IMAP_PARSE_INVALID_COMMAND)
|
|
525 {
|
|
526 return rc;
|
|
527 }
|
|
528
|
|
529 if (rc == NGX_ERROR) {
|
|
530 ngx_imap_close_connection(s->connection);
|
|
531 return NGX_ERROR;
|
|
532 }
|
|
533
|
|
534 return NGX_OK;
|
|
535 }
|
|
536
|
|
537
|
|
538 void
|
|
539 ngx_imap_session_internal_server_error(ngx_imap_session_t *s)
|
|
540 {
|
|
541 (void) ngx_send(s->connection, internal_server_errors[s->protocol].data,
|
|
542 internal_server_errors[s->protocol].len);
|
|
543
|
|
544 ngx_imap_close_connection(s->connection);
|
|
545 }
|
|
546
|
|
547
|
|
548 void
|
|
549 ngx_imap_close_connection(ngx_connection_t *c)
|
|
550 {
|
|
551 ngx_pool_t *pool;
|
|
552
|
|
553 ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0,
|
|
554 "close imap connection: %d", c->fd);
|
|
555
|
|
556 pool = c->pool;
|
|
557
|
|
558 ngx_close_connection(c);
|
|
559
|
|
560 ngx_destroy_pool(pool);
|
|
561 }
|