comparison src/http/ngx_http_parse.c.orig @ 174:3080c5392b89 NGINX_0_3_34

nginx 0.3.34 *) Feature: the "add_header" directive supports the variables.
author Igor Sysoev <http://sysoev.ru>
date Tue, 21 Mar 2006 00:00:00 +0300
parents
children
comparison
equal deleted inserted replaced
173:298e7ea28d33 174:3080c5392b89
1
2 /*
3 * Copyright (C) Igor Sysoev
4 */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_http.h>
10
11
12 ngx_int_t
13 ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
14 {
15 u_char c, ch, *p, *m;
16 enum {
17 sw_start = 0,
18 sw_method,
19 sw_space_after_method,
20 sw_spaces_before_uri,
21 sw_schema,
22 sw_schema_slash,
23 sw_schema_slash_slash,
24 sw_host,
25 sw_port,
26 sw_after_slash_in_uri,
27 sw_check_uri,
28 sw_uri,
29 sw_http_09,
30 sw_http_H,
31 sw_http_HT,
32 sw_http_HTT,
33 sw_http_HTTP,
34 sw_first_major_digit,
35 sw_major_digit,
36 sw_first_minor_digit,
37 sw_minor_digit,
38 sw_almost_done
39 } state;
40
41 state = r->state;
42
43 for (p = b->pos; p < b->last; p++) {
44 ch = *p;
45
46 /* gcc 2.95.2 and msvc 6.0 compile this switch as an jump table */
47
48 switch (state) {
49
50 /* HTTP methods: GET, HEAD, POST */
51 case sw_start:
52 r->request_start = p;
53
54 if (ch == CR || ch == LF) {
55 break;
56 }
57
58 if (ch < 'A' || ch > 'Z') {
59 return NGX_HTTP_PARSE_INVALID_METHOD;
60 }
61
62 state = sw_method;
63 break;
64
65 case sw_method:
66 if (ch == ' ') {
67 r->method_end = p - 1;
68 m = r->request_start;
69
70 if (p - m == 3) {
71
72 if (m[0] == 'G' && m[1] == 'E' && m[2] == 'T') {
73 r->method = NGX_HTTP_GET;
74 }
75
76 } else if (p - m == 4) {
77
78 if (m[0] == 'P' && m[1] == 'O'
79 && m[2] == 'S' && m[3] == 'T')
80 {
81 r->method = NGX_HTTP_POST;
82
83 } else if (m[0] == 'H' && m[1] == 'E'
84 && m[2] == 'A' && m[3] == 'D')
85 {
86 r->method = NGX_HTTP_HEAD;
87 }
88 }
89
90 state = sw_spaces_before_uri;
91 break;
92 }
93
94 if (ch < 'A' || ch > 'Z') {
95 return NGX_HTTP_PARSE_INVALID_METHOD;
96 }
97
98 break;
99
100 /* single space after method */
101 case sw_space_after_method:
102 switch (ch) {
103 case ' ':
104 state = sw_spaces_before_uri;
105 break;
106 default:
107 return NGX_HTTP_PARSE_INVALID_METHOD;
108 }
109 break;
110
111 /* space* before URI */
112 case sw_spaces_before_uri:
113
114 c = (u_char) (ch | 0x20);
115 if (c >= 'a' && c <= 'z') {
116 r->schema_start = p;
117 state = sw_schema;
118 break;
119 }
120
121 switch (ch) {
122 case '/':
123 r->uri_start = p;
124 state = sw_after_slash_in_uri;
125 break;
126 case ' ':
127 break;
128 default:
129 return NGX_HTTP_PARSE_INVALID_REQUEST;
130 }
131 break;
132
133 case sw_schema:
134
135 c = (u_char) (ch | 0x20);
136 if (c >= 'a' && c <= 'z') {
137 break;
138 }
139
140 switch (ch) {
141 case ':':
142 r->schema_end = p;
143 state = sw_schema_slash;
144 break;
145 default:
146 return NGX_HTTP_PARSE_INVALID_REQUEST;
147 }
148 break;
149
150 case sw_schema_slash:
151 switch (ch) {
152 case '/':
153 state = sw_schema_slash_slash;
154 break;
155 default:
156 return NGX_HTTP_PARSE_INVALID_REQUEST;
157 }
158 break;
159
160 case sw_schema_slash_slash:
161 switch (ch) {
162 case '/':
163 r->host_start = p;
164 state = sw_host;
165 break;
166 default:
167 return NGX_HTTP_PARSE_INVALID_REQUEST;
168 }
169 break;
170
171 case sw_host:
172
173 c = (u_char) (ch | 0x20);
174 if (c >= 'a' && c <= 'z') {
175 break;
176 }
177
178 if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-')
179 {
180 break;
181 }
182
183 switch (ch) {
184 case ':':
185 r->host_end = p;
186 state = sw_port;
187 break;
188 case '/':
189 r->host_end = p;
190 r->uri_start = p;
191 state = sw_after_slash_in_uri;
192 break;
193 default:
194 return NGX_HTTP_PARSE_INVALID_REQUEST;
195 }
196 break;
197
198 case sw_port:
199 if (ch >= '0' && ch <= '9') {
200 break;
201 }
202
203 switch (ch) {
204 case '/':
205 r->port_end = p;
206 r->uri_start = p;
207 state = sw_after_slash_in_uri;
208 break;
209 default:
210 return NGX_HTTP_PARSE_INVALID_REQUEST;
211 }
212 break;
213
214 /* check "/.", "//", "%", and "\" (Win32) in URI */
215 case sw_after_slash_in_uri:
216
217 c = (u_char) (ch | 0x20);
218 if (c >= 'a' && c <= 'z') {
219 state = sw_check_uri;
220 break;
221 }
222
223 if (ch >= '0' && ch <= '9') {
224 state = sw_check_uri;
225 break;
226 }
227
228 switch (ch) {
229 case ' ':
230 r->uri_end = p;
231 state = sw_http_09;
232 break;
233 case CR:
234 r->uri_end = p;
235 r->http_minor = 9;
236 state = sw_almost_done;
237 break;
238 case LF:
239 r->uri_end = p;
240 r->http_minor = 9;
241 goto done;
242 case '.':
243 r->complex_uri = 1;
244 state = sw_uri;
245 break;
246 case '%':
247 r->quoted_uri = 1;
248 state = sw_uri;
249 break;
250 case '/':
251 r->complex_uri = 1;
252 state = sw_uri;
253 break;
254 #if (NGX_WIN32)
255 case '\\':
256 r->complex_uri = 1;
257 state = sw_uri;
258 break;
259 #endif
260 case '?':
261 r->args_start = p + 1;
262 state = sw_uri;
263 break;
264 case '+':
265 r->plus_in_uri = 1;
266 break;
267 case '\0':
268 r->zero_in_uri = 1;
269 break;
270 default:
271 state = sw_check_uri;
272 break;
273 }
274 break;
275
276 /* check "/", "%" and "\" (Win32) in URI */
277 case sw_check_uri:
278
279 c = (u_char) (ch | 0x20);
280 if (c >= 'a' && c <= 'z') {
281 break;
282 }
283
284 if (ch >= '0' && ch <= '9') {
285 break;
286 }
287
288 switch (ch) {
289 case '/':
290 r->uri_ext = NULL;
291 state = sw_after_slash_in_uri;
292 break;
293 case '.':
294 r->uri_ext = p + 1;
295 break;
296 case ' ':
297 r->uri_end = p;
298 state = sw_http_09;
299 break;
300 case CR:
301 r->uri_end = p;
302 r->http_minor = 9;
303 state = sw_almost_done;
304 break;
305 case LF:
306 r->uri_end = p;
307 r->http_minor = 9;
308 goto done;
309 #if (NGX_WIN32)
310 case '\\':
311 r->complex_uri = 1;
312 state = sw_after_slash_in_uri;
313 break;
314 #endif
315 case '%':
316 r->quoted_uri = 1;
317 state = sw_uri;
318 break;
319 case '+':
320 r->plus_in_uri = 1;
321 break;
322 case '?':
323 r->args_start = p + 1;
324 state = sw_uri;
325 break;
326 case '\0':
327 r->zero_in_uri = 1;
328 break;
329 }
330 break;
331
332 /* URI */
333 case sw_uri:
334 switch (ch) {
335 case ' ':
336 r->uri_end = p;
337 state = sw_http_09;
338 break;
339 case CR:
340 r->uri_end = p;
341 r->http_minor = 9;
342 state = sw_almost_done;
343 break;
344 case LF:
345 r->uri_end = p;
346 r->http_minor = 9;
347 goto done;
348 case '+':
349 r->plus_in_uri = 1;
350 break;
351 case '\0':
352 r->zero_in_uri = 1;
353 break;
354 }
355 break;
356
357 /* space+ after URI */
358 case sw_http_09:
359 switch (ch) {
360 case ' ':
361 break;
362 case CR:
363 r->http_minor = 9;
364 state = sw_almost_done;
365 break;
366 case LF:
367 r->http_minor = 9;
368 goto done;
369 case 'H':
370 r->http_protocol.data = p;
371 state = sw_http_H;
372 break;
373 default:
374 return NGX_HTTP_PARSE_INVALID_REQUEST;
375 }
376 break;
377
378 case sw_http_H:
379 switch (ch) {
380 case 'T':
381 state = sw_http_HT;
382 break;
383 default:
384 return NGX_HTTP_PARSE_INVALID_REQUEST;
385 }
386 break;
387
388 case sw_http_HT:
389 switch (ch) {
390 case 'T':
391 state = sw_http_HTT;
392 break;
393 default:
394 return NGX_HTTP_PARSE_INVALID_REQUEST;
395 }
396 break;
397
398 case sw_http_HTT:
399 switch (ch) {
400 case 'P':
401 state = sw_http_HTTP;
402 break;
403 default:
404 return NGX_HTTP_PARSE_INVALID_REQUEST;
405 }
406 break;
407
408 case sw_http_HTTP:
409 switch (ch) {
410 case '/':
411 state = sw_first_major_digit;
412 break;
413 default:
414 return NGX_HTTP_PARSE_INVALID_REQUEST;
415 }
416 break;
417
418 /* first digit of major HTTP version */
419 case sw_first_major_digit:
420 if (ch < '1' || ch > '9') {
421 return NGX_HTTP_PARSE_INVALID_REQUEST;
422 }
423
424 r->http_major = ch - '0';
425 state = sw_major_digit;
426 break;
427
428 /* major HTTP version or dot */
429 case sw_major_digit:
430 if (ch == '.') {
431 state = sw_first_minor_digit;
432 break;
433 }
434
435 if (ch < '0' || ch > '9') {
436 return NGX_HTTP_PARSE_INVALID_REQUEST;
437 }
438
439 r->http_major = r->http_major * 10 + ch - '0';
440 break;
441
442 /* first digit of minor HTTP version */
443 case sw_first_minor_digit:
444 if (ch < '0' || ch > '9') {
445 return NGX_HTTP_PARSE_INVALID_REQUEST;
446 }
447
448 r->http_minor = ch - '0';
449 state = sw_minor_digit;
450 break;
451
452 /* minor HTTP version or end of request line */
453 case sw_minor_digit:
454 if (ch == CR) {
455 state = sw_almost_done;
456 break;
457 }
458
459 if (ch == LF) {
460 goto done;
461 }
462
463 if (ch < '0' || ch > '9') {
464 return NGX_HTTP_PARSE_INVALID_REQUEST;
465 }
466
467 r->http_minor = r->http_minor * 10 + ch - '0';
468 break;
469
470 /* end of request line */
471 case sw_almost_done:
472 r->request_end = p - 1;
473 switch (ch) {
474 case LF:
475 goto done;
476 default:
477 return NGX_HTTP_PARSE_INVALID_REQUEST;
478 }
479 }
480 }
481
482 b->pos = p;
483 r->state = state;
484
485 return NGX_AGAIN;
486
487 done:
488
489 b->pos = p + 1;
490
491 if (r->request_end == NULL) {
492 r->request_end = p;
493 }
494
495 r->http_version = r->http_major * 1000 + r->http_minor;
496 r->state = sw_start;
497
498 if (r->http_version == 9 && r->method != NGX_HTTP_GET) {
499 return NGX_HTTP_PARSE_INVALID_09_METHOD;
500 }
501
502 return NGX_OK;
503 }
504
505
506 ngx_int_t
507 ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b)
508 {
509 u_char c, ch, *p;
510 ngx_uint_t hash;
511 enum {
512 sw_start = 0,
513 sw_name,
514 sw_space_before_value,
515 sw_value,
516 sw_space_after_value,
517 sw_ignore_line,
518 sw_almost_done,
519 sw_header_almost_done
520 } state;
521
522 state = r->state;
523 hash = r->header_hash;
524
525 for (p = b->pos; p < b->last; p++) {
526 ch = *p;
527
528 switch (state) {
529
530 /* first char */
531 case sw_start:
532 r->invalid_header = 0;
533
534 switch (ch) {
535 case CR:
536 r->header_end = p;
537 state = sw_header_almost_done;
538 break;
539 case LF:
540 r->header_end = p;
541 goto header_done;
542 default:
543 state = sw_name;
544 r->header_name_start = p;
545
546 c = (u_char) (ch | 0x20);
547 if (c >= 'a' && c <= 'z') {
548 hash = c;
549 break;
550 }
551
552 if (ch >= '0' && ch <= '9') {
553 hash = ch;
554 break;
555 }
556
557 r->invalid_header = 1;
558
559 break;
560
561 }
562 break;
563
564 /* header name */
565 case sw_name:
566 c = (u_char) (ch | 0x20);
567 if (c >= 'a' && c <= 'z') {
568 hash += c;
569 break;
570 }
571
572 if (ch == ':') {
573 r->header_name_end = p;
574 state = sw_space_before_value;
575 break;
576 }
577
578 if (ch == '-') {
579 hash += ch;
580 break;
581 }
582
583 if (ch >= '0' && ch <= '9') {
584 hash += ch;
585 break;
586 }
587
588 if (ch == CR) {
589 r->header_name_end = p;
590 r->header_start = p;
591 r->header_end = p;
592 state = sw_almost_done;
593 break;
594 }
595
596 if (ch == LF) {
597 r->header_name_end = p;
598 r->header_start = p;
599 r->header_end = p;
600 goto done;
601 }
602
603 /* IIS may send the duplicate "HTTP/1.1 ..." lines */
604 if (ch == '/'
605 && r->upstream
606 && p - r->header_name_start == 4
607 && ngx_strncmp(r->header_name_start, "HTTP", 4) == 0)
608 {
609 state = sw_ignore_line;
610 break;
611 }
612
613 r->invalid_header = 1;
614
615 break;
616
617 /* space* before header value */
618 case sw_space_before_value:
619 switch (ch) {
620 case ' ':
621 break;
622 case CR:
623 r->header_start = p;
624 r->header_end = p;
625 state = sw_almost_done;
626 break;
627 case LF:
628 r->header_start = p;
629 r->header_end = p;
630 goto done;
631 default:
632 r->header_start = p;
633 state = sw_value;
634 break;
635 }
636 break;
637
638 /* header value */
639 case sw_value:
640 switch (ch) {
641 case ' ':
642 r->header_end = p;
643 state = sw_space_after_value;
644 break;
645 case CR:
646 r->header_end = p;
647 state = sw_almost_done;
648 break;
649 case LF:
650 r->header_end = p;
651 goto done;
652 }
653 break;
654
655 /* space* before end of header line */
656 case sw_space_after_value:
657 switch (ch) {
658 case ' ':
659 break;
660 case CR:
661 state = sw_almost_done;
662 break;
663 case LF:
664 goto done;
665 default:
666 state = sw_value;
667 break;
668 }
669 break;
670
671 /* ignore header line */
672 case sw_ignore_line:
673 switch (ch) {
674 case LF:
675 state = sw_start;
676 break;
677 default:
678 break;
679 }
680 break;
681
682 /* end of header line */
683 case sw_almost_done:
684 switch (ch) {
685 case CR:
686 break;
687 case LF:
688 goto done;
689 default:
690 return NGX_HTTP_PARSE_INVALID_HEADER;
691 }
692
693 /* end of header */
694 case sw_header_almost_done:
695 switch (ch) {
696 case LF:
697 goto header_done;
698 default:
699 return NGX_HTTP_PARSE_INVALID_HEADER;
700 }
701 }
702 }
703
704 b->pos = p;
705 r->state = state;
706 r->header_hash = hash;
707
708 return NGX_AGAIN;
709
710 done:
711
712 b->pos = p + 1;
713 r->state = sw_start;
714 r->header_hash = hash;
715
716 return NGX_OK;
717
718 header_done:
719
720 b->pos = p + 1;
721 r->state = sw_start;
722
723 return NGX_HTTP_PARSE_HEADER_DONE;
724 }
725
726
727 ngx_int_t
728 ngx_http_parse_complex_uri(ngx_http_request_t *r)
729 {
730 u_char c, ch, decoded, *p, *u;
731 enum {
732 sw_usual = 0,
733 sw_slash,
734 sw_dot,
735 sw_dot_dot,
736 #if (NGX_WIN32)
737 sw_dot_dot_dot,
738 #endif
739 sw_quoted,
740 sw_quoted_second
741 } state, quoted_state;
742
743 #if (NGX_SUPPRESS_WARN)
744 decoded = '\0';
745 quoted_state = sw_usual;
746 #endif
747
748 state = sw_usual;
749 p = r->uri_start;
750 u = r->uri.data;
751 r->uri_ext = NULL;
752 r->args_start = NULL;
753
754 ch = *p++;
755
756 while (p <= r->uri_end) {
757
758 /*
759 * we use "ch = *p++" inside the cycle, but this operation is safe,
760 * because after the URI there is always at least one charcter:
761 * the line feed
762 */
763
764 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
765 "s:%d in:'%Xd:%c', out:'%c'", state, ch, ch, *u);
766
767 switch (state) {
768
769 case sw_usual:
770 switch(ch) {
771 #if (NGX_WIN32)
772 case '\\':
773 r->uri_ext = NULL;
774
775 if (p == r->uri_start + r->uri.len) {
776
777 /*
778 * we omit the last "\" to cause redirect because
779 * the browsers do not treat "\" as "/" in relative URL path
780 */
781
782 break;
783 }
784
785 state = sw_slash;
786 *u++ = '/';
787 break;
788 #endif
789 case '/':
790 r->uri_ext = NULL;
791 state = sw_slash;
792 *u++ = ch;
793 break;
794 case '%':
795 quoted_state = state;
796 state = sw_quoted;
797 break;
798 case '?':
799 r->args_start = p;
800 goto done;
801 case '.':
802 r->uri_ext = u + 1;
803 *u++ = ch;
804 break;
805 default:
806 *u++ = ch;
807 break;
808 }
809 ch = *p++;
810 break;
811
812 case sw_slash:
813 switch(ch) {
814 #if (NGX_WIN32)
815 case '\\':
816 #endif
817 case '/':
818 break;
819 case '.':
820 state = sw_dot;
821 *u++ = ch;
822 break;
823 case '%':
824 quoted_state = state;
825 state = sw_quoted;
826 break;
827 case '?':
828 r->args_start = p;
829 goto done;
830 default:
831 state = sw_usual;
832 *u++ = ch;
833 break;
834 }
835 ch = *p++;
836 break;
837
838 case sw_dot:
839 switch(ch) {
840 #if (NGX_WIN32)
841 case '\\':
842 #endif
843 case '/':
844 state = sw_slash;
845 u--;
846 break;
847 case '.':
848 state = sw_dot_dot;
849 *u++ = ch;
850 break;
851 case '%':
852 quoted_state = state;
853 state = sw_quoted;
854 break;
855 case '?':
856 r->args_start = p;
857 goto done;
858 default:
859 state = sw_usual;
860 *u++ = ch;
861 break;
862 }
863 ch = *p++;
864 break;
865
866 case sw_dot_dot:
867 switch(ch) {
868 #if (NGX_WIN32)
869 case '\\':
870 #endif
871 case '/':
872 state = sw_slash;
873 u -= 4;
874 if (u < r->uri.data) {
875 return NGX_HTTP_PARSE_INVALID_REQUEST;
876 }
877 while (*(u - 1) != '/') {
878 u--;
879 }
880 break;
881 case '%':
882 quoted_state = state;
883 state = sw_quoted;
884 break;
885 case '?':
886 r->args_start = p;
887 goto done;
888 #if (NGX_WIN32)
889 case '.':
890 state = sw_dot_dot_dot;
891 *u++ = ch;
892 break;
893 #endif
894 default:
895 state = sw_usual;
896 *u++ = ch;
897 break;
898 }
899 ch = *p++;
900 break;
901
902 #if (NGX_WIN32)
903 case sw_dot_dot_dot:
904 switch(ch) {
905 case '\\':
906 case '/':
907 state = sw_slash;
908 u -= 5;
909 if (u < r->uri.data) {
910 return NGX_HTTP_PARSE_INVALID_REQUEST;
911 }
912 while (*u != '/') {
913 u--;
914 }
915 if (u < r->uri.data) {
916 return NGX_HTTP_PARSE_INVALID_REQUEST;
917 }
918 while (*(u - 1) != '/') {
919 u--;
920 }
921 break;
922 case '%':
923 quoted_state = state;
924 state = sw_quoted;
925 break;
926 case '?':
927 r->args_start = p;
928 goto done;
929 default:
930 state = sw_usual;
931 *u++ = ch;
932 break;
933 }
934 ch = *p++;
935 break;
936 #endif
937
938 case sw_quoted:
939 if (ch >= '0' && ch <= '9') {
940 decoded = (u_char) (ch - '0');
941 state = sw_quoted_second;
942 ch = *p++;
943 break;
944 }
945
946 c = (u_char) (ch | 0x20);
947 if (c >= 'a' && c <= 'f') {
948 decoded = (u_char) (c - 'a' + 10);
949 state = sw_quoted_second;
950 ch = *p++;
951 break;
952 }
953
954 return NGX_HTTP_PARSE_INVALID_REQUEST;
955
956 case sw_quoted_second:
957 if (ch >= '0' && ch <= '9') {
958 ch = (u_char) ((decoded << 4) + ch - '0');
959
960 if (ch == '%') {
961 state = sw_usual;
962 *u++ = ch;
963 ch = *p++;
964 break;
965 }
966
967 if (ch == '\0') {
968 r->zero_in_uri = 1;
969 *u++ = ch;
970 ch = *p++;
971 }
972
973 state = quoted_state;
974 break;
975 }
976
977 c = (u_char) (ch | 0x20);
978 if (c >= 'a' && c <= 'f') {
979 ch = (u_char) ((decoded << 4) + c - 'a' + 10);
980 if (ch == '?') {
981 *u++ = ch;
982 ch = *p++;
983 }
984 state = quoted_state;
985 break;
986 }
987
988 return NGX_HTTP_PARSE_INVALID_REQUEST;
989 }
990 }
991
992 done:
993
994 r->uri.len = u - r->uri.data;
995 r->uri.data[r->uri.len] = '\0';
996
997 if (r->uri_ext) {
998 r->exten.len = u - r->uri_ext;
999 r->exten.data = r->uri_ext;
1000 }
1001
1002 r->uri_ext = NULL;
1003
1004 return NGX_OK;
1005 }
1006
1007
1008 ngx_int_t
1009 ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri,
1010 ngx_str_t *args, ngx_uint_t *flags)
1011 {
1012 u_char ch, *p;
1013 size_t len;
1014
1015 len = uri->len;
1016 p = uri->data;
1017
1018 if (len == 0 || p[0] == '?') {
1019 goto unsafe;
1020 }
1021
1022 if (p[0] == '.' && len == 3 && p[1] == '.' && (p[2] == '/'
1023 #if (NGX_WIN32)
1024 || p[2] == '\\'
1025 #endif
1026 ))
1027 {
1028 goto unsafe;
1029 }
1030
1031 for ( /* void */ ; len; len--) {
1032
1033 ch = *p++;
1034
1035 if (ch == '?') {
1036 args->len = len - 1;
1037 args->data = p;
1038 uri->len -= len;
1039
1040 return NGX_OK;
1041 }
1042
1043 if (ch == '\0') {
1044 *flags |= NGX_HTTP_ZERO_IN_URI;
1045 continue;
1046 }
1047
1048 if (ch != '/'
1049 #if (NGX_WIN32)
1050 && ch != '\\'
1051 #endif
1052 )
1053 {
1054 continue;
1055 }
1056
1057 if (len > 2) {
1058
1059 /* detect "/../" */
1060
1061 if (p[0] == '.' && p[1] == '.' && p[2] == '/') {
1062 goto unsafe;
1063 }
1064
1065 #if (NGX_WIN32)
1066
1067 if (p[2] == '\\') {
1068 goto unsafe;
1069 }
1070
1071 if (len > 3) {
1072
1073 /* detect "/.../" */
1074
1075 if (p[0] == '.' && p[1] == '.' && p[2] == '.'
1076 && (p[3] == '/' || p[3] == '\\'))
1077 {
1078 goto unsafe;
1079 }
1080 }
1081 #endif
1082 }
1083 }
1084
1085 return NGX_OK;
1086
1087 unsafe:
1088
1089 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1090 "unsafe URI \"%V\" was detected", uri);
1091
1092 return NGX_ERROR;
1093 }
1094
1095
1096 ngx_int_t
1097 ngx_http_parse_multi_header_lines(ngx_array_t *headers, ngx_str_t *name,
1098 ngx_str_t *value)
1099 {
1100 ngx_uint_t i;
1101 u_char *start, *last, *end, ch;
1102 ngx_table_elt_t **h;
1103
1104 h = headers->elts;
1105
1106 for (i = 0; i < headers->nelts; i++) {
1107
1108 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, headers->pool->log, 0,
1109 "parse header: \"%V: %V\"", &h[i]->key, &h[i]->value);
1110
1111 if (name->len > h[i]->value.len) {
1112 continue;
1113 }
1114
1115 start = h[i]->value.data;
1116 end = h[i]->value.data + h[i]->value.len;
1117
1118 while (start < end) {
1119
1120 if (ngx_strncasecmp(start, name->data, name->len) != 0) {
1121 goto skip;
1122 }
1123
1124 for (start += name->len; start < end && *start == ' '; start++) {
1125 /* void */
1126 }
1127
1128 if (value == NULL) {
1129 if (start == end || *start == ',') {
1130 return i;
1131 }
1132
1133 goto skip;
1134 }
1135
1136 if (start == end || *start++ != '=') {
1137 /* the invalid header value */
1138 goto skip;
1139 }
1140
1141 while (start < end && *start == ' ') { start++; }
1142
1143 for (last = start; last < end && *last != ';'; last++) {
1144 /* void */
1145 }
1146
1147 value->len = last - start;
1148 value->data = start;
1149
1150 return i;
1151
1152 skip:
1153
1154 while (start < end) {
1155 ch = *start++;
1156 if (ch == ';' || ch == ',') {
1157 break;
1158 }
1159 }
1160
1161 while (start < end && *start == ' ') { start++; }
1162 }
1163 }
1164
1165 return NGX_DECLINED;
1166 }