comparison src/mail/ngx_mail_parse.c @ 290:f745bf973510 NGINX_0_5_15

nginx 0.5.15 *) Feature: the mail proxy supports authenticated SMTP proxying and the "smtp_auth", "smtp_capablities", and "xclient" directives. Thanks to Anton Yuzhaninov and Maxim Dounin. *) Feature: now the keep-alive connections are closed just after receiving the reconfiguration signal. *) Change: the "imap" and "auth" directives were renamed to the "mail" and "pop3_auth" directives. *) Bugfix: a segmentation fault occurred in worker process if the CRAM-MD5 authentication method was used and the APOP method was disabled. *) Bugfix: if the "starttls only" directive was used in POP3 protocol, then nginx allowed authentication without switching to the SSL mode. *) Bugfix: worker processes did not exit after reconfiguration and did not rotate logs if the eventport method was used. *) Bugfix: a worker process may got caught in an endless loop, if the "ip_hash" directive was used. *) Bugfix: now nginx does not log some alerts if eventport or /dev/poll methods are used.
author Igor Sysoev <http://sysoev.ru>
date Mon, 19 Mar 2007 00:00:00 +0300
parents
children d16d691432c9
comparison
equal deleted inserted replaced
289:a9323c9433a7 290:f745bf973510
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_mail.h>
11
12
13 ngx_int_t ngx_pop3_parse_command(ngx_mail_session_t *s)
14 {
15 u_char ch, *p, *c, c0, c1, c2, c3;
16 ngx_str_t *arg;
17 enum {
18 sw_start = 0,
19 sw_spaces_before_argument,
20 sw_argument,
21 sw_almost_done
22 } state;
23
24 state = s->state;
25
26 for (p = s->buffer->pos; p < s->buffer->last; p++) {
27 ch = *p;
28
29 switch (state) {
30
31 /* POP3 command */
32 case sw_start:
33 if (ch == ' ' || ch == CR || ch == LF) {
34 c = s->buffer->start;
35
36 if (p - c == 4) {
37
38 c0 = ngx_toupper(c[0]);
39 c1 = ngx_toupper(c[1]);
40 c2 = ngx_toupper(c[2]);
41 c3 = ngx_toupper(c[3]);
42
43 if (c0 == 'U' && c1 == 'S' && c2 == 'E' && c3 == 'R')
44 {
45 s->command = NGX_POP3_USER;
46
47 } else if (c0 == 'P' && c1 == 'A' && c2 == 'S' && c3 == 'S')
48 {
49 s->command = NGX_POP3_PASS;
50
51 } else if (c0 == 'A' && c1 == 'P' && c2 == 'O' && c3 == 'P')
52 {
53 s->command = NGX_POP3_APOP;
54
55 } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T')
56 {
57 s->command = NGX_POP3_QUIT;
58
59 } else if (c0 == 'C' && c1 == 'A' && c2 == 'P' && c3 == 'A')
60 {
61 s->command = NGX_POP3_CAPA;
62
63 } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H')
64 {
65 s->command = NGX_POP3_AUTH;
66
67 } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')
68 {
69 s->command = NGX_POP3_NOOP;
70 #if (NGX_MAIL_SSL)
71 } else if (c0 == 'S' && c1 == 'T' && c2 == 'L' && c3 == 'S')
72 {
73 s->command = NGX_POP3_STLS;
74 #endif
75 } else {
76 goto invalid;
77 }
78
79 } else {
80 goto invalid;
81 }
82
83 switch (ch) {
84 case ' ':
85 state = sw_spaces_before_argument;
86 break;
87 case CR:
88 state = sw_almost_done;
89 break;
90 case LF:
91 goto done;
92 }
93 break;
94 }
95
96 if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
97 goto invalid;
98 }
99
100 break;
101
102 case sw_spaces_before_argument:
103 switch (ch) {
104 case ' ':
105 break;
106 case CR:
107 state = sw_almost_done;
108 s->arg_end = p;
109 break;
110 case LF:
111 s->arg_end = p;
112 goto done;
113 default:
114 if (s->args.nelts <= 2) {
115 state = sw_argument;
116 s->arg_start = p;
117 break;
118 }
119 goto invalid;
120 }
121 break;
122
123 case sw_argument:
124 switch (ch) {
125
126 case ' ':
127
128 /*
129 * the space should be considered as part of the at username
130 * or password, but not of argument in other commands
131 */
132
133 if (s->command == NGX_POP3_USER
134 || s->command == NGX_POP3_PASS)
135 {
136 break;
137 }
138
139 /* fall through */
140
141 case CR:
142 case LF:
143 arg = ngx_array_push(&s->args);
144 if (arg == NULL) {
145 return NGX_ERROR;
146 }
147 arg->len = p - s->arg_start;
148 arg->data = s->arg_start;
149 s->arg_start = NULL;
150
151 switch (ch) {
152 case ' ':
153 state = sw_spaces_before_argument;
154 break;
155 case CR:
156 state = sw_almost_done;
157 break;
158 case LF:
159 goto done;
160 }
161 break;
162
163 default:
164 break;
165 }
166 break;
167
168 case sw_almost_done:
169 switch (ch) {
170 case LF:
171 goto done;
172 default:
173 goto invalid;
174 }
175 }
176 }
177
178 s->buffer->pos = p;
179 s->state = state;
180
181 return NGX_AGAIN;
182
183 done:
184
185 s->buffer->pos = p + 1;
186
187 if (s->arg_start) {
188 arg = ngx_array_push(&s->args);
189 if (arg == NULL) {
190 return NGX_ERROR;
191 }
192 arg->len = s->arg_end - s->arg_start;
193 arg->data = s->arg_start;
194 s->arg_start = NULL;
195 }
196
197 s->state = (s->command != NGX_POP3_AUTH) ? sw_start : sw_argument;
198
199 return NGX_OK;
200
201 invalid:
202
203 s->state = sw_start;
204 s->arg_start = NULL;
205
206 return NGX_MAIL_PARSE_INVALID_COMMAND;
207 }
208
209
210 ngx_int_t ngx_imap_parse_command(ngx_mail_session_t *s)
211 {
212 u_char ch, *p, *c;
213 ngx_str_t *arg;
214 enum {
215 sw_start = 0,
216 sw_spaces_before_command,
217 sw_command,
218 sw_spaces_before_argument,
219 sw_argument,
220 sw_backslash,
221 sw_literal,
222 sw_no_sync_literal_argument,
223 sw_start_literal_argument,
224 sw_literal_argument,
225 sw_end_literal_argument,
226 sw_almost_done
227 } state;
228
229 state = s->state;
230
231 for (p = s->buffer->pos; p < s->buffer->last; p++) {
232 ch = *p;
233
234 switch (state) {
235
236 /* IMAP tag */
237 case sw_start:
238 switch (ch) {
239 case ' ':
240 s->tag.len = p - s->buffer->start + 1;
241 s->tag.data = s->buffer->start;
242 state = sw_spaces_before_command;
243 break;
244 case CR:
245 s->state = sw_start;
246 return NGX_MAIL_PARSE_INVALID_COMMAND;
247 case LF:
248 s->state = sw_start;
249 return NGX_MAIL_PARSE_INVALID_COMMAND;
250 }
251 break;
252
253 case sw_spaces_before_command:
254 switch (ch) {
255 case ' ':
256 break;
257 case CR:
258 s->state = sw_start;
259 return NGX_MAIL_PARSE_INVALID_COMMAND;
260 case LF:
261 s->state = sw_start;
262 return NGX_MAIL_PARSE_INVALID_COMMAND;
263 default:
264 s->cmd_start = p;
265 state = sw_command;
266 break;
267 }
268 break;
269
270 case sw_command:
271 if (ch == ' ' || ch == CR || ch == LF) {
272
273 c = s->cmd_start;
274
275 switch (p - c) {
276
277 case 4:
278 if ((c[0] == 'N' || c[0] == 'n')
279 && (c[1] == 'O'|| c[1] == 'o')
280 && (c[2] == 'O'|| c[2] == 'o')
281 && (c[3] == 'P'|| c[3] == 'p'))
282 {
283 s->command = NGX_IMAP_NOOP;
284
285 } else {
286 goto invalid;
287 }
288 break;
289
290 case 5:
291 if ((c[0] == 'L'|| c[0] == 'l')
292 && (c[1] == 'O'|| c[1] == 'o')
293 && (c[2] == 'G'|| c[2] == 'g')
294 && (c[3] == 'I'|| c[3] == 'i')
295 && (c[4] == 'N'|| c[4] == 'n'))
296 {
297 s->command = NGX_IMAP_LOGIN;
298
299 } else {
300 goto invalid;
301 }
302 break;
303
304 case 6:
305 if ((c[0] == 'L'|| c[0] == 'l')
306 && (c[1] == 'O'|| c[1] == 'o')
307 && (c[2] == 'G'|| c[2] == 'g')
308 && (c[3] == 'O'|| c[3] == 'o')
309 && (c[4] == 'U'|| c[4] == 'u')
310 && (c[5] == 'T'|| c[5] == 't'))
311 {
312 s->command = NGX_IMAP_LOGOUT;
313
314 } else {
315 goto invalid;
316 }
317 break;
318
319 #if (NGX_MAIL_SSL)
320 case 8:
321 if ((c[0] == 'S'|| c[0] == 's')
322 && (c[1] == 'T'|| c[1] == 't')
323 && (c[2] == 'A'|| c[2] == 'a')
324 && (c[3] == 'R'|| c[3] == 'r')
325 && (c[4] == 'T'|| c[4] == 't')
326 && (c[5] == 'T'|| c[5] == 't')
327 && (c[6] == 'L'|| c[6] == 'l')
328 && (c[7] == 'S'|| c[7] == 's'))
329 {
330 s->command = NGX_IMAP_STARTTLS;
331
332 } else {
333 goto invalid;
334 }
335 break;
336 #endif
337
338 case 10:
339 if ((c[0] == 'C'|| c[0] == 'c')
340 && (c[1] == 'A'|| c[1] == 'a')
341 && (c[2] == 'P'|| c[2] == 'p')
342 && (c[3] == 'A'|| c[3] == 'a')
343 && (c[4] == 'B'|| c[4] == 'b')
344 && (c[5] == 'I'|| c[5] == 'i')
345 && (c[6] == 'L'|| c[6] == 'l')
346 && (c[7] == 'I'|| c[7] == 'i')
347 && (c[8] == 'T'|| c[8] == 't')
348 && (c[9] == 'Y'|| c[9] == 'y'))
349 {
350 s->command = NGX_IMAP_CAPABILITY;
351
352 } else {
353 goto invalid;
354 }
355 break;
356
357 default:
358 goto invalid;
359 }
360
361 switch (ch) {
362 case ' ':
363 state = sw_spaces_before_argument;
364 break;
365 case CR:
366 state = sw_almost_done;
367 break;
368 case LF:
369 goto done;
370 }
371 break;
372 }
373
374 if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
375 goto invalid;
376 }
377
378 break;
379
380 case sw_spaces_before_argument:
381 switch (ch) {
382 case ' ':
383 break;
384 case CR:
385 state = sw_almost_done;
386 s->arg_end = p;
387 break;
388 case LF:
389 s->arg_end = p;
390 goto done;
391 case '"':
392 if (s->args.nelts <= 2) {
393 s->quoted = 1;
394 s->arg_start = p + 1;
395 state = sw_argument;
396 break;
397 }
398 goto invalid;
399 case '{':
400 if (s->args.nelts <= 2) {
401 state = sw_literal;
402 break;
403 }
404 goto invalid;
405 default:
406 if (s->args.nelts <= 2) {
407 s->arg_start = p;
408 state = sw_argument;
409 break;
410 }
411 goto invalid;
412 }
413 break;
414
415 case sw_argument:
416 switch (ch) {
417 case '"':
418 if (!s->quoted) {
419 break;
420 }
421 s->quoted = 0;
422 /* fall through */
423 case ' ':
424 case CR:
425 case LF:
426 arg = ngx_array_push(&s->args);
427 if (arg == NULL) {
428 return NGX_ERROR;
429 }
430 arg->len = p - s->arg_start;
431 arg->data = s->arg_start;
432 s->arg_start = NULL;
433
434 switch (ch) {
435 case '"':
436 case ' ':
437 state = sw_spaces_before_argument;
438 break;
439 case CR:
440 state = sw_almost_done;
441 break;
442 case LF:
443 goto done;
444 }
445 break;
446 case '\\':
447 if (s->quoted) {
448 s->backslash = 1;
449 state = sw_backslash;
450 }
451 break;
452 }
453 break;
454
455 case sw_backslash:
456 switch (ch) {
457 case CR:
458 case LF:
459 goto invalid;
460 default:
461 state = sw_argument;
462 }
463 break;
464
465 case sw_literal:
466 if (ch >= '0' && ch <= '9') {
467 s->literal_len = s->literal_len * 10 + (ch - '0');
468 break;
469 }
470 if (ch == '}') {
471 state = sw_start_literal_argument;
472 break;
473 }
474 if (ch == '+') {
475 state = sw_no_sync_literal_argument;
476 break;
477 }
478 goto invalid;
479
480 case sw_no_sync_literal_argument:
481 if (ch == '}') {
482 s->no_sync_literal = 1;
483 state = sw_start_literal_argument;
484 break;
485 }
486 goto invalid;
487
488 case sw_start_literal_argument:
489 switch (ch) {
490 case CR:
491 break;
492 case LF:
493 s->buffer->pos = p + 1;
494 s->arg_start = p + 1;
495 if (s->no_sync_literal == 0) {
496 s->state = sw_literal_argument;
497 return NGX_IMAP_NEXT;
498 }
499 state = sw_literal_argument;
500 s->no_sync_literal = 0;
501 break;
502 default:
503 goto invalid;
504 }
505 break;
506
507 case sw_literal_argument:
508 if (s->literal_len && --s->literal_len) {
509 break;
510 }
511
512 arg = ngx_array_push(&s->args);
513 if (arg == NULL) {
514 return NGX_ERROR;
515 }
516 arg->len = p + 1 - s->arg_start;
517 arg->data = s->arg_start;
518 s->arg_start = NULL;
519 state = sw_end_literal_argument;
520
521 break;
522
523 case sw_end_literal_argument:
524 switch (ch) {
525 case '{':
526 if (s->args.nelts <= 2) {
527 state = sw_literal;
528 break;
529 }
530 goto invalid;
531 case CR:
532 state = sw_almost_done;
533 break;
534 case LF:
535 goto done;
536 default:
537 state = sw_spaces_before_argument;
538 break;
539 }
540 break;
541
542 case sw_almost_done:
543 switch (ch) {
544 case LF:
545 goto done;
546 default:
547 goto invalid;
548 }
549 }
550 }
551
552 s->buffer->pos = p;
553 s->state = state;
554
555 return NGX_AGAIN;
556
557 done:
558
559 s->buffer->pos = p + 1;
560
561 if (s->arg_start) {
562 arg = ngx_array_push(&s->args);
563 if (arg == NULL) {
564 return NGX_ERROR;
565 }
566 arg->len = s->arg_end - s->arg_start;
567 arg->data = s->arg_start;
568
569 s->arg_start = NULL;
570 s->cmd_start = NULL;
571 s->quoted = 0;
572 s->no_sync_literal = 0;
573 s->literal_len = 0;
574 }
575
576 s->state = sw_start;
577
578 return NGX_OK;
579
580 invalid:
581
582 s->state = sw_start;
583 s->quoted = 0;
584 s->no_sync_literal = 0;
585 s->literal_len = 0;
586
587 return NGX_MAIL_PARSE_INVALID_COMMAND;
588 }
589
590
591 ngx_int_t ngx_smtp_parse_command(ngx_mail_session_t *s)
592 {
593 u_char ch, *p, *c, c0, c1, c2, c3;
594 ngx_str_t *arg;
595 enum {
596 sw_start = 0,
597 sw_spaces_before_argument,
598 sw_argument,
599 sw_almost_done
600 } state;
601
602 state = s->state;
603
604 for (p = s->buffer->pos; p < s->buffer->last; p++) {
605 ch = *p;
606
607 switch (state) {
608
609 /* SMTP command */
610 case sw_start:
611 if (ch == ' ' || ch == CR || ch == LF) {
612 c = s->buffer->start;
613
614 if (p - c == 4) {
615
616 c0 = ngx_toupper(c[0]);
617 c1 = ngx_toupper(c[1]);
618 c2 = ngx_toupper(c[2]);
619 c3 = ngx_toupper(c[3]);
620
621 if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'O')
622 {
623 s->command = NGX_SMTP_HELO;
624
625 } else if (c0 == 'E' && c1 == 'H' && c2 == 'L' && c3 == 'O')
626 {
627 s->command = NGX_SMTP_EHLO;
628
629 } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T')
630 {
631 s->command = NGX_SMTP_QUIT;
632
633 } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H')
634 {
635 s->command = NGX_SMTP_AUTH;
636
637 } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')
638 {
639 s->command = NGX_SMTP_NOOP;
640
641 } else if (c0 == 'M' && c1 == 'A' && c2 == 'I' && c3 == 'L')
642 {
643 s->command = NGX_SMTP_MAIL;
644
645 } else if (c0 == 'R' && c1 == 'S' && c2 == 'E' && c3 == 'T')
646 {
647 s->command = NGX_SMTP_RSET;
648
649 } else {
650 goto invalid;
651 }
652
653 } else {
654 goto invalid;
655 }
656
657 switch (ch) {
658 case ' ':
659 state = sw_spaces_before_argument;
660 break;
661 case CR:
662 state = sw_almost_done;
663 break;
664 case LF:
665 goto done;
666 }
667 break;
668 }
669
670 if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
671 goto invalid;
672 }
673
674 break;
675
676 case sw_spaces_before_argument:
677 switch (ch) {
678 case ' ':
679 break;
680 case CR:
681 state = sw_almost_done;
682 s->arg_end = p;
683 break;
684 case LF:
685 s->arg_end = p;
686 goto done;
687 default:
688 if (s->args.nelts <= 2) {
689 state = sw_argument;
690 s->arg_start = p;
691 break;
692 }
693 goto invalid;
694 }
695 break;
696
697 case sw_argument:
698 switch (ch) {
699 case ' ':
700 case CR:
701 case LF:
702 arg = ngx_array_push(&s->args);
703 if (arg == NULL) {
704 return NGX_ERROR;
705 }
706 arg->len = p - s->arg_start;
707 arg->data = s->arg_start;
708 s->arg_start = NULL;
709
710 switch (ch) {
711 case ' ':
712 state = sw_spaces_before_argument;
713 break;
714 case CR:
715 state = sw_almost_done;
716 break;
717 case LF:
718 goto done;
719 }
720 break;
721
722 default:
723 break;
724 }
725 break;
726
727 case sw_almost_done:
728 switch (ch) {
729 case LF:
730 goto done;
731 default:
732 goto invalid;
733 }
734 }
735 }
736
737 s->buffer->pos = p;
738 s->state = state;
739
740 return NGX_AGAIN;
741
742 done:
743
744 s->buffer->pos = p + 1;
745
746 if (s->arg_start) {
747 arg = ngx_array_push(&s->args);
748 if (arg == NULL) {
749 return NGX_ERROR;
750 }
751 arg->len = s->arg_end - s->arg_start;
752 arg->data = s->arg_start;
753 s->arg_start = NULL;
754 }
755
756 s->state = (s->command != NGX_SMTP_AUTH) ? sw_start : sw_argument;
757
758 return NGX_OK;
759
760 invalid:
761
762 s->state = sw_start;
763 s->arg_start = NULL;
764
765 return NGX_MAIL_PARSE_INVALID_COMMAND;
766 }