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