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