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