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