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