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