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_clen(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_truncate(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_truncate(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_clen(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_truncate(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_clen(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_truncate(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_truncate(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 r->http_host = http_header_request_set_ptr(r, HTTP_HEADER_HOST, 464 CONST_STR_LEN("Host")); 465 buffer_copy_string_len(r->http_host, host, hostlen); 466 return nuri; 467 } else if (!(http_parseopts & HTTP_PARSEOPT_HEADER_STRICT) /*(!http_header_strict)*/ 468 || (HTTP_METHOD_CONNECT == r->http_method && (uri[0] == ':' || light_isdigit(uri[0]))) 469 || (HTTP_METHOD_OPTIONS == r->http_method && uri[0] == '*' && 1 == len)) { 470 /* (permitted) */ 471 return uri; 472 } else { 473 http_request_header_line_invalid(r, 400, "request-URI parse error -> 400"); 474 return NULL; 475 } 476 } 477 478 479 __attribute_cold__ 480 __attribute_noinline__ 481 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); 482 483 484 int 485 http_request_validate_pseudohdrs (request_st * const restrict r, const int scheme, const unsigned int http_parseopts) 486 { 487 /* :method is required to indicate method 488 * CONNECT method must have :method and :authority 489 * All other methods must have at least :method :scheme :path */ 490 491 if (HTTP_METHOD_UNSET == r->http_method) 492 return http_request_header_line_invalid(r, 400, 493 "missing pseudo-header method -> 400"); 494 495 if (HTTP_METHOD_CONNECT != r->http_method) { 496 if (!scheme) 497 return http_request_header_line_invalid(r, 400, 498 "missing pseudo-header scheme -> 400"); 499 500 if (buffer_is_blank(&r->target)) 501 return http_request_header_line_invalid(r, 400, 502 "missing pseudo-header path -> 400"); 503 504 const char * const uri = r->target.ptr; 505 if (*uri != '/') { /* (common case: (*uri == '/')) */ 506 if (uri[0] != '*' || uri[1] != '\0' 507 || HTTP_METHOD_OPTIONS != r->http_method) 508 return http_request_header_line_invalid(r, 400, 509 "invalid pseudo-header path -> 400"); 510 } 511 } 512 else { /* HTTP_METHOD_CONNECT */ 513 if (NULL == r->http_host) 514 return http_request_header_line_invalid(r, 400, 515 "missing pseudo-header authority -> 400"); 516 if (!buffer_is_blank(&r->target) || scheme) 517 return http_request_header_line_invalid(r, 400, 518 "invalid pseudo-header with CONNECT -> 400"); 519 /*(reuse uri and ulen to assign to r->target)*/ 520 buffer_copy_buffer(&r->target, r->http_host); 521 } 522 buffer_copy_buffer(&r->target_orig, &r->target); 523 524 /* r->http_host, if set, is checked with http_request_host_policy() 525 * in http_request_parse() */ 526 527 /* copied and modified from end of http_request_parse_reqline() */ 528 529 /* check uri for invalid characters */ 530 const unsigned int http_header_strict = 531 (http_parseopts & HTTP_PARSEOPT_HEADER_STRICT); 532 const uint32_t ulen = buffer_clen(&r->target); 533 const uint8_t * const uri = (uint8_t *)r->target.ptr; 534 if (http_header_strict) { 535 if (http_parseopts & HTTP_PARSEOPT_URL_NORMALIZE_CTRLS_REJECT) 536 return 0; /* URI will be checked in http_request_parse_target() */ 537 for (uint32_t i = 0; i < ulen; ++i) { 538 if (!request_uri_is_valid_char(uri[i])) 539 return http_request_header_char_invalid(r, uri[i], 540 "invalid character in URI -> 400"); 541 } 542 } 543 else { 544 if (NULL != memchr(uri, '\0', ulen)) 545 return http_request_header_char_invalid(r, '\0', 546 "invalid character in header -> 400"); 547 } 548 549 return 0; 550 } 551 552 553 int 554 http_request_parse_header (request_st * const restrict r, http_header_parse_ctx * const restrict hpctx) 555 { 556 /* Note: k and v might not be '\0' terminated strings; 557 * care must be taken to avoid libc funcs which expect z-strings */ 558 const char * const restrict k = hpctx->k; 559 const char * const restrict v = hpctx->v; 560 const uint32_t klen = hpctx->klen; 561 const uint32_t vlen = hpctx->vlen; 562 563 if (0 == klen) 564 return http_request_header_line_invalid(r, 400, 565 "invalid header key -> 400"); 566 567 if ((hpctx->hlen += klen + vlen + 4) > hpctx->max_request_field_size) { 568 /*(configurable with server.max-request-field-size; default 8k)*/ 569 #if 1 /* emit to error log for people sending large headers */ 570 log_error(r->conf.errh, __FILE__, __LINE__, 571 "oversized request header -> 431"); 572 return 431; /* Request Header Fields Too Large */ 573 #else 574 /* 431 Request Header Fields Too Large */ 575 return http_request_header_line_invalid(r, 431, 576 "oversized request header -> 431"); 577 #endif 578 } 579 580 if (__builtin_expect( (2 == klen), 0) && k[0] == 't' && k[1] == 'e' 581 && !buffer_eq_icase_ss(v, vlen, CONST_STR_LEN("trailers"))) 582 return http_request_header_line_invalid(r, 400, 583 "invalid TE header value with HTTP/2 -> 400"); 584 585 if (!hpctx->trailers) { 586 if (*k == ':') { 587 /* HTTP/2 request pseudo-header fields */ 588 if (!hpctx->pseudo) /*(pseudo header after non-pseudo header)*/ 589 return http_request_header_line_invalid(r, 400, 590 "invalid pseudo-header -> 400"); 591 if (0 == vlen) 592 return http_request_header_line_invalid(r, 400, 593 "invalid header value -> 400"); 594 switch (klen-1) { 595 case 4: 596 if (0 == memcmp(k+1, "path", 4)) { 597 if (!buffer_is_blank(&r->target)) 598 return http_request_header_line_invalid(r, 400, 599 "repeated pseudo-header -> 400"); 600 buffer_copy_string_len(&r->target, v, vlen); 601 return 0; 602 } 603 break; 604 case 6: 605 if (0 == memcmp(k+1, "method", 6)) { 606 if (HTTP_METHOD_UNSET != r->http_method) 607 return http_request_header_line_invalid(r, 400, 608 "repeated pseudo-header -> 400"); 609 r->http_method = get_http_method_key(v, vlen); 610 if (HTTP_METHOD_UNSET >= r->http_method) 611 return http_request_header_line_invalid(r, 501, 612 "unknown http-method -> 501"); 613 return 0; 614 } 615 else if (0 == memcmp(k+1, "scheme", 6)) { 616 if (hpctx->scheme) 617 return http_request_header_line_invalid(r, 400, 618 "repeated pseudo-header -> 400"); 619 switch (vlen) {/*(validated, but then ignored)*/ 620 case 5: /* "https" */ 621 if (v[4]!='s') break; 622 __attribute_fallthrough__ 623 case 4: /* "http" */ 624 if (v[0]=='h' && v[1]=='t' && v[2]=='t' && v[3]=='p') { 625 hpctx->scheme = 1; 626 return 0; 627 } 628 break; 629 default: 630 break; 631 } 632 return http_request_header_line_invalid(r, 400, 633 "unknown pseudo-header scheme -> 400"); 634 } 635 break; 636 case 9: 637 if (0 == memcmp(k+1, "authority", 9)) { 638 if (r->http_host) 639 return http_request_header_line_invalid(r, 400, 640 "repeated pseudo-header -> 400"); 641 if (vlen >= 1024) /*(expecting < 256)*/ 642 return http_request_header_line_invalid(r, 400, 643 "invalid pseudo-header authority too long -> 400"); 644 /* insert as host header */ 645 r->http_host = 646 http_header_request_set_ptr(r, HTTP_HEADER_HOST, 647 CONST_STR_LEN("Host")); 648 buffer_copy_string_len(r->http_host, v, vlen); 649 return 0; 650 } 651 break; 652 default: 653 break; 654 } 655 return http_request_header_line_invalid(r, 400, 656 "invalid pseudo-header -> 400"); 657 } 658 else { /*(non-pseudo headers)*/ 659 if (hpctx->pseudo) { /*(transition to non-pseudo headers)*/ 660 hpctx->pseudo = 0; 661 int status = 662 http_request_validate_pseudohdrs(r, hpctx->scheme, 663 hpctx->http_parseopts); 664 if (0 != status) return status; 665 } 666 if (0 == vlen) 667 return 0; 668 669 uint32_t j = 0; 670 while (j < klen && (light_islower(k[j]) || k[j] == '-')) 671 ++j; 672 673 const unsigned int http_header_strict = 674 (hpctx->http_parseopts & HTTP_PARSEOPT_HEADER_STRICT); 675 676 if (__builtin_expect( (j != klen), 0)) { 677 if (light_isupper(k[j])) 678 return 400; 679 if (0 != http_request_parse_header_other(r, k+j, klen-j, 680 http_header_strict)) 681 return 400; 682 } 683 684 if (http_header_strict) { 685 for (j = 0; j < vlen; ++j) { 686 if ((((uint8_t *)v)[j] < 32 && v[j] != '\t') || v[j]==127) 687 return http_request_header_char_invalid(r, v[j], 688 "invalid character in header -> 400"); 689 } 690 } 691 else { 692 if (NULL != memchr(v, '\0', vlen)) 693 return http_request_header_char_invalid(r, '\0', 694 "invalid character in header -> 400"); 695 } 696 697 const enum http_header_e id = 698 hpctx->id ? hpctx->id : http_header_hkey_get_lc(k, klen); 699 return http_request_parse_single_header(r, id, k, klen, v, vlen); 700 } 701 } 702 else { /*(trailers)*/ 703 /* ignore trailers (after required HPACK decoding) if streaming 704 * request body to backend since headers have already been sent 705 * to backend via Common Gateway Interface (CGI) (CGI, FastCGI, 706 * SCGI, etc) or HTTP/1.1 (proxy) (mod_proxy does not currently 707 * support using HTTP/2 to connect to backends) */ 708 #if 0 /* (if needed, save flag in hpctx instead of fdevent.h dependency)*/ 709 if (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST) 710 return 0; 711 #endif 712 /* Note: do not unconditionally merge into headers since if 713 * headers had already been sent to backend, then mod_accesslog 714 * logging of request headers might be inaccurate. 715 * Many simple backends do not support HTTP/1.1 requests sending 716 * Transfer-Encoding: chunked, and even those that do might not 717 * handle trailers. Some backends do not even support HTTP/1.1. 718 * For all these reasons, ignore trailers if streaming request 719 * body to backend. Revisit in future if adding support for 720 * connecting to backends using HTTP/2 (with explicit config 721 * option to force connecting to backends using HTTP/2) */ 722 723 /* XXX: TODO: request trailers not handled if streaming reqbody 724 * XXX: must ensure that trailers are not disallowed field-names 725 */ 726 727 #if 0 728 if (0 == vlen) 729 return 0; 730 #endif 731 732 return 0; 733 } 734 } 735 736 737 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) { 738 size_t len = hoff[2]; 739 740 /* parse the first line of the request 741 * <method> <uri> <protocol>\r\n 742 * */ 743 if (len < 13) /* minimum len with (!http_header_strict): "x x HTTP/1.0\n" */ 744 return http_request_header_line_invalid(r, 400, "invalid request line (too short) -> 400"); 745 if (ptr[len-2] == '\r') 746 len-=2; 747 else if (!(http_parseopts & HTTP_PARSEOPT_HEADER_STRICT)) /*(!http_header_strict)*/ 748 len-=1; 749 else 750 return http_request_header_line_invalid(r, 400, "missing CR before LF in header -> 400"); 751 752 /* 753 * RFC7230: 754 * HTTP-version = HTTP-name "/" DIGIT "." DIGIT 755 * HTTP-name = %x48.54.54.50 ; "HTTP", case-sensitive 756 */ 757 758 /* protocol is expected to be " HTTP/1.1" or " HTTP/1.0" at end of line */ 759 union proto_un { 760 char c[8]; 761 uint64_t u; 762 }; 763 static const union proto_un http_1_1 = {{'H','T','T','P','/','1','.','1'}}; 764 static const union proto_un http_1_0 = {{'H','T','T','P','/','1','.','0'}}; 765 const char *p = ptr + len - 8; 766 union proto_un proto8; 767 proto8.c[0]=p[0]; proto8.c[1]=p[1]; proto8.c[2]=p[2]; proto8.c[3]=p[3]; 768 proto8.c[4]=p[4]; proto8.c[5]=p[5]; proto8.c[6]=p[6]; proto8.c[7]=p[7]; 769 if (p[-1] == ' ' && http_1_1.u == proto8.u) { 770 r->http_version = HTTP_VERSION_1_1; 771 r->keep_alive = 1; /* keep-alive default: HTTP/1.1 -> true */ 772 } 773 else if (p[-1] == ' ' && http_1_0.u == proto8.u) { 774 r->http_version = HTTP_VERSION_1_0; 775 r->keep_alive = 0; /* keep-alive default: HTTP/1.0 -> false */ 776 } 777 else { 778 int status = http_request_parse_proto_loose(r,ptr,len,http_parseopts); 779 if (0 != status) return status; 780 /*(space char must exist if http_request_parse_proto_loose() succeeds)*/ 781 for (p = ptr + len - 9; p[-1] != ' '; --p) ; 782 } 783 784 /* method is expected to be a short string in the general case */ 785 size_t i = 0; 786 while (ptr[i] != ' ') ++i; 787 #if 0 /*(space must exist if protocol was parsed successfully)*/ 788 while (i < len && ptr[i] != ' ') ++i; 789 if (ptr[i] != ' ') 790 return http_request_header_line_invalid(r, 400, "incomplete request line -> 400"); 791 #endif 792 793 r->http_method = get_http_method_key(ptr, i); 794 if (HTTP_METHOD_UNSET >= r->http_method) 795 return http_request_header_line_invalid(r, 501, "unknown http-method -> 501"); 796 797 const char *uri = ptr + i + 1; 798 799 if (uri == p) 800 return http_request_header_line_invalid(r, 400, "no uri specified -> 400"); 801 len = (size_t)(p - uri - 1); 802 803 if (*uri != '/') { /* (common case: (*uri == '/')) */ 804 uri = http_request_parse_reqline_uri(r, uri, len, http_parseopts); 805 if (NULL == uri) return 400; 806 len = (size_t)(p - uri - 1); 807 } 808 809 if (0 == len) 810 return http_request_header_line_invalid(r, 400, "no uri specified -> 400"); 811 812 /* check uri for invalid characters */ 813 if (http_parseopts & HTTP_PARSEOPT_HEADER_STRICT) { /* http_header_strict */ 814 if ((http_parseopts & HTTP_PARSEOPT_URL_NORMALIZE_CTRLS_REJECT)) { 815 /* URI will be checked in http_request_parse_target() */ 816 } 817 else { 818 for (i = 0; i < len; ++i) { 819 if (!request_uri_is_valid_char(uri[i])) 820 return http_request_header_char_invalid(r, uri[i], "invalid character in URI -> 400"); 821 } 822 } 823 } 824 else { 825 /* check entire set of request headers for '\0' */ 826 if (NULL != memchr(ptr, '\0', hoff[hoff[0]])) 827 return http_request_header_char_invalid(r, '\0', "invalid character in header -> 400"); 828 } 829 830 buffer_copy_string_len(&r->target, uri, len); 831 buffer_copy_string_len(&r->target_orig, uri, len); 832 return 0; 833 } 834 835 int http_request_parse_target(request_st * const r, int scheme_port) { 836 /* URI is parsed into components at start of request and may 837 * also be re-parsed upon HANDLER_COMEBACK during the request 838 * r->target is expected to be a "/url-part?query-part" 839 * (and *not* a fully-qualified URI starting https://...) 840 * r->uri.authority is expected to be parsed elsewhere into r->http_host 841 */ 842 843 /** 844 * prepare strings 845 * 846 * - uri.path 847 * - uri.query 848 * 849 */ 850 851 /** 852 * Name according to RFC 2396 853 * 854 * - scheme 855 * - authority 856 * - path 857 * - query 858 * 859 * (scheme)://(authority)(path)?(query)#fragment 860 * 861 */ 862 863 /* take initial scheme value from connection-level state 864 * (request r->uri.scheme can be overwritten for later, 865 * for example by mod_extforward or mod_magnet) */ 866 buffer_copy_string_len(&r->uri.scheme, "https", scheme_port == 443 ? 5 : 4); 867 868 buffer * const target = &r->target; 869 if (r->http_method == HTTP_METHOD_CONNECT 870 || (r->http_method == HTTP_METHOD_OPTIONS 871 && target->ptr[0] == '*' 872 && target->ptr[1] == '\0')) { 873 /* CONNECT ... (or) OPTIONS * ... */ 874 buffer_copy_buffer(&r->uri.path, target); 875 buffer_clear(&r->uri.query); 876 return 0; 877 } 878 879 char *qstr; 880 if (r->conf.http_parseopts & HTTP_PARSEOPT_URL_NORMALIZE) { 881 /*uint32_t len = buffer_clen(target);*/ 882 int qs = burl_normalize(target, r->tmp_buf, r->conf.http_parseopts); 883 if (-2 == qs) 884 return http_request_header_line_invalid(r, 400, 885 "invalid character in URI -> 400"); /* Bad Request */ 886 qstr = (-1 == qs) ? NULL : target->ptr+qs; 887 #if 0 /* future: might enable here, or below for all requests */ 888 /* (Note: total header size not recalculated on HANDLER_COMEBACK 889 * even if other request headers changed during processing) 890 * (If (0 != r->loops_per_request), then the generated 891 * request is too large. Should a different error be returned?) */ 892 r->rqst_header_len -= len; 893 len = buffer_clen(target); 894 r->rqst_header_len += len; 895 if (len > MAX_HTTP_REQUEST_URI) { 896 return 414; /* 414 URI Too Long */ 897 } 898 if (r->rqst_header_len > MAX_HTTP_REQUEST_HEADER) { 899 log_error(r->conf.errh, __FILE__, __LINE__, 900 "request header fields too large: %u -> 431", 901 r->rqst_header_len); 902 return 431; /* Request Header Fields Too Large */ 903 } 904 #endif 905 } 906 else { 907 size_t rlen = buffer_clen(target); 908 qstr = memchr(target->ptr, '#', rlen);/* discard fragment */ 909 if (qstr) { 910 rlen = (size_t)(qstr - target->ptr); 911 buffer_truncate(target, rlen); 912 } 913 qstr = memchr(target->ptr, '?', rlen); 914 } 915 916 /** extract query string from target */ 917 const char * const pstr = target->ptr; 918 const uint32_t rlen = buffer_clen(target); 919 uint32_t plen; 920 if (NULL != qstr) { 921 plen = (uint32_t)(qstr - pstr); 922 buffer_copy_string_len(&r->uri.query, qstr + 1, rlen - plen - 1); 923 } 924 else { 925 plen = rlen; 926 buffer_clear(&r->uri.query); 927 } 928 buffer_copy_string_len(&r->uri.path, pstr, plen); 929 930 /* decode url to path 931 * 932 * - decode url-encodings (e.g. %20 -> ' ') 933 * - remove path-modifiers (e.g. /../) 934 */ 935 936 buffer_urldecode_path(&r->uri.path); 937 buffer_path_simplify(&r->uri.path); 938 if (r->uri.path.ptr[0] != '/') 939 return http_request_header_line_invalid(r, 400, 940 "uri-path does not begin with '/' -> 400"); /* Bad Request */ 941 942 return 0; 943 } 944 945 __attribute_cold__ 946 __attribute_noinline__ 947 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) { 948 for (int i = 0; i < klen; ++i) { 949 if (light_isalpha(k[i]) || k[i] == '-') continue; /*(common cases)*/ 950 /** 951 * 1*<any CHAR except CTLs or separators> 952 * CTLs == 0-31 + 127, CHAR = 7-bit ascii (0..127) 953 * 954 */ 955 switch(k[i]) { 956 case ' ': 957 case '\t': 958 return http_request_header_line_invalid(r, 400, "WS character in key -> 400"); 959 case '(': 960 case ')': 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 return http_request_header_char_invalid(r, k[i], "invalid character in header key -> 400"); 976 default: 977 if (http_header_strict ? (k[i] < 32 || ((unsigned char *)k)[i] >= 127) : k[i] == '\0') 978 return http_request_header_char_invalid(r, k[i], "invalid character in header key -> 400"); 979 break; /* ok */ 980 } 981 } 982 return 0; 983 } 984 985 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) { 986 const unsigned int http_header_strict = (http_parseopts & HTTP_PARSEOPT_HEADER_STRICT); 987 988 #if 0 /*(not checked here; will later result in invalid label for HTTP header)*/ 989 int i = hoff[2]; 990 991 if (ptr[i] == ' ' || ptr[i] == '\t') { 992 return http_request_header_line_invalid(r, 400, "WS at the start of first line -> 400"); 993 } 994 #endif 995 996 for (int i = 2; i < hoff[0]; ++i) { 997 const char *k = ptr + hoff[i]; 998 /* one past last line hoff[hoff[0]] is to final "\r\n" */ 999 char *end = ptr + hoff[i+1]; 1000 1001 const char *colon = memchr(k, ':', end - k); 1002 if (NULL == colon) 1003 return http_request_header_line_invalid(r, 400, "invalid header missing ':' -> 400"); 1004 1005 const char *v = colon + 1; 1006 1007 /* RFC7230 Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing 1008 * 3.2.4. Field Parsing 1009 * [...] 1010 * No whitespace is allowed between the header field-name and colon. In 1011 * the past, differences in the handling of such whitespace have led to 1012 * security vulnerabilities in request routing and response handling. A 1013 * server MUST reject any received request message that contains 1014 * whitespace between a header field-name and colon with a response code 1015 * of 400 (Bad Request). A proxy MUST remove any such whitespace from a 1016 * response message before forwarding the message downstream. 1017 */ 1018 /* (line k[-1] is always preceded by a '\n', 1019 * including first header after request-line, 1020 * so no need to check colon != k) */ 1021 if (colon[-1] == ' ' || colon[-1] == '\t') { 1022 if (http_header_strict) { 1023 return http_request_header_line_invalid(r, 400, "invalid whitespace between field-name and colon -> 400"); 1024 } 1025 else { 1026 /* remove trailing whitespace from key(if !http_header_strict)*/ 1027 do { --colon; } while (colon[-1] == ' ' || colon[-1] == '\t'); 1028 } 1029 } 1030 1031 const int klen = (int)(colon - k); 1032 if (0 == klen) 1033 return http_request_header_line_invalid(r, 400, "invalid header key -> 400"); 1034 const enum http_header_e id = http_header_hkey_get(k, klen); 1035 1036 if (id == HTTP_HEADER_OTHER) { 1037 for (int j = 0; j < klen; ++j) { 1038 if (light_isalpha(k[j]) || k[j] == '-') continue; /*(common cases)*/ 1039 if (0 != http_request_parse_header_other(r, k+j, klen-j, http_header_strict)) 1040 return 400; 1041 break; 1042 } 1043 } 1044 1045 /* remove leading whitespace from value */ 1046 while (*v == ' ' || *v == '\t') ++v; 1047 1048 for (; i+1 <= hoff[0]; ++i) { 1049 end = ptr + hoff[i+1]; 1050 if (end[0] != ' ' && end[0] != '\t') break; 1051 1052 /* line folding */ 1053 #ifdef __COVERITY__ 1054 force_assert(end - k >= 2); 1055 #endif 1056 if (end[-2] == '\r') 1057 end[-2] = ' '; 1058 else if (http_header_strict) 1059 return http_request_header_line_invalid(r, 400, "missing CR before LF in header -> 400"); 1060 end[-1] = ' '; 1061 } 1062 #ifdef __COVERITY__ 1063 /*(buf holding k has non-zero request-line, so end[-2] valid)*/ 1064 force_assert(end >= k + 2); 1065 #endif 1066 if (end[-2] == '\r') 1067 --end; 1068 else if (http_header_strict) 1069 return http_request_header_line_invalid(r, 400, "missing CR before LF in header -> 400"); 1070 /* remove trailing whitespace from value (+ remove '\r\n') */ 1071 /* (line k[-1] is always preceded by a '\n', 1072 * including first header after request-line, 1073 * so no need to check (end != k)) */ 1074 do { --end; } while (end[-1] == ' ' || end[-1] == '\t'); 1075 1076 const int vlen = (int)(end - v); 1077 /* empty header-fields are not allowed by HTTP-RFC, we just ignore them */ 1078 if (vlen <= 0) continue; /* ignore header */ 1079 1080 if (http_header_strict) { 1081 for (int j = 0; j < vlen; ++j) { 1082 if ((((unsigned char *)v)[j] < 32 && v[j] != '\t') || v[j]==127) 1083 return http_request_header_char_invalid(r, v[j], "invalid character in header -> 400"); 1084 } 1085 } /* else URI already checked in http_request_parse_reqline() for any '\0' */ 1086 1087 int status = http_request_parse_single_header(r, id, k, (size_t)klen, v, (size_t)vlen); 1088 if (0 != status) return status; 1089 } 1090 1091 return 0; 1092 } 1093 1094 1095 static int 1096 http_request_parse (request_st * const restrict r, const int scheme_port) 1097 { 1098 int status = http_request_parse_target(r, scheme_port); 1099 if (0 != status) return status; 1100 1101 /* post-processing */ 1102 const unsigned int http_parseopts = r->conf.http_parseopts; 1103 1104 /* check hostname field if it is set */ 1105 /*(r->http_host might not be set until after parsing request headers)*/ 1106 if (__builtin_expect( (r->http_host != NULL), 1)) { 1107 buffer_copy_buffer(&r->uri.authority, r->http_host); 1108 buffer_to_lower(&r->uri.authority); 1109 if (0 != http_request_host_policy(r->http_host, 1110 http_parseopts, scheme_port)) 1111 return http_request_header_line_invalid(r, 400, "Invalid Hostname -> 400"); 1112 } 1113 else { 1114 buffer_copy_string_len(&r->uri.authority, CONST_STR_LEN("")); 1115 if (r->http_version >= HTTP_VERSION_1_1) 1116 return http_request_header_line_invalid(r, 400, "HTTP/1.1 but Host missing -> 400"); 1117 } 1118 1119 if (0 == r->reqbody_length) { 1120 /* POST requires Content-Length (or Transfer-Encoding) 1121 * (-1 == r->reqbody_length when Transfer-Encoding: chunked)*/ 1122 if (HTTP_METHOD_POST == r->http_method 1123 && !light_btst(r->rqst_htags, HTTP_HEADER_CONTENT_LENGTH)) { 1124 return http_request_header_line_invalid(r, 411, "POST-request, but content-length missing -> 411"); 1125 } 1126 } 1127 else { 1128 /* (-1 == r->reqbody_length when Transfer-Encoding: chunked)*/ 1129 if (-1 == r->reqbody_length 1130 && light_btst(r->rqst_htags, HTTP_HEADER_CONTENT_LENGTH)) { 1131 /* RFC7230 Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing 1132 * 3.3.3. Message Body Length 1133 * [...] 1134 * If a message is received with both a Transfer-Encoding and a 1135 * Content-Length header field, the Transfer-Encoding overrides the 1136 * Content-Length. Such a message might indicate an attempt to 1137 * perform request smuggling (Section 9.5) or response splitting 1138 * (Section 9.4) and ought to be handled as an error. A sender MUST 1139 * remove the received Content-Length field prior to forwarding such 1140 * a message downstream. 1141 */ 1142 const unsigned int http_header_strict = 1143 (http_parseopts & HTTP_PARSEOPT_HEADER_STRICT); 1144 if (http_header_strict) { 1145 return http_request_header_line_invalid(r, 400, "invalid Transfer-Encoding + Content-Length -> 400"); 1146 } 1147 else { 1148 /* ignore Content-Length */ 1149 http_header_request_unset(r, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length")); 1150 } 1151 } 1152 if (http_method_get_or_head(r->http_method) 1153 && !(http_parseopts & HTTP_PARSEOPT_METHOD_GET_BODY)) { 1154 return http_request_header_line_invalid(r, 400, "GET/HEAD with content-length -> 400"); 1155 } 1156 } 1157 1158 return 0; 1159 } 1160 1161 1162 static int 1163 http_request_parse_hoff (request_st * const restrict r, char * const restrict hdrs, const unsigned short * const restrict hoff, const int scheme_port) 1164 { 1165 /* 1166 * Request: "^(GET|POST|HEAD|...) ([^ ]+(\\?[^ ]+|)) (HTTP/1\\.[01])$" 1167 * Header : "^([-a-zA-Z]+): (.+)$" 1168 * End : "^$" 1169 */ 1170 1171 int status; 1172 const unsigned int http_parseopts = r->conf.http_parseopts; 1173 1174 status = http_request_parse_reqline(r, hdrs, hoff, http_parseopts); 1175 if (0 != status) return status; 1176 1177 status = http_request_parse_headers(r, hdrs, hoff, http_parseopts); 1178 if (0 != status) return status; 1179 1180 return http_request_parse(r, scheme_port); 1181 } 1182 1183 1184 static void 1185 http_request_headers_fin (request_st * const restrict r) 1186 { 1187 if (0 == r->http_status) { 1188 #if 0 1189 r->conditional_is_valid = (1 << COMP_SERVER_SOCKET) 1190 | (1 << COMP_HTTP_SCHEME) 1191 | (1 << COMP_HTTP_HOST) 1192 | (1 << COMP_HTTP_REMOTE_IP) 1193 | (1 << COMP_HTTP_REQUEST_METHOD) 1194 | (1 << COMP_HTTP_URL) 1195 | (1 << COMP_HTTP_QUERY_STRING) 1196 | (1 << COMP_HTTP_REQUEST_HEADER); 1197 #else 1198 /* all config conditions are valid after parsing header 1199 * (set all bits; remove dependency on plugin_config.h) */ 1200 r->conditional_is_valid = ~0u; 1201 #endif 1202 } 1203 else { 1204 r->keep_alive = 0; 1205 r->reqbody_length = 0; 1206 } 1207 } 1208 1209 1210 void 1211 http_request_headers_process (request_st * const restrict r, char * const restrict hdrs, const unsigned short * const restrict hoff, const int scheme_port) 1212 { 1213 r->http_status = http_request_parse_hoff(r, hdrs, hoff, scheme_port); 1214 1215 http_request_headers_fin(r); 1216 1217 if (__builtin_expect( (0 != r->http_status), 0)) { 1218 if (r->conf.log_request_header_on_error) { 1219 /*(http_request_parse_headers() modifies hdrs only to 1220 * undo line-wrapping in-place using spaces)*/ 1221 log_error(r->conf.errh, __FILE__, __LINE__, 1222 "request-header:\n%.*s", (int)r->rqst_header_len, hdrs); 1223 } 1224 } 1225 } 1226 1227 1228 void 1229 http_request_headers_process_h2 (request_st * const restrict r, const int scheme_port) 1230 { 1231 if (0 == r->http_status) 1232 r->http_status = http_request_parse(r, scheme_port); 1233 1234 if (0 == r->http_status) { 1235 if (light_btst(r->rqst_htags, HTTP_HEADER_CONNECTION)) 1236 r->http_status = http_request_header_line_invalid(r, 400, 1237 "invalid Connection header with HTTP/2 -> 400"); 1238 } 1239 1240 http_request_headers_fin(r); 1241 1242 /* limited; headers not collected into a single buf for HTTP/2 */ 1243 if (__builtin_expect( (0 != r->http_status), 0)) { 1244 if (r->conf.log_request_header_on_error) { 1245 log_error(r->conf.errh, __FILE__, __LINE__, 1246 "request-header:\n:authority: %s\n:method: %s\n:path: %s", 1247 r->http_host ? r->http_host->ptr : "", 1248 (HTTP_METHOD_UNSET != r->http_method) 1249 ? get_http_method_name(r->http_method) 1250 : "", 1251 !buffer_is_blank(&r->target) ? r->target.ptr : ""); 1252 } 1253 } 1254 1255 /* ignore Upgrade if using HTTP/2 */ 1256 if (light_btst(r->rqst_htags, HTTP_HEADER_UPGRADE)) 1257 http_header_request_unset(r, HTTP_HEADER_UPGRADE, 1258 CONST_STR_LEN("upgrade")); 1259 /* XXX: should filter out other hop-by-hop connection headers, too */ 1260 } 1261