ngx_http_parse.c revision e18a033b
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_http.h>
11
12
13static uint32_t  usual[] = {
14    0xffffdbfe, /* 1111 1111 1111 1111  1101 1011 1111 1110 */
15
16                /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
17    0x7fff37d6, /* 0111 1111 1111 1111  0011 0111 1101 0110 */
18
19                /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
20#if (NGX_WIN32)
21    0xefffffff, /* 1110 1111 1111 1111  1111 1111 1111 1111 */
22#else
23    0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
24#endif
25
26                /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
27    0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
28
29    0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
30    0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
31    0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
32    0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
33};
34
35
36#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
37
38#define ngx_str3_cmp(m, c0, c1, c2, c3)                                       \
39    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
40
41#define ngx_str3Ocmp(m, c0, c1, c2, c3)                                       \
42    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
43
44#define ngx_str4cmp(m, c0, c1, c2, c3)                                        \
45    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
46
47#define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \
48    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
49        && m[4] == c4
50
51#define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5)                                \
52    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
53        && (((uint32_t *) m)[1] & 0xffff) == ((c5 << 8) | c4)
54
55#define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                       \
56    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
57        && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)
58
59#define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                        \
60    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
61        && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)
62
63#define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8)                    \
64    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
65        && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)  \
66        && m[8] == c8
67
68#else /* !(NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) */
69
70#define ngx_str3_cmp(m, c0, c1, c2, c3)                                       \
71    m[0] == c0 && m[1] == c1 && m[2] == c2
72
73#define ngx_str3Ocmp(m, c0, c1, c2, c3)                                       \
74    m[0] == c0 && m[2] == c2 && m[3] == c3
75
76#define ngx_str4cmp(m, c0, c1, c2, c3)                                        \
77    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3
78
79#define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \
80    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4
81
82#define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5)                                \
83    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
84        && m[4] == c4 && m[5] == c5
85
86#define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                       \
87    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
88        && m[4] == c4 && m[5] == c5 && m[6] == c6
89
90#define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                        \
91    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
92        && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7
93
94#define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8)                    \
95    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
96        && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7 && m[8] == c8
97
98#endif
99
100
101/* gcc, icc, msvc and others compile these switches as an jump table */
102
103ngx_int_t
104ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
105{
106    u_char  c, ch, *p, *m;
107    enum {
108        sw_start = 0,
109        sw_method,
110        sw_spaces_before_uri,
111        sw_schema,
112        sw_schema_slash,
113        sw_schema_slash_slash,
114        sw_host_start,
115        sw_host,
116        sw_host_end,
117        sw_host_ip_literal,
118        sw_port,
119        sw_host_http_09,
120        sw_after_slash_in_uri,
121        sw_check_uri,
122        sw_check_uri_http_09,
123        sw_uri,
124        sw_http_09,
125        sw_http_H,
126        sw_http_HT,
127        sw_http_HTT,
128        sw_http_HTTP,
129        sw_first_major_digit,
130        sw_major_digit,
131        sw_first_minor_digit,
132        sw_minor_digit,
133        sw_spaces_after_digit,
134        sw_almost_done
135    } state;
136
137    state = r->state;
138
139    for (p = b->pos; p < b->last; p++) {
140        ch = *p;
141
142        switch (state) {
143
144        /* HTTP methods: GET, HEAD, POST */
145        case sw_start:
146            r->request_start = p;
147
148            if (ch == CR || ch == LF) {
149                break;
150            }
151
152            if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {
153                return NGX_HTTP_PARSE_INVALID_METHOD;
154            }
155
156            state = sw_method;
157            break;
158
159        case sw_method:
160            if (ch == ' ') {
161                r->method_end = p - 1;
162                m = r->request_start;
163
164                switch (p - m) {
165
166                case 3:
167                    if (ngx_str3_cmp(m, 'G', 'E', 'T', ' ')) {
168                        r->method = NGX_HTTP_GET;
169                        break;
170                    }
171
172                    if (ngx_str3_cmp(m, 'P', 'U', 'T', ' ')) {
173                        r->method = NGX_HTTP_PUT;
174                        break;
175                    }
176
177                    break;
178
179                case 4:
180                    if (m[1] == 'O') {
181
182                        if (ngx_str3Ocmp(m, 'P', 'O', 'S', 'T')) {
183                            r->method = NGX_HTTP_POST;
184                            break;
185                        }
186
187                        if (ngx_str3Ocmp(m, 'C', 'O', 'P', 'Y')) {
188                            r->method = NGX_HTTP_COPY;
189                            break;
190                        }
191
192                        if (ngx_str3Ocmp(m, 'M', 'O', 'V', 'E')) {
193                            r->method = NGX_HTTP_MOVE;
194                            break;
195                        }
196
197                        if (ngx_str3Ocmp(m, 'L', 'O', 'C', 'K')) {
198                            r->method = NGX_HTTP_LOCK;
199                            break;
200                        }
201
202                    } else {
203
204                        if (ngx_str4cmp(m, 'H', 'E', 'A', 'D')) {
205                            r->method = NGX_HTTP_HEAD;
206                            break;
207                        }
208                    }
209
210                    break;
211
212                case 5:
213                    if (ngx_str5cmp(m, 'M', 'K', 'C', 'O', 'L')) {
214                        r->method = NGX_HTTP_MKCOL;
215                        break;
216                    }
217
218                    if (ngx_str5cmp(m, 'P', 'A', 'T', 'C', 'H')) {
219                        r->method = NGX_HTTP_PATCH;
220                        break;
221                    }
222
223                    if (ngx_str5cmp(m, 'T', 'R', 'A', 'C', 'E')) {
224                        r->method = NGX_HTTP_TRACE;
225                        break;
226                    }
227
228                    break;
229
230                case 6:
231                    if (ngx_str6cmp(m, 'D', 'E', 'L', 'E', 'T', 'E')) {
232                        r->method = NGX_HTTP_DELETE;
233                        break;
234                    }
235
236                    if (ngx_str6cmp(m, 'U', 'N', 'L', 'O', 'C', 'K')) {
237                        r->method = NGX_HTTP_UNLOCK;
238                        break;
239                    }
240
241                    break;
242
243                case 7:
244                    if (ngx_str7_cmp(m, 'O', 'P', 'T', 'I', 'O', 'N', 'S', ' '))
245                    {
246                        r->method = NGX_HTTP_OPTIONS;
247                    }
248
249                    break;
250
251                case 8:
252                    if (ngx_str8cmp(m, 'P', 'R', 'O', 'P', 'F', 'I', 'N', 'D'))
253                    {
254                        r->method = NGX_HTTP_PROPFIND;
255                    }
256
257                    break;
258
259                case 9:
260                    if (ngx_str9cmp(m,
261                            'P', 'R', 'O', 'P', 'P', 'A', 'T', 'C', 'H'))
262                    {
263                        r->method = NGX_HTTP_PROPPATCH;
264                    }
265
266                    break;
267                }
268
269                state = sw_spaces_before_uri;
270                break;
271            }
272
273            if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {
274                return NGX_HTTP_PARSE_INVALID_METHOD;
275            }
276
277            break;
278
279        /* space* before URI */
280        case sw_spaces_before_uri:
281
282            if (ch == '/') {
283                r->uri_start = p;
284                state = sw_after_slash_in_uri;
285                break;
286            }
287
288            c = (u_char) (ch | 0x20);
289            if (c >= 'a' && c <= 'z') {
290                r->schema_start = p;
291                state = sw_schema;
292                break;
293            }
294
295            switch (ch) {
296            case ' ':
297                break;
298            default:
299                return NGX_HTTP_PARSE_INVALID_REQUEST;
300            }
301            break;
302
303        case sw_schema:
304
305            c = (u_char) (ch | 0x20);
306            if (c >= 'a' && c <= 'z') {
307                break;
308            }
309
310            switch (ch) {
311            case ':':
312                r->schema_end = p;
313                state = sw_schema_slash;
314                break;
315            default:
316                return NGX_HTTP_PARSE_INVALID_REQUEST;
317            }
318            break;
319
320        case sw_schema_slash:
321            switch (ch) {
322            case '/':
323                state = sw_schema_slash_slash;
324                break;
325            default:
326                return NGX_HTTP_PARSE_INVALID_REQUEST;
327            }
328            break;
329
330        case sw_schema_slash_slash:
331            switch (ch) {
332            case '/':
333                state = sw_host_start;
334                break;
335            default:
336                return NGX_HTTP_PARSE_INVALID_REQUEST;
337            }
338            break;
339
340        case sw_host_start:
341
342            r->host_start = p;
343
344            if (ch == '[') {
345                state = sw_host_ip_literal;
346                break;
347            }
348
349            state = sw_host;
350
351            /* fall through */
352
353        case sw_host:
354
355            c = (u_char) (ch | 0x20);
356            if (c >= 'a' && c <= 'z') {
357                break;
358            }
359
360            if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') {
361                break;
362            }
363
364            /* fall through */
365
366        case sw_host_end:
367
368            r->host_end = p;
369
370            switch (ch) {
371            case ':':
372                state = sw_port;
373                break;
374            case '/':
375                r->uri_start = p;
376                state = sw_after_slash_in_uri;
377                break;
378            case ' ':
379                /*
380                 * use single "/" from request line to preserve pointers,
381                 * if request line will be copied to large client buffer
382                 */
383                r->uri_start = r->schema_end + 1;
384                r->uri_end = r->schema_end + 2;
385                state = sw_host_http_09;
386                break;
387            default:
388                return NGX_HTTP_PARSE_INVALID_REQUEST;
389            }
390            break;
391
392        case sw_host_ip_literal:
393
394            if (ch >= '0' && ch <= '9') {
395                break;
396            }
397
398            c = (u_char) (ch | 0x20);
399            if (c >= 'a' && c <= 'z') {
400                break;
401            }
402
403            switch (ch) {
404            case ':':
405                break;
406            case ']':
407                state = sw_host_end;
408                break;
409            case '-':
410            case '.':
411            case '_':
412            case '~':
413                /* unreserved */
414                break;
415            case '!':
416            case '$':
417            case '&':
418            case '\'':
419            case '(':
420            case ')':
421            case '*':
422            case '+':
423            case ',':
424            case ';':
425            case '=':
426                /* sub-delims */
427                break;
428            default:
429                return NGX_HTTP_PARSE_INVALID_REQUEST;
430            }
431            break;
432
433        case sw_port:
434            if (ch >= '0' && ch <= '9') {
435                break;
436            }
437
438            switch (ch) {
439            case '/':
440                r->port_end = p;
441                r->uri_start = p;
442                state = sw_after_slash_in_uri;
443                break;
444            case ' ':
445                r->port_end = p;
446                /*
447                 * use single "/" from request line to preserve pointers,
448                 * if request line will be copied to large client buffer
449                 */
450                r->uri_start = r->schema_end + 1;
451                r->uri_end = r->schema_end + 2;
452                state = sw_host_http_09;
453                break;
454            default:
455                return NGX_HTTP_PARSE_INVALID_REQUEST;
456            }
457            break;
458
459        /* space+ after "http://host[:port] " */
460        case sw_host_http_09:
461            switch (ch) {
462            case ' ':
463                break;
464            case CR:
465                r->http_minor = 9;
466                state = sw_almost_done;
467                break;
468            case LF:
469                r->http_minor = 9;
470                goto done;
471            case 'H':
472                r->http_protocol.data = p;
473                state = sw_http_H;
474                break;
475            default:
476                return NGX_HTTP_PARSE_INVALID_REQUEST;
477            }
478            break;
479
480
481        /* check "/.", "//", "%", and "\" (Win32) in URI */
482        case sw_after_slash_in_uri:
483
484            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
485                state = sw_check_uri;
486                break;
487            }
488
489            switch (ch) {
490            case ' ':
491                r->uri_end = p;
492                state = sw_check_uri_http_09;
493                break;
494            case CR:
495                r->uri_end = p;
496                r->http_minor = 9;
497                state = sw_almost_done;
498                break;
499            case LF:
500                r->uri_end = p;
501                r->http_minor = 9;
502                goto done;
503            case '.':
504                r->complex_uri = 1;
505                state = sw_uri;
506                break;
507            case '%':
508                r->quoted_uri = 1;
509                state = sw_uri;
510                break;
511            case '/':
512                r->complex_uri = 1;
513                state = sw_uri;
514                break;
515#if (NGX_WIN32)
516            case '\\':
517                r->complex_uri = 1;
518                state = sw_uri;
519                break;
520#endif
521            case '?':
522                r->args_start = p + 1;
523                state = sw_uri;
524                break;
525            case '#':
526                r->complex_uri = 1;
527                state = sw_uri;
528                break;
529            case '+':
530                r->plus_in_uri = 1;
531                break;
532            case '\0':
533                return NGX_HTTP_PARSE_INVALID_REQUEST;
534            default:
535                state = sw_check_uri;
536                break;
537            }
538            break;
539
540        /* check "/", "%" and "\" (Win32) in URI */
541        case sw_check_uri:
542
543            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
544                break;
545            }
546
547            switch (ch) {
548            case '/':
549#if (NGX_WIN32)
550                if (r->uri_ext == p) {
551                    r->complex_uri = 1;
552                    state = sw_uri;
553                    break;
554                }
555#endif
556                r->uri_ext = NULL;
557                state = sw_after_slash_in_uri;
558                break;
559            case '.':
560                r->uri_ext = p + 1;
561                break;
562            case ' ':
563                r->uri_end = p;
564                state = sw_check_uri_http_09;
565                break;
566            case CR:
567                r->uri_end = p;
568                r->http_minor = 9;
569                state = sw_almost_done;
570                break;
571            case LF:
572                r->uri_end = p;
573                r->http_minor = 9;
574                goto done;
575#if (NGX_WIN32)
576            case '\\':
577                r->complex_uri = 1;
578                state = sw_after_slash_in_uri;
579                break;
580#endif
581            case '%':
582                r->quoted_uri = 1;
583                state = sw_uri;
584                break;
585            case '?':
586                r->args_start = p + 1;
587                state = sw_uri;
588                break;
589            case '#':
590                r->complex_uri = 1;
591                state = sw_uri;
592                break;
593            case '+':
594                r->plus_in_uri = 1;
595                break;
596            case '\0':
597                return NGX_HTTP_PARSE_INVALID_REQUEST;
598            }
599            break;
600
601        /* space+ after URI */
602        case sw_check_uri_http_09:
603            switch (ch) {
604            case ' ':
605                break;
606            case CR:
607                r->http_minor = 9;
608                state = sw_almost_done;
609                break;
610            case LF:
611                r->http_minor = 9;
612                goto done;
613            case 'H':
614                r->http_protocol.data = p;
615                state = sw_http_H;
616                break;
617            default:
618                r->space_in_uri = 1;
619                state = sw_check_uri;
620                p--;
621                break;
622            }
623            break;
624
625
626        /* URI */
627        case sw_uri:
628
629            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
630                break;
631            }
632
633            switch (ch) {
634            case ' ':
635                r->uri_end = p;
636                state = sw_http_09;
637                break;
638            case CR:
639                r->uri_end = p;
640                r->http_minor = 9;
641                state = sw_almost_done;
642                break;
643            case LF:
644                r->uri_end = p;
645                r->http_minor = 9;
646                goto done;
647            case '#':
648                r->complex_uri = 1;
649                break;
650            case '\0':
651                return NGX_HTTP_PARSE_INVALID_REQUEST;
652            }
653            break;
654
655        /* space+ after URI */
656        case sw_http_09:
657            switch (ch) {
658            case ' ':
659                break;
660            case CR:
661                r->http_minor = 9;
662                state = sw_almost_done;
663                break;
664            case LF:
665                r->http_minor = 9;
666                goto done;
667            case 'H':
668                r->http_protocol.data = p;
669                state = sw_http_H;
670                break;
671            default:
672                r->space_in_uri = 1;
673                state = sw_uri;
674                p--;
675                break;
676            }
677            break;
678
679        case sw_http_H:
680            switch (ch) {
681            case 'T':
682                state = sw_http_HT;
683                break;
684            default:
685                return NGX_HTTP_PARSE_INVALID_REQUEST;
686            }
687            break;
688
689        case sw_http_HT:
690            switch (ch) {
691            case 'T':
692                state = sw_http_HTT;
693                break;
694            default:
695                return NGX_HTTP_PARSE_INVALID_REQUEST;
696            }
697            break;
698
699        case sw_http_HTT:
700            switch (ch) {
701            case 'P':
702                state = sw_http_HTTP;
703                break;
704            default:
705                return NGX_HTTP_PARSE_INVALID_REQUEST;
706            }
707            break;
708
709        case sw_http_HTTP:
710            switch (ch) {
711            case '/':
712                state = sw_first_major_digit;
713                break;
714            default:
715                return NGX_HTTP_PARSE_INVALID_REQUEST;
716            }
717            break;
718
719        /* first digit of major HTTP version */
720        case sw_first_major_digit:
721            if (ch < '1' || ch > '9') {
722                return NGX_HTTP_PARSE_INVALID_REQUEST;
723            }
724
725            r->http_major = ch - '0';
726            state = sw_major_digit;
727            break;
728
729        /* major HTTP version or dot */
730        case sw_major_digit:
731            if (ch == '.') {
732                state = sw_first_minor_digit;
733                break;
734            }
735
736            if (ch < '0' || ch > '9') {
737                return NGX_HTTP_PARSE_INVALID_REQUEST;
738            }
739
740            if (r->http_major > 99) {
741                return NGX_HTTP_PARSE_INVALID_REQUEST;
742            }
743
744            r->http_major = r->http_major * 10 + ch - '0';
745            break;
746
747        /* first digit of minor HTTP version */
748        case sw_first_minor_digit:
749            if (ch < '0' || ch > '9') {
750                return NGX_HTTP_PARSE_INVALID_REQUEST;
751            }
752
753            r->http_minor = ch - '0';
754            state = sw_minor_digit;
755            break;
756
757        /* minor HTTP version or end of request line */
758        case sw_minor_digit:
759            if (ch == CR) {
760                state = sw_almost_done;
761                break;
762            }
763
764            if (ch == LF) {
765                goto done;
766            }
767
768            if (ch == ' ') {
769                state = sw_spaces_after_digit;
770                break;
771            }
772
773            if (ch < '0' || ch > '9') {
774                return NGX_HTTP_PARSE_INVALID_REQUEST;
775            }
776
777            if (r->http_minor > 99) {
778                return NGX_HTTP_PARSE_INVALID_REQUEST;
779            }
780
781            r->http_minor = r->http_minor * 10 + ch - '0';
782            break;
783
784        case sw_spaces_after_digit:
785            switch (ch) {
786            case ' ':
787                break;
788            case CR:
789                state = sw_almost_done;
790                break;
791            case LF:
792                goto done;
793            default:
794                return NGX_HTTP_PARSE_INVALID_REQUEST;
795            }
796            break;
797
798        /* end of request line */
799        case sw_almost_done:
800            r->request_end = p - 1;
801            switch (ch) {
802            case LF:
803                goto done;
804            default:
805                return NGX_HTTP_PARSE_INVALID_REQUEST;
806            }
807        }
808    }
809
810    b->pos = p;
811    r->state = state;
812
813    return NGX_AGAIN;
814
815done:
816
817    b->pos = p + 1;
818
819    if (r->request_end == NULL) {
820        r->request_end = p;
821    }
822
823    r->http_version = r->http_major * 1000 + r->http_minor;
824    r->state = sw_start;
825
826    if (r->http_version == 9 && r->method != NGX_HTTP_GET) {
827        return NGX_HTTP_PARSE_INVALID_09_METHOD;
828    }
829
830    return NGX_OK;
831}
832
833
834ngx_int_t
835ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b,
836    ngx_uint_t allow_underscores)
837{
838    u_char      c, ch, *p;
839    ngx_uint_t  hash, i;
840    enum {
841        sw_start = 0,
842        sw_name,
843        sw_space_before_value,
844        sw_value,
845        sw_space_after_value,
846        sw_ignore_line,
847        sw_almost_done,
848        sw_header_almost_done
849    } state;
850
851    /* the last '\0' is not needed because string is zero terminated */
852
853    static u_char  lowcase[] =
854        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
855        "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
856        "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
857        "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
858        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
859        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
860        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
861        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
862
863    state = r->state;
864    hash = r->header_hash;
865    i = r->lowcase_index;
866
867    for (p = b->pos; p < b->last; p++) {
868        ch = *p;
869
870        switch (state) {
871
872        /* first char */
873        case sw_start:
874            r->header_name_start = p;
875            r->invalid_header = 0;
876
877            switch (ch) {
878            case CR:
879                r->header_end = p;
880                state = sw_header_almost_done;
881                break;
882            case LF:
883                r->header_end = p;
884                goto header_done;
885            default:
886                state = sw_name;
887
888                c = lowcase[ch];
889
890                if (c) {
891                    hash = ngx_hash(0, c);
892                    r->lowcase_header[0] = c;
893                    i = 1;
894                    break;
895                }
896
897                if (ch == '_') {
898                    if (allow_underscores) {
899                        hash = ngx_hash(0, ch);
900                        r->lowcase_header[0] = ch;
901                        i = 1;
902
903                    } else {
904                        r->invalid_header = 1;
905                    }
906
907                    break;
908                }
909
910                if (ch == '\0') {
911                    return NGX_HTTP_PARSE_INVALID_HEADER;
912                }
913
914                r->invalid_header = 1;
915
916                break;
917
918            }
919            break;
920
921        /* header name */
922        case sw_name:
923            c = lowcase[ch];
924
925            if (c) {
926                hash = ngx_hash(hash, c);
927                r->lowcase_header[i++] = c;
928                i &= (NGX_HTTP_LC_HEADER_LEN - 1);
929                break;
930            }
931
932            if (ch == '_') {
933                if (allow_underscores) {
934                    hash = ngx_hash(hash, ch);
935                    r->lowcase_header[i++] = ch;
936                    i &= (NGX_HTTP_LC_HEADER_LEN - 1);
937
938                } else {
939                    r->invalid_header = 1;
940                }
941
942                break;
943            }
944
945            if (ch == ':') {
946                r->header_name_end = p;
947                state = sw_space_before_value;
948                break;
949            }
950
951            if (ch == CR) {
952                r->header_name_end = p;
953                r->header_start = p;
954                r->header_end = p;
955                state = sw_almost_done;
956                break;
957            }
958
959            if (ch == LF) {
960                r->header_name_end = p;
961                r->header_start = p;
962                r->header_end = p;
963                goto done;
964            }
965
966            /* IIS may send the duplicate "HTTP/1.1 ..." lines */
967            if (ch == '/'
968                && r->upstream
969                && p - r->header_name_start == 4
970                && ngx_strncmp(r->header_name_start, "HTTP", 4) == 0)
971            {
972                state = sw_ignore_line;
973                break;
974            }
975
976            if (ch == '\0') {
977                return NGX_HTTP_PARSE_INVALID_HEADER;
978            }
979
980            r->invalid_header = 1;
981
982            break;
983
984        /* space* before header value */
985        case sw_space_before_value:
986            switch (ch) {
987            case ' ':
988                break;
989            case CR:
990                r->header_start = p;
991                r->header_end = p;
992                state = sw_almost_done;
993                break;
994            case LF:
995                r->header_start = p;
996                r->header_end = p;
997                goto done;
998            case '\0':
999                return NGX_HTTP_PARSE_INVALID_HEADER;
1000            default:
1001                r->header_start = p;
1002                state = sw_value;
1003                break;
1004            }
1005            break;
1006
1007        /* header value */
1008        case sw_value:
1009            switch (ch) {
1010            case ' ':
1011                r->header_end = p;
1012                state = sw_space_after_value;
1013                break;
1014            case CR:
1015                r->header_end = p;
1016                state = sw_almost_done;
1017                break;
1018            case LF:
1019                r->header_end = p;
1020                goto done;
1021            case '\0':
1022                return NGX_HTTP_PARSE_INVALID_HEADER;
1023            }
1024            break;
1025
1026        /* space* before end of header line */
1027        case sw_space_after_value:
1028            switch (ch) {
1029            case ' ':
1030                break;
1031            case CR:
1032                state = sw_almost_done;
1033                break;
1034            case LF:
1035                goto done;
1036            case '\0':
1037                return NGX_HTTP_PARSE_INVALID_HEADER;
1038            default:
1039                state = sw_value;
1040                break;
1041            }
1042            break;
1043
1044        /* ignore header line */
1045        case sw_ignore_line:
1046            switch (ch) {
1047            case LF:
1048                state = sw_start;
1049                break;
1050            default:
1051                break;
1052            }
1053            break;
1054
1055        /* end of header line */
1056        case sw_almost_done:
1057            switch (ch) {
1058            case LF:
1059                goto done;
1060            case CR:
1061                break;
1062            default:
1063                return NGX_HTTP_PARSE_INVALID_HEADER;
1064            }
1065            break;
1066
1067        /* end of header */
1068        case sw_header_almost_done:
1069            switch (ch) {
1070            case LF:
1071                goto header_done;
1072            default:
1073                return NGX_HTTP_PARSE_INVALID_HEADER;
1074            }
1075        }
1076    }
1077
1078    b->pos = p;
1079    r->state = state;
1080    r->header_hash = hash;
1081    r->lowcase_index = i;
1082
1083    return NGX_AGAIN;
1084
1085done:
1086
1087    b->pos = p + 1;
1088    r->state = sw_start;
1089    r->header_hash = hash;
1090    r->lowcase_index = i;
1091
1092    return NGX_OK;
1093
1094header_done:
1095
1096    b->pos = p + 1;
1097    r->state = sw_start;
1098
1099    return NGX_HTTP_PARSE_HEADER_DONE;
1100}
1101
1102
1103ngx_int_t
1104ngx_http_parse_uri(ngx_http_request_t *r)
1105{
1106    u_char  *p, ch;
1107    enum {
1108        sw_start = 0,
1109        sw_after_slash_in_uri,
1110        sw_check_uri,
1111        sw_uri
1112    } state;
1113
1114    state = sw_start;
1115
1116    for (p = r->uri_start; p != r->uri_end; p++) {
1117
1118        ch = *p;
1119
1120        switch (state) {
1121
1122        case sw_start:
1123
1124            if (ch != '/') {
1125                return NGX_ERROR;
1126            }
1127
1128            state = sw_after_slash_in_uri;
1129            break;
1130
1131        /* check "/.", "//", "%", and "\" (Win32) in URI */
1132        case sw_after_slash_in_uri:
1133
1134            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1135                state = sw_check_uri;
1136                break;
1137            }
1138
1139            switch (ch) {
1140            case ' ':
1141                r->space_in_uri = 1;
1142                state = sw_check_uri;
1143                break;
1144            case '.':
1145                r->complex_uri = 1;
1146                state = sw_uri;
1147                break;
1148            case '%':
1149                r->quoted_uri = 1;
1150                state = sw_uri;
1151                break;
1152            case '/':
1153                r->complex_uri = 1;
1154                state = sw_uri;
1155                break;
1156#if (NGX_WIN32)
1157            case '\\':
1158                r->complex_uri = 1;
1159                state = sw_uri;
1160                break;
1161#endif
1162            case '?':
1163                r->args_start = p + 1;
1164                state = sw_uri;
1165                break;
1166            case '#':
1167                r->complex_uri = 1;
1168                state = sw_uri;
1169                break;
1170            case '+':
1171                r->plus_in_uri = 1;
1172                break;
1173            default:
1174                state = sw_check_uri;
1175                break;
1176            }
1177            break;
1178
1179        /* check "/", "%" and "\" (Win32) in URI */
1180        case sw_check_uri:
1181
1182            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1183                break;
1184            }
1185
1186            switch (ch) {
1187            case '/':
1188#if (NGX_WIN32)
1189                if (r->uri_ext == p) {
1190                    r->complex_uri = 1;
1191                    state = sw_uri;
1192                    break;
1193                }
1194#endif
1195                r->uri_ext = NULL;
1196                state = sw_after_slash_in_uri;
1197                break;
1198            case '.':
1199                r->uri_ext = p + 1;
1200                break;
1201            case ' ':
1202                r->space_in_uri = 1;
1203                break;
1204#if (NGX_WIN32)
1205            case '\\':
1206                r->complex_uri = 1;
1207                state = sw_after_slash_in_uri;
1208                break;
1209#endif
1210            case '%':
1211                r->quoted_uri = 1;
1212                state = sw_uri;
1213                break;
1214            case '?':
1215                r->args_start = p + 1;
1216                state = sw_uri;
1217                break;
1218            case '#':
1219                r->complex_uri = 1;
1220                state = sw_uri;
1221                break;
1222            case '+':
1223                r->plus_in_uri = 1;
1224                break;
1225            }
1226            break;
1227
1228        /* URI */
1229        case sw_uri:
1230
1231            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1232                break;
1233            }
1234
1235            switch (ch) {
1236            case ' ':
1237                r->space_in_uri = 1;
1238                break;
1239            case '#':
1240                r->complex_uri = 1;
1241                break;
1242            }
1243            break;
1244        }
1245    }
1246
1247    return NGX_OK;
1248}
1249
1250
1251ngx_int_t
1252ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes)
1253{
1254    u_char  c, ch, decoded, *p, *u;
1255    enum {
1256        sw_usual = 0,
1257        sw_slash,
1258        sw_dot,
1259        sw_dot_dot,
1260        sw_quoted,
1261        sw_quoted_second
1262    } state, quoted_state;
1263
1264#if (NGX_SUPPRESS_WARN)
1265    decoded = '\0';
1266    quoted_state = sw_usual;
1267#endif
1268
1269    state = sw_usual;
1270    p = r->uri_start;
1271    u = r->uri.data;
1272    r->uri_ext = NULL;
1273    r->args_start = NULL;
1274
1275    ch = *p++;
1276
1277    while (p <= r->uri_end) {
1278
1279        /*
1280         * we use "ch = *p++" inside the cycle, but this operation is safe,
1281         * because after the URI there is always at least one character:
1282         * the line feed
1283         */
1284
1285        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1286                       "s:%d in:'%Xd:%c'", state, ch, ch);
1287
1288        switch (state) {
1289
1290        case sw_usual:
1291
1292            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1293                *u++ = ch;
1294                ch = *p++;
1295                break;
1296            }
1297
1298            switch (ch) {
1299#if (NGX_WIN32)
1300            case '\\':
1301                if (u - 2 >= r->uri.data
1302                    && *(u - 1) == '.' && *(u - 2) != '.')
1303                {
1304                    u--;
1305                }
1306
1307                r->uri_ext = NULL;
1308
1309                if (p == r->uri_start + r->uri.len) {
1310
1311                    /*
1312                     * we omit the last "\" to cause redirect because
1313                     * the browsers do not treat "\" as "/" in relative URL path
1314                     */
1315
1316                    break;
1317                }
1318
1319                state = sw_slash;
1320                *u++ = '/';
1321                break;
1322#endif
1323            case '/':
1324#if (NGX_WIN32)
1325                if (u - 2 >= r->uri.data
1326                    && *(u - 1) == '.' && *(u - 2) != '.')
1327                {
1328                    u--;
1329                }
1330#endif
1331                r->uri_ext = NULL;
1332                state = sw_slash;
1333                *u++ = ch;
1334                break;
1335            case '%':
1336                quoted_state = state;
1337                state = sw_quoted;
1338                break;
1339            case '?':
1340                r->args_start = p;
1341                goto args;
1342            case '#':
1343                goto done;
1344            case '.':
1345                r->uri_ext = u + 1;
1346                *u++ = ch;
1347                break;
1348            case '+':
1349                r->plus_in_uri = 1;
1350                /* fall through */
1351            default:
1352                *u++ = ch;
1353                break;
1354            }
1355
1356            ch = *p++;
1357            break;
1358
1359        case sw_slash:
1360
1361            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1362                state = sw_usual;
1363                *u++ = ch;
1364                ch = *p++;
1365                break;
1366            }
1367
1368            switch (ch) {
1369#if (NGX_WIN32)
1370            case '\\':
1371                break;
1372#endif
1373            case '/':
1374                if (!merge_slashes) {
1375                    *u++ = ch;
1376                }
1377                break;
1378            case '.':
1379                state = sw_dot;
1380                *u++ = ch;
1381                break;
1382            case '%':
1383                quoted_state = state;
1384                state = sw_quoted;
1385                break;
1386            case '?':
1387                r->args_start = p;
1388                goto args;
1389            case '#':
1390                goto done;
1391            case '+':
1392                r->plus_in_uri = 1;
1393            default:
1394                state = sw_usual;
1395                *u++ = ch;
1396                break;
1397            }
1398
1399            ch = *p++;
1400            break;
1401
1402        case sw_dot:
1403
1404            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1405                state = sw_usual;
1406                *u++ = ch;
1407                ch = *p++;
1408                break;
1409            }
1410
1411            switch (ch) {
1412#if (NGX_WIN32)
1413            case '\\':
1414#endif
1415            case '/':
1416                state = sw_slash;
1417                u--;
1418                break;
1419            case '.':
1420                state = sw_dot_dot;
1421                *u++ = ch;
1422                break;
1423            case '%':
1424                quoted_state = state;
1425                state = sw_quoted;
1426                break;
1427            case '?':
1428                r->args_start = p;
1429                goto args;
1430            case '#':
1431                goto done;
1432            case '+':
1433                r->plus_in_uri = 1;
1434            default:
1435                state = sw_usual;
1436                *u++ = ch;
1437                break;
1438            }
1439
1440            ch = *p++;
1441            break;
1442
1443        case sw_dot_dot:
1444
1445            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1446                state = sw_usual;
1447                *u++ = ch;
1448                ch = *p++;
1449                break;
1450            }
1451
1452            switch (ch) {
1453#if (NGX_WIN32)
1454            case '\\':
1455#endif
1456            case '/':
1457                state = sw_slash;
1458                u -= 5;
1459                for ( ;; ) {
1460                    if (u < r->uri.data) {
1461                        return NGX_HTTP_PARSE_INVALID_REQUEST;
1462                    }
1463                    if (*u == '/') {
1464                        u++;
1465                        break;
1466                    }
1467                    u--;
1468                }
1469                break;
1470            case '%':
1471                quoted_state = state;
1472                state = sw_quoted;
1473                break;
1474            case '?':
1475                r->args_start = p;
1476                goto args;
1477            case '#':
1478                goto done;
1479            case '+':
1480                r->plus_in_uri = 1;
1481            default:
1482                state = sw_usual;
1483                *u++ = ch;
1484                break;
1485            }
1486
1487            ch = *p++;
1488            break;
1489
1490        case sw_quoted:
1491            r->quoted_uri = 1;
1492
1493            if (ch >= '0' && ch <= '9') {
1494                decoded = (u_char) (ch - '0');
1495                state = sw_quoted_second;
1496                ch = *p++;
1497                break;
1498            }
1499
1500            c = (u_char) (ch | 0x20);
1501            if (c >= 'a' && c <= 'f') {
1502                decoded = (u_char) (c - 'a' + 10);
1503                state = sw_quoted_second;
1504                ch = *p++;
1505                break;
1506            }
1507
1508            return NGX_HTTP_PARSE_INVALID_REQUEST;
1509
1510        case sw_quoted_second:
1511            if (ch >= '0' && ch <= '9') {
1512                ch = (u_char) ((decoded << 4) + ch - '0');
1513
1514                if (ch == '%' || ch == '#') {
1515                    state = sw_usual;
1516                    *u++ = ch;
1517                    ch = *p++;
1518                    break;
1519
1520                } else if (ch == '\0') {
1521                    return NGX_HTTP_PARSE_INVALID_REQUEST;
1522                }
1523
1524                state = quoted_state;
1525                break;
1526            }
1527
1528            c = (u_char) (ch | 0x20);
1529            if (c >= 'a' && c <= 'f') {
1530                ch = (u_char) ((decoded << 4) + c - 'a' + 10);
1531
1532                if (ch == '?') {
1533                    state = sw_usual;
1534                    *u++ = ch;
1535                    ch = *p++;
1536                    break;
1537
1538                } else if (ch == '+') {
1539                    r->plus_in_uri = 1;
1540                }
1541
1542                state = quoted_state;
1543                break;
1544            }
1545
1546            return NGX_HTTP_PARSE_INVALID_REQUEST;
1547        }
1548    }
1549
1550done:
1551
1552    r->uri.len = u - r->uri.data;
1553
1554    if (r->uri_ext) {
1555        r->exten.len = u - r->uri_ext;
1556        r->exten.data = r->uri_ext;
1557    }
1558
1559    r->uri_ext = NULL;
1560
1561    return NGX_OK;
1562
1563args:
1564
1565    while (p < r->uri_end) {
1566        if (*p++ != '#') {
1567            continue;
1568        }
1569
1570        r->args.len = p - 1 - r->args_start;
1571        r->args.data = r->args_start;
1572        r->args_start = NULL;
1573
1574        break;
1575    }
1576
1577    r->uri.len = u - r->uri.data;
1578
1579    if (r->uri_ext) {
1580        r->exten.len = u - r->uri_ext;
1581        r->exten.data = r->uri_ext;
1582    }
1583
1584    r->uri_ext = NULL;
1585
1586    return NGX_OK;
1587}
1588
1589
1590ngx_int_t
1591ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b,
1592    ngx_http_status_t *status)
1593{
1594    u_char   ch;
1595    u_char  *p;
1596    enum {
1597        sw_start = 0,
1598        sw_H,
1599        sw_HT,
1600        sw_HTT,
1601        sw_HTTP,
1602        sw_first_major_digit,
1603        sw_major_digit,
1604        sw_first_minor_digit,
1605        sw_minor_digit,
1606        sw_status,
1607        sw_space_after_status,
1608        sw_status_text,
1609        sw_almost_done
1610    } state;
1611
1612    state = r->state;
1613
1614    for (p = b->pos; p < b->last; p++) {
1615        ch = *p;
1616
1617        switch (state) {
1618
1619        /* "HTTP/" */
1620        case sw_start:
1621            switch (ch) {
1622            case 'H':
1623                state = sw_H;
1624                break;
1625            default:
1626                return NGX_ERROR;
1627            }
1628            break;
1629
1630        case sw_H:
1631            switch (ch) {
1632            case 'T':
1633                state = sw_HT;
1634                break;
1635            default:
1636                return NGX_ERROR;
1637            }
1638            break;
1639
1640        case sw_HT:
1641            switch (ch) {
1642            case 'T':
1643                state = sw_HTT;
1644                break;
1645            default:
1646                return NGX_ERROR;
1647            }
1648            break;
1649
1650        case sw_HTT:
1651            switch (ch) {
1652            case 'P':
1653                state = sw_HTTP;
1654                break;
1655            default:
1656                return NGX_ERROR;
1657            }
1658            break;
1659
1660        case sw_HTTP:
1661            switch (ch) {
1662            case '/':
1663                state = sw_first_major_digit;
1664                break;
1665            default:
1666                return NGX_ERROR;
1667            }
1668            break;
1669
1670        /* the first digit of major HTTP version */
1671        case sw_first_major_digit:
1672            if (ch < '1' || ch > '9') {
1673                return NGX_ERROR;
1674            }
1675
1676            r->http_major = ch - '0';
1677            state = sw_major_digit;
1678            break;
1679
1680        /* the major HTTP version or dot */
1681        case sw_major_digit:
1682            if (ch == '.') {
1683                state = sw_first_minor_digit;
1684                break;
1685            }
1686
1687            if (ch < '0' || ch > '9') {
1688                return NGX_ERROR;
1689            }
1690
1691            if (r->http_major > 99) {
1692                return NGX_ERROR;
1693            }
1694
1695            r->http_major = r->http_major * 10 + ch - '0';
1696            break;
1697
1698        /* the first digit of minor HTTP version */
1699        case sw_first_minor_digit:
1700            if (ch < '0' || ch > '9') {
1701                return NGX_ERROR;
1702            }
1703
1704            r->http_minor = ch - '0';
1705            state = sw_minor_digit;
1706            break;
1707
1708        /* the minor HTTP version or the end of the request line */
1709        case sw_minor_digit:
1710            if (ch == ' ') {
1711                state = sw_status;
1712                break;
1713            }
1714
1715            if (ch < '0' || ch > '9') {
1716                return NGX_ERROR;
1717            }
1718
1719            if (r->http_minor > 99) {
1720                return NGX_ERROR;
1721            }
1722
1723            r->http_minor = r->http_minor * 10 + ch - '0';
1724            break;
1725
1726        /* HTTP status code */
1727        case sw_status:
1728            if (ch == ' ') {
1729                break;
1730            }
1731
1732            if (ch < '0' || ch > '9') {
1733                return NGX_ERROR;
1734            }
1735
1736            status->code = status->code * 10 + ch - '0';
1737
1738            if (++status->count == 3) {
1739                state = sw_space_after_status;
1740                status->start = p - 2;
1741            }
1742
1743            break;
1744
1745        /* space or end of line */
1746        case sw_space_after_status:
1747            switch (ch) {
1748            case ' ':
1749                state = sw_status_text;
1750                break;
1751            case '.':                    /* IIS may send 403.1, 403.2, etc */
1752                state = sw_status_text;
1753                break;
1754            case CR:
1755                state = sw_almost_done;
1756                break;
1757            case LF:
1758                goto done;
1759            default:
1760                return NGX_ERROR;
1761            }
1762            break;
1763
1764        /* any text until end of line */
1765        case sw_status_text:
1766            switch (ch) {
1767            case CR:
1768                state = sw_almost_done;
1769
1770                break;
1771            case LF:
1772                goto done;
1773            }
1774            break;
1775
1776        /* end of status line */
1777        case sw_almost_done:
1778            status->end = p - 1;
1779            switch (ch) {
1780            case LF:
1781                goto done;
1782            default:
1783                return NGX_ERROR;
1784            }
1785        }
1786    }
1787
1788    b->pos = p;
1789    r->state = state;
1790
1791    return NGX_AGAIN;
1792
1793done:
1794
1795    b->pos = p + 1;
1796
1797    if (status->end == NULL) {
1798        status->end = p;
1799    }
1800
1801    status->http_version = r->http_major * 1000 + r->http_minor;
1802    r->state = sw_start;
1803
1804    return NGX_OK;
1805}
1806
1807
1808ngx_int_t
1809ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri,
1810    ngx_str_t *args, ngx_uint_t *flags)
1811{
1812    u_char      ch, *p, *src, *dst;
1813    size_t      len;
1814    ngx_uint_t  quoted;
1815
1816    len = uri->len;
1817    p = uri->data;
1818    quoted = 0;
1819
1820    if (len == 0 || p[0] == '?') {
1821        goto unsafe;
1822    }
1823
1824    if (p[0] == '.' && len > 1 && p[1] == '.'
1825        && (len == 2 || ngx_path_separator(p[2])))
1826    {
1827        goto unsafe;
1828    }
1829
1830    for ( /* void */ ; len; len--) {
1831
1832        ch = *p++;
1833
1834        if (ch == '%') {
1835            quoted = 1;
1836            continue;
1837        }
1838
1839        if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1840            continue;
1841        }
1842
1843        if (ch == '?') {
1844            args->len = len - 1;
1845            args->data = p;
1846            uri->len -= len;
1847
1848            break;
1849        }
1850
1851        if (ch == '\0') {
1852            goto unsafe;
1853        }
1854
1855        if (ngx_path_separator(ch) && len > 2) {
1856
1857            /* detect "/../" and "/.." */
1858
1859            if (p[0] == '.' && p[1] == '.'
1860                && (len == 3 || ngx_path_separator(p[2])))
1861            {
1862                goto unsafe;
1863            }
1864        }
1865    }
1866
1867    if (quoted) {
1868        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1869                       "escaped URI: \"%V\"", uri);
1870
1871        src = uri->data;
1872
1873        dst = ngx_pnalloc(r->pool, uri->len);
1874        if (dst == NULL) {
1875            return NGX_ERROR;
1876        }
1877
1878        uri->data = dst;
1879
1880        ngx_unescape_uri(&dst, &src, uri->len, 0);
1881
1882        uri->len = dst - uri->data;
1883
1884        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1885                       "unescaped URI: \"%V\"", uri);
1886
1887        len = uri->len;
1888        p = uri->data;
1889
1890        if (p[0] == '.' && len > 1 && p[1] == '.'
1891            && (len == 2 || ngx_path_separator(p[2])))
1892        {
1893            goto unsafe;
1894        }
1895
1896        for ( /* void */ ; len; len--) {
1897
1898            ch = *p++;
1899
1900            if (ch == '\0') {
1901                goto unsafe;
1902            }
1903
1904            if (ngx_path_separator(ch) && len > 2) {
1905
1906                /* detect "/../" and "/.." */
1907
1908                if (p[0] == '.' && p[1] == '.'
1909                    && (len == 3 || ngx_path_separator(p[2])))
1910                {
1911                    goto unsafe;
1912                }
1913            }
1914        }
1915    }
1916
1917    return NGX_OK;
1918
1919unsafe:
1920
1921    if (*flags & NGX_HTTP_LOG_UNSAFE) {
1922        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1923                      "unsafe URI \"%V\" was detected", uri);
1924    }
1925
1926    return NGX_ERROR;
1927}
1928
1929
1930ngx_int_t
1931ngx_http_parse_multi_header_lines(ngx_array_t *headers, ngx_str_t *name,
1932    ngx_str_t *value)
1933{
1934    ngx_uint_t         i;
1935    u_char            *start, *last, *end, ch;
1936    ngx_table_elt_t  **h;
1937
1938    h = headers->elts;
1939
1940    for (i = 0; i < headers->nelts; i++) {
1941
1942        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, headers->pool->log, 0,
1943                       "parse header: \"%V: %V\"", &h[i]->key, &h[i]->value);
1944
1945        if (name->len > h[i]->value.len) {
1946            continue;
1947        }
1948
1949        start = h[i]->value.data;
1950        end = h[i]->value.data + h[i]->value.len;
1951
1952        while (start < end) {
1953
1954            if (ngx_strncasecmp(start, name->data, name->len) != 0) {
1955                goto skip;
1956            }
1957
1958            for (start += name->len; start < end && *start == ' '; start++) {
1959                /* void */
1960            }
1961
1962            if (value == NULL) {
1963                if (start == end || *start == ',') {
1964                    return i;
1965                }
1966
1967                goto skip;
1968            }
1969
1970            if (start == end || *start++ != '=') {
1971                /* the invalid header value */
1972                goto skip;
1973            }
1974
1975            while (start < end && *start == ' ') { start++; }
1976
1977            for (last = start; last < end && *last != ';'; last++) {
1978                /* void */
1979            }
1980
1981            value->len = last - start;
1982            value->data = start;
1983
1984            return i;
1985
1986        skip:
1987
1988            while (start < end) {
1989                ch = *start++;
1990                if (ch == ';' || ch == ',') {
1991                    break;
1992                }
1993            }
1994
1995            while (start < end && *start == ' ') { start++; }
1996        }
1997    }
1998
1999    return NGX_DECLINED;
2000}
2001
2002
2003ngx_int_t
2004ngx_http_parse_set_cookie_lines(ngx_array_t *headers, ngx_str_t *name,
2005    ngx_str_t *value)
2006{
2007    ngx_uint_t         i;
2008    u_char            *start, *last, *end;
2009    ngx_table_elt_t  **h;
2010
2011    h = headers->elts;
2012
2013    for (i = 0; i < headers->nelts; i++) {
2014
2015        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, headers->pool->log, 0,
2016                       "parse header: \"%V: %V\"", &h[i]->key, &h[i]->value);
2017
2018        if (name->len >= h[i]->value.len) {
2019            continue;
2020        }
2021
2022        start = h[i]->value.data;
2023        end = h[i]->value.data + h[i]->value.len;
2024
2025        if (ngx_strncasecmp(start, name->data, name->len) != 0) {
2026            continue;
2027        }
2028
2029        for (start += name->len; start < end && *start == ' '; start++) {
2030            /* void */
2031        }
2032
2033        if (start == end || *start++ != '=') {
2034            /* the invalid header value */
2035            continue;
2036        }
2037
2038        while (start < end && *start == ' ') { start++; }
2039
2040        for (last = start; last < end && *last != ';'; last++) {
2041            /* void */
2042        }
2043
2044        value->len = last - start;
2045        value->data = start;
2046
2047        return i;
2048    }
2049
2050    return NGX_DECLINED;
2051}
2052
2053
2054ngx_int_t
2055ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value)
2056{
2057    u_char  *p, *last;
2058
2059    if (r->args.len == 0) {
2060        return NGX_DECLINED;
2061    }
2062
2063    p = r->args.data;
2064    last = p + r->args.len;
2065
2066    for ( /* void */ ; p < last; p++) {
2067
2068        /* we need '=' after name, so drop one char from last */
2069
2070        p = ngx_strlcasestrn(p, last - 1, name, len - 1);
2071
2072        if (p == NULL) {
2073            return NGX_DECLINED;
2074        }
2075
2076        if ((p == r->args.data || *(p - 1) == '&') && *(p + len) == '=') {
2077
2078            value->data = p + len + 1;
2079
2080            p = ngx_strlchr(p, last, '&');
2081
2082            if (p == NULL) {
2083                p = r->args.data + r->args.len;
2084            }
2085
2086            value->len = p - value->data;
2087
2088            return NGX_OK;
2089        }
2090    }
2091
2092    return NGX_DECLINED;
2093}
2094
2095
2096void
2097ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args)
2098{
2099    u_char  *p, *last;
2100
2101    last = uri->data + uri->len;
2102
2103    p = ngx_strlchr(uri->data, last, '?');
2104
2105    if (p) {
2106        uri->len = p - uri->data;
2107        p++;
2108        args->len = last - p;
2109        args->data = p;
2110
2111    } else {
2112        args->len = 0;
2113    }
2114}
2115
2116
2117ngx_int_t
2118ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
2119    ngx_http_chunked_t *ctx)
2120{
2121    u_char     *pos, ch, c;
2122    ngx_int_t   rc;
2123    enum {
2124        sw_chunk_start = 0,
2125        sw_chunk_size,
2126        sw_chunk_extension,
2127        sw_chunk_extension_almost_done,
2128        sw_chunk_data,
2129        sw_after_data,
2130        sw_after_data_almost_done,
2131        sw_last_chunk_extension,
2132        sw_last_chunk_extension_almost_done,
2133        sw_trailer,
2134        sw_trailer_almost_done,
2135        sw_trailer_header,
2136        sw_trailer_header_almost_done
2137    } state;
2138
2139    state = ctx->state;
2140
2141    if (state == sw_chunk_data && ctx->size == 0) {
2142        state = sw_after_data;
2143    }
2144
2145    rc = NGX_AGAIN;
2146
2147    for (pos = b->pos; pos < b->last; pos++) {
2148
2149        ch = *pos;
2150
2151        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2152                       "http chunked byte: %02Xd s:%d", ch, state);
2153
2154        switch (state) {
2155
2156        case sw_chunk_start:
2157            if (ch >= '0' && ch <= '9') {
2158                state = sw_chunk_size;
2159                ctx->size = ch - '0';
2160                break;
2161            }
2162
2163            c = (u_char) (ch | 0x20);
2164
2165            if (c >= 'a' && c <= 'f') {
2166                state = sw_chunk_size;
2167                ctx->size = c - 'a' + 10;
2168                break;
2169            }
2170
2171            goto invalid;
2172
2173        case sw_chunk_size:
2174            if (ctx->size > NGX_MAX_OFF_T_VALUE / 16) {
2175                goto invalid;
2176            }
2177
2178            if (ch >= '0' && ch <= '9') {
2179                ctx->size = ctx->size * 16 + (ch - '0');
2180                break;
2181            }
2182
2183            c = (u_char) (ch | 0x20);
2184
2185            if (c >= 'a' && c <= 'f') {
2186                ctx->size = ctx->size * 16 + (c - 'a' + 10);
2187                break;
2188            }
2189
2190            if (ctx->size == 0) {
2191
2192                switch (ch) {
2193                case CR:
2194                    state = sw_last_chunk_extension_almost_done;
2195                    break;
2196                case LF:
2197                    state = sw_trailer;
2198                    break;
2199                case ';':
2200                case ' ':
2201                case '\t':
2202                    state = sw_last_chunk_extension;
2203                    break;
2204                default:
2205                    goto invalid;
2206                }
2207
2208                break;
2209            }
2210
2211            switch (ch) {
2212            case CR:
2213                state = sw_chunk_extension_almost_done;
2214                break;
2215            case LF:
2216                state = sw_chunk_data;
2217                break;
2218            case ';':
2219            case ' ':
2220            case '\t':
2221                state = sw_chunk_extension;
2222                break;
2223            default:
2224                goto invalid;
2225            }
2226
2227            break;
2228
2229        case sw_chunk_extension:
2230            switch (ch) {
2231            case CR:
2232                state = sw_chunk_extension_almost_done;
2233                break;
2234            case LF:
2235                state = sw_chunk_data;
2236            }
2237            break;
2238
2239        case sw_chunk_extension_almost_done:
2240            if (ch == LF) {
2241                state = sw_chunk_data;
2242                break;
2243            }
2244            goto invalid;
2245
2246        case sw_chunk_data:
2247            rc = NGX_OK;
2248            goto data;
2249
2250        case sw_after_data:
2251            switch (ch) {
2252            case CR:
2253                state = sw_after_data_almost_done;
2254                break;
2255            case LF:
2256                state = sw_chunk_start;
2257            }
2258            break;
2259
2260        case sw_after_data_almost_done:
2261            if (ch == LF) {
2262                state = sw_chunk_start;
2263                break;
2264            }
2265            goto invalid;
2266
2267        case sw_last_chunk_extension:
2268            switch (ch) {
2269            case CR:
2270                state = sw_last_chunk_extension_almost_done;
2271                break;
2272            case LF:
2273                state = sw_trailer;
2274            }
2275            break;
2276
2277        case sw_last_chunk_extension_almost_done:
2278            if (ch == LF) {
2279                state = sw_trailer;
2280                break;
2281            }
2282            goto invalid;
2283
2284        case sw_trailer:
2285            switch (ch) {
2286            case CR:
2287                state = sw_trailer_almost_done;
2288                break;
2289            case LF:
2290                goto done;
2291            default:
2292                state = sw_trailer_header;
2293            }
2294            break;
2295
2296        case sw_trailer_almost_done:
2297            if (ch == LF) {
2298                goto done;
2299            }
2300            goto invalid;
2301
2302        case sw_trailer_header:
2303            switch (ch) {
2304            case CR:
2305                state = sw_trailer_header_almost_done;
2306                break;
2307            case LF:
2308                state = sw_trailer;
2309            }
2310            break;
2311
2312        case sw_trailer_header_almost_done:
2313            if (ch == LF) {
2314                state = sw_trailer;
2315                break;
2316            }
2317            goto invalid;
2318
2319        }
2320    }
2321
2322data:
2323
2324    ctx->state = state;
2325    b->pos = pos;
2326
2327    if (ctx->size > NGX_MAX_OFF_T_VALUE - 5) {
2328        goto invalid;
2329    }
2330
2331    switch (state) {
2332
2333    case sw_chunk_start:
2334        ctx->length = 3 /* "0" LF LF */;
2335        break;
2336    case sw_chunk_size:
2337        ctx->length = 1 /* LF */
2338                      + (ctx->size ? ctx->size + 4 /* LF "0" LF LF */
2339                                   : 1 /* LF */);
2340        break;
2341    case sw_chunk_extension:
2342    case sw_chunk_extension_almost_done:
2343        ctx->length = 1 /* LF */ + ctx->size + 4 /* LF "0" LF LF */;
2344        break;
2345    case sw_chunk_data:
2346        ctx->length = ctx->size + 4 /* LF "0" LF LF */;
2347        break;
2348    case sw_after_data:
2349    case sw_after_data_almost_done:
2350        ctx->length = 4 /* LF "0" LF LF */;
2351        break;
2352    case sw_last_chunk_extension:
2353    case sw_last_chunk_extension_almost_done:
2354        ctx->length = 2 /* LF LF */;
2355        break;
2356    case sw_trailer:
2357    case sw_trailer_almost_done:
2358        ctx->length = 1 /* LF */;
2359        break;
2360    case sw_trailer_header:
2361    case sw_trailer_header_almost_done:
2362        ctx->length = 2 /* LF LF */;
2363        break;
2364
2365    }
2366
2367    return rc;
2368
2369done:
2370
2371    ctx->state = 0;
2372    b->pos = pos + 1;
2373
2374    return NGX_DONE;
2375
2376invalid:
2377
2378    return NGX_ERROR;
2379}
2380