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