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