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 static uint32_t usual[] = {
13 0xffffdbfe, /* 1111 1111 1111 1111 1101 1011 1111 1110 */
14
15 /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
16 0x7fff37d6, /* 0111 1111 1111 1111 0011 0111 1101 0110 */
17
18 /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
19 #if (NGX_WIN32)
20 0xefffffff, /* 1110 1111 1111 1111 1111 1111 1111 1111 */
21 #else
22 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
23 #endif
24
25 /* ~}| {zyx wvut srqp onml kjih gfed cba` */
26 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
27
28 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
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 };
33
34
35 #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
36
37 #define ngx_str3_cmp(m, c0, c1, c2, c3) \
38 *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
39
40 #define ngx_str3Ocmp(m, c0, c1, c2, c3) \
41 *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
42
43 #define ngx_str4cmp(m, c0, c1, c2, c3) \
44 *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
45
46 #define ngx_str5cmp(m, c0, c1, c2, c3, c4) \
47 *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \
48 && m[4] == c4
49
50 #define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5) \
51 *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \
52 && (((uint32_t *) m)[1] & 0xffff) == ((c5 << 8) | c4)
53
54 #define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \
55 *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \
56 && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)
57
58 #define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \
59 *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \
60 && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)
61
62 #define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8) \
63 *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \
64 && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4) \
65 && m[8] == c8
66
67 #else /* !(NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) */
68
69 #define ngx_str3_cmp(m, c0, c1, c2, c3) \
70 m[0] == c0 && m[1] == c1 && m[2] == c2
71
72 #define ngx_str3Ocmp(m, c0, c1, c2, c3) \
73 m[0] == c0 && m[2] == c2 && m[3] == c3
74
75 #define ngx_str4cmp(m, c0, c1, c2, c3) \
76 m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3
77
78 #define ngx_str5cmp(m, c0, c1, c2, c3, c4) \
79 m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4
80
81 #define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5) \
82 m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \
83 && m[4] == c4 && m[5] == c5
84
85 #define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \
86 m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \
87 && m[4] == c4 && m[5] == c5 && m[6] == c6
88
89 #define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \
90 m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \
91 && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7
92
93 #define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8) \
94 m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \
95 && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7 && m[8] == c8
96
97 #endif
98
99
100 /* gcc, icc, msvc and others compile these switches as an jump table */
101
102 ngx_int_t
103 ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
104 {
105 u_char c, ch, *p, *m;
106 enum {
107 sw_start = 0,
108 sw_method,
109 sw_spaces_before_uri,
110 sw_schema,
111 sw_schema_slash,
112 sw_schema_slash_slash,
113 sw_host,
114 sw_port,
115 sw_after_slash_in_uri,
116 sw_check_uri,
117 sw_uri,
118 sw_http_09,
119 sw_http_H,
120 sw_http_HT,
121 sw_http_HTT,
122 sw_http_HTTP,
123 sw_first_major_digit,
124 sw_major_digit,
125 sw_first_minor_digit,
126 sw_minor_digit,
127 sw_spaces_after_digit,
128 sw_almost_done
129 } state;
130
131 state = r->state;
132
133 for (p = b->pos; p < b->last; p++) {
134 ch = *p;
135
136 switch (state) {
137
138 /* HTTP methods: GET, HEAD, POST */
139 case sw_start:
140 r->request_start = p;
141
142 if (ch == CR || ch == LF) {
143 break;
144 }
145
146 if ((ch < 'A' || ch > 'Z') && ch != '_') {
147 return NGX_HTTP_PARSE_INVALID_METHOD;
148 }
149
150 state = sw_method;
151 break;
152
153 case sw_method:
154 if (ch == ' ') {
155 r->method_end = p - 1;
156 m = r->request_start;
157
158 switch (p - m) {
159
160 case 3:
161 if (ngx_str3_cmp(m, 'G', 'E', 'T', ' ')) {
162 r->method = NGX_HTTP_GET;
163 break;
164 }
165
166 if (ngx_str3_cmp(m, 'P', 'U', 'T', ' ')) {
167 r->method = NGX_HTTP_PUT;
168 break;
169 }
170
171 break;
172
173 case 4:
174 if (m[1] == 'O') {
175
176 if (ngx_str3Ocmp(m, 'P', 'O', 'S', 'T')) {
177 r->method = NGX_HTTP_POST;
178 break;
179 }
180
181 if (ngx_str3Ocmp(m, 'C', 'O', 'P', 'Y')) {
182 r->method = NGX_HTTP_COPY;
183 break;
184 }
185
186 if (ngx_str3Ocmp(m, 'M', 'O', 'V', 'E')) {
187 r->method = NGX_HTTP_MOVE;
188 break;
189 }
190
191 if (ngx_str3Ocmp(m, 'L', 'O', 'C', 'K')) {
192 r->method = NGX_HTTP_LOCK;
193 break;
194 }
195
196 } else {
197
198 if (ngx_str4cmp(m, 'H', 'E', 'A', 'D')) {
199 r->method = NGX_HTTP_HEAD;
200 break;
201 }
202 }
203
204 break;
205
206 case 5:
207 if (ngx_str5cmp(m, 'M', 'K', 'C', 'O', 'L')) {
208 r->method = NGX_HTTP_MKCOL;
209 }
210
211 if (ngx_str5cmp(m, 'T', 'R', 'A', 'C', 'E')) {
212 r->method = NGX_HTTP_TRACE;
213 }
214
215 break;
216
217 case 6:
218 if (ngx_str6cmp(m, 'D', 'E', 'L', 'E', 'T', 'E')) {
219 r->method = NGX_HTTP_DELETE;
220 break;
221 }
222
223 if (ngx_str6cmp(m, 'U', 'N', 'L', 'O', 'C', 'K')) {
224 r->method = NGX_HTTP_UNLOCK;
225 break;
226 }
227
228 break;
229
230 case 7:
231 if (ngx_str7_cmp(m, 'O', 'P', 'T', 'I', 'O', 'N', 'S', ' '))
232 {
233 r->method = NGX_HTTP_OPTIONS;
234 }
235
236 break;
237
238 case 8:
239 if (ngx_str8cmp(m, 'P', 'R', 'O', 'P', 'F', 'I', 'N', 'D'))
240 {
241 r->method = NGX_HTTP_PROPFIND;
242 }
243
244 break;
245
246 case 9:
247 if (ngx_str9cmp(m,
248 'P', 'R', 'O', 'P', 'P', 'A', 'T', 'C', 'H'))
249 {
250 r->method = NGX_HTTP_PROPPATCH;
251 }
252
253 break;
254 }
255
256 state = sw_spaces_before_uri;
257 break;
258 }
259
260 if ((ch < 'A' || ch > 'Z') && ch != '_') {
261 return NGX_HTTP_PARSE_INVALID_METHOD;
262 }
263
264 break;
265
266 /* space* before URI */
267 case sw_spaces_before_uri:
268
269 if (ch == '/' ){
270 r->uri_start = p;
271 state = sw_after_slash_in_uri;
272 break;
273 }
274
275 c = (u_char) (ch | 0x20);
276 if (c >= 'a' && c <= 'z') {
277 r->schema_start = p;
278 state = sw_schema;
279 break;
280 }
281
282 switch (ch) {
283 case ' ':
284 break;
285 default:
286 return NGX_HTTP_PARSE_INVALID_REQUEST;
287 }
288 break;
289
290 case sw_schema:
291
292 c = (u_char) (ch | 0x20);
293 if (c >= 'a' && c <= 'z') {
294 break;
295 }
296
297 switch (ch) {
298 case ':':
299 r->schema_end = p;
300 state = sw_schema_slash;
301 break;
302 default:
303 return NGX_HTTP_PARSE_INVALID_REQUEST;
304 }
305 break;
306
307 case sw_schema_slash:
308 switch (ch) {
309 case '/':
310 state = sw_schema_slash_slash;
311 break;
312 default:
313 return NGX_HTTP_PARSE_INVALID_REQUEST;
314 }
315 break;
316
317 case sw_schema_slash_slash:
318 switch (ch) {
319 case '/':
320 r->host_start = p + 1;
321 state = sw_host;
322 break;
323 default:
324 return NGX_HTTP_PARSE_INVALID_REQUEST;
325 }
326 break;
327
328 case sw_host:
329
330 c = (u_char) (ch | 0x20);
331 if (c >= 'a' && c <= 'z') {
332 break;
333 }
334
335 if ((ch >= '' && ch <= '9') || ch == '.' || ch == '-') {
336 break;
337 }
338
339 r->host_end = p;
340
341 switch (ch) {
342 case ':':
343 state = sw_port;
344 break;
345 case '/':
346 r->uri_start = p;
347 state = sw_after_slash_in_uri;
348 break;
349 case ' ':
350 /*
351 * use single "/" from request line to preserve pointers,
352 * if request line will be copied to large client buffer
353 */
354 r->uri_start = r->schema_end + 1;
355 r->uri_end = r->schema_end + 2;
356 state = sw_http_09;
357 break;
358 default:
359 return NGX_HTTP_PARSE_INVALID_REQUEST;
360 }
361 break;
362
363 case sw_port:
364 if (ch >= '' && ch <= '9') {
365 break;
366 }
367
368 switch (ch) {
369 case '/':
370 r->port_end = p;
371 r->uri_start = p;
372 state = sw_after_slash_in_uri;
373 break;
374 case ' ':
375 r->port_end = p;
376 /*
377 * use single "/" from request line to preserve pointers,
378 * if request line will be copied to large client buffer
379 */
380 r->uri_start = r->schema_end + 1;
381 r->uri_end = r->schema_end + 2;
382 state = sw_http_09;
383 break;
384 default:
385 return NGX_HTTP_PARSE_INVALID_REQUEST;
386 }
387 break;
388
389 /* check "/.", "//", "%", and "\" (Win32) in URI */
390 case sw_after_slash_in_uri:
391
392 if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
393 state = sw_check_uri;
394 break;
395 }
396
397 switch (ch) {
398 case ' ':
399 r->uri_end = p;
400 state = sw_http_09;
401 break;
402 case CR:
403 r->uri_end = p;
404 r->http_minor = 9;
405 state = sw_almost_done;
406 break;
407 case LF:
408 r->uri_end = p;
409 r->http_minor = 9;
410 goto done;
411 case '.':
412 r->complex_uri = 1;
413 state = sw_uri;
414 break;
415 case '%':
416 r->quoted_uri = 1;
417 state = sw_uri;
418 break;
419 case '/':
420 r->complex_uri = 1;
421 state = sw_uri;
422 break;
423 #if (NGX_WIN32)
424 case '\\':
425 r->complex_uri = 1;
426 state = sw_uri;
427 break;
428 #endif
429 case '?':
430 r->args_start = p + 1;
431 state = sw_uri;
432 break;
433 case '#':
434 r->complex_uri = 1;
435 state = sw_uri;
436 break;
437 case '+':
438 r->plus_in_uri = 1;
439 break;
440 case '\0':
441 r->zero_in_uri = 1;
442 break;
443 default:
444 state = sw_check_uri;
445 break;
446 }
447 break;
448
449 /* check "/", "%" and "\" (Win32) in URI */
450 case sw_check_uri:
451
452 if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
453 break;
454 }
455
456 switch (ch) {
457 case '/':
458 r->uri_ext = NULL;
459 state = sw_after_slash_in_uri;
460 break;
461 case '.':
462 r->uri_ext = p + 1;
463 break;
464 case ' ':
465 r->uri_end = p;
466 state = sw_http_09;
467 break;
468 case CR:
469 r->uri_end = p;
470 r->http_minor = 9;
471 state = sw_almost_done;
472 break;
473 case LF:
474 r->uri_end = p;
475 r->http_minor = 9;
476 goto done;
477 #if (NGX_WIN32)
478 case '\\':
479 r->complex_uri = 1;
480 state = sw_after_slash_in_uri;
481 break;
482 #endif
483 case '%':
484 r->quoted_uri = 1;
485 state = sw_uri;
486 break;
487 case '?':
488 r->args_start = p + 1;
489 state = sw_uri;
490 break;
491 case '#':
492 r->complex_uri = 1;
493 state = sw_uri;
494 break;
495 case '+':
496 r->plus_in_uri = 1;
497 break;
498 case '\0':
499 r->zero_in_uri = 1;
500 break;
501 }
502 break;
503
504 /* URI */
505 case sw_uri:
506
507 if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
508 break;
509 }
510
511 switch (ch) {
512 case ' ':
513 r->uri_end = p;
514 state = sw_http_09;
515 break;
516 case CR:
517 r->uri_end = p;
518 r->http_minor = 9;
519 state = sw_almost_done;
520 break;
521 case LF:
522 r->uri_end = p;
523 r->http_minor = 9;
524 goto done;
525 case '#':
526 r->complex_uri = 1;
527 break;
528 case '\0':
529 r->zero_in_uri = 1;
530 break;
531 }
532 break;
533
534 /* space+ after URI */
535 case sw_http_09:
536 switch (ch) {
537 case ' ':
538 break;
539 case CR:
540 r->http_minor = 9;
541 state = sw_almost_done;
542 break;
543 case LF:
544 r->http_minor = 9;
545 goto done;
546 case 'H':
547 r->http_protocol.data = p;
548 state = sw_http_H;
549 break;
550 default:
551 return NGX_HTTP_PARSE_INVALID_REQUEST;
552 }
553 break;
554
555 case sw_http_H:
556 switch (ch) {
557 case 'T':
558 state = sw_http_HT;
559 break;
560 default:
561 return NGX_HTTP_PARSE_INVALID_REQUEST;
562 }
563 break;
564
565 case sw_http_HT:
566 switch (ch) {
567 case 'T':
568 state = sw_http_HTT;
569 break;
570 default:
571 return NGX_HTTP_PARSE_INVALID_REQUEST;
572 }
573 break;
574
575 case sw_http_HTT:
576 switch (ch) {
577 case 'P':
578 state = sw_http_HTTP;
579 break;
580 default:
581 return NGX_HTTP_PARSE_INVALID_REQUEST;
582 }
583 break;
584
585 case sw_http_HTTP:
586 switch (ch) {
587 case '/':
588 state = sw_first_major_digit;
589 break;
590 default:
591 return NGX_HTTP_PARSE_INVALID_REQUEST;
592 }
593 break;
594
595 /* first digit of major HTTP version */
596 case sw_first_major_digit:
597 if (ch < '1' || ch > '9') {
598 return NGX_HTTP_PARSE_INVALID_REQUEST;
599 }
600
601 r->http_major = ch - '';
602 state = sw_major_digit;
603 break;
604
605 /* major HTTP version or dot */
606 case sw_major_digit:
607 if (ch == '.') {
608 state = sw_first_minor_digit;
609 break;
610 }
611
612 if (ch < '' || ch > '9') {
613 return NGX_HTTP_PARSE_INVALID_REQUEST;
614 }
615
616 r->http_major = r->http_major * 10 + ch - '';
617 break;
618
619 /* first digit of minor HTTP version */
620 case sw_first_minor_digit:
621 if (ch < '' || ch > '9') {
622 return NGX_HTTP_PARSE_INVALID_REQUEST;
623 }
624
625 r->http_minor = ch - '';
626 state = sw_minor_digit;
627 break;
628
629 /* minor HTTP version or end of request line */
630 case sw_minor_digit:
631 if (ch == CR) {
632 state = sw_almost_done;
633 break;
634 }
635
636 if (ch == LF) {
637 goto done;
638 }
639
640 if (ch == ' ') {
641 state = sw_spaces_after_digit;
642 break;
643 }
644
645 if (ch < '' || ch > '9') {
646 return NGX_HTTP_PARSE_INVALID_REQUEST;
647 }
648
649 r->http_minor = r->http_minor * 10 + ch - '';
650 break;
651
652 case sw_spaces_after_digit:
653 switch (ch) {
654 case ' ':
655 break;
656 case CR:
657 state = sw_almost_done;
658 break;
659 case LF:
660 goto done;
661 default:
662 return NGX_HTTP_PARSE_INVALID_REQUEST;
663 }
664 break;
665
666 /* end of request line */
667 case sw_almost_done:
668 r->request_end = p - 1;
669 switch (ch) {
670 case LF:
671 goto done;
672 default:
673 return NGX_HTTP_PARSE_INVALID_REQUEST;
674 }
675 }
676 }
677
678 b->pos = p;
679 r->state = state;
680
681 return NGX_AGAIN;
682
683 done:
684
685 b->pos = p + 1;
686
687 if (r->request_end == NULL) {
688 r->request_end = p;
689 }
690
691 r->http_version = r->http_major * 1000 + r->http_minor;
692 r->state = sw_start;
693
694 if (r->http_version == 9 && r->method != NGX_HTTP_GET) {
695 return NGX_HTTP_PARSE_INVALID_09_METHOD;
696 }
697
698 return NGX_OK;
699 }
700
701
702 ngx_int_t
703 ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b,
704 ngx_uint_t allow_underscores)
705 {
706 u_char c, ch, *p;
707 ngx_uint_t hash, i;
708 enum {
709 sw_start = 0,
710 sw_name,
711 sw_space_before_value,
712 sw_value,
713 sw_space_after_value,
714 sw_ignore_line,
715 sw_almost_done,
716 sw_header_almost_done
717 } state;
718
719 /* the last '\0' is not needed because string is zero terminated */
720
721 static u_char lowcase[] =
722 "\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"
723 "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
724 "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
725 "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
726 "\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"
727 "\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"
728 "\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"
729 "\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";
730
731 state = r->state;
732 hash = r->header_hash;
733 i = r->lowcase_index;
734
735 for (p = b->pos; p < b->last; p++) {
736 ch = *p;
737
738 switch (state) {
739
740 /* first char */
741 case sw_start:
742 r->header_name_start = p;
743 r->invalid_header = 0;
744
745 switch (ch) {
746 case CR:
747 r->header_end = p;
748 state = sw_header_almost_done;
749 break;
750 case LF:
751 r->header_end = p;
752 goto header_done;
753 default:
754 state = sw_name;
755
756 c = lowcase[ch];
757
758 if (c) {
759 hash = ngx_hash(0, c);
760 r->lowcase_header[0] = c;
761 i = 1;
762 break;
763 }
764
765 r->invalid_header = 1;
766
767 break;
768
769 }
770 break;
771
772 /* header name */
773 case sw_name:
774 c = lowcase[ch];
775
776 if (c) {
777 hash = ngx_hash(hash, c);
778 r->lowcase_header[i++] = c;
779 i &= (NGX_HTTP_LC_HEADER_LEN - 1);
780 break;
781 }
782
783 if (ch == '_') {
784 if (allow_underscores) {
785 hash = ngx_hash(hash, ch);
786 r->lowcase_header[i++] = ch;
787 i &= (NGX_HTTP_LC_HEADER_LEN - 1);
788
789 } else {
790 r->invalid_header = 1;
791 }
792
793 break;
794 }
795
796 if (ch == ':') {
797 r->header_name_end = p;
798 state = sw_space_before_value;
799 break;
800 }
801
802 if (ch == CR) {
803 r->header_name_end = p;
804 r->header_start = p;
805 r->header_end = p;
806 state = sw_almost_done;
807 break;
808 }
809
810 if (ch == LF) {
811 r->header_name_end = p;
812 r->header_start = p;
813 r->header_end = p;
814 goto done;
815 }
816
817 /* IIS may send the duplicate "HTTP/1.1 ..." lines */
818 if (ch == '/'
819 && r->upstream
820 && p - r->header_name_start == 4
821 && ngx_strncmp(r->header_name_start, "HTTP", 4) == 0)
822 {
823 state = sw_ignore_line;
824 break;
825 }
826
827 r->invalid_header = 1;
828
829 break;
830
831 /* space* before header value */
832 case sw_space_before_value:
833 switch (ch) {
834 case ' ':
835 break;
836 case CR:
837 r->header_start = p;
838 r->header_end = p;
839 state = sw_almost_done;
840 break;
841 case LF:
842 r->header_start = p;
843 r->header_end = p;
844 goto done;
845 default:
846 r->header_start = p;
847 state = sw_value;
848 break;
849 }
850 break;
851
852 /* header value */
853 case sw_value:
854 switch (ch) {
855 case ' ':
856 r->header_end = p;
857 state = sw_space_after_value;
858 break;
859 case CR:
860 r->header_end = p;
861 state = sw_almost_done;
862 break;
863 case LF:
864 r->header_end = p;
865 goto done;
866 }
867 break;
868
869 /* space* before end of header line */
870 case sw_space_after_value:
871 switch (ch) {
872 case ' ':
873 break;
874 case CR:
875 state = sw_almost_done;
876 break;
877 case LF:
878 goto done;
879 default:
880 state = sw_value;
881 break;
882 }
883 break;
884
885 /* ignore header line */
886 case sw_ignore_line:
887 switch (ch) {
888 case LF:
889 state = sw_start;
890 break;
891 default:
892 break;
893 }
894 break;
895
896 /* end of header line */
897 case sw_almost_done:
898 switch (ch) {
899 case LF:
900 goto done;
901 case CR:
902 break;
903 default:
904 return NGX_HTTP_PARSE_INVALID_HEADER;
905 }
906 break;
907
908 /* end of header */
909 case sw_header_almost_done:
910 switch (ch) {
911 case LF:
912 goto header_done;
913 default:
914 return NGX_HTTP_PARSE_INVALID_HEADER;
915 }
916 }
917 }
918
919 b->pos = p;
920 r->state = state;
921 r->header_hash = hash;
922 r->lowcase_index = i;
923
924 return NGX_AGAIN;
925
926 done:
927
928 b->pos = p + 1;
929 r->state = sw_start;
930 r->header_hash = hash;
931 r->lowcase_index = i;
932
933 return NGX_OK;
934
935 header_done:
936
937 b->pos = p + 1;
938 r->state = sw_start;
939
940 return NGX_HTTP_PARSE_HEADER_DONE;
941 }
942
943
944 ngx_int_t
945 ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes)
946 {
947 u_char c, ch, decoded, *p, *u;
948 enum {
949 sw_usual = 0,
950 sw_slash,
951 sw_dot,
952 sw_dot_dot,
953 sw_quoted,
954 sw_quoted_second
955 } state, quoted_state;
956
957 #if (NGX_SUPPRESS_WARN)
958 decoded = '\0';
959 quoted_state = sw_usual;
960 #endif
961
962 state = sw_usual;
963 p = r->uri_start;
964 u = r->uri.data;
965 r->uri_ext = NULL;
966 r->args_start = NULL;
967
968 ch = *p++;
969
970 while (p <= r->uri_end) {
971
972 /*
973 * we use "ch = *p++" inside the cycle, but this operation is safe,
974 * because after the URI there is always at least one charcter:
975 * the line feed
976 */
977
978 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
979 "s:%d in:'%Xd:%c', out:'%c'", state, ch, ch, *u);
980
981 switch (state) {
982
983 case sw_usual:
984
985 if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
986 *u++ = ch;
987 ch = *p++;
988 break;
989 }
990
991 switch(ch) {
992 #if (NGX_WIN32)
993 case '\\':
994 r->uri_ext = NULL;
995
996 if (p == r->uri_start + r->uri.len) {
997
998 /*
999 * we omit the last "\" to cause redirect because
1000 * the browsers do not treat "\" as "/" in relative URL path
1001 */
1002
1003 break;
1004 }
1005
1006 state = sw_slash;
1007 *u++ = '/';
1008 break;
1009 #endif
1010 case '/':
1011 r->uri_ext = NULL;
1012 state = sw_slash;
1013 *u++ = ch;
1014 break;
1015 case '%':
1016 quoted_state = state;
1017 state = sw_quoted;
1018 break;
1019 case '?':
1020 r->args_start = p;
1021 goto args;
1022 case '#':
1023 goto done;
1024 case '.':
1025 r->uri_ext = u + 1;
1026 *u++ = ch;
1027 break;
1028 case '+':
1029 r->plus_in_uri = 1;
1030 default:
1031 *u++ = ch;
1032 break;
1033 }
1034
1035 ch = *p++;
1036 break;
1037
1038 case sw_slash:
1039
1040 if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1041 state = sw_usual;
1042 *u++ = ch;
1043 ch = *p++;
1044 break;
1045 }
1046
1047 switch(ch) {
1048 #if (NGX_WIN32)
1049 case '\\':
1050 break;
1051 #endif
1052 case '/':
1053 if (!merge_slashes) {
1054 *u++ = ch;
1055 }
1056 break;
1057 case '.':
1058 state = sw_dot;
1059 *u++ = ch;
1060 break;
1061 case '%':
1062 quoted_state = state;
1063 state = sw_quoted;
1064 break;
1065 case '?':
1066 r->args_start = p;
1067 goto args;
1068 case '#':
1069 goto done;
1070 case '+':
1071 r->plus_in_uri = 1;
1072 default:
1073 state = sw_usual;
1074 *u++ = ch;
1075 break;
1076 }
1077
1078 ch = *p++;
1079 break;
1080
1081 case sw_dot:
1082
1083 if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1084 state = sw_usual;
1085 *u++ = ch;
1086 ch = *p++;
1087 break;
1088 }
1089
1090 switch(ch) {
1091 #if (NGX_WIN32)
1092 case '\\':
1093 #endif
1094 case '/':
1095 state = sw_slash;
1096 u--;
1097 break;
1098 case '.':
1099 state = sw_dot_dot;
1100 *u++ = ch;
1101 break;
1102 case '%':
1103 quoted_state = state;
1104 state = sw_quoted;
1105 break;
1106 case '?':
1107 r->args_start = p;
1108 goto args;
1109 case '#':
1110 goto done;
1111 case '+':
1112 r->plus_in_uri = 1;
1113 default:
1114 state = sw_usual;
1115 *u++ = ch;
1116 break;
1117 }
1118
1119 ch = *p++;
1120 break;
1121
1122 case sw_dot_dot:
1123
1124 if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1125 state = sw_usual;
1126 *u++ = ch;
1127 ch = *p++;
1128 break;
1129 }
1130
1131 switch(ch) {
1132 #if (NGX_WIN32)
1133 case '\\':
1134 #endif
1135 case '/':
1136 state = sw_slash;
1137 u -= 5;
1138 for ( ;; ) {
1139 if (u < r->uri.data) {
1140 return NGX_HTTP_PARSE_INVALID_REQUEST;
1141 }
1142 if (*u == '/') {
1143 u++;
1144 break;
1145 }
1146 u--;
1147 }
1148 break;
1149 case '%':
1150 quoted_state = state;
1151 state = sw_quoted;
1152 break;
1153 case '?':
1154 r->args_start = p;
1155 goto args;
1156 case '#':
1157 goto done;
1158 case '+':
1159 r->plus_in_uri = 1;
1160 default:
1161 state = sw_usual;
1162 *u++ = ch;
1163 break;
1164 }
1165
1166 ch = *p++;
1167 break;
1168
1169 case sw_quoted:
1170 r->quoted_uri = 1;
1171
1172 if (ch >= '' && ch <= '9') {
1173 decoded = (u_char) (ch - '');
1174 state = sw_quoted_second;
1175 ch = *p++;
1176 break;
1177 }
1178
1179 c = (u_char) (ch | 0x20);
1180 if (c >= 'a' && c <= 'f') {
1181 decoded = (u_char) (c - 'a' + 10);
1182 state = sw_quoted_second;
1183 ch = *p++;
1184 break;
1185 }
1186
1187 return NGX_HTTP_PARSE_INVALID_REQUEST;
1188
1189 case sw_quoted_second:
1190 if (ch >= '' && ch <= '9') {
1191 ch = (u_char) ((decoded << 4) + ch - '');
1192
1193 if (ch == '%') {
1194 state = sw_usual;
1195 *u++ = ch;
1196 ch = *p++;
1197 break;
1198 }
1199
1200 if (ch == '#') {
1201 *u++ = ch;
1202 ch = *p++;
1203
1204 } else if (ch == '\0') {
1205 r->zero_in_uri = 1;
1206 }
1207
1208 state = quoted_state;
1209 break;
1210 }
1211
1212 c = (u_char) (ch | 0x20);
1213 if (c >= 'a' && c <= 'f') {
1214 ch = (u_char) ((decoded << 4) + c - 'a' + 10);
1215
1216 if (ch == '?') {
1217 *u++ = ch;
1218 ch = *p++;
1219
1220 } else if (ch == '+') {
1221 r->plus_in_uri = 1;
1222 }
1223
1224 state = quoted_state;
1225 break;
1226 }
1227
1228 return NGX_HTTP_PARSE_INVALID_REQUEST;
1229 }
1230 }
1231
1232 done:
1233
1234 r->uri.len = u - r->uri.data;
1235
1236 if (r->uri_ext) {
1237 r->exten.len = u - r->uri_ext;
1238 r->exten.data = r->uri_ext;
1239 }
1240
1241 r->uri_ext = NULL;
1242
1243 return NGX_OK;
1244
1245 args:
1246
1247 while (p < r->uri_end) {
1248 if (*p++ != '#') {
1249 continue;
1250 }
1251
1252 r->args.len = p - 1 - r->args_start;
1253 r->args.data = r->args_start;
1254 r->args_start = NULL;
1255
1256 break;
1257 }
1258
1259 r->uri.len = u - r->uri.data;
1260
1261 if (r->uri_ext) {
1262 r->exten.len = u - r->uri_ext;
1263 r->exten.data = r->uri_ext;
1264 }
1265
1266 r->uri_ext = NULL;
1267
1268 return NGX_OK;
1269 }
1270
1271
1272 ngx_int_t
1273 ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri,
1274 ngx_str_t *args, ngx_uint_t *flags)
1275 {
1276 u_char ch, *p;
1277 size_t len;
1278
1279 len = uri->len;
1280 p = uri->data;
1281
1282 if (len == 0 || p[0] == '?') {
1283 goto unsafe;
1284 }
1285
1286 if (p[0] == '.' && len == 3 && p[1] == '.' && (ngx_path_separator(p[2]))) {
1287 goto unsafe;
1288 }
1289
1290 for ( /* void */ ; len; len--) {
1291
1292 ch = *p++;
1293
1294 if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1295 continue;
1296 }
1297
1298 if (ch == '?') {
1299 args->len = len - 1;
1300 args->data = p;
1301 uri->len -= len;
1302
1303 return NGX_OK;
1304 }
1305
1306 if (ch == '\0') {
1307 *flags |= NGX_HTTP_ZERO_IN_URI;
1308 continue;
1309 }
1310
1311 if (ngx_path_separator(ch) && len > 2) {
1312
1313 /* detect "/../" */
1314
1315 if (p[0] == '.' && p[1] == '.' && ngx_path_separator(p[2])) {
1316 goto unsafe;
1317 }
1318 }
1319 }
1320
1321 return NGX_OK;
1322
1323 unsafe:
1324
1325 if (*flags & NGX_HTTP_LOG_UNSAFE) {
1326 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1327 "unsafe URI \"%V\" was detected", uri);
1328 }
1329
1330 return NGX_ERROR;
1331 }
1332
1333
1334 ngx_int_t
1335 ngx_http_parse_multi_header_lines(ngx_array_t *headers, ngx_str_t *name,
1336 ngx_str_t *value)
1337 {
1338 ngx_uint_t i;
1339 u_char *start, *last, *end, ch;
1340 ngx_table_elt_t **h;
1341
1342 h = headers->elts;
1343
1344 for (i = 0; i < headers->nelts; i++) {
1345
1346 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, headers->pool->log, 0,
1347 "parse header: \"%V: %V\"", &h[i]->key, &h[i]->value);
1348
1349 if (name->len > h[i]->value.len) {
1350 continue;
1351 }
1352
1353 start = h[i]->value.data;
1354 end = h[i]->value.data + h[i]->value.len;
1355
1356 while (start < end) {
1357
1358 if (ngx_strncasecmp(start, name->data, name->len) != 0) {
1359 goto skip;
1360 }
1361
1362 for (start += name->len; start < end && *start == ' '; start++) {
1363 /* void */
1364 }
1365
1366 if (value == NULL) {
1367 if (start == end || *start == ',') {
1368 return i;
1369 }
1370
1371 goto skip;
1372 }
1373
1374 if (start == end || *start++ != '=') {
1375 /* the invalid header value */
1376 goto skip;
1377 }
1378
1379 while (start < end && *start == ' ') { start++; }
1380
1381 for (last = start; last < end && *last != ';'; last++) {
1382 /* void */
1383 }
1384
1385 value->len = last - start;
1386 value->data = start;
1387
1388 return i;
1389
1390 skip:
1391
1392 while (start < end) {
1393 ch = *start++;
1394 if (ch == ';' || ch == ',') {
1395 break;
1396 }
1397 }
1398
1399 while (start < end && *start == ' ') { start++; }
1400 }
1401 }
1402
1403 return NGX_DECLINED;
1404 }
1405
1406
1407 ngx_int_t
1408 ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value)
1409 {
1410 u_char *p, *last;
1411
1412 if (r->args.len == 0) {
1413 return NGX_DECLINED;
1414 }
1415
1416 p = r->args.data;
1417 last = p + r->args.len;
1418
1419 for ( /* void */ ; p < last; p++) {
1420
1421 /* we need '=' after name, so drop one char from last */
1422
1423 p = ngx_strlcasestrn(p, last - 1, name, len - 1);
1424
1425 if (p == NULL) {
1426 return NGX_DECLINED;
1427 }
1428
1429 if ((p == r->args.data || *(p - 1) == '&') && *(p + len) == '=') {
1430
1431 value->data = p + len + 1;
1432
1433 p = ngx_strlchr(p, last, '&');
1434
1435 if (p == NULL) {
1436 p = r->args.data + r->args.len;
1437 }
1438
1439 value->len = p - value->data;
1440
1441 return NGX_OK;
1442 }
1443 }
1444
1445 return NGX_DECLINED;
1446 }
1447
1448
1449 void
1450 ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args)
1451 {
1452 u_char ch, *p, *last;
1453
1454 p = uri->data;
1455
1456 last = p + uri->len;
1457
1458 args->len = 0;
1459
1460 while (p < last) {
1461
1462 ch = *p++;
1463
1464 if (ch == '?') {
1465 args->len = last - p;
1466 args->data = p;
1467
1468 uri->len = p - 1 - uri->data;
1469
1470 if (ngx_strlchr(p, last, '\0') != NULL) {
1471 r->zero_in_uri = 1;
1472 }
1473
1474 return;
1475 }
1476
1477 if (ch == '\0') {
1478 r->zero_in_uri = 1;
1479 continue;
1480 }
1481 }
1482 }
1483
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.