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