Mercurial > hg > nginx-vendor-0-6
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 } |