1 /* 2 * request - HTTP request processing 3 * 4 * Fully-rewritten from original 5 * Copyright(c) 2018 Glenn Strauss gstrauss()gluelogic.com All rights reserved 6 * License: BSD 3-clause (same as lighttpd) 7 */ 8 #include "first.h" 9 10 #include "request.h" 11 #include "burl.h" 12 #include "http_header.h" 13 #include "http_kv.h" 14 #include "log.h" 15 #include "sock_addr.h" 16 17 #include <limits.h> 18 #include <stdint.h> 19 #include <stdlib.h> 20 #include <string.h> 21 22 static int request_check_hostname(buffer * const host) { 23 /* 24 * hostport = host [ ":" port ] 25 * host = hostname | IPv4address | IPv6address 26 * hostname = *( domainlabel "." ) toplabel [ "." ] 27 * domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum 28 * toplabel = alpha | alpha *( alphanum | "-" ) alphanum 29 * IPv4address = 1*digit "." 1*digit "." 1*digit "." 1*digit 30 * IPv6address = "[" ... "]" 31 * port = *digit 32 */ 33 34 const char *h = host->ptr; 35 36 if (*h != '[') { 37 uint32_t len = buffer_string_length(host); 38 const char * const colon = memchr(h, ':', len); 39 uint32_t hlen = colon ? (colon - h) : len; 40 41 /* if hostname ends in ".", strip it */ 42 if (__builtin_expect( (0 == hlen), 0)) return -1; 43 if (__builtin_expect( (h[hlen-1] == '.'), 0)) { 44 /* shift port info one left */ 45 if (--hlen == 0) return -1; 46 --len; 47 if (NULL != colon) 48 memmove(host->ptr+hlen, colon, len - hlen); 49 buffer_string_set_length(host, len); 50 } 51 52 int label_len = 0; 53 int allnumeric = 1; 54 int numeric = 1; 55 int level = 0; 56 for (uint32_t i = 0; i < hlen; ++i) { 57 const int ch = h[i]; 58 ++label_len; 59 if (light_isdigit(ch)) 60 continue; 61 else if ((light_isalpha(ch) || (ch == '-' && i != 0))) 62 numeric = 0; 63 else if (ch == '.' && 1 != label_len && '-' != h[i+1]) { 64 allnumeric &= numeric; 65 numeric = 1; 66 label_len = 0; 67 ++level; 68 } 69 else 70 return -1; 71 } 72 /* (if last segment numeric, then IPv4 and must have 4 numeric parts) */ 73 if (0 == label_len || (numeric && (level != 3 || !allnumeric))) 74 return -1; 75 76 h += hlen; 77 } 78 else { /* IPv6 address */ 79 /* check the address inside [...]; note: not fully validating */ 80 /* (note: not allowing scoped literals, e.g. %eth0 suffix) */ 81 ++h; /* step past '[' */ 82 int cnt = 0; 83 while (light_isxdigit(*h) || *h == '.' || (*h == ':' && ++cnt < 8)) ++h; 84 /*(invalid char, too many ':', missing ']', or empty "[]")*/ 85 if (*h != ']' || h - host->ptr == 1) return -1; 86 ++h; /* step past ']' */ 87 } 88 89 /* check numerical port, if present */ 90 if (*h == ':') { 91 if (__builtin_expect( (h[1] == '\0'), 0)) /*(remove trailing colon)*/ 92 buffer_string_set_length(host, h - host->ptr); 93 do { ++h; } while (light_isdigit(*h)); 94 } 95 96 return (*h == '\0') ? 0 : -1; 97 } 98 99 int http_request_host_normalize(buffer * const b, const int scheme_port) { 100 /* 101 * check for and canonicalize numeric IP address and portnum (optional) 102 * (IP address may be followed by ":portnum" (optional)) 103 * - IPv6: "[...]" 104 * - IPv4: "x.x.x.x" 105 * - IPv4: 12345678 (32-bit decimal number) 106 * - IPv4: 012345678 (32-bit octal number) 107 * - IPv4: 0x12345678 (32-bit hex number) 108 * 109 * allow any chars (except ':' and '\0' and stray '[' or ']') 110 * (other code may check chars more strictly or more pedantically) 111 * ':' delimits (optional) port at end of string 112 * "[]" wraps IPv6 address literal 113 * '\0' should have been rejected earlier were it present 114 * 115 * any chars includes, but is not limited to: 116 * - allow '-' any where, even at beginning of word 117 * (security caution: might be confused for cmd flag if passed to shell) 118 * - allow all-digit TLDs 119 * (might be mistaken for IPv4 addr by inet_aton() 120 * unless non-digits appear in subdomain) 121 */ 122 123 /* Note: not using getaddrinfo() since it does not support "[]" around IPv6 124 * and is not as lenient as inet_aton() and inet_addr() for IPv4 strings. 125 * Not using inet_pton() (when available) on IPv4 for similar reasons. */ 126 127 const char * const p = b->ptr; 128 const size_t blen = buffer_string_length(b); 129 long port = 0; 130 131 if (*p != '[') { 132 char * const colon = (char *)memchr(p, ':', blen); 133 if (colon) { 134 if (*p == ':') return -1; /*(empty host then port, or naked IPv6)*/ 135 if (colon[1] != '\0') { 136 char *e; 137 port = strtol(colon+1, &e, 0); /*(allow decimal, octal, hex)*/ 138 if (0 < port && port <= USHRT_MAX && *e == '\0') { 139 /* valid port */ 140 } else { 141 return -1; 142 } 143 } /*(else ignore stray colon at string end)*/ 144 buffer_string_set_length(b, (size_t)(colon - p)); /*(remove port str)*/ 145 } 146 147 if (light_isdigit(*p)) do { 148 /* (IPv4 address literal or domain starting w/ digit (e.g. 3com))*/ 149 /* (check one-element cache of normalized IPv4 address string) */ 150 static struct { char s[INET_ADDRSTRLEN]; size_t n; } laddr; 151 size_t n = colon ? (size_t)(colon - p) : blen; 152 sock_addr addr; 153 if (n == laddr.n && 0 == memcmp(p, laddr.s, n)) break; 154 if (1 == sock_addr_inet_pton(&addr, p, AF_INET, 0)) { 155 sock_addr_inet_ntop_copy_buffer(b, &addr); 156 n = buffer_string_length(b); 157 if (n < sizeof(laddr.s)) memcpy(laddr.s, b->ptr, (laddr.n = n)); 158 } 159 } while (0); 160 } else do { /* IPv6 addr */ 161 #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON) 162 163 /* (check one-element cache of normalized IPv4 address string) */ 164 static struct { char s[INET6_ADDRSTRLEN]; size_t n; } laddr; 165 sock_addr addr; 166 char *bracket = b->ptr+blen-1; 167 char *percent = strchr(b->ptr+1, '%'); 168 size_t len; 169 int rc; 170 char buf[INET6_ADDRSTRLEN+16]; /*(+16 for potential %interface name)*/ 171 if (blen <= 2) return -1; /*(invalid "[]")*/ 172 if (*bracket != ']') { 173 bracket = (char *)memchr(b->ptr+1, ']', blen-1); 174 if (NULL == bracket || bracket[1] != ':' || bracket - b->ptr == 1){ 175 return -1; 176 } 177 if (bracket[2] != '\0') { /*(ignore stray colon at string end)*/ 178 char *e; 179 port = strtol(bracket+2, &e, 0); /*(allow decimal, octal, hex)*/ 180 if (0 < port && port <= USHRT_MAX && *e == '\0') { 181 /* valid port */ 182 } else { 183 return -1; 184 } 185 } 186 } 187 188 len = (size_t)((percent ? percent : bracket) - (b->ptr+1)); 189 if (laddr.n == len && 0 == memcmp(laddr.s, b->ptr+1, len)) { 190 /* truncate after ']' and re-add normalized port, if needed */ 191 buffer_string_set_length(b, (size_t)(bracket - b->ptr + 1)); 192 break; 193 } 194 195 *bracket = '\0';/*(terminate IPv6 string)*/ 196 if (percent) *percent = '\0'; /*(remove %interface from address)*/ 197 rc = sock_addr_inet_pton(&addr, b->ptr+1, AF_INET6, 0); 198 if (percent) *percent = '%'; /*(restore %interface)*/ 199 *bracket = ']'; /*(restore bracket)*/ 200 if (1 != rc) return -1; 201 202 sock_addr_inet_ntop(&addr, buf, sizeof(buf)); 203 len = strlen(buf); 204 if (percent) { 205 if (percent > bracket) return -1; 206 if (len + (size_t)(bracket - percent) >= sizeof(buf)) return -1; 207 if (len < sizeof(laddr.s)) memcpy(laddr.s, buf, (laddr.n = len)); 208 memcpy(buf+len, percent, (size_t)(bracket - percent)); 209 len += (size_t)(bracket - percent); 210 } 211 buffer_string_set_length(b, 1); /* truncate after '[' */ 212 buffer_append_str2(b, buf, len, CONST_STR_LEN("]")); 213 214 #else 215 216 return -1; 217 218 #endif 219 } while (0); 220 221 if (0 != port && port != scheme_port) { 222 buffer_append_string_len(b, CONST_STR_LEN(":")); 223 buffer_append_int(b, (int)port); 224 } 225 226 return 0; 227 } 228 229 int http_request_host_policy (buffer * const b, const unsigned int http_parseopts, const int scheme_port) { 230 return (((http_parseopts & HTTP_PARSEOPT_HOST_STRICT) 231 && 0 != request_check_hostname(b)) 232 || ((http_parseopts & HTTP_PARSEOPT_HOST_NORMALIZE) 233 && 0 != http_request_host_normalize(b, scheme_port))); 234 } 235 236 __attribute_const__ 237 static int request_uri_is_valid_char(const unsigned char c) { 238 return (c > 32 && c != 127 && c != 255); 239 } 240 241 __attribute_cold__ 242 __attribute_noinline__ 243 static int http_request_header_line_invalid(request_st * const restrict r, const int status, const char * const restrict msg) { 244 if (r->conf.log_request_header_on_error) { 245 if (msg) log_error(r->conf.errh, __FILE__, __LINE__, "%s", msg); 246 } 247 return status; 248 } 249 250 __attribute_cold__ 251 __attribute_noinline__ 252 static int http_request_header_char_invalid(request_st * const restrict r, const char ch, const char * const restrict msg) { 253 if (r->conf.log_request_header_on_error) { 254 if ((unsigned char)ch > 32 && ch != 127) { 255 log_error(r->conf.errh, __FILE__, __LINE__, "%s ('%c')", msg, ch); 256 } 257 else { 258 log_error(r->conf.errh, __FILE__, __LINE__, "%s (0x%x)", msg, ch); 259 } 260 } 261 return 400; 262 } 263 264 265 int64_t 266 li_restricted_strtoint64 (const char *v, const uint32_t vlen, const char ** const err) 267 { 268 /* base 10 strtoll() parsing exactly vlen chars and requiring digits 0-9 */ 269 /* rejects negative numbers and considers values > INT64_MAX an error */ 270 /* note: errno is not set; detect error if *err != v+vlen upon return */ 271 /*(caller must check 0 == vlen if that is to be an error for caller)*/ 272 int64_t rv = 0; 273 uint32_t i; 274 for (i = 0; i < vlen; ++i) { 275 const uint8_t c = ((uint8_t *)v)[i] - '0'; /*(unsigned; underflow ok)*/ 276 if (c > 9) break; 277 if (rv > INT64_MAX/10) break; 278 rv *= 10; 279 if (rv > INT64_MAX - c) break; 280 rv += c; 281 } 282 *err = v+i; 283 return rv; 284 } 285 286 287 /* add header to list of headers 288 * certain headers are also parsed 289 * might drop a header if deemed unnecessary/broken 290 * 291 * returns 0 on success, HTTP status on error 292 */ 293 static int http_request_parse_single_header(request_st * const restrict r, const enum http_header_e id, const char * const restrict k, const size_t klen, const char * const restrict v, const size_t vlen) { 294 buffer **saveb = NULL; 295 296 /* 297 * Note: k might not be '\0'-terminated 298 * Note: v is not '\0'-terminated 299 * With lighttpd HTTP/1.1 parser, v ends with whitespace 300 * (one of '\r' '\n' ' ' '\t') 301 * With lighttpd HTTP/2 parser, v should not be accessed beyond vlen 302 * (care must be taken to avoid libc funcs which expect z-strings) 303 */ 304 /*assert(vlen);*//*(caller must not call this func with 0 klen or 0 vlen)*/ 305 306 switch (id) { 307 /*case HTTP_HEADER_OTHER:*/ 308 default: 309 break; 310 case HTTP_HEADER_HOST: 311 if (!light_btst(r->rqst_htags, HTTP_HEADER_HOST)) { 312 saveb = &r->http_host; 313 if (vlen >= 1024) { /*(expecting < 256)*/ 314 return http_request_header_line_invalid(r, 400, "uri-authority too long -> 400"); 315 } 316 } 317 else if (NULL != r->http_host 318 && buffer_is_equal_string(r->http_host, v, vlen)) { 319 /* ignore all Host: headers if match authority in request line */ 320 return 0; /* ignore header */ 321 } 322 else { 323 return http_request_header_line_invalid(r, 400, "duplicate Host header -> 400"); 324 } 325 break; 326 case HTTP_HEADER_CONNECTION: 327 /* "Connection: close" is common case if header is present */ 328 if ((vlen == 5 && buffer_eq_icase_ssn(v, CONST_STR_LEN("close"))) 329 || http_header_str_contains_token(v,vlen,CONST_STR_LEN("close"))) { 330 r->keep_alive = 0; 331 break; 332 } 333 if (http_header_str_contains_token(v,vlen,CONST_STR_LEN("keep-alive"))){ 334 r->keep_alive = 1; 335 break; 336 } 337 break; 338 case HTTP_HEADER_CONTENT_TYPE: 339 if (light_btst(r->rqst_htags, HTTP_HEADER_CONTENT_TYPE)) { 340 return http_request_header_line_invalid(r, 400, "duplicate Content-Type header -> 400"); 341 } 342 break; 343 case HTTP_HEADER_IF_NONE_MATCH: 344 /* if dup, only the first one will survive */ 345 if (light_btst(r->rqst_htags, HTTP_HEADER_IF_NONE_MATCH)) { 346 return 0; /* ignore header */ 347 } 348 break; 349 case HTTP_HEADER_CONTENT_LENGTH: 350 if (!light_btst(r->rqst_htags, HTTP_HEADER_CONTENT_LENGTH)) { 351 /*(trailing whitespace was removed from vlen)*/ 352 /*(not using strtoll() since v might not be z-string)*/ 353 const char *err; 354 off_t clen = (off_t)li_restricted_strtoint64(v, vlen, &err); 355 if (err == v+vlen) { 356 /* (set only if not set to -1 by Transfer-Encoding: chunked) */ 357 if (0 == r->reqbody_length) r->reqbody_length = clen; 358 } 359 else { 360 return http_request_header_line_invalid(r, 400, "invalid Content-Length header -> 400"); 361 } 362 } 363 else { 364 return http_request_header_line_invalid(r, 400, "duplicate Content-Length header -> 400"); 365 } 366 break; 367 case HTTP_HEADER_HTTP2_SETTINGS: 368 if (light_btst(r->rqst_htags, HTTP_HEADER_HTTP2_SETTINGS)) { 369 return http_request_header_line_invalid(r, 400, "duplicate HTTP2-Settings header -> 400"); 370 } 371 break; 372 case HTTP_HEADER_IF_MODIFIED_SINCE: 373 if (light_btst(r->rqst_htags, HTTP_HEADER_IF_MODIFIED_SINCE)) { 374 /* Proxies sometimes send dup headers 375 * if they are the same we ignore the second 376 * if not, we raise an error */ 377 const buffer *vb = 378 http_header_request_get(r, HTTP_HEADER_IF_MODIFIED_SINCE, 379 CONST_STR_LEN("If-Modified-Since")); 380 if (vb && buffer_eq_icase_slen(vb, v, vlen)) { 381 /* ignore it if they are the same */ 382 return 0; /* ignore header */ 383 } 384 else { 385 return http_request_header_line_invalid(r, 400, "duplicate If-Modified-Since header -> 400"); 386 } 387 } 388 break; 389 case HTTP_HEADER_TRANSFER_ENCODING: 390 if (HTTP_VERSION_1_1 != r->http_version) { 391 return http_request_header_line_invalid(r, 400, 392 HTTP_VERSION_1_0 == r->http_version 393 ? "HTTP/1.0 with Transfer-Encoding (bad HTTP/1.0 proxy?) -> 400" 394 : "HTTP/2 with Transfer-Encoding is invalid -> 400"); 395 } 396 397 if (!buffer_eq_icase_ss(v, vlen, CONST_STR_LEN("chunked"))) { 398 /* Transfer-Encoding might contain additional encodings, 399 * which are not currently supported by lighttpd */ 400 return http_request_header_line_invalid(r, 501, NULL); /* Not Implemented */ 401 } 402 r->reqbody_length = -1; 403 404 /* Transfer-Encoding is a hop-by-hop header, 405 * which must not be blindly forwarded to backends */ 406 return 0; /* skip header */ 407 } 408 409 http_header_request_append(r, id, k, klen, v, vlen); 410 411 if (saveb) { 412 *saveb = http_header_request_get(r, id, k, klen); 413 } 414 415 return 0; 416 } 417 418 __attribute_cold__ 419 static int http_request_parse_proto_loose(request_st * const restrict r, const char * const restrict ptr, const size_t len, const unsigned int http_parseopts) { 420 const char * proto = memchr(ptr, ' ', len); 421 if (NULL == proto) 422 return http_request_header_line_invalid(r, 400, "incomplete request line -> 400"); 423 proto = memchr(proto+1, ' ', len - (proto+1 - ptr)); 424 if (NULL == proto) 425 return http_request_header_line_invalid(r, 400, "incomplete request line -> 400"); 426 ++proto; 427 428 if (proto[0]=='H' && proto[1]=='T' && proto[2]=='T' && proto[3]=='P' && proto[4] == '/') { 429 if (proto[5] == '1' && proto[6] == '.' && (proto[7] == '1' || proto[7] == '0')) { 430 /* length already checked before calling this routine */ 431 /* (len != (size_t)(proto - ptr + 8)) */ 432 if (http_parseopts & HTTP_PARSEOPT_HEADER_STRICT) /*(http_header_strict)*/ 433 return http_request_header_line_invalid(r, 400, "incomplete request line -> 400"); 434 r->http_version = (proto[7] == '1') ? HTTP_VERSION_1_1 : HTTP_VERSION_1_0; 435 } 436 else 437 return http_request_header_line_invalid(r, 505, "unknown HTTP version -> 505"); 438 } 439 else 440 return http_request_header_line_invalid(r, 400, "unknown protocol -> 400"); 441 442 /* keep-alive default: HTTP/1.1 -> true; HTTP/1.0 -> false */ 443 r->keep_alive = (HTTP_VERSION_1_0 != r->http_version); 444 445 return 0; 446 } 447 448 __attribute_cold__ 449 static const char * http_request_parse_reqline_uri(request_st * const restrict r, const char * const restrict uri, const size_t len, const unsigned int http_parseopts) { 450 const char *nuri; 451 if ((len > 7 && buffer_eq_icase_ssn(uri, "http://", 7) 452 && NULL != (nuri = memchr(uri + 7, '/', len-7))) 453 || 454 (len > 8 && buffer_eq_icase_ssn(uri, "https://", 8) 455 && NULL != (nuri = memchr(uri + 8, '/', len-8)))) { 456 const char * const host = uri + (uri[4] == ':' ? 7 : 8); 457 const size_t hostlen = nuri - host; 458 if (0 == hostlen || hostlen >= 1024) { /*(expecting < 256)*/ 459 http_request_header_line_invalid(r, 400, "uri-authority empty or too long -> 400"); 460 return NULL; 461 } 462 /* Insert as host header */ 463 http_header_request_set(r, HTTP_HEADER_HOST, CONST_STR_LEN("Host"), host, hostlen); 464 r->http_host = http_header_request_get(r, HTTP_HEADER_HOST, CONST_STR_LEN("Host")); 465 return nuri; 466 } else if (!(http_parseopts & HTTP_PARSEOPT_HEADER_STRICT) /*(!http_header_strict)*/ 467 || (HTTP_METHOD_CONNECT == r->http_method && (uri[0] == ':' || light_isdigit(uri[0]))) 468 || (HTTP_METHOD_OPTIONS == r->http_method && uri[0] == '*' && 1 == len)) { 469 /* (permitted) */ 470 return uri; 471 } else { 472 http_request_header_line_invalid(r, 400, "request-URI parse error -> 400"); 473 return NULL; 474 } 475 } 476 477 478 __attribute_cold__ 479 __attribute_noinline__ 480 static int http_request_parse_header_other(request_st * const restrict r, const char * const restrict k, const int klen, const unsigned int http_header_strict); 481 482 483 int 484 http_request_validate_pseudohdrs (request_st * const restrict r, const int scheme, const unsigned int http_parseopts) 485 { 486 /* :method is required to indicate method 487 * CONNECT method must have :method and :authority 488 * All other methods must have at least :method :scheme :path */ 489 490 if (HTTP_METHOD_UNSET == r->http_method) 491 return http_request_header_line_invalid(r, 400, 492 "missing pseudo-header method -> 400"); 493 494 if (HTTP_METHOD_CONNECT != r->http_method) { 495 if (!scheme) 496 return http_request_header_line_invalid(r, 400, 497 "missing pseudo-header scheme -> 400"); 498 499 if (buffer_string_is_empty(&r->target)) 500 return http_request_header_line_invalid(r, 400, 501 "missing pseudo-header path -> 400"); 502 503 const char * const uri = r->target.ptr; 504 if (*uri != '/') { /* (common case: (*uri == '/')) */ 505 if (uri[0] != '*' || uri[1] != '\0' 506 || HTTP_METHOD_OPTIONS != r->http_method) 507 return http_request_header_line_invalid(r, 400, 508 "invalid pseudo-header path -> 400"); 509 } 510 } 511 else { /* HTTP_METHOD_CONNECT */ 512 if (NULL == r->http_host) 513 return http_request_header_line_invalid(r, 400, 514 "missing pseudo-header authority -> 400"); 515 if (!buffer_string_is_empty(&r->target) || scheme) 516 return http_request_header_line_invalid(r, 400, 517 "invalid pseudo-header with CONNECT -> 400"); 518 /*(reuse uri and ulen to assign to r->target)*/ 519 buffer_copy_buffer(&r->target, r->http_host); 520 } 521 buffer_copy_buffer(&r->target_orig, &r->target); 522 523 /* r->http_host, if set, is checked with http_request_host_policy() 524 * in http_request_parse() */ 525 526 /* copied and modified from end of http_request_parse_reqline() */ 527 528 /* check uri for invalid characters */ 529 const unsigned int http_header_strict = 530 (http_parseopts & HTTP_PARSEOPT_HEADER_STRICT); 531 if (http_header_strict 532 && (http_parseopts & HTTP_PARSEOPT_URL_NORMALIZE_CTRLS_REJECT)) 533 return 0; /* URI will be checked in http_request_parse_target() */ 534 535 const uint32_t ulen = buffer_string_length(&r->target); 536 const uint8_t * const uri = (uint8_t *)r->target.ptr; 537 if (http_header_strict) { 538 for (uint32_t i = 0; i < ulen; ++i) { 539 if (!request_uri_is_valid_char(uri[i])) 540 return http_request_header_char_invalid(r, uri[i], 541 "invalid character in URI -> 400"); 542 } 543 } 544 else { 545 if (NULL != memchr(uri, '\0', ulen)) 546 return http_request_header_char_invalid(r, '\0', 547 "invalid character in header -> 400"); 548 } 549 550 return 0; 551 } 552 553 554 int 555 http_request_parse_header (request_st * const restrict r, http_header_parse_ctx * const restrict hpctx) 556 { 557 /* Note: k and v might not be '\0' terminated strings; 558 * care must be taken to avoid libc funcs which expect z-strings */ 559 const char * const restrict k = hpctx->k; 560 const char * const restrict v = hpctx->v; 561 const uint32_t klen = hpctx->klen; 562 const uint32_t vlen = hpctx->vlen; 563 564 if (0 == klen) 565 return http_request_header_line_invalid(r, 400, 566 "invalid header key -> 400"); 567 568 if ((hpctx->hlen += klen + vlen + 4) > hpctx->max_request_field_size) { 569 /*(configurable with server.max-request-field-size; default 8k)*/ 570 #if 1 /* emit to error log for people sending large headers */ 571 log_error(r->conf.errh, __FILE__, __LINE__, 572 "oversized request header -> 431"); 573 return 431; /* Request Header Fields Too Large */ 574 #else 575 /* 431 Request Header Fields Too Large */ 576 return http_request_header_line_invalid(r, 431, 577 "oversized request header -> 431"); 578 #endif 579 } 580 581 if (__builtin_expect( (2 == klen), 0) && k[0] == 't' && k[1] == 'e' 582 && !buffer_eq_icase_ss(v, vlen, CONST_STR_LEN("trailers"))) 583 return http_request_header_line_invalid(r, 400, 584 "invalid TE header value with HTTP/2 -> 400"); 585 586 if (!hpctx->trailers) { 587 if (*k == ':') { 588 /* HTTP/2 request pseudo-header fields */ 589 if (!hpctx->pseudo) /*(pseudo header after non-pseudo header)*/ 590 return http_request_header_line_invalid(r, 400, 591 "invalid pseudo-header -> 400"); 592 if (0 == vlen) 593 return http_request_header_line_invalid(r, 400, 594 "invalid header value -> 400"); 595 switch (klen-1) { 596 case 4: 597 if (0 == memcmp(k+1, "path", 4)) { 598 if (!buffer_string_is_empty(&r->target)) 599 return http_request_header_line_invalid(r, 400, 600 "repeated pseudo-header -> 400"); 601 buffer_copy_string_len(&r->target, v, vlen); 602 return 0; 603 } 604 break; 605 case 6: 606 if (0 == memcmp(k+1, "method", 6)) { 607 if (HTTP_METHOD_UNSET != r->http_method) 608 return http_request_header_line_invalid(r, 400, 609 "repeated pseudo-header -> 400"); 610 r->http_method = get_http_method_key(v, vlen); 611 if (HTTP_METHOD_UNSET >= r->http_method) 612 return http_request_header_line_invalid(r, 501, 613 "unknown http-method -> 501"); 614 return 0; 615 } 616 else if (0 == memcmp(k+1, "scheme", 6)) { 617 if (hpctx->scheme) 618 return http_request_header_line_invalid(r, 400, 619 "repeated pseudo-header -> 400"); 620 switch (vlen) {/*(validated, but then ignored)*/ 621 case 5: /* "https" */ 622 if (v[4]!='s') break; 623 __attribute_fallthrough__ 624 case 4: /* "http" */ 625 if (v[0]=='h' && v[1]=='t' && v[2]=='t' && v[3]=='p') { 626 hpctx->scheme = 1; 627 return 0; 628 } 629 break; 630 default: 631 break; 632 } 633 return http_request_header_line_invalid(r, 400, 634 "unknown pseudo-header scheme -> 400"); 635 } 636 break; 637 case 9: 638 if (0 == memcmp(k+1, "authority", 9)) { 639 if (r->http_host) 640 return http_request_header_line_invalid(r, 400, 641 "repeated pseudo-header -> 400"); 642 if (vlen >= 1024) /*(expecting < 256)*/ 643 return http_request_header_line_invalid(r, 400, 644 "invalid pseudo-header authority too long -> 400"); 645 /* insert as host header */ 646 http_header_request_set(r, HTTP_HEADER_HOST, 647 CONST_STR_LEN("host"), v, vlen); 648 r->http_host = 649 http_header_request_get(r, HTTP_HEADER_HOST, 650 CONST_STR_LEN("Host")); 651 return 0; 652 } 653 break; 654 default: 655 break; 656 } 657 return http_request_header_line_invalid(r, 400, 658 "invalid pseudo-header -> 400"); 659 } 660 else { /*(non-pseudo headers)*/ 661 if (hpctx->pseudo) { /*(transition to non-pseudo headers)*/ 662 hpctx->pseudo = 0; 663 int status = 664 http_request_validate_pseudohdrs(r, hpctx->scheme, 665 hpctx->http_parseopts); 666 if (0 != status) return status; 667 } 668 if (0 == vlen) 669 return 0; 670 671 uint32_t j = 0; 672 while (j < klen && (light_islower(k[j]) || k[j] == '-')) 673 ++j; 674 675 const unsigned int http_header_strict = 676 (hpctx->http_parseopts & HTTP_PARSEOPT_HEADER_STRICT); 677 678 if (__builtin_expect( (j != klen), 0)) { 679 if (light_isupper(k[j])) 680 return 400; 681 if (0 != http_request_parse_header_other(r, k+j, klen-j, 682 http_header_strict)) 683 return 400; 684 } 685 686 if (http_header_strict) { 687 for (j = 0; j < vlen; ++j) { 688 if ((((uint8_t *)v)[j] < 32 && v[j] != '\t') || v[j]==127) 689 return http_request_header_char_invalid(r, v[j], 690 "invalid character in header -> 400"); 691 } 692 } 693 else { 694 if (NULL != memchr(v, '\0', vlen)) 695 return http_request_header_char_invalid(r, '\0', 696 "invalid character in header -> 400"); 697 } 698 699 const enum http_header_e id = 700 hpctx->id ? hpctx->id : http_header_hkey_get_lc(k, klen); 701 return http_request_parse_single_header(r, id, k, klen, v, vlen); 702 } 703 } 704 else { /*(trailers)*/ 705 /* ignore trailers (after required HPACK decoding) if streaming 706 * request body to backend since headers have already been sent 707 * to backend via Common Gateway Interface (CGI) (CGI, FastCGI, 708 * SCGI, etc) or HTTP/1.1 (proxy) (mod_proxy does not currently 709 * support using HTTP/2 to connect to backends) */ 710 #if 0 /* (if needed, save flag in hpctx instead of fdevent.h dependency)*/ 711 if (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST) 712 return 0; 713 #endif 714 /* Note: do not unconditionally merge into headers since if 715 * headers had already been sent to backend, then mod_accesslog 716 * logging of request headers might be inaccurate. 717 * Many simple backends do not support HTTP/1.1 requests sending 718 * Transfer-Encoding: chunked, and even those that do might not 719 * handle trailers. Some backends do not even support HTTP/1.1. 720 * For all these reasons, ignore trailers if streaming request 721 * body to backend. Revisit in future if adding support for 722 * connecting to backends using HTTP/2 (with explicit config 723 * option to force connecting to backends using HTTP/2) */ 724 725 /* XXX: TODO: request trailers not handled if streaming reqbody 726 * XXX: must ensure that trailers are not disallowed field-names 727 */ 728 729 #if 0 730 if (0 == vlen) 731 return 0; 732 #endif 733 734 return 0; 735 } 736 } 737 738 739 static int http_request_parse_reqline(request_st * const restrict r, const char * const restrict ptr, const unsigned short * const restrict hoff, const unsigned int http_parseopts) { 740 size_t len = hoff[2]; 741 742 /* parse the first line of the request 743 * <method> <uri> <protocol>\r\n 744 * */ 745 if (len < 13) /* minimum len with (!http_header_strict): "x x HTTP/1.0\n" */ 746 return http_request_header_line_invalid(r, 400, "invalid request line (too short) -> 400"); 747 if (ptr[len-2] == '\r') 748 len-=2; 749 else if (!(http_parseopts & HTTP_PARSEOPT_HEADER_STRICT)) /*(!http_header_strict)*/ 750 len-=1; 751 else 752 return http_request_header_line_invalid(r, 400, "missing CR before LF in header -> 400"); 753 754 /* 755 * RFC7230: 756 * HTTP-version = HTTP-name "/" DIGIT "." DIGIT 757 * HTTP-name = %x48.54.54.50 ; "HTTP", case-sensitive 758 */ 759 760 /* protocol is expected to be " HTTP/1.1" or " HTTP/1.0" at end of line */ 761 union proto_un { 762 char c[8]; 763 uint64_t u; 764 }; 765 static const union proto_un http_1_1 = {{'H','T','T','P','/','1','.','1'}}; 766 static const union proto_un http_1_0 = {{'H','T','T','P','/','1','.','0'}}; 767 const char *p = ptr + len - 8; 768 union proto_un proto8; 769 proto8.c[0]=p[0]; proto8.c[1]=p[1]; proto8.c[2]=p[2]; proto8.c[3]=p[3]; 770 proto8.c[4]=p[4]; proto8.c[5]=p[5]; proto8.c[6]=p[6]; proto8.c[7]=p[7]; 771 if (p[-1] == ' ' && http_1_1.u == proto8.u) { 772 r->http_version = HTTP_VERSION_1_1; 773 r->keep_alive = 1; /* keep-alive default: HTTP/1.1 -> true */ 774 } 775 else if (p[-1] == ' ' && http_1_0.u == proto8.u) { 776 r->http_version = HTTP_VERSION_1_0; 777 r->keep_alive = 0; /* keep-alive default: HTTP/1.0 -> false */ 778 } 779 else { 780 int status = http_request_parse_proto_loose(r,ptr,len,http_parseopts); 781 if (0 != status) return status; 782 /*(space char must exist if http_request_parse_proto_loose() succeeds)*/ 783 for (p = ptr + len - 9; p[-1] != ' '; --p) ; 784 } 785 786 /* method is expected to be a short string in the general case */ 787 size_t i = 0; 788 while (ptr[i] != ' ') ++i; 789 #if 0 /*(space must exist if protocol was parsed successfully)*/ 790 while (i < len && ptr[i] != ' ') ++i; 791 if (ptr[i] != ' ') 792 return http_request_header_line_invalid(r, 400, "incomplete request line -> 400"); 793 #endif 794 795 r->http_method = get_http_method_key(ptr, i); 796 if (HTTP_METHOD_UNSET >= r->http_method) 797 return http_request_header_line_invalid(r, 501, "unknown http-method -> 501"); 798 799 const char *uri = ptr + i + 1; 800 801 if (uri == p) 802 return http_request_header_line_invalid(r, 400, "no uri specified -> 400"); 803 len = (size_t)(p - uri - 1); 804 805 if (*uri != '/') { /* (common case: (*uri == '/')) */ 806 uri = http_request_parse_reqline_uri(r, uri, len, http_parseopts); 807 if (NULL == uri) return 400; 808 len = (size_t)(p - uri - 1); 809 } 810 811 if (0 == len) 812 return http_request_header_line_invalid(r, 400, "no uri specified -> 400"); 813 814 /* check uri for invalid characters */ 815 if (http_parseopts & HTTP_PARSEOPT_HEADER_STRICT) { /* http_header_strict */ 816 if ((http_parseopts & HTTP_PARSEOPT_URL_NORMALIZE_CTRLS_REJECT)) { 817 /* URI will be checked in http_request_parse_target() */ 818 } 819 else { 820 for (i = 0; i < len; ++i) { 821 if (!request_uri_is_valid_char(uri[i])) 822 return http_request_header_char_invalid(r, uri[i], "invalid character in URI -> 400"); 823 } 824 } 825 } 826 else { 827 /* check entire set of request headers for '\0' */ 828 if (NULL != memchr(ptr, '\0', hoff[hoff[0]])) 829 return http_request_header_char_invalid(r, '\0', "invalid character in header -> 400"); 830 } 831 832 buffer_copy_string_len(&r->target, uri, len); 833 buffer_copy_string_len(&r->target_orig, uri, len); 834 return 0; 835 } 836 837 int http_request_parse_target(request_st * const r, int scheme_port) { 838 /* URI is parsed into components at start of request and may 839 * also be re-parsed upon HANDLER_COMEBACK during the request 840 * r->target is expected to be a "/url-part?query-part" 841 * (and *not* a fully-qualified URI starting https://...) 842 * r->uri.authority is expected to be parsed elsewhere into r->http_host 843 */ 844 845 /** 846 * prepare strings 847 * 848 * - uri.path 849 * - uri.query 850 * 851 */ 852 853 /** 854 * Name according to RFC 2396 855 * 856 * - scheme 857 * - authority 858 * - path 859 * - query 860 * 861 * (scheme)://(authority)(path)?(query)#fragment 862 * 863 */ 864 865 /* take initial scheme value from connection-level state 866 * (request r->uri.scheme can be overwritten for later, 867 * for example by mod_extforward or mod_magnet) */ 868 buffer_copy_string_len(&r->uri.scheme, "https", scheme_port == 443 ? 5 : 4); 869 870 buffer * const target = &r->target; 871 if (r->http_method == HTTP_METHOD_CONNECT 872 || (r->http_method == HTTP_METHOD_OPTIONS 873 && target->ptr[0] == '*' 874 && target->ptr[1] == '\0')) { 875 /* CONNECT ... (or) OPTIONS * ... */ 876 buffer_copy_buffer(&r->uri.path, target); 877 buffer_clear(&r->uri.query); 878 return 0; 879 } 880 881 char *qstr; 882 if (r->conf.http_parseopts & HTTP_PARSEOPT_URL_NORMALIZE) { 883 /*uint32_t len = (uint32_t)buffer_string_length(target);*/ 884 int qs = burl_normalize(target, r->tmp_buf, r->conf.http_parseopts); 885 if (-2 == qs) 886 return http_request_header_line_invalid(r, 400, 887 "invalid character in URI -> 400"); /* Bad Request */ 888 qstr = (-1 == qs) ? NULL : target->ptr+qs; 889 #if 0 /* future: might enable here, or below for all requests */ 890 /* (Note: total header size not recalculated on HANDLER_COMEBACK 891 * even if other request headers changed during processing) 892 * (If (0 != r->loops_per_request), then the generated 893 * request is too large. Should a different error be returned?) */ 894 r->rqst_header_len -= len; 895 len = buffer_string_length(target); 896 r->rqst_header_len += len; 897 if (len > MAX_HTTP_REQUEST_URI) { 898 return 414; /* 414 URI Too Long */ 899 } 900 if (r->rqst_header_len > MAX_HTTP_REQUEST_HEADER) { 901 log_error(r->conf.errh, __FILE__, __LINE__, 902 "request header fields too large: %u -> 431", 903 r->rqst_header_len); 904 return 431; /* Request Header Fields Too Large */ 905 } 906 #endif 907 } 908 else { 909 size_t rlen = buffer_string_length(target); 910 qstr = memchr(target->ptr, '#', rlen);/* discard fragment */ 911 if (qstr) { 912 rlen = (size_t)(qstr - target->ptr); 913 buffer_string_set_length(target, rlen); 914 } 915 qstr = memchr(target->ptr, '?', rlen); 916 } 917 918 /** extract query string from target */ 919 const char * const pstr = target->ptr; 920 const uint32_t rlen = buffer_string_length(target); 921 uint32_t plen; 922 if (NULL != qstr) { 923 plen = (uint32_t)(qstr - pstr); 924 buffer_copy_string_len(&r->uri.query, qstr + 1, rlen - plen - 1); 925 } 926 else { 927 plen = rlen; 928 buffer_clear(&r->uri.query); 929 } 930 buffer_copy_string_len(&r->uri.path, pstr, plen); 931 932 /* decode url to path 933 * 934 * - decode url-encodings (e.g. %20 -> ' ') 935 * - remove path-modifiers (e.g. /../) 936 */ 937 938 buffer_urldecode_path(&r->uri.path); 939 buffer_path_simplify(&r->uri.path); 940 if (r->uri.path.ptr[0] != '/') 941 return http_request_header_line_invalid(r, 400, 942 "uri-path does not begin with '/' -> 400"); /* Bad Request */ 943 944 return 0; 945 } 946 947 __attribute_cold__ 948 __attribute_noinline__ 949 static int http_request_parse_header_other(request_st * const restrict r, const char * const restrict k, const int klen, const unsigned int http_header_strict) { 950 for (int i = 0; i < klen; ++i) { 951 if (light_isalpha(k[i]) || k[i] == '-') continue; /*(common cases)*/ 952 /** 953 * 1*<any CHAR except CTLs or separators> 954 * CTLs == 0-31 + 127, CHAR = 7-bit ascii (0..127) 955 * 956 */ 957 switch(k[i]) { 958 case ' ': 959 case '\t': 960 return http_request_header_line_invalid(r, 400, "WS character in key -> 400"); 961 case '(': 962 case ')': 963 case '<': 964 case '>': 965 case '@': 966 case ',': 967 case ';': 968 case '\\': 969 case '\"': 970 case '/': 971 case '[': 972 case ']': 973 case '?': 974 case '=': 975 case '{': 976 case '}': 977 return http_request_header_char_invalid(r, k[i], "invalid character in header key -> 400"); 978 default: 979 if (http_header_strict ? (k[i] < 32 || ((unsigned char *)k)[i] >= 127) : k[i] == '\0') 980 return http_request_header_char_invalid(r, k[i], "invalid character in header key -> 400"); 981 break; /* ok */ 982 } 983 } 984 return 0; 985 } 986 987 static int http_request_parse_headers(request_st * const restrict r, char * const restrict ptr, const unsigned short * const restrict hoff, const unsigned int http_parseopts) { 988 const unsigned int http_header_strict = (http_parseopts & HTTP_PARSEOPT_HEADER_STRICT); 989 990 #if 0 /*(not checked here; will later result in invalid label for HTTP header)*/ 991 int i = hoff[2]; 992 993 if (ptr[i] == ' ' || ptr[i] == '\t') { 994 return http_request_header_line_invalid(r, 400, "WS at the start of first line -> 400"); 995 } 996 #endif 997 998 for (int i = 2; i < hoff[0]; ++i) { 999 const char *k = ptr + hoff[i]; 1000 /* one past last line hoff[hoff[0]] is to final "\r\n" */ 1001 char *end = ptr + hoff[i+1]; 1002 1003 const char *colon = memchr(k, ':', end - k); 1004 if (NULL == colon) 1005 return http_request_header_line_invalid(r, 400, "invalid header missing ':' -> 400"); 1006 1007 const char *v = colon + 1; 1008 1009 /* RFC7230 Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing 1010 * 3.2.4. Field Parsing 1011 * [...] 1012 * No whitespace is allowed between the header field-name and colon. In 1013 * the past, differences in the handling of such whitespace have led to 1014 * security vulnerabilities in request routing and response handling. A 1015 * server MUST reject any received request message that contains 1016 * whitespace between a header field-name and colon with a response code 1017 * of 400 (Bad Request). A proxy MUST remove any such whitespace from a 1018 * response message before forwarding the message downstream. 1019 */ 1020 /* (line k[-1] is always preceded by a '\n', 1021 * including first header after request-line, 1022 * so no need to check colon != k) */ 1023 if (colon[-1] == ' ' || colon[-1] == '\t') { 1024 if (http_header_strict) { 1025 return http_request_header_line_invalid(r, 400, "invalid whitespace between field-name and colon -> 400"); 1026 } 1027 else { 1028 /* remove trailing whitespace from key(if !http_header_strict)*/ 1029 do { --colon; } while (colon[-1] == ' ' || colon[-1] == '\t'); 1030 } 1031 } 1032 1033 const int klen = (int)(colon - k); 1034 if (0 == klen) 1035 return http_request_header_line_invalid(r, 400, "invalid header key -> 400"); 1036 const enum http_header_e id = http_header_hkey_get(k, klen); 1037 1038 if (id == HTTP_HEADER_OTHER) { 1039 for (int j = 0; j < klen; ++j) { 1040 if (light_isalpha(k[j]) || k[j] == '-') continue; /*(common cases)*/ 1041 if (0 != http_request_parse_header_other(r, k+j, klen-j, http_header_strict)) 1042 return 400; 1043 break; 1044 } 1045 } 1046 1047 /* remove leading whitespace from value */ 1048 while (*v == ' ' || *v == '\t') ++v; 1049 1050 for (; i+1 <= hoff[0]; ++i) { 1051 end = ptr + hoff[i+1]; 1052 if (end[0] != ' ' && end[0] != '\t') break; 1053 1054 /* line folding */ 1055 #ifdef __COVERITY__ 1056 force_assert(end - k >= 2); 1057 #endif 1058 if (end[-2] == '\r') 1059 end[-2] = ' '; 1060 else if (http_header_strict) 1061 return http_request_header_line_invalid(r, 400, "missing CR before LF in header -> 400"); 1062 end[-1] = ' '; 1063 } 1064 #ifdef __COVERITY__ 1065 /*(buf holding k has non-zero request-line, so end[-2] valid)*/ 1066 force_assert(end >= k + 2); 1067 #endif 1068 if (end[-2] == '\r') 1069 --end; 1070 else if (http_header_strict) 1071 return http_request_header_line_invalid(r, 400, "missing CR before LF in header -> 400"); 1072 /* remove trailing whitespace from value (+ remove '\r\n') */ 1073 /* (line k[-1] is always preceded by a '\n', 1074 * including first header after request-line, 1075 * so no need to check (end != k)) */ 1076 do { --end; } while (end[-1] == ' ' || end[-1] == '\t'); 1077 1078 const int vlen = (int)(end - v); 1079 /* empty header-fields are not allowed by HTTP-RFC, we just ignore them */ 1080 if (vlen <= 0) continue; /* ignore header */ 1081 1082 if (http_header_strict) { 1083 for (int j = 0; j < vlen; ++j) { 1084 if ((((unsigned char *)v)[j] < 32 && v[j] != '\t') || v[j]==127) 1085 return http_request_header_char_invalid(r, v[j], "invalid character in header -> 400"); 1086 } 1087 } /* else URI already checked in http_request_parse_reqline() for any '\0' */ 1088 1089 int status = http_request_parse_single_header(r, id, k, (size_t)klen, v, (size_t)vlen); 1090 if (0 != status) return status; 1091 } 1092 1093 return 0; 1094 } 1095 1096 1097 static int 1098 http_request_parse (request_st * const restrict r, const int scheme_port) 1099 { 1100 int status = http_request_parse_target(r, scheme_port); 1101 if (0 != status) return status; 1102 1103 /*(r->http_host might not be set until after parsing request headers)*/ 1104 buffer_copy_buffer(&r->uri.authority, r->http_host);/*(copy even if empty)*/ 1105 buffer_to_lower(&r->uri.authority); 1106 1107 /* post-processing */ 1108 const unsigned int http_parseopts = r->conf.http_parseopts; 1109 1110 /* check hostname field if it is set */ 1111 if (r->http_host) { 1112 if (0 != http_request_host_policy(r->http_host, 1113 http_parseopts, scheme_port)) 1114 return http_request_header_line_invalid(r, 400, "Invalid Hostname -> 400"); 1115 } 1116 else { 1117 if (r->http_version >= HTTP_VERSION_1_1) 1118 return http_request_header_line_invalid(r, 400, "HTTP/1.1 but Host missing -> 400"); 1119 } 1120 1121 if (0 == r->reqbody_length) { 1122 /* POST requires Content-Length (or Transfer-Encoding) 1123 * (-1 == r->reqbody_length when Transfer-Encoding: chunked)*/ 1124 if (HTTP_METHOD_POST == r->http_method 1125 && !light_btst(r->rqst_htags, HTTP_HEADER_CONTENT_LENGTH)) { 1126 return http_request_header_line_invalid(r, 411, "POST-request, but content-length missing -> 411"); 1127 } 1128 } 1129 else { 1130 /* (-1 == r->reqbody_length when Transfer-Encoding: chunked)*/ 1131 if (-1 == r->reqbody_length 1132 && light_btst(r->rqst_htags, HTTP_HEADER_CONTENT_LENGTH)) { 1133 /* RFC7230 Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing 1134 * 3.3.3. Message Body Length 1135 * [...] 1136 * If a message is received with both a Transfer-Encoding and a 1137 * Content-Length header field, the Transfer-Encoding overrides the 1138 * Content-Length. Such a message might indicate an attempt to 1139 * perform request smuggling (Section 9.5) or response splitting 1140 * (Section 9.4) and ought to be handled as an error. A sender MUST 1141 * remove the received Content-Length field prior to forwarding such 1142 * a message downstream. 1143 */ 1144 const unsigned int http_header_strict = 1145 (http_parseopts & HTTP_PARSEOPT_HEADER_STRICT); 1146 if (http_header_strict) { 1147 return http_request_header_line_invalid(r, 400, "invalid Transfer-Encoding + Content-Length -> 400"); 1148 } 1149 else { 1150 /* ignore Content-Length */ 1151 http_header_request_unset(r, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length")); 1152 } 1153 } 1154 if (http_method_get_or_head(r->http_method) 1155 && !(http_parseopts & HTTP_PARSEOPT_METHOD_GET_BODY)) { 1156 return http_request_header_line_invalid(r, 400, "GET/HEAD with content-length -> 400"); 1157 } 1158 } 1159 1160 return 0; 1161 } 1162 1163 1164 static int 1165 http_request_parse_hoff (request_st * const restrict r, char * const restrict hdrs, const unsigned short * const restrict hoff, const int scheme_port) 1166 { 1167 /* 1168 * Request: "^(GET|POST|HEAD|...) ([^ ]+(\\?[^ ]+|)) (HTTP/1\\.[01])$" 1169 * Header : "^([-a-zA-Z]+): (.+)$" 1170 * End : "^$" 1171 */ 1172 1173 int status; 1174 const unsigned int http_parseopts = r->conf.http_parseopts; 1175 1176 status = http_request_parse_reqline(r, hdrs, hoff, http_parseopts); 1177 if (0 != status) return status; 1178 1179 status = http_request_parse_headers(r, hdrs, hoff, http_parseopts); 1180 if (0 != status) return status; 1181 1182 return http_request_parse(r, scheme_port); 1183 } 1184 1185 1186 static void 1187 http_request_headers_fin (request_st * const restrict r) 1188 { 1189 if (0 == r->http_status) { 1190 #if 0 1191 r->conditional_is_valid = (1 << COMP_SERVER_SOCKET) 1192 | (1 << COMP_HTTP_SCHEME) 1193 | (1 << COMP_HTTP_HOST) 1194 | (1 << COMP_HTTP_REMOTE_IP) 1195 | (1 << COMP_HTTP_REQUEST_METHOD) 1196 | (1 << COMP_HTTP_URL) 1197 | (1 << COMP_HTTP_QUERY_STRING) 1198 | (1 << COMP_HTTP_REQUEST_HEADER); 1199 #else 1200 /* all config conditions are valid after parsing header 1201 * (set all bits; remove dependency on plugin_config.h) */ 1202 r->conditional_is_valid = ~0u; 1203 #endif 1204 } 1205 else { 1206 r->keep_alive = 0; 1207 r->reqbody_length = 0; 1208 } 1209 } 1210 1211 1212 void 1213 http_request_headers_process (request_st * const restrict r, char * const restrict hdrs, const unsigned short * const restrict hoff, const int scheme_port) 1214 { 1215 r->http_status = http_request_parse_hoff(r, hdrs, hoff, scheme_port); 1216 1217 http_request_headers_fin(r); 1218 1219 if (__builtin_expect( (0 != r->http_status), 0)) { 1220 if (r->conf.log_request_header_on_error) { 1221 /*(http_request_parse_headers() modifies hdrs only to 1222 * undo line-wrapping in-place using spaces)*/ 1223 log_error(r->conf.errh, __FILE__, __LINE__, 1224 "request-header:\n%.*s", (int)r->rqst_header_len, hdrs); 1225 } 1226 } 1227 } 1228 1229 1230 void 1231 http_request_headers_process_h2 (request_st * const restrict r, const int scheme_port) 1232 { 1233 if (0 == r->http_status) 1234 r->http_status = http_request_parse(r, scheme_port); 1235 1236 if (0 == r->http_status) { 1237 if (light_btst(r->rqst_htags, HTTP_HEADER_CONNECTION)) 1238 r->http_status = http_request_header_line_invalid(r, 400, 1239 "invalid Connection header with HTTP/2 -> 400"); 1240 } 1241 1242 http_request_headers_fin(r); 1243 1244 /* limited; headers not collected into a single buf for HTTP/2 */ 1245 if (__builtin_expect( (0 != r->http_status), 0)) { 1246 if (r->conf.log_request_header_on_error) { 1247 log_error(r->conf.errh, __FILE__, __LINE__, 1248 "request-header:\n:authority: %s\n:method: %s\n:path: %s", 1249 r->http_host ? r->http_host->ptr : "", 1250 (HTTP_METHOD_UNSET != r->http_method) 1251 ? get_http_method_name(r->http_method) 1252 : "", 1253 !buffer_string_is_empty(&r->target) ? r->target.ptr : ""); 1254 } 1255 } 1256 1257 /* ignore Upgrade if using HTTP/2 */ 1258 if (light_btst(r->rqst_htags, HTTP_HEADER_UPGRADE)) 1259 http_header_request_unset(r, HTTP_HEADER_UPGRADE, 1260 CONST_STR_LEN("upgrade")); 1261 /* XXX: should filter out other hop-by-hop connection headers, too */ 1262 } 1263