1 #include "first.h" 2 3 #undef NDEBUG 4 #include <assert.h> 5 #include <stdlib.h> 6 #include <stdio.h> 7 #include <string.h> 8 9 #include "request.c" 10 11 static void test_request_reset(request_st * const r) 12 { 13 r->http_method = HTTP_METHOD_UNSET; 14 r->http_version = HTTP_VERSION_UNSET; 15 r->http_host = NULL; 16 r->rqst_htags = 0; 17 r->reqbody_length = 0; 18 buffer_clear(&r->target_orig); 19 buffer_clear(&r->target); 20 array_reset_data_strings(&r->rqst_headers); 21 } 22 23 static void run_http_request_parse(request_st * const r, int line, int status, const char *desc, const char *req, size_t reqlen) 24 { 25 unsigned short hloffsets[32]; 26 char hdrs[1024]; 27 test_request_reset(r); 28 assert(reqlen < sizeof(hdrs)); 29 memcpy(hdrs, req, reqlen); 30 hloffsets[0] = 1; 31 hloffsets[1] = 0; 32 for (const char *n=req, *end=req+reqlen; (n=memchr(n,'\n',end-n)); ++n) { 33 if (++hloffsets[0] >= sizeof(hloffsets)/sizeof(*hloffsets)) break; 34 hloffsets[hloffsets[0]] = n - req + 1; 35 } 36 --hloffsets[0]; /*(ignore final blank line "\r\n" ending headers)*/ 37 const int proto_default_port = 80; 38 int http_status = 39 http_request_parse_hoff(r, hdrs, hloffsets, proto_default_port); 40 if (http_status != status) { 41 fprintf(stderr, 42 "%s.%d: %s() failed: expected '%d', got '%d' for test %s\n", 43 __FILE__, line, "http_request_parse_hoff", status, http_status, 44 desc); 45 fflush(stderr); 46 abort(); 47 } 48 } 49 50 static void test_request_http_request_parse(request_st * const r) 51 { 52 data_string *ds; 53 54 run_http_request_parse(r, __LINE__, 400, 55 "invalid request-line: space", 56 CONST_STR_LEN(" \r\n" 57 "Host: www.example.org\r\n" 58 "\r\n")); 59 60 run_http_request_parse(r, __LINE__, 400, 61 "invalid request-line: space, char", 62 CONST_STR_LEN(" a\r\n" 63 "Host: www.example.org\r\n" 64 "\r\n")); 65 66 run_http_request_parse(r, __LINE__, 400, 67 "invalid request-line: dot", 68 CONST_STR_LEN(".\r\n" 69 "Host: www.example.org\r\n" 70 "\r\n")); 71 72 run_http_request_parse(r, __LINE__, 400, 73 "invalid request-line: single char", 74 CONST_STR_LEN("a\r\n" 75 "Host: www.example.org\r\n" 76 "\r\n")); 77 78 run_http_request_parse(r, __LINE__, 400, 79 "invalid request-line: char, space", 80 CONST_STR_LEN("a \r\n" 81 "Host: www.example.org\r\n" 82 "\r\n")); 83 84 run_http_request_parse(r, __LINE__, 400, 85 "invalid request-line: method only", 86 CONST_STR_LEN("GET\r\n" 87 "Host: www.example.org\r\n" 88 "\r\n")); 89 90 run_http_request_parse(r, __LINE__, 400, 91 "invalid request-line: method space", 92 CONST_STR_LEN("GET \r\n" 93 "Host: www.example.org\r\n" 94 "\r\n")); 95 96 run_http_request_parse(r, __LINE__, 400, 97 "invalid request-line: method space space", 98 CONST_STR_LEN("GET \r\n" 99 "Host: www.example.org\r\n" 100 "\r\n")); 101 102 run_http_request_parse(r, __LINE__, 400, 103 "invalid request-line: method space proto", 104 CONST_STR_LEN("GET HTTP/1.0\r\n" 105 "Host: www.example.org\r\n" 106 "\r\n")); 107 108 run_http_request_parse(r, __LINE__, 400, 109 "invalid request-line: method space space proto", 110 CONST_STR_LEN("GET HTTP/1.0\r\n" 111 "Host: www.example.org\r\n" 112 "\r\n")); 113 114 run_http_request_parse(r, __LINE__, 400, 115 "invalid request-line: method space space space proto", 116 CONST_STR_LEN("GET HTTP/1.0\r\n" 117 "Host: www.example.org\r\n" 118 "\r\n")); 119 120 run_http_request_parse(r, __LINE__, 400, 121 "invalid request-line: method slash proto, no spaces", 122 CONST_STR_LEN("GET/HTTP/1.0\r\n" 123 "Host: www.example.org\r\n" 124 "\r\n")); 125 126 run_http_request_parse(r, __LINE__, 400, 127 "invalid request-line: method space slash proto", 128 CONST_STR_LEN("GET /HTTP/1.0\r\n" 129 "Host: www.example.org\r\n" 130 "\r\n")); 131 132 run_http_request_parse(r, __LINE__, 400, 133 "invalid request-line: method space space slash proto", 134 CONST_STR_LEN("GET /HTTP/1.0\r\n" 135 "Host: www.example.org\r\n" 136 "\r\n")); 137 138 run_http_request_parse(r, __LINE__, 501, 139 "invalid request-line: method slash space proto", 140 CONST_STR_LEN("GET/ HTTP/1.0\r\n" 141 "Host: www.example.org\r\n" 142 "\r\n")); 143 144 run_http_request_parse(r, __LINE__, 501, 145 "invalid request-line: method slash space space proto", 146 CONST_STR_LEN("GET/ HTTP/1.0\r\n" 147 "Host: www.example.org\r\n" 148 "\r\n")); 149 150 run_http_request_parse(r, __LINE__, 0, 151 "hostname", 152 CONST_STR_LEN("GET / HTTP/1.0\r\n" 153 "Host: www.example.org\r\n" 154 "\r\n")); 155 assert(buffer_eq_slen(r->http_host, CONST_STR_LEN("www.example.org"))); 156 157 run_http_request_parse(r, __LINE__, 0, 158 "IPv4 address", 159 CONST_STR_LEN("GET / HTTP/1.0\r\n" 160 "Host: 127.0.0.1\r\n" 161 "\r\n")); 162 assert(buffer_eq_slen(r->http_host, CONST_STR_LEN("127.0.0.1"))); 163 164 run_http_request_parse(r, __LINE__, 0, 165 "IPv6 address", 166 CONST_STR_LEN("GET / HTTP/1.0\r\n" 167 "Host: [::1]\r\n" 168 "\r\n")); 169 assert(buffer_eq_slen(r->http_host, CONST_STR_LEN("[::1]"))); 170 171 run_http_request_parse(r, __LINE__, 0, 172 "hostname + port", 173 CONST_STR_LEN("GET / HTTP/1.0\r\n" 174 "Host: www.example.org:80\r\n" 175 "\r\n")); 176 assert(buffer_eq_slen(r->http_host, CONST_STR_LEN("www.example.org"))); 177 178 run_http_request_parse(r, __LINE__, 0, 179 "IPv4 address + port", 180 CONST_STR_LEN("GET / HTTP/1.0\r\n" 181 "Host: 127.0.0.1:80\r\n" 182 "\r\n")); 183 assert(buffer_eq_slen(r->http_host, CONST_STR_LEN("127.0.0.1"))); 184 185 run_http_request_parse(r, __LINE__, 0, 186 "IPv6 address + port", 187 CONST_STR_LEN("GET / HTTP/1.0\r\n" 188 "Host: [::1]:80\r\n" 189 "\r\n")); 190 assert(buffer_eq_slen(r->http_host, CONST_STR_LEN("[::1]"))); 191 192 run_http_request_parse(r, __LINE__, 400, 193 "directory traversal", 194 CONST_STR_LEN("GET / HTTP/1.0\r\n" 195 "Host: ../123.org\r\n" 196 "\r\n")); 197 198 run_http_request_parse(r, __LINE__, 400, 199 "leading and trailing dot", 200 CONST_STR_LEN("GET / HTTP/1.0\r\n" 201 "Host: .jsdh.sfdg.sdfg.\r\n" 202 "\r\n")); 203 204 run_http_request_parse(r, __LINE__, 0, 205 "trailing dot is ok", 206 CONST_STR_LEN("GET / HTTP/1.0\r\n" 207 "Host: jsdh.sfdg.sdfg.\r\n" 208 "\r\n")); 209 assert(buffer_eq_slen(r->http_host, CONST_STR_LEN("jsdh.sfdg.sdfg"))); 210 211 run_http_request_parse(r, __LINE__, 400, 212 "leading dot", 213 CONST_STR_LEN("GET / HTTP/1.0\r\n" 214 "Host: .jsdh.sfdg.sdfg\r\n" 215 "\r\n")); 216 217 run_http_request_parse(r, __LINE__, 400, 218 "two dots", 219 CONST_STR_LEN("GET / HTTP/1.0\r\n" 220 "Host: jsdh..sfdg.sdfg\r\n" 221 "\r\n")); 222 223 run_http_request_parse(r, __LINE__, 400, 224 "broken port-number", 225 CONST_STR_LEN("GET / HTTP/1.0\r\n" 226 "Host: jsdh.sfdg.sdfg:asd\r\n" 227 "\r\n")); 228 229 run_http_request_parse(r, __LINE__, 400, 230 "negative port-number", 231 CONST_STR_LEN("GET / HTTP/1.0\r\n" 232 "Host: jsdh.sfdg.sdfg:-1\r\n" 233 "\r\n")); 234 235 run_http_request_parse(r, __LINE__, 400, 236 "port given but host missing", 237 CONST_STR_LEN("GET / HTTP/1.0\r\n" 238 "Host: :80\r\n" 239 "\r\n")); 240 241 run_http_request_parse(r, __LINE__, 400, 242 "port and host are broken", 243 CONST_STR_LEN("GET / HTTP/1.0\r\n" 244 "Host: .jsdh.sfdg.:sdfg.\r\n" 245 "\r\n")); 246 247 run_http_request_parse(r, __LINE__, 0, 248 "allowed characters in host-name", 249 CONST_STR_LEN("GET / HTTP/1.0\r\n" 250 "Host: a.b-c.d123\r\n" 251 "\r\n")); 252 assert(buffer_eq_slen(r->http_host, CONST_STR_LEN("a.b-c.d123"))); 253 254 run_http_request_parse(r, __LINE__, 400, 255 "leading dash", 256 CONST_STR_LEN("GET / HTTP/1.0\r\n" 257 "Host: -a.c\r\n" 258 "\r\n")); 259 260 run_http_request_parse(r, __LINE__, 400, 261 "dot only", 262 CONST_STR_LEN("GET / HTTP/1.0\r\n" 263 "Host: .\r\n" 264 "\r\n")); 265 266 run_http_request_parse(r, __LINE__, 400, 267 "broken IPv4 address - non-digit", 268 CONST_STR_LEN("GET / HTTP/1.0\r\n" 269 "Host: a192.168.2.10:1234\r\n" 270 "\r\n")); 271 272 run_http_request_parse(r, __LINE__, 400, 273 "broken IPv4 address - too short", 274 CONST_STR_LEN("GET / HTTP/1.0\r\n" 275 "Host: 192.168.2:1234\r\n" 276 "\r\n")); 277 278 run_http_request_parse(r, __LINE__, 400, 279 "IPv6 address + SQL injection", 280 CONST_STR_LEN("GET / HTTP/1.0\r\n" 281 "Host: [::1]' UNION SELECT '/\r\n" 282 "\r\n")); 283 284 run_http_request_parse(r, __LINE__, 400, 285 "IPv6 address + path traversal", 286 CONST_STR_LEN("GET / HTTP/1.0\r\n" 287 "Host: [::1]/../../../\r\n" 288 "\r\n")); 289 290 run_http_request_parse(r, __LINE__, 400, 291 "negative Content-Length", 292 CONST_STR_LEN("POST /12345.txt HTTP/1.0\r\n" 293 "Content-Length: -2\r\n" 294 "\r\n")); 295 296 run_http_request_parse(r, __LINE__, 411, 297 "Content-Length is empty", 298 CONST_STR_LEN("POST /12345.txt HTTP/1.0\r\n" 299 "Host: 123.example.org\r\n" 300 "Content-Length:\r\n" 301 "\r\n")); 302 303 run_http_request_parse(r, __LINE__, 400, 304 "Host missing", 305 CONST_STR_LEN("GET / HTTP/1.1\r\n" 306 "\r\n")); 307 308 run_http_request_parse(r, __LINE__, 400, 309 "empty request-URI", 310 CONST_STR_LEN("GET HTTP/1.0\r\n" 311 "\r\n")); 312 313 run_http_request_parse(r, __LINE__, 0, 314 "#1232 - duplicate headers with line-wrapping", 315 CONST_STR_LEN("GET / HTTP/1.0\r\n" 316 "Location: foo\r\n" 317 "Location: foobar\r\n" 318 " baz\r\n" 319 "\r\n")); 320 ds = (data_string *) 321 array_get_element_klen(&r->rqst_headers, CONST_STR_LEN("Location")); 322 assert(ds 323 && buffer_eq_slen(&ds->value, CONST_STR_LEN("foo, foobar baz"))); 324 325 run_http_request_parse(r, __LINE__, 0, 326 "#1232 - duplicate headers with line-wrapping - test 2", 327 CONST_STR_LEN("GET / HTTP/1.0\r\n" 328 "Location: \r\n" 329 "Location: foobar\r\n" 330 " baz\r\n" 331 "\r\n")); 332 ds = (data_string *) 333 array_get_element_klen(&r->rqst_headers, CONST_STR_LEN("Location")); 334 assert(ds && buffer_eq_slen(&ds->value, CONST_STR_LEN("foobar baz"))); 335 336 run_http_request_parse(r, __LINE__, 0, 337 "#1232 - duplicate headers with line-wrapping - test 3", 338 CONST_STR_LEN("GET / HTTP/1.0\r\n" 339 "A: \r\n" 340 "Location: foobar\r\n" 341 " baz\r\n" 342 "\r\n")); 343 ds = (data_string *) 344 array_get_element_klen(&r->rqst_headers, CONST_STR_LEN("Location")); 345 assert(ds && buffer_eq_slen(&ds->value, CONST_STR_LEN("foobar baz"))); 346 347 run_http_request_parse(r, __LINE__, 400, 348 "missing protocol", 349 CONST_STR_LEN("GET /\r\n" 350 "\r\n")); 351 352 run_http_request_parse(r, __LINE__, 505, 353 "zeros in protocol version", 354 CONST_STR_LEN("GET / HTTP/01.01\r\n" 355 "Host: foo\r\n" 356 "\r\n")); 357 358 run_http_request_parse(r, __LINE__, 505, 359 "missing major version", 360 CONST_STR_LEN("GET / HTTP/.01\r\n" 361 "\r\n")); 362 363 run_http_request_parse(r, __LINE__, 505, 364 "missing minor version", 365 CONST_STR_LEN("GET / HTTP/01.\r\n" 366 "\r\n")); 367 368 run_http_request_parse(r, __LINE__, 505, 369 "strings as version", 370 CONST_STR_LEN("GET / HTTP/a.b\r\n" 371 "\r\n")); 372 373 run_http_request_parse(r, __LINE__, 400, 374 "missing protocol + unknown method", 375 CONST_STR_LEN("BC /\r\n" 376 "\r\n")); 377 378 run_http_request_parse(r, __LINE__, 400, 379 "missing protocol + unknown method + missing URI", 380 CONST_STR_LEN("ABC\r\n" 381 "\r\n")); 382 383 run_http_request_parse(r, __LINE__, 501, 384 "unknown method", 385 CONST_STR_LEN("ABC / HTTP/1.0\r\n" 386 "\r\n")); 387 388 run_http_request_parse(r, __LINE__, 505, 389 "unknown protocol", 390 CONST_STR_LEN("GET / HTTP/1.3\r\n" 391 "\r\n")); 392 393 run_http_request_parse(r, __LINE__, 0, 394 "absolute URI", 395 CONST_STR_LEN("GET http://www.example.org/ HTTP/1.0\r\n" 396 "\r\n")); 397 assert(buffer_eq_slen(r->http_host, CONST_STR_LEN("www.example.org"))); 398 assert(buffer_eq_slen(&r->target, CONST_STR_LEN("/"))); 399 400 run_http_request_parse(r, __LINE__, 400, 401 "whitespace after key", 402 CONST_STR_LEN("GET / HTTP/1.0\r\n" 403 "ABC : foo\r\n" 404 "\r\n")); 405 406 run_http_request_parse(r, __LINE__, 400, 407 "whitespace within key", 408 CONST_STR_LEN("GET / HTTP/1.0\r\n" 409 "ABC a: foo\r\n" 410 "\r\n")); 411 412 run_http_request_parse(r, __LINE__, 0, 413 "no whitespace", 414 CONST_STR_LEN("GET / HTTP/1.0\r\n" 415 "ABC:foo\r\n" 416 "\r\n")); 417 ds = (data_string *) 418 array_get_element_klen(&r->rqst_headers, CONST_STR_LEN("ABC")); 419 assert(ds && buffer_eq_slen(&ds->value, CONST_STR_LEN("foo"))); 420 421 run_http_request_parse(r, __LINE__, 0, 422 "line-folding", 423 CONST_STR_LEN("GET / HTTP/1.0\r\n" 424 "ABC:foo\r\n" 425 " bc\r\n" 426 "\r\n")); 427 ds = (data_string *) 428 array_get_element_klen(&r->rqst_headers, CONST_STR_LEN("ABC")); 429 assert(ds && buffer_eq_slen(&ds->value, CONST_STR_LEN("foo bc"))); 430 431 run_http_request_parse(r, __LINE__, 411, 432 "POST request, no Content-Length", 433 CONST_STR_LEN("POST / HTTP/1.0\r\n" 434 "\r\n")); 435 436 run_http_request_parse(r, __LINE__, 400, 437 "Duplicate Host headers, Bug #25", 438 CONST_STR_LEN("GET / HTTP/1.0\r\n" 439 "Host: www.example.org\r\n" 440 "Host: 123.example.org\r\n" 441 "\r\n")); 442 443 run_http_request_parse(r, __LINE__, 400, 444 "Duplicate Content-Length headers", 445 CONST_STR_LEN("GET / HTTP/1.0\r\n" 446 "Content-Length: 5\r\n" 447 "Content-Length: 4\r\n" 448 "\r\n")); 449 450 run_http_request_parse(r, __LINE__, 400, 451 "Duplicate Content-Type headers", 452 CONST_STR_LEN("GET / HTTP/1.0\r\n" 453 "Content-Type: 5\r\n" 454 "Content-Type: 4\r\n" 455 "\r\n")); 456 457 /* (not actually testing Range here anymore; parsing deferred until use) */ 458 459 run_http_request_parse(r, __LINE__, 0, 460 "Duplicate Range headers (get appended)", 461 CONST_STR_LEN("GET / HTTP/1.0\r\n" 462 "Range: bytes=5-6\r\n" 463 "Range: bytes=5-9\r\n" 464 "\r\n")); 465 466 run_http_request_parse(r, __LINE__, 0, 467 "Duplicate Range headers with invalid range (a)", 468 CONST_STR_LEN("GET / HTTP/1.0\r\n" 469 "Range: bytes=0\r\n" 470 "Range: bytes=5-9\r\n" 471 "\r\n")); 472 run_http_request_parse(r, __LINE__, 0, 473 "Duplicate Range headers with invalid range (b)", 474 CONST_STR_LEN("GET / HTTP/1.0\r\n" 475 "Range: bytes=5-9\r\n" 476 "Range: bytes=0\r\n" 477 "\r\n")); 478 run_http_request_parse(r, __LINE__, 0, 479 "Duplicate Range headers with invalid range (c)", 480 CONST_STR_LEN("GET / HTTP/1.0\r\n" 481 "Range: 0\r\n" 482 "Range: bytes=5-9\r\n" 483 "\r\n")); 484 run_http_request_parse(r, __LINE__, 0, 485 "Duplicate Range headers with invalid range (d)", 486 CONST_STR_LEN("GET / HTTP/1.0\r\n" 487 "Range: bytes=5-9\r\n" 488 "Range: 0\r\n" 489 "\r\n")); 490 491 run_http_request_parse(r, __LINE__, 0, 492 "Duplicate If-None-Match headers", 493 CONST_STR_LEN("GET / HTTP/1.0\r\n" 494 "If-None-Match: 5\r\n" 495 "If-None-Match: 4\r\n" 496 "\r\n")); 497 498 run_http_request_parse(r, __LINE__, 400, 499 "Duplicate If-Modified-Since headers", 500 CONST_STR_LEN("GET / HTTP/1.0\r\n" 501 "If-Modified-Since: 5\r\n" 502 "If-Modified-Since: 4\r\n" 503 "\r\n")); 504 505 run_http_request_parse(r, __LINE__, 400, 506 "GET with Content-Length", 507 CONST_STR_LEN("GET / HTTP/1.0\r\n" 508 "Content-Length: 4\r\n" 509 "\r\n" 510 "1234")); 511 512 run_http_request_parse(r, __LINE__, 400, 513 "HEAD with Content-Length", 514 CONST_STR_LEN("HEAD / HTTP/1.0\r\n" 515 "Content-Length: 4\r\n" 516 "\r\n" 517 "1234")); 518 519 run_http_request_parse(r, __LINE__, 400, 520 "invalid chars in Header values (bug #1286)", 521 CONST_STR_LEN("GET / HTTP/1.0\r\n" 522 "If-Modified-Since: \0\r\n" 523 "\r\n")); 524 525 run_http_request_parse(r, __LINE__, 0, 526 "absolute-uri in request-line (without Host)", 527 CONST_STR_LEN("GET http://zzz.example.org/ HTTP/1.1\r\n" 528 "Connection: close\r\n" 529 "\r\n")); 530 ds = (data_string *) 531 array_get_element_klen(&r->rqst_headers, CONST_STR_LEN("Host")); 532 assert(ds && buffer_eq_slen(&ds->value, CONST_STR_LEN("zzz.example.org"))); 533 534 run_http_request_parse(r, __LINE__, 0, 535 "absolute-uri in request-line (with Host match)", 536 CONST_STR_LEN("GET http://zzz.example.org/ HTTP/1.1\r\n" 537 "Host: zzz.example.org\r\n" 538 "Connection: close\r\n" 539 "\r\n")); 540 ds = (data_string *) 541 array_get_element_klen(&r->rqst_headers, CONST_STR_LEN("Host")); 542 assert(ds && buffer_eq_slen(&ds->value, CONST_STR_LEN("zzz.example.org"))); 543 544 run_http_request_parse(r, __LINE__, 400, 545 "absolute-uri in request-line (with Host mismatch)", 546 CONST_STR_LEN("GET http://zzz.example.org/ HTTP/1.1\r\n" 547 "Host: aaa.example.org\r\n" 548 "Connection: close\r\n" 549 "\r\n")); 550 551 /* (quick check that none of above tests were left in a state 552 * which resulted in subsequent tests returning 400 for other 553 * reasons) */ 554 run_http_request_parse(r, __LINE__, 0, 555 "valid", 556 CONST_STR_LEN("GET / HTTP/1.0\r\n" 557 "Host: www.example.org\r\n" 558 "\r\n")); 559 } 560 561 #include "base.h" 562 #include "burl.h" 563 #include "log.h" 564 565 int main (void) 566 { 567 request_st r; 568 569 memset(&r, 0, sizeof(request_st)); 570 r.conf.errh = log_error_st_init(); 571 r.conf.errh->errorlog_fd = -1; /* (disable) */ 572 r.conf.allow_http11 = 1; 573 r.conf.http_parseopts = HTTP_PARSEOPT_HEADER_STRICT 574 | HTTP_PARSEOPT_HOST_STRICT 575 | HTTP_PARSEOPT_HOST_NORMALIZE; 576 577 test_request_http_request_parse(&r); 578 579 free(r.target_orig.ptr); 580 free(r.target.ptr); 581 array_free_data(&r.rqst_headers); 582 583 log_error_st_free(r.conf.errh); 584 585 return 0; 586 } 587