Annotation of embedaddon/nginx/src/mail/ngx_mail_parse.c, revision 1.1.1.1
1.1 misho 1:
2: /*
3: * Copyright (C) Igor Sysoev
4: * Copyright (C) Nginx, Inc.
5: */
6:
7:
8: #include <ngx_config.h>
9: #include <ngx_core.h>
10: #include <ngx_event.h>
11: #include <ngx_mail.h>
12: #include <ngx_mail_pop3_module.h>
13: #include <ngx_mail_imap_module.h>
14: #include <ngx_mail_smtp_module.h>
15:
16:
17: ngx_int_t
18: ngx_mail_pop3_parse_command(ngx_mail_session_t *s)
19: {
20: u_char ch, *p, *c, c0, c1, c2, c3;
21: ngx_str_t *arg;
22: enum {
23: sw_start = 0,
24: sw_spaces_before_argument,
25: sw_argument,
26: sw_almost_done
27: } state;
28:
29: state = s->state;
30:
31: for (p = s->buffer->pos; p < s->buffer->last; p++) {
32: ch = *p;
33:
34: switch (state) {
35:
36: /* POP3 command */
37: case sw_start:
38: if (ch == ' ' || ch == CR || ch == LF) {
39: c = s->buffer->start;
40:
41: if (p - c == 4) {
42:
43: c0 = ngx_toupper(c[0]);
44: c1 = ngx_toupper(c[1]);
45: c2 = ngx_toupper(c[2]);
46: c3 = ngx_toupper(c[3]);
47:
48: if (c0 == 'U' && c1 == 'S' && c2 == 'E' && c3 == 'R')
49: {
50: s->command = NGX_POP3_USER;
51:
52: } else if (c0 == 'P' && c1 == 'A' && c2 == 'S' && c3 == 'S')
53: {
54: s->command = NGX_POP3_PASS;
55:
56: } else if (c0 == 'A' && c1 == 'P' && c2 == 'O' && c3 == 'P')
57: {
58: s->command = NGX_POP3_APOP;
59:
60: } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T')
61: {
62: s->command = NGX_POP3_QUIT;
63:
64: } else if (c0 == 'C' && c1 == 'A' && c2 == 'P' && c3 == 'A')
65: {
66: s->command = NGX_POP3_CAPA;
67:
68: } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H')
69: {
70: s->command = NGX_POP3_AUTH;
71:
72: } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')
73: {
74: s->command = NGX_POP3_NOOP;
75: #if (NGX_MAIL_SSL)
76: } else if (c0 == 'S' && c1 == 'T' && c2 == 'L' && c3 == 'S')
77: {
78: s->command = NGX_POP3_STLS;
79: #endif
80: } else {
81: goto invalid;
82: }
83:
84: } else {
85: goto invalid;
86: }
87:
88: switch (ch) {
89: case ' ':
90: state = sw_spaces_before_argument;
91: break;
92: case CR:
93: state = sw_almost_done;
94: break;
95: case LF:
96: goto done;
97: }
98: break;
99: }
100:
101: if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
102: goto invalid;
103: }
104:
105: break;
106:
107: case sw_spaces_before_argument:
108: switch (ch) {
109: case ' ':
110: break;
111: case CR:
112: state = sw_almost_done;
113: s->arg_end = p;
114: break;
115: case LF:
116: s->arg_end = p;
117: goto done;
118: default:
119: if (s->args.nelts <= 2) {
120: state = sw_argument;
121: s->arg_start = p;
122: break;
123: }
124: goto invalid;
125: }
126: break;
127:
128: case sw_argument:
129: switch (ch) {
130:
131: case ' ':
132:
133: /*
134: * the space should be considered as part of the at username
135: * or password, but not of argument in other commands
136: */
137:
138: if (s->command == NGX_POP3_USER
139: || s->command == NGX_POP3_PASS)
140: {
141: break;
142: }
143:
144: /* fall through */
145:
146: case CR:
147: case LF:
148: arg = ngx_array_push(&s->args);
149: if (arg == NULL) {
150: return NGX_ERROR;
151: }
152: arg->len = p - s->arg_start;
153: arg->data = s->arg_start;
154: s->arg_start = NULL;
155:
156: switch (ch) {
157: case ' ':
158: state = sw_spaces_before_argument;
159: break;
160: case CR:
161: state = sw_almost_done;
162: break;
163: case LF:
164: goto done;
165: }
166: break;
167:
168: default:
169: break;
170: }
171: break;
172:
173: case sw_almost_done:
174: switch (ch) {
175: case LF:
176: goto done;
177: default:
178: goto invalid;
179: }
180: }
181: }
182:
183: s->buffer->pos = p;
184: s->state = state;
185:
186: return NGX_AGAIN;
187:
188: done:
189:
190: s->buffer->pos = p + 1;
191:
192: if (s->arg_start) {
193: arg = ngx_array_push(&s->args);
194: if (arg == NULL) {
195: return NGX_ERROR;
196: }
197: arg->len = s->arg_end - s->arg_start;
198: arg->data = s->arg_start;
199: s->arg_start = NULL;
200: }
201:
202: s->state = (s->command != NGX_POP3_AUTH) ? sw_start : sw_argument;
203:
204: return NGX_OK;
205:
206: invalid:
207:
208: s->state = sw_start;
209: s->arg_start = NULL;
210:
211: return NGX_MAIL_PARSE_INVALID_COMMAND;
212: }
213:
214:
215: ngx_int_t
216: ngx_mail_imap_parse_command(ngx_mail_session_t *s)
217: {
218: u_char ch, *p, *c;
219: ngx_str_t *arg;
220: enum {
221: sw_start = 0,
222: sw_spaces_before_command,
223: sw_command,
224: sw_spaces_before_argument,
225: sw_argument,
226: sw_backslash,
227: sw_literal,
228: sw_no_sync_literal_argument,
229: sw_start_literal_argument,
230: sw_literal_argument,
231: sw_end_literal_argument,
232: sw_almost_done
233: } state;
234:
235: state = s->state;
236:
237: for (p = s->buffer->pos; p < s->buffer->last; p++) {
238: ch = *p;
239:
240: switch (state) {
241:
242: /* IMAP tag */
243: case sw_start:
244: switch (ch) {
245: case ' ':
246: s->tag.len = p - s->buffer->start + 1;
247: s->tag.data = s->buffer->start;
248: state = sw_spaces_before_command;
249: break;
250: case CR:
251: s->state = sw_start;
252: return NGX_MAIL_PARSE_INVALID_COMMAND;
253: case LF:
254: s->state = sw_start;
255: return NGX_MAIL_PARSE_INVALID_COMMAND;
256: }
257: break;
258:
259: case sw_spaces_before_command:
260: switch (ch) {
261: case ' ':
262: break;
263: case CR:
264: s->state = sw_start;
265: return NGX_MAIL_PARSE_INVALID_COMMAND;
266: case LF:
267: s->state = sw_start;
268: return NGX_MAIL_PARSE_INVALID_COMMAND;
269: default:
270: s->cmd_start = p;
271: state = sw_command;
272: break;
273: }
274: break;
275:
276: case sw_command:
277: if (ch == ' ' || ch == CR || ch == LF) {
278:
279: c = s->cmd_start;
280:
281: switch (p - c) {
282:
283: case 4:
284: if ((c[0] == 'N' || c[0] == 'n')
285: && (c[1] == 'O'|| c[1] == 'o')
286: && (c[2] == 'O'|| c[2] == 'o')
287: && (c[3] == 'P'|| c[3] == 'p'))
288: {
289: s->command = NGX_IMAP_NOOP;
290:
291: } else {
292: goto invalid;
293: }
294: break;
295:
296: case 5:
297: if ((c[0] == 'L'|| c[0] == 'l')
298: && (c[1] == 'O'|| c[1] == 'o')
299: && (c[2] == 'G'|| c[2] == 'g')
300: && (c[3] == 'I'|| c[3] == 'i')
301: && (c[4] == 'N'|| c[4] == 'n'))
302: {
303: s->command = NGX_IMAP_LOGIN;
304:
305: } else {
306: goto invalid;
307: }
308: break;
309:
310: case 6:
311: if ((c[0] == 'L'|| c[0] == 'l')
312: && (c[1] == 'O'|| c[1] == 'o')
313: && (c[2] == 'G'|| c[2] == 'g')
314: && (c[3] == 'O'|| c[3] == 'o')
315: && (c[4] == 'U'|| c[4] == 'u')
316: && (c[5] == 'T'|| c[5] == 't'))
317: {
318: s->command = NGX_IMAP_LOGOUT;
319:
320: } else {
321: goto invalid;
322: }
323: break;
324:
325: #if (NGX_MAIL_SSL)
326: case 8:
327: if ((c[0] == 'S'|| c[0] == 's')
328: && (c[1] == 'T'|| c[1] == 't')
329: && (c[2] == 'A'|| c[2] == 'a')
330: && (c[3] == 'R'|| c[3] == 'r')
331: && (c[4] == 'T'|| c[4] == 't')
332: && (c[5] == 'T'|| c[5] == 't')
333: && (c[6] == 'L'|| c[6] == 'l')
334: && (c[7] == 'S'|| c[7] == 's'))
335: {
336: s->command = NGX_IMAP_STARTTLS;
337:
338: } else {
339: goto invalid;
340: }
341: break;
342: #endif
343:
344: case 10:
345: if ((c[0] == 'C'|| c[0] == 'c')
346: && (c[1] == 'A'|| c[1] == 'a')
347: && (c[2] == 'P'|| c[2] == 'p')
348: && (c[3] == 'A'|| c[3] == 'a')
349: && (c[4] == 'B'|| c[4] == 'b')
350: && (c[5] == 'I'|| c[5] == 'i')
351: && (c[6] == 'L'|| c[6] == 'l')
352: && (c[7] == 'I'|| c[7] == 'i')
353: && (c[8] == 'T'|| c[8] == 't')
354: && (c[9] == 'Y'|| c[9] == 'y'))
355: {
356: s->command = NGX_IMAP_CAPABILITY;
357:
358: } else {
359: goto invalid;
360: }
361: break;
362:
363: case 12:
364: if ((c[0] == 'A'|| c[0] == 'a')
365: && (c[1] == 'U'|| c[1] == 'u')
366: && (c[2] == 'T'|| c[2] == 't')
367: && (c[3] == 'H'|| c[3] == 'h')
368: && (c[4] == 'E'|| c[4] == 'e')
369: && (c[5] == 'N'|| c[5] == 'n')
370: && (c[6] == 'T'|| c[6] == 't')
371: && (c[7] == 'I'|| c[7] == 'i')
372: && (c[8] == 'C'|| c[8] == 'c')
373: && (c[9] == 'A'|| c[9] == 'a')
374: && (c[10] == 'T'|| c[10] == 't')
375: && (c[11] == 'E'|| c[11] == 'e'))
376: {
377: s->command = NGX_IMAP_AUTHENTICATE;
378:
379: } else {
380: goto invalid;
381: }
382: break;
383:
384: default:
385: goto invalid;
386: }
387:
388: switch (ch) {
389: case ' ':
390: state = sw_spaces_before_argument;
391: break;
392: case CR:
393: state = sw_almost_done;
394: break;
395: case LF:
396: goto done;
397: }
398: break;
399: }
400:
401: if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
402: goto invalid;
403: }
404:
405: break;
406:
407: case sw_spaces_before_argument:
408: switch (ch) {
409: case ' ':
410: break;
411: case CR:
412: state = sw_almost_done;
413: s->arg_end = p;
414: break;
415: case LF:
416: s->arg_end = p;
417: goto done;
418: case '"':
419: if (s->args.nelts <= 2) {
420: s->quoted = 1;
421: s->arg_start = p + 1;
422: state = sw_argument;
423: break;
424: }
425: goto invalid;
426: case '{':
427: if (s->args.nelts <= 2) {
428: state = sw_literal;
429: break;
430: }
431: goto invalid;
432: default:
433: if (s->args.nelts <= 2) {
434: s->arg_start = p;
435: state = sw_argument;
436: break;
437: }
438: goto invalid;
439: }
440: break;
441:
442: case sw_argument:
443: if (ch == ' ' && s->quoted) {
444: break;
445: }
446:
447: switch (ch) {
448: case '"':
449: if (!s->quoted) {
450: break;
451: }
452: s->quoted = 0;
453: /* fall through */
454: case ' ':
455: case CR:
456: case LF:
457: arg = ngx_array_push(&s->args);
458: if (arg == NULL) {
459: return NGX_ERROR;
460: }
461: arg->len = p - s->arg_start;
462: arg->data = s->arg_start;
463: s->arg_start = NULL;
464:
465: switch (ch) {
466: case '"':
467: case ' ':
468: state = sw_spaces_before_argument;
469: break;
470: case CR:
471: state = sw_almost_done;
472: break;
473: case LF:
474: goto done;
475: }
476: break;
477: case '\\':
478: if (s->quoted) {
479: s->backslash = 1;
480: state = sw_backslash;
481: }
482: break;
483: }
484: break;
485:
486: case sw_backslash:
487: switch (ch) {
488: case CR:
489: case LF:
490: goto invalid;
491: default:
492: state = sw_argument;
493: }
494: break;
495:
496: case sw_literal:
497: if (ch >= '0' && ch <= '9') {
498: s->literal_len = s->literal_len * 10 + (ch - '0');
499: break;
500: }
501: if (ch == '}') {
502: state = sw_start_literal_argument;
503: break;
504: }
505: if (ch == '+') {
506: state = sw_no_sync_literal_argument;
507: break;
508: }
509: goto invalid;
510:
511: case sw_no_sync_literal_argument:
512: if (ch == '}') {
513: s->no_sync_literal = 1;
514: state = sw_start_literal_argument;
515: break;
516: }
517: goto invalid;
518:
519: case sw_start_literal_argument:
520: switch (ch) {
521: case CR:
522: break;
523: case LF:
524: s->buffer->pos = p + 1;
525: s->arg_start = p + 1;
526: if (s->no_sync_literal == 0) {
527: s->state = sw_literal_argument;
528: return NGX_IMAP_NEXT;
529: }
530: state = sw_literal_argument;
531: s->no_sync_literal = 0;
532: break;
533: default:
534: goto invalid;
535: }
536: break;
537:
538: case sw_literal_argument:
539: if (s->literal_len && --s->literal_len) {
540: break;
541: }
542:
543: arg = ngx_array_push(&s->args);
544: if (arg == NULL) {
545: return NGX_ERROR;
546: }
547: arg->len = p + 1 - s->arg_start;
548: arg->data = s->arg_start;
549: s->arg_start = NULL;
550: state = sw_end_literal_argument;
551:
552: break;
553:
554: case sw_end_literal_argument:
555: switch (ch) {
556: case '{':
557: if (s->args.nelts <= 2) {
558: state = sw_literal;
559: break;
560: }
561: goto invalid;
562: case CR:
563: state = sw_almost_done;
564: break;
565: case LF:
566: goto done;
567: default:
568: state = sw_spaces_before_argument;
569: break;
570: }
571: break;
572:
573: case sw_almost_done:
574: switch (ch) {
575: case LF:
576: goto done;
577: default:
578: goto invalid;
579: }
580: }
581: }
582:
583: s->buffer->pos = p;
584: s->state = state;
585:
586: return NGX_AGAIN;
587:
588: done:
589:
590: s->buffer->pos = p + 1;
591:
592: if (s->arg_start) {
593: arg = ngx_array_push(&s->args);
594: if (arg == NULL) {
595: return NGX_ERROR;
596: }
597: arg->len = s->arg_end - s->arg_start;
598: arg->data = s->arg_start;
599:
600: s->arg_start = NULL;
601: s->cmd_start = NULL;
602: s->quoted = 0;
603: s->no_sync_literal = 0;
604: s->literal_len = 0;
605: }
606:
607: s->state = (s->command != NGX_IMAP_AUTHENTICATE) ? sw_start : sw_argument;
608:
609: return NGX_OK;
610:
611: invalid:
612:
613: s->state = sw_start;
614: s->quoted = 0;
615: s->no_sync_literal = 0;
616: s->literal_len = 0;
617:
618: return NGX_MAIL_PARSE_INVALID_COMMAND;
619: }
620:
621:
622: ngx_int_t
623: ngx_mail_smtp_parse_command(ngx_mail_session_t *s)
624: {
625: u_char ch, *p, *c, c0, c1, c2, c3;
626: ngx_str_t *arg;
627: enum {
628: sw_start = 0,
629: sw_spaces_before_argument,
630: sw_argument,
631: sw_almost_done
632: } state;
633:
634: state = s->state;
635:
636: for (p = s->buffer->pos; p < s->buffer->last; p++) {
637: ch = *p;
638:
639: switch (state) {
640:
641: /* SMTP command */
642: case sw_start:
643: if (ch == ' ' || ch == CR || ch == LF) {
644: c = s->buffer->start;
645:
646: if (p - c == 4) {
647:
648: c0 = ngx_toupper(c[0]);
649: c1 = ngx_toupper(c[1]);
650: c2 = ngx_toupper(c[2]);
651: c3 = ngx_toupper(c[3]);
652:
653: if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'O')
654: {
655: s->command = NGX_SMTP_HELO;
656:
657: } else if (c0 == 'E' && c1 == 'H' && c2 == 'L' && c3 == 'O')
658: {
659: s->command = NGX_SMTP_EHLO;
660:
661: } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T')
662: {
663: s->command = NGX_SMTP_QUIT;
664:
665: } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H')
666: {
667: s->command = NGX_SMTP_AUTH;
668:
669: } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')
670: {
671: s->command = NGX_SMTP_NOOP;
672:
673: } else if (c0 == 'M' && c1 == 'A' && c2 == 'I' && c3 == 'L')
674: {
675: s->command = NGX_SMTP_MAIL;
676:
677: } else if (c0 == 'R' && c1 == 'S' && c2 == 'E' && c3 == 'T')
678: {
679: s->command = NGX_SMTP_RSET;
680:
681: } else if (c0 == 'R' && c1 == 'C' && c2 == 'P' && c3 == 'T')
682: {
683: s->command = NGX_SMTP_RCPT;
684:
685: } else if (c0 == 'V' && c1 == 'R' && c2 == 'F' && c3 == 'Y')
686: {
687: s->command = NGX_SMTP_VRFY;
688:
689: } else if (c0 == 'E' && c1 == 'X' && c2 == 'P' && c3 == 'N')
690: {
691: s->command = NGX_SMTP_EXPN;
692:
693: } else if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'P')
694: {
695: s->command = NGX_SMTP_HELP;
696:
697: } else {
698: goto invalid;
699: }
700: #if (NGX_MAIL_SSL)
701: } else if (p - c == 8) {
702:
703: if ((c[0] == 'S'|| c[0] == 's')
704: && (c[1] == 'T'|| c[1] == 't')
705: && (c[2] == 'A'|| c[2] == 'a')
706: && (c[3] == 'R'|| c[3] == 'r')
707: && (c[4] == 'T'|| c[4] == 't')
708: && (c[5] == 'T'|| c[5] == 't')
709: && (c[6] == 'L'|| c[6] == 'l')
710: && (c[7] == 'S'|| c[7] == 's'))
711: {
712: s->command = NGX_SMTP_STARTTLS;
713:
714: } else {
715: goto invalid;
716: }
717: #endif
718: } else {
719: goto invalid;
720: }
721:
722: switch (ch) {
723: case ' ':
724: state = sw_spaces_before_argument;
725: break;
726: case CR:
727: state = sw_almost_done;
728: break;
729: case LF:
730: goto done;
731: }
732: break;
733: }
734:
735: if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
736: goto invalid;
737: }
738:
739: break;
740:
741: case sw_spaces_before_argument:
742: switch (ch) {
743: case ' ':
744: break;
745: case CR:
746: state = sw_almost_done;
747: s->arg_end = p;
748: break;
749: case LF:
750: s->arg_end = p;
751: goto done;
752: default:
753: if (s->args.nelts <= 10) {
754: state = sw_argument;
755: s->arg_start = p;
756: break;
757: }
758: goto invalid;
759: }
760: break;
761:
762: case sw_argument:
763: switch (ch) {
764: case ' ':
765: case CR:
766: case LF:
767: arg = ngx_array_push(&s->args);
768: if (arg == NULL) {
769: return NGX_ERROR;
770: }
771: arg->len = p - s->arg_start;
772: arg->data = s->arg_start;
773: s->arg_start = NULL;
774:
775: switch (ch) {
776: case ' ':
777: state = sw_spaces_before_argument;
778: break;
779: case CR:
780: state = sw_almost_done;
781: break;
782: case LF:
783: goto done;
784: }
785: break;
786:
787: default:
788: break;
789: }
790: break;
791:
792: case sw_almost_done:
793: switch (ch) {
794: case LF:
795: goto done;
796: default:
797: goto invalid;
798: }
799: }
800: }
801:
802: s->buffer->pos = p;
803: s->state = state;
804:
805: return NGX_AGAIN;
806:
807: done:
808:
809: s->buffer->pos = p + 1;
810:
811: if (s->arg_start) {
812: arg = ngx_array_push(&s->args);
813: if (arg == NULL) {
814: return NGX_ERROR;
815: }
816: arg->len = s->arg_end - s->arg_start;
817: arg->data = s->arg_start;
818: s->arg_start = NULL;
819: }
820:
821: s->state = (s->command != NGX_SMTP_AUTH) ? sw_start : sw_argument;
822:
823: return NGX_OK;
824:
825: invalid:
826:
827: s->state = sw_start;
828: s->arg_start = NULL;
829:
830: return NGX_MAIL_PARSE_INVALID_COMMAND;
831: }
832:
833:
834: ngx_int_t
835: ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c)
836: {
837: ngx_str_t *arg;
838:
839: #if (NGX_MAIL_SSL)
840: if (ngx_mail_starttls_only(s, c)) {
841: return NGX_MAIL_PARSE_INVALID_COMMAND;
842: }
843: #endif
844:
845: arg = s->args.elts;
846:
847: if (arg[0].len == 5) {
848:
849: if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5) == 0) {
850:
851: if (s->args.nelts == 1) {
852: return NGX_MAIL_AUTH_LOGIN;
853: }
854:
855: if (s->args.nelts == 2) {
856: return NGX_MAIL_AUTH_LOGIN_USERNAME;
857: }
858:
859: return NGX_MAIL_PARSE_INVALID_COMMAND;
860: }
861:
862: if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN", 5) == 0) {
863:
864: if (s->args.nelts == 1) {
865: return NGX_MAIL_AUTH_PLAIN;
866: }
867:
868: if (s->args.nelts == 2) {
869: return ngx_mail_auth_plain(s, c, 1);
870: }
871: }
872:
873: return NGX_MAIL_PARSE_INVALID_COMMAND;
874: }
875:
876: if (arg[0].len == 8) {
877:
878: if (s->args.nelts != 1) {
879: return NGX_MAIL_PARSE_INVALID_COMMAND;
880: }
881:
882: if (ngx_strncasecmp(arg[0].data, (u_char *) "CRAM-MD5", 8) == 0) {
883: return NGX_MAIL_AUTH_CRAM_MD5;
884: }
885: }
886:
887: return NGX_MAIL_PARSE_INVALID_COMMAND;
888: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>