2016-03-19 15:14:35 +00:00
|
|
|
#include "first.h"
|
|
|
|
|
2009-10-11 14:31:42 +00:00
|
|
|
#include "request.h"
|
2018-03-25 07:45:05 +00:00
|
|
|
#include "base.h"
|
2018-05-01 04:20:26 +00:00
|
|
|
#include "burl.h"
|
2018-09-09 05:50:33 +00:00
|
|
|
#include "http_header.h"
|
2018-04-22 00:21:54 +00:00
|
|
|
#include "http_kv.h"
|
2009-10-11 14:31:42 +00:00
|
|
|
#include "log.h"
|
2017-10-29 05:23:19 +00:00
|
|
|
#include "sock_addr.h"
|
2009-10-11 14:31:42 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
#include <limits.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
[config] opts for http header parsing strictness (fixes #551, fixes #1086, fixes #1184, fixes #2143, #2258, #2281, fixes #946, fixes #1330, fixes #602, #1016)
server.http-parseopt-header-strict = "enable"
server.http-parseopt-host-strict = "enable" (implies host-normalize)
server.http-parseopt-host-normalize = "disable"
defaults retain current behavior, which is strict header parsing
and strict host parsing, with enhancement to normalize IPv4 address
and port number strings.
For lighttpd tests, these need to be enabled (and are by default)
For marginally faster HTTP header parsing for benchmarks, disable these.
To allow
- underscores in hostname
- hypen ('-') at beginning of hostname
- all-numeric TLDs
server.http-parseopt-host-strict = "disable"
x-ref:
"lighttpd doesn't allow underscores in host names"
https://redmine.lighttpd.net/issues/551
"hyphen in hostname"
https://redmine.lighttpd.net/issues/1086
"a numeric tld"
https://redmine.lighttpd.net/issues/1184
"Numeric tld's"
https://redmine.lighttpd.net/issues/2143
"Bad Request"
https://redmine.lighttpd.net/issues/2258
"400 Bad Request when using Numeric TLDs"
https://redmine.lighttpd.net/issues/2281
To allow a variety of numerical formats to be converted to IP addresses
server.http-parseopt-host-strict = "disable"
server.http-parseopt-host-normalize = "enable"
x-ref:
"URL encoding leads to "400 - Bad Request""
https://redmine.lighttpd.net/issues/946
"400 Bad Request when using IP's numeric value ("ip2long()")"
https://redmine.lighttpd.net/issues/1330
To allow most 8-bit and 7-bit chars in headers
server.http-parseopt-header-strict = "disable" (not recommended)
x-ref:
"Russian letters not alowed?"
https://redmine.lighttpd.net/issues/602
"header Content-Disposition with russian '?' (CP1251, ascii code 255) causes error"
https://redmine.lighttpd.net/issues/1016
2016-05-18 09:42:42 +00:00
|
|
|
static int request_check_hostname(buffer *host) {
|
2005-02-20 14:27:00 +00:00
|
|
|
enum { DOMAINLABEL, TOPLABEL } stage = TOPLABEL;
|
|
|
|
size_t i;
|
|
|
|
int label_len = 0;
|
2016-06-23 07:57:19 +00:00
|
|
|
size_t host_len, hostport_len;
|
2005-02-20 14:27:00 +00:00
|
|
|
char *colon;
|
2006-10-04 13:26:23 +00:00
|
|
|
int is_ip = -1; /* -1 don't know yet, 0 no, 1 yes */
|
2005-02-20 14:27:00 +00:00
|
|
|
int level = 0;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/*
|
|
|
|
* hostport = host [ ":" port ]
|
|
|
|
* host = hostname | IPv4address | IPv6address
|
|
|
|
* hostname = *( domainlabel "." ) toplabel [ "." ]
|
|
|
|
* domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
|
|
|
|
* toplabel = alpha | alpha *( alphanum | "-" ) alphanum
|
|
|
|
* IPv4address = 1*digit "." 1*digit "." 1*digit "." 1*digit
|
|
|
|
* IPv6address = "[" ... "]"
|
|
|
|
* port = *digit
|
|
|
|
*/
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* IPv6 adress */
|
|
|
|
if (host->ptr[0] == '[') {
|
|
|
|
char *c = host->ptr + 1;
|
|
|
|
int colon_cnt = 0;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2014-03-12 12:03:55 +00:00
|
|
|
/* check the address inside [...] */
|
2005-02-20 14:27:00 +00:00
|
|
|
for (; *c && *c != ']'; c++) {
|
|
|
|
if (*c == ':') {
|
|
|
|
if (++colon_cnt > 7) {
|
|
|
|
return -1;
|
|
|
|
}
|
2011-11-30 20:46:49 +00:00
|
|
|
} else if (!light_isxdigit(*c) && '.' != *c) {
|
2005-02-20 14:27:00 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* missing ] */
|
|
|
|
if (!*c) {
|
|
|
|
return -1;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* check port */
|
|
|
|
if (*(c+1) == ':') {
|
|
|
|
for (c += 2; *c; c++) {
|
|
|
|
if (!light_isdigit(*c)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-03-12 12:03:55 +00:00
|
|
|
else if ('\0' != *(c+1)) {
|
|
|
|
/* only a port is allowed to follow [...] */
|
|
|
|
return -1;
|
|
|
|
}
|
2005-02-20 14:27:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2016-06-23 07:57:19 +00:00
|
|
|
hostport_len = host_len = buffer_string_length(host);
|
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (NULL != (colon = memchr(host->ptr, ':', host_len))) {
|
|
|
|
char *c = colon + 1;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* check portnumber */
|
|
|
|
for (; *c; c++) {
|
|
|
|
if (!light_isdigit(*c)) return -1;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* remove the port from the host-len */
|
|
|
|
host_len = colon - host->ptr;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* Host is empty */
|
|
|
|
if (host_len == 0) return -1;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2006-11-10 17:37:19 +00:00
|
|
|
/* if the hostname ends in a "." strip it */
|
2009-04-09 16:51:36 +00:00
|
|
|
if (host->ptr[host_len-1] == '.') {
|
|
|
|
/* shift port info one left */
|
2016-06-23 07:57:19 +00:00
|
|
|
if (NULL != colon) memmove(colon-1, colon, hostport_len - host_len);
|
|
|
|
buffer_string_set_length(host, --hostport_len);
|
|
|
|
if (--host_len == 0) return -1;
|
2009-04-09 16:51:36 +00:00
|
|
|
}
|
|
|
|
|
2006-11-10 17:37:19 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* scan from the right and skip the \0 */
|
2009-04-09 16:51:36 +00:00
|
|
|
for (i = host_len; i-- > 0; ) {
|
2005-02-28 08:40:00 +00:00
|
|
|
const char c = host->ptr[i];
|
2005-02-20 14:27:00 +00:00
|
|
|
|
|
|
|
switch (stage) {
|
2006-10-04 13:26:23 +00:00
|
|
|
case TOPLABEL:
|
2005-02-20 14:27:00 +00:00
|
|
|
if (c == '.') {
|
|
|
|
/* only switch stage, if this is not the last character */
|
|
|
|
if (i != host_len - 1) {
|
|
|
|
if (label_len == 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* check the first character at right of the dot */
|
|
|
|
if (is_ip == 0) {
|
2009-07-14 12:57:27 +00:00
|
|
|
if (!light_isalnum(host->ptr[i+1])) {
|
2006-10-04 13:26:23 +00:00
|
|
|
return -1;
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
} else if (!light_isdigit(host->ptr[i+1])) {
|
|
|
|
is_ip = 0;
|
|
|
|
} else if ('-' == host->ptr[i+1]) {
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
/* just digits */
|
|
|
|
is_ip = 1;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
stage = DOMAINLABEL;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
label_len = 0;
|
|
|
|
level++;
|
|
|
|
} else if (i == 0) {
|
|
|
|
/* just a dot and nothing else is evil */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else if (i == 0) {
|
|
|
|
/* the first character of the hostname */
|
2009-07-14 12:57:27 +00:00
|
|
|
if (!light_isalnum(c)) {
|
2005-02-20 14:27:00 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
label_len++;
|
|
|
|
} else {
|
|
|
|
if (c != '-' && !light_isalnum(c)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (is_ip == -1) {
|
|
|
|
if (!light_isdigit(c)) is_ip = 0;
|
|
|
|
}
|
|
|
|
label_len++;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
break;
|
|
|
|
case DOMAINLABEL:
|
|
|
|
if (is_ip == 1) {
|
|
|
|
if (c == '.') {
|
|
|
|
if (label_len == 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
label_len = 0;
|
|
|
|
level++;
|
|
|
|
} else if (!light_isdigit(c)) {
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
label_len++;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (c == '.') {
|
|
|
|
if (label_len == 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* c is either - or alphanum here */
|
|
|
|
if ('-' == host->ptr[i+1]) {
|
|
|
|
return -1;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
label_len = 0;
|
|
|
|
level++;
|
|
|
|
} else if (i == 0) {
|
|
|
|
if (!light_isalnum(c)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
label_len++;
|
|
|
|
} else {
|
|
|
|
if (c != '-' && !light_isalnum(c)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
label_len++;
|
|
|
|
}
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* a IP has to consist of 4 parts */
|
|
|
|
if (is_ip == 1 && level != 3) {
|
|
|
|
return -1;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (label_len == 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-04-25 15:12:53 +00:00
|
|
|
int http_request_host_normalize(buffer *b, int scheme_port) {
|
[config] opts for http header parsing strictness (fixes #551, fixes #1086, fixes #1184, fixes #2143, #2258, #2281, fixes #946, fixes #1330, fixes #602, #1016)
server.http-parseopt-header-strict = "enable"
server.http-parseopt-host-strict = "enable" (implies host-normalize)
server.http-parseopt-host-normalize = "disable"
defaults retain current behavior, which is strict header parsing
and strict host parsing, with enhancement to normalize IPv4 address
and port number strings.
For lighttpd tests, these need to be enabled (and are by default)
For marginally faster HTTP header parsing for benchmarks, disable these.
To allow
- underscores in hostname
- hypen ('-') at beginning of hostname
- all-numeric TLDs
server.http-parseopt-host-strict = "disable"
x-ref:
"lighttpd doesn't allow underscores in host names"
https://redmine.lighttpd.net/issues/551
"hyphen in hostname"
https://redmine.lighttpd.net/issues/1086
"a numeric tld"
https://redmine.lighttpd.net/issues/1184
"Numeric tld's"
https://redmine.lighttpd.net/issues/2143
"Bad Request"
https://redmine.lighttpd.net/issues/2258
"400 Bad Request when using Numeric TLDs"
https://redmine.lighttpd.net/issues/2281
To allow a variety of numerical formats to be converted to IP addresses
server.http-parseopt-host-strict = "disable"
server.http-parseopt-host-normalize = "enable"
x-ref:
"URL encoding leads to "400 - Bad Request""
https://redmine.lighttpd.net/issues/946
"400 Bad Request when using IP's numeric value ("ip2long()")"
https://redmine.lighttpd.net/issues/1330
To allow most 8-bit and 7-bit chars in headers
server.http-parseopt-header-strict = "disable" (not recommended)
x-ref:
"Russian letters not alowed?"
https://redmine.lighttpd.net/issues/602
"header Content-Disposition with russian '?' (CP1251, ascii code 255) causes error"
https://redmine.lighttpd.net/issues/1016
2016-05-18 09:42:42 +00:00
|
|
|
/*
|
|
|
|
* check for and canonicalize numeric IP address and portnum (optional)
|
|
|
|
* (IP address may be followed by ":portnum" (optional))
|
|
|
|
* - IPv6: "[...]"
|
|
|
|
* - IPv4: "x.x.x.x"
|
|
|
|
* - IPv4: 12345678 (32-bit decimal number)
|
|
|
|
* - IPv4: 012345678 (32-bit octal number)
|
|
|
|
* - IPv4: 0x12345678 (32-bit hex number)
|
|
|
|
*
|
|
|
|
* allow any chars (except ':' and '\0' and stray '[' or ']')
|
|
|
|
* (other code may check chars more strictly or more pedantically)
|
|
|
|
* ':' delimits (optional) port at end of string
|
|
|
|
* "[]" wraps IPv6 address literal
|
|
|
|
* '\0' should have been rejected earlier were it present
|
|
|
|
*
|
|
|
|
* any chars includes, but is not limited to:
|
|
|
|
* - allow '-' any where, even at beginning of word
|
|
|
|
* (security caution: might be confused for cmd flag if passed to shell)
|
|
|
|
* - allow all-digit TLDs
|
|
|
|
* (might be mistaken for IPv4 addr by inet_aton()
|
|
|
|
* unless non-digits appear in subdomain)
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Note: not using getaddrinfo() since it does not support "[]" around IPv6
|
|
|
|
* and is not as lenient as inet_aton() and inet_addr() for IPv4 strings.
|
|
|
|
* Not using inet_pton() (when available) on IPv4 for similar reasons. */
|
|
|
|
|
|
|
|
const char * const p = b->ptr;
|
|
|
|
const size_t blen = buffer_string_length(b);
|
|
|
|
long port = 0;
|
|
|
|
|
|
|
|
if (*p != '[') {
|
|
|
|
char * const colon = (char *)memchr(p, ':', blen);
|
|
|
|
if (colon) {
|
|
|
|
if (*p == ':') return -1; /*(empty host then port, or naked IPv6)*/
|
|
|
|
if (colon[1] != '\0') {
|
|
|
|
char *e;
|
|
|
|
port = strtol(colon+1, &e, 0); /*(allow decimal, octal, hex)*/
|
|
|
|
if (0 < port && port <= USHRT_MAX && *e == '\0') {
|
|
|
|
/* valid port */
|
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} /*(else ignore stray colon at string end)*/
|
2016-05-23 18:33:09 +00:00
|
|
|
buffer_string_set_length(b, (size_t)(colon - p)); /*(remove port str)*/
|
[config] opts for http header parsing strictness (fixes #551, fixes #1086, fixes #1184, fixes #2143, #2258, #2281, fixes #946, fixes #1330, fixes #602, #1016)
server.http-parseopt-header-strict = "enable"
server.http-parseopt-host-strict = "enable" (implies host-normalize)
server.http-parseopt-host-normalize = "disable"
defaults retain current behavior, which is strict header parsing
and strict host parsing, with enhancement to normalize IPv4 address
and port number strings.
For lighttpd tests, these need to be enabled (and are by default)
For marginally faster HTTP header parsing for benchmarks, disable these.
To allow
- underscores in hostname
- hypen ('-') at beginning of hostname
- all-numeric TLDs
server.http-parseopt-host-strict = "disable"
x-ref:
"lighttpd doesn't allow underscores in host names"
https://redmine.lighttpd.net/issues/551
"hyphen in hostname"
https://redmine.lighttpd.net/issues/1086
"a numeric tld"
https://redmine.lighttpd.net/issues/1184
"Numeric tld's"
https://redmine.lighttpd.net/issues/2143
"Bad Request"
https://redmine.lighttpd.net/issues/2258
"400 Bad Request when using Numeric TLDs"
https://redmine.lighttpd.net/issues/2281
To allow a variety of numerical formats to be converted to IP addresses
server.http-parseopt-host-strict = "disable"
server.http-parseopt-host-normalize = "enable"
x-ref:
"URL encoding leads to "400 - Bad Request""
https://redmine.lighttpd.net/issues/946
"400 Bad Request when using IP's numeric value ("ip2long()")"
https://redmine.lighttpd.net/issues/1330
To allow most 8-bit and 7-bit chars in headers
server.http-parseopt-header-strict = "disable" (not recommended)
x-ref:
"Russian letters not alowed?"
https://redmine.lighttpd.net/issues/602
"header Content-Disposition with russian '?' (CP1251, ascii code 255) causes error"
https://redmine.lighttpd.net/issues/1016
2016-05-18 09:42:42 +00:00
|
|
|
}
|
|
|
|
|
2018-10-24 04:16:14 +00:00
|
|
|
if (light_isdigit(*p)) do {
|
[config] opts for http header parsing strictness (fixes #551, fixes #1086, fixes #1184, fixes #2143, #2258, #2281, fixes #946, fixes #1330, fixes #602, #1016)
server.http-parseopt-header-strict = "enable"
server.http-parseopt-host-strict = "enable" (implies host-normalize)
server.http-parseopt-host-normalize = "disable"
defaults retain current behavior, which is strict header parsing
and strict host parsing, with enhancement to normalize IPv4 address
and port number strings.
For lighttpd tests, these need to be enabled (and are by default)
For marginally faster HTTP header parsing for benchmarks, disable these.
To allow
- underscores in hostname
- hypen ('-') at beginning of hostname
- all-numeric TLDs
server.http-parseopt-host-strict = "disable"
x-ref:
"lighttpd doesn't allow underscores in host names"
https://redmine.lighttpd.net/issues/551
"hyphen in hostname"
https://redmine.lighttpd.net/issues/1086
"a numeric tld"
https://redmine.lighttpd.net/issues/1184
"Numeric tld's"
https://redmine.lighttpd.net/issues/2143
"Bad Request"
https://redmine.lighttpd.net/issues/2258
"400 Bad Request when using Numeric TLDs"
https://redmine.lighttpd.net/issues/2281
To allow a variety of numerical formats to be converted to IP addresses
server.http-parseopt-host-strict = "disable"
server.http-parseopt-host-normalize = "enable"
x-ref:
"URL encoding leads to "400 - Bad Request""
https://redmine.lighttpd.net/issues/946
"400 Bad Request when using IP's numeric value ("ip2long()")"
https://redmine.lighttpd.net/issues/1330
To allow most 8-bit and 7-bit chars in headers
server.http-parseopt-header-strict = "disable" (not recommended)
x-ref:
"Russian letters not alowed?"
https://redmine.lighttpd.net/issues/602
"header Content-Disposition with russian '?' (CP1251, ascii code 255) causes error"
https://redmine.lighttpd.net/issues/1016
2016-05-18 09:42:42 +00:00
|
|
|
/* (IPv4 address literal or domain starting w/ digit (e.g. 3com))*/
|
2018-10-24 04:16:14 +00:00
|
|
|
/* (check one-element cache of normalized IPv4 address string) */
|
|
|
|
static struct { char s[INET_ADDRSTRLEN]; size_t n; } laddr;
|
|
|
|
size_t n = colon ? (size_t)(colon - p) : blen;
|
2017-04-03 18:50:14 +00:00
|
|
|
sock_addr addr;
|
2018-10-24 04:16:14 +00:00
|
|
|
if (n == laddr.n && 0 == memcmp(p, laddr.s, n)) break;
|
2017-04-03 18:50:14 +00:00
|
|
|
if (1 == sock_addr_inet_pton(&addr, p, AF_INET, 0)) {
|
|
|
|
sock_addr_inet_ntop_copy_buffer(b, &addr);
|
2018-10-24 04:16:14 +00:00
|
|
|
n = buffer_string_length(b);
|
|
|
|
if (n < sizeof(laddr.s)) memcpy(laddr.s, b->ptr, (laddr.n = n));
|
[config] opts for http header parsing strictness (fixes #551, fixes #1086, fixes #1184, fixes #2143, #2258, #2281, fixes #946, fixes #1330, fixes #602, #1016)
server.http-parseopt-header-strict = "enable"
server.http-parseopt-host-strict = "enable" (implies host-normalize)
server.http-parseopt-host-normalize = "disable"
defaults retain current behavior, which is strict header parsing
and strict host parsing, with enhancement to normalize IPv4 address
and port number strings.
For lighttpd tests, these need to be enabled (and are by default)
For marginally faster HTTP header parsing for benchmarks, disable these.
To allow
- underscores in hostname
- hypen ('-') at beginning of hostname
- all-numeric TLDs
server.http-parseopt-host-strict = "disable"
x-ref:
"lighttpd doesn't allow underscores in host names"
https://redmine.lighttpd.net/issues/551
"hyphen in hostname"
https://redmine.lighttpd.net/issues/1086
"a numeric tld"
https://redmine.lighttpd.net/issues/1184
"Numeric tld's"
https://redmine.lighttpd.net/issues/2143
"Bad Request"
https://redmine.lighttpd.net/issues/2258
"400 Bad Request when using Numeric TLDs"
https://redmine.lighttpd.net/issues/2281
To allow a variety of numerical formats to be converted to IP addresses
server.http-parseopt-host-strict = "disable"
server.http-parseopt-host-normalize = "enable"
x-ref:
"URL encoding leads to "400 - Bad Request""
https://redmine.lighttpd.net/issues/946
"400 Bad Request when using IP's numeric value ("ip2long()")"
https://redmine.lighttpd.net/issues/1330
To allow most 8-bit and 7-bit chars in headers
server.http-parseopt-header-strict = "disable" (not recommended)
x-ref:
"Russian letters not alowed?"
https://redmine.lighttpd.net/issues/602
"header Content-Disposition with russian '?' (CP1251, ascii code 255) causes error"
https://redmine.lighttpd.net/issues/1016
2016-05-18 09:42:42 +00:00
|
|
|
}
|
2018-10-24 04:16:14 +00:00
|
|
|
} while (0);
|
|
|
|
} else do { /* IPv6 addr */
|
[config] opts for http header parsing strictness (fixes #551, fixes #1086, fixes #1184, fixes #2143, #2258, #2281, fixes #946, fixes #1330, fixes #602, #1016)
server.http-parseopt-header-strict = "enable"
server.http-parseopt-host-strict = "enable" (implies host-normalize)
server.http-parseopt-host-normalize = "disable"
defaults retain current behavior, which is strict header parsing
and strict host parsing, with enhancement to normalize IPv4 address
and port number strings.
For lighttpd tests, these need to be enabled (and are by default)
For marginally faster HTTP header parsing for benchmarks, disable these.
To allow
- underscores in hostname
- hypen ('-') at beginning of hostname
- all-numeric TLDs
server.http-parseopt-host-strict = "disable"
x-ref:
"lighttpd doesn't allow underscores in host names"
https://redmine.lighttpd.net/issues/551
"hyphen in hostname"
https://redmine.lighttpd.net/issues/1086
"a numeric tld"
https://redmine.lighttpd.net/issues/1184
"Numeric tld's"
https://redmine.lighttpd.net/issues/2143
"Bad Request"
https://redmine.lighttpd.net/issues/2258
"400 Bad Request when using Numeric TLDs"
https://redmine.lighttpd.net/issues/2281
To allow a variety of numerical formats to be converted to IP addresses
server.http-parseopt-host-strict = "disable"
server.http-parseopt-host-normalize = "enable"
x-ref:
"URL encoding leads to "400 - Bad Request""
https://redmine.lighttpd.net/issues/946
"400 Bad Request when using IP's numeric value ("ip2long()")"
https://redmine.lighttpd.net/issues/1330
To allow most 8-bit and 7-bit chars in headers
server.http-parseopt-header-strict = "disable" (not recommended)
x-ref:
"Russian letters not alowed?"
https://redmine.lighttpd.net/issues/602
"header Content-Disposition with russian '?' (CP1251, ascii code 255) causes error"
https://redmine.lighttpd.net/issues/1016
2016-05-18 09:42:42 +00:00
|
|
|
#if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
|
|
|
|
|
2018-10-24 04:16:14 +00:00
|
|
|
/* (check one-element cache of normalized IPv4 address string) */
|
|
|
|
static struct { char s[INET6_ADDRSTRLEN]; size_t n; } laddr;
|
2017-04-03 18:50:14 +00:00
|
|
|
sock_addr addr;
|
[config] opts for http header parsing strictness (fixes #551, fixes #1086, fixes #1184, fixes #2143, #2258, #2281, fixes #946, fixes #1330, fixes #602, #1016)
server.http-parseopt-header-strict = "enable"
server.http-parseopt-host-strict = "enable" (implies host-normalize)
server.http-parseopt-host-normalize = "disable"
defaults retain current behavior, which is strict header parsing
and strict host parsing, with enhancement to normalize IPv4 address
and port number strings.
For lighttpd tests, these need to be enabled (and are by default)
For marginally faster HTTP header parsing for benchmarks, disable these.
To allow
- underscores in hostname
- hypen ('-') at beginning of hostname
- all-numeric TLDs
server.http-parseopt-host-strict = "disable"
x-ref:
"lighttpd doesn't allow underscores in host names"
https://redmine.lighttpd.net/issues/551
"hyphen in hostname"
https://redmine.lighttpd.net/issues/1086
"a numeric tld"
https://redmine.lighttpd.net/issues/1184
"Numeric tld's"
https://redmine.lighttpd.net/issues/2143
"Bad Request"
https://redmine.lighttpd.net/issues/2258
"400 Bad Request when using Numeric TLDs"
https://redmine.lighttpd.net/issues/2281
To allow a variety of numerical formats to be converted to IP addresses
server.http-parseopt-host-strict = "disable"
server.http-parseopt-host-normalize = "enable"
x-ref:
"URL encoding leads to "400 - Bad Request""
https://redmine.lighttpd.net/issues/946
"400 Bad Request when using IP's numeric value ("ip2long()")"
https://redmine.lighttpd.net/issues/1330
To allow most 8-bit and 7-bit chars in headers
server.http-parseopt-header-strict = "disable" (not recommended)
x-ref:
"Russian letters not alowed?"
https://redmine.lighttpd.net/issues/602
"header Content-Disposition with russian '?' (CP1251, ascii code 255) causes error"
https://redmine.lighttpd.net/issues/1016
2016-05-18 09:42:42 +00:00
|
|
|
char *bracket = b->ptr+blen-1;
|
2016-07-25 04:43:05 +00:00
|
|
|
char *percent = strchr(b->ptr+1, '%');
|
|
|
|
size_t len;
|
[config] opts for http header parsing strictness (fixes #551, fixes #1086, fixes #1184, fixes #2143, #2258, #2281, fixes #946, fixes #1330, fixes #602, #1016)
server.http-parseopt-header-strict = "enable"
server.http-parseopt-host-strict = "enable" (implies host-normalize)
server.http-parseopt-host-normalize = "disable"
defaults retain current behavior, which is strict header parsing
and strict host parsing, with enhancement to normalize IPv4 address
and port number strings.
For lighttpd tests, these need to be enabled (and are by default)
For marginally faster HTTP header parsing for benchmarks, disable these.
To allow
- underscores in hostname
- hypen ('-') at beginning of hostname
- all-numeric TLDs
server.http-parseopt-host-strict = "disable"
x-ref:
"lighttpd doesn't allow underscores in host names"
https://redmine.lighttpd.net/issues/551
"hyphen in hostname"
https://redmine.lighttpd.net/issues/1086
"a numeric tld"
https://redmine.lighttpd.net/issues/1184
"Numeric tld's"
https://redmine.lighttpd.net/issues/2143
"Bad Request"
https://redmine.lighttpd.net/issues/2258
"400 Bad Request when using Numeric TLDs"
https://redmine.lighttpd.net/issues/2281
To allow a variety of numerical formats to be converted to IP addresses
server.http-parseopt-host-strict = "disable"
server.http-parseopt-host-normalize = "enable"
x-ref:
"URL encoding leads to "400 - Bad Request""
https://redmine.lighttpd.net/issues/946
"400 Bad Request when using IP's numeric value ("ip2long()")"
https://redmine.lighttpd.net/issues/1330
To allow most 8-bit and 7-bit chars in headers
server.http-parseopt-header-strict = "disable" (not recommended)
x-ref:
"Russian letters not alowed?"
https://redmine.lighttpd.net/issues/602
"header Content-Disposition with russian '?' (CP1251, ascii code 255) causes error"
https://redmine.lighttpd.net/issues/1016
2016-05-18 09:42:42 +00:00
|
|
|
int rc;
|
2016-07-25 04:43:05 +00:00
|
|
|
char buf[INET6_ADDRSTRLEN+16]; /*(+16 for potential %interface name)*/
|
2016-06-21 07:39:17 +00:00
|
|
|
if (blen <= 2) return -1; /*(invalid "[]")*/
|
[config] opts for http header parsing strictness (fixes #551, fixes #1086, fixes #1184, fixes #2143, #2258, #2281, fixes #946, fixes #1330, fixes #602, #1016)
server.http-parseopt-header-strict = "enable"
server.http-parseopt-host-strict = "enable" (implies host-normalize)
server.http-parseopt-host-normalize = "disable"
defaults retain current behavior, which is strict header parsing
and strict host parsing, with enhancement to normalize IPv4 address
and port number strings.
For lighttpd tests, these need to be enabled (and are by default)
For marginally faster HTTP header parsing for benchmarks, disable these.
To allow
- underscores in hostname
- hypen ('-') at beginning of hostname
- all-numeric TLDs
server.http-parseopt-host-strict = "disable"
x-ref:
"lighttpd doesn't allow underscores in host names"
https://redmine.lighttpd.net/issues/551
"hyphen in hostname"
https://redmine.lighttpd.net/issues/1086
"a numeric tld"
https://redmine.lighttpd.net/issues/1184
"Numeric tld's"
https://redmine.lighttpd.net/issues/2143
"Bad Request"
https://redmine.lighttpd.net/issues/2258
"400 Bad Request when using Numeric TLDs"
https://redmine.lighttpd.net/issues/2281
To allow a variety of numerical formats to be converted to IP addresses
server.http-parseopt-host-strict = "disable"
server.http-parseopt-host-normalize = "enable"
x-ref:
"URL encoding leads to "400 - Bad Request""
https://redmine.lighttpd.net/issues/946
"400 Bad Request when using IP's numeric value ("ip2long()")"
https://redmine.lighttpd.net/issues/1330
To allow most 8-bit and 7-bit chars in headers
server.http-parseopt-header-strict = "disable" (not recommended)
x-ref:
"Russian letters not alowed?"
https://redmine.lighttpd.net/issues/602
"header Content-Disposition with russian '?' (CP1251, ascii code 255) causes error"
https://redmine.lighttpd.net/issues/1016
2016-05-18 09:42:42 +00:00
|
|
|
if (*bracket != ']') {
|
|
|
|
bracket = (char *)memchr(b->ptr+1, ']', blen-1);
|
|
|
|
if (NULL == bracket || bracket[1] != ':' || bracket - b->ptr == 1){
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (bracket[2] != '\0') { /*(ignore stray colon at string end)*/
|
|
|
|
char *e;
|
|
|
|
port = strtol(bracket+2, &e, 0); /*(allow decimal, octal, hex)*/
|
|
|
|
if (0 < port && port <= USHRT_MAX && *e == '\0') {
|
|
|
|
/* valid port */
|
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-24 04:16:14 +00:00
|
|
|
len = (size_t)((percent ? percent : bracket) - (b->ptr+1));
|
|
|
|
if (laddr.n == len && 0 == memcmp(laddr.s, b->ptr+1, len)) {
|
|
|
|
/* truncate after ']' and re-add normalized port, if needed */
|
|
|
|
buffer_string_set_length(b, (size_t)(bracket - b->ptr + 1));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
[config] opts for http header parsing strictness (fixes #551, fixes #1086, fixes #1184, fixes #2143, #2258, #2281, fixes #946, fixes #1330, fixes #602, #1016)
server.http-parseopt-header-strict = "enable"
server.http-parseopt-host-strict = "enable" (implies host-normalize)
server.http-parseopt-host-normalize = "disable"
defaults retain current behavior, which is strict header parsing
and strict host parsing, with enhancement to normalize IPv4 address
and port number strings.
For lighttpd tests, these need to be enabled (and are by default)
For marginally faster HTTP header parsing for benchmarks, disable these.
To allow
- underscores in hostname
- hypen ('-') at beginning of hostname
- all-numeric TLDs
server.http-parseopt-host-strict = "disable"
x-ref:
"lighttpd doesn't allow underscores in host names"
https://redmine.lighttpd.net/issues/551
"hyphen in hostname"
https://redmine.lighttpd.net/issues/1086
"a numeric tld"
https://redmine.lighttpd.net/issues/1184
"Numeric tld's"
https://redmine.lighttpd.net/issues/2143
"Bad Request"
https://redmine.lighttpd.net/issues/2258
"400 Bad Request when using Numeric TLDs"
https://redmine.lighttpd.net/issues/2281
To allow a variety of numerical formats to be converted to IP addresses
server.http-parseopt-host-strict = "disable"
server.http-parseopt-host-normalize = "enable"
x-ref:
"URL encoding leads to "400 - Bad Request""
https://redmine.lighttpd.net/issues/946
"400 Bad Request when using IP's numeric value ("ip2long()")"
https://redmine.lighttpd.net/issues/1330
To allow most 8-bit and 7-bit chars in headers
server.http-parseopt-header-strict = "disable" (not recommended)
x-ref:
"Russian letters not alowed?"
https://redmine.lighttpd.net/issues/602
"header Content-Disposition with russian '?' (CP1251, ascii code 255) causes error"
https://redmine.lighttpd.net/issues/1016
2016-05-18 09:42:42 +00:00
|
|
|
*bracket = '\0';/*(terminate IPv6 string)*/
|
2016-07-25 04:43:05 +00:00
|
|
|
if (percent) *percent = '\0'; /*(remove %interface from address)*/
|
2017-04-03 18:50:14 +00:00
|
|
|
rc = sock_addr_inet_pton(&addr, b->ptr+1, AF_INET6, 0);
|
2016-07-25 04:43:05 +00:00
|
|
|
if (percent) *percent = '%'; /*(restore %interface)*/
|
[config] opts for http header parsing strictness (fixes #551, fixes #1086, fixes #1184, fixes #2143, #2258, #2281, fixes #946, fixes #1330, fixes #602, #1016)
server.http-parseopt-header-strict = "enable"
server.http-parseopt-host-strict = "enable" (implies host-normalize)
server.http-parseopt-host-normalize = "disable"
defaults retain current behavior, which is strict header parsing
and strict host parsing, with enhancement to normalize IPv4 address
and port number strings.
For lighttpd tests, these need to be enabled (and are by default)
For marginally faster HTTP header parsing for benchmarks, disable these.
To allow
- underscores in hostname
- hypen ('-') at beginning of hostname
- all-numeric TLDs
server.http-parseopt-host-strict = "disable"
x-ref:
"lighttpd doesn't allow underscores in host names"
https://redmine.lighttpd.net/issues/551
"hyphen in hostname"
https://redmine.lighttpd.net/issues/1086
"a numeric tld"
https://redmine.lighttpd.net/issues/1184
"Numeric tld's"
https://redmine.lighttpd.net/issues/2143
"Bad Request"
https://redmine.lighttpd.net/issues/2258
"400 Bad Request when using Numeric TLDs"
https://redmine.lighttpd.net/issues/2281
To allow a variety of numerical formats to be converted to IP addresses
server.http-parseopt-host-strict = "disable"
server.http-parseopt-host-normalize = "enable"
x-ref:
"URL encoding leads to "400 - Bad Request""
https://redmine.lighttpd.net/issues/946
"400 Bad Request when using IP's numeric value ("ip2long()")"
https://redmine.lighttpd.net/issues/1330
To allow most 8-bit and 7-bit chars in headers
server.http-parseopt-header-strict = "disable" (not recommended)
x-ref:
"Russian letters not alowed?"
https://redmine.lighttpd.net/issues/602
"header Content-Disposition with russian '?' (CP1251, ascii code 255) causes error"
https://redmine.lighttpd.net/issues/1016
2016-05-18 09:42:42 +00:00
|
|
|
*bracket = ']'; /*(restore bracket)*/
|
|
|
|
if (1 != rc) return -1;
|
|
|
|
|
2017-04-03 18:50:14 +00:00
|
|
|
sock_addr_inet_ntop(&addr, buf, sizeof(buf));
|
2016-07-25 04:43:05 +00:00
|
|
|
len = strlen(buf);
|
|
|
|
if (percent) {
|
|
|
|
if (percent > bracket) return -1;
|
|
|
|
if (len + (size_t)(bracket - percent) >= sizeof(buf)) return -1;
|
2018-10-24 04:16:14 +00:00
|
|
|
if (len < sizeof(laddr.s)) memcpy(laddr.s, buf, (laddr.n = len));
|
2016-07-25 04:43:05 +00:00
|
|
|
memcpy(buf+len, percent, (size_t)(bracket - percent));
|
|
|
|
len += (size_t)(bracket - percent);
|
|
|
|
}
|
2016-05-23 18:12:24 +00:00
|
|
|
buffer_string_set_length(b, 1); /* truncate after '[' */
|
2016-07-25 04:43:05 +00:00
|
|
|
buffer_append_string_len(b, buf, len);
|
[config] opts for http header parsing strictness (fixes #551, fixes #1086, fixes #1184, fixes #2143, #2258, #2281, fixes #946, fixes #1330, fixes #602, #1016)
server.http-parseopt-header-strict = "enable"
server.http-parseopt-host-strict = "enable" (implies host-normalize)
server.http-parseopt-host-normalize = "disable"
defaults retain current behavior, which is strict header parsing
and strict host parsing, with enhancement to normalize IPv4 address
and port number strings.
For lighttpd tests, these need to be enabled (and are by default)
For marginally faster HTTP header parsing for benchmarks, disable these.
To allow
- underscores in hostname
- hypen ('-') at beginning of hostname
- all-numeric TLDs
server.http-parseopt-host-strict = "disable"
x-ref:
"lighttpd doesn't allow underscores in host names"
https://redmine.lighttpd.net/issues/551
"hyphen in hostname"
https://redmine.lighttpd.net/issues/1086
"a numeric tld"
https://redmine.lighttpd.net/issues/1184
"Numeric tld's"
https://redmine.lighttpd.net/issues/2143
"Bad Request"
https://redmine.lighttpd.net/issues/2258
"400 Bad Request when using Numeric TLDs"
https://redmine.lighttpd.net/issues/2281
To allow a variety of numerical formats to be converted to IP addresses
server.http-parseopt-host-strict = "disable"
server.http-parseopt-host-normalize = "enable"
x-ref:
"URL encoding leads to "400 - Bad Request""
https://redmine.lighttpd.net/issues/946
"400 Bad Request when using IP's numeric value ("ip2long()")"
https://redmine.lighttpd.net/issues/1330
To allow most 8-bit and 7-bit chars in headers
server.http-parseopt-header-strict = "disable" (not recommended)
x-ref:
"Russian letters not alowed?"
https://redmine.lighttpd.net/issues/602
"header Content-Disposition with russian '?' (CP1251, ascii code 255) causes error"
https://redmine.lighttpd.net/issues/1016
2016-05-18 09:42:42 +00:00
|
|
|
buffer_append_string_len(b, CONST_STR_LEN("]"));
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
#endif
|
2018-10-24 04:16:14 +00:00
|
|
|
} while (0);
|
[config] opts for http header parsing strictness (fixes #551, fixes #1086, fixes #1184, fixes #2143, #2258, #2281, fixes #946, fixes #1330, fixes #602, #1016)
server.http-parseopt-header-strict = "enable"
server.http-parseopt-host-strict = "enable" (implies host-normalize)
server.http-parseopt-host-normalize = "disable"
defaults retain current behavior, which is strict header parsing
and strict host parsing, with enhancement to normalize IPv4 address
and port number strings.
For lighttpd tests, these need to be enabled (and are by default)
For marginally faster HTTP header parsing for benchmarks, disable these.
To allow
- underscores in hostname
- hypen ('-') at beginning of hostname
- all-numeric TLDs
server.http-parseopt-host-strict = "disable"
x-ref:
"lighttpd doesn't allow underscores in host names"
https://redmine.lighttpd.net/issues/551
"hyphen in hostname"
https://redmine.lighttpd.net/issues/1086
"a numeric tld"
https://redmine.lighttpd.net/issues/1184
"Numeric tld's"
https://redmine.lighttpd.net/issues/2143
"Bad Request"
https://redmine.lighttpd.net/issues/2258
"400 Bad Request when using Numeric TLDs"
https://redmine.lighttpd.net/issues/2281
To allow a variety of numerical formats to be converted to IP addresses
server.http-parseopt-host-strict = "disable"
server.http-parseopt-host-normalize = "enable"
x-ref:
"URL encoding leads to "400 - Bad Request""
https://redmine.lighttpd.net/issues/946
"400 Bad Request when using IP's numeric value ("ip2long()")"
https://redmine.lighttpd.net/issues/1330
To allow most 8-bit and 7-bit chars in headers
server.http-parseopt-header-strict = "disable" (not recommended)
x-ref:
"Russian letters not alowed?"
https://redmine.lighttpd.net/issues/602
"header Content-Disposition with russian '?' (CP1251, ascii code 255) causes error"
https://redmine.lighttpd.net/issues/1016
2016-05-18 09:42:42 +00:00
|
|
|
|
2017-04-25 15:12:53 +00:00
|
|
|
if (0 != port && port != scheme_port) {
|
[config] opts for http header parsing strictness (fixes #551, fixes #1086, fixes #1184, fixes #2143, #2258, #2281, fixes #946, fixes #1330, fixes #602, #1016)
server.http-parseopt-header-strict = "enable"
server.http-parseopt-host-strict = "enable" (implies host-normalize)
server.http-parseopt-host-normalize = "disable"
defaults retain current behavior, which is strict header parsing
and strict host parsing, with enhancement to normalize IPv4 address
and port number strings.
For lighttpd tests, these need to be enabled (and are by default)
For marginally faster HTTP header parsing for benchmarks, disable these.
To allow
- underscores in hostname
- hypen ('-') at beginning of hostname
- all-numeric TLDs
server.http-parseopt-host-strict = "disable"
x-ref:
"lighttpd doesn't allow underscores in host names"
https://redmine.lighttpd.net/issues/551
"hyphen in hostname"
https://redmine.lighttpd.net/issues/1086
"a numeric tld"
https://redmine.lighttpd.net/issues/1184
"Numeric tld's"
https://redmine.lighttpd.net/issues/2143
"Bad Request"
https://redmine.lighttpd.net/issues/2258
"400 Bad Request when using Numeric TLDs"
https://redmine.lighttpd.net/issues/2281
To allow a variety of numerical formats to be converted to IP addresses
server.http-parseopt-host-strict = "disable"
server.http-parseopt-host-normalize = "enable"
x-ref:
"URL encoding leads to "400 - Bad Request""
https://redmine.lighttpd.net/issues/946
"400 Bad Request when using IP's numeric value ("ip2long()")"
https://redmine.lighttpd.net/issues/1330
To allow most 8-bit and 7-bit chars in headers
server.http-parseopt-header-strict = "disable" (not recommended)
x-ref:
"Russian letters not alowed?"
https://redmine.lighttpd.net/issues/602
"header Content-Disposition with russian '?' (CP1251, ascii code 255) causes error"
https://redmine.lighttpd.net/issues/1016
2016-05-18 09:42:42 +00:00
|
|
|
buffer_append_string_len(b, CONST_STR_LEN(":"));
|
|
|
|
buffer_append_int(b, (int)port);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-04-25 15:12:53 +00:00
|
|
|
static int scheme_port (const buffer *scheme)
|
|
|
|
{
|
|
|
|
return buffer_is_equal_string(scheme, CONST_STR_LEN("https")) ? 443 : 80;
|
|
|
|
}
|
|
|
|
|
|
|
|
int http_request_host_policy (connection *con, buffer *b, const buffer *scheme) {
|
2017-04-01 21:23:56 +00:00
|
|
|
return (((con->conf.http_parseopts & HTTP_PARSEOPT_HOST_STRICT)
|
|
|
|
&& 0 != request_check_hostname(b))
|
|
|
|
|| ((con->conf.http_parseopts & HTTP_PARSEOPT_HOST_NORMALIZE)
|
2017-04-25 15:12:53 +00:00
|
|
|
&& 0 != http_request_host_normalize(b, scheme_port(scheme))));
|
2017-04-01 21:23:56 +00:00
|
|
|
}
|
|
|
|
|
2018-09-08 20:42:42 +00:00
|
|
|
static int http_request_split_value(array *vals, const char *current, size_t len) {
|
2005-02-20 14:27:00 +00:00
|
|
|
int state = 0;
|
2012-11-21 12:01:44 +00:00
|
|
|
const char *token_start = NULL, *token_end = NULL;
|
2006-10-04 13:26:23 +00:00
|
|
|
/*
|
|
|
|
* parse
|
|
|
|
*
|
2005-02-20 14:27:00 +00:00
|
|
|
* val1, val2, val3, val4
|
2006-10-04 13:26:23 +00:00
|
|
|
*
|
2018-09-08 20:42:42 +00:00
|
|
|
* into a array (more or less a explode() incl. stripping of whitespaces
|
2005-02-20 14:27:00 +00:00
|
|
|
*/
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2018-09-08 20:42:42 +00:00
|
|
|
for (size_t i = 0; i <= len; ++i, ++current) {
|
2005-02-20 14:27:00 +00:00
|
|
|
switch (state) {
|
2012-11-21 12:01:44 +00:00
|
|
|
case 0: /* find start of a token */
|
|
|
|
switch (*current) {
|
|
|
|
case ' ':
|
|
|
|
case '\t': /* skip white space */
|
|
|
|
case ',': /* skip empty token */
|
|
|
|
break;
|
|
|
|
case '\0': /* end of string */
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
/* found real data, switch to state 1 to find the end of the token */
|
|
|
|
token_start = token_end = current;
|
|
|
|
state = 1;
|
|
|
|
break;
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2012-11-21 12:01:44 +00:00
|
|
|
break;
|
|
|
|
case 1: /* find end of token and last non white space character */
|
|
|
|
switch (*current) {
|
|
|
|
case ' ':
|
|
|
|
case '\t':
|
|
|
|
/* space - don't update token_end */
|
|
|
|
break;
|
|
|
|
case ',':
|
|
|
|
case '\0': /* end of string also marks the end of a token */
|
2018-09-08 18:23:23 +00:00
|
|
|
array_insert_value(vals, token_start, token_end-token_start+1);
|
2005-02-20 14:27:00 +00:00
|
|
|
state = 0;
|
2012-11-21 12:01:44 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* no white space, update token_end to include current character */
|
|
|
|
token_end = current;
|
|
|
|
break;
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2012-11-21 12:01:44 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-03-07 21:05:37 +00:00
|
|
|
static int request_uri_is_valid_char(unsigned char c) {
|
2005-07-09 20:17:40 +00:00
|
|
|
if (c <= 32) return 0;
|
|
|
|
if (c == 127) return 0;
|
|
|
|
if (c == 255) return 0;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-07-09 20:17:40 +00:00
|
|
|
return 1;
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
|
2018-08-25 10:43:03 +00:00
|
|
|
static void http_request_missing_CR_before_LF(server *srv, connection *con) {
|
2019-02-07 05:35:49 +00:00
|
|
|
UNUSED(con);
|
2017-10-16 06:17:07 +00:00
|
|
|
if (srv->srvconf.log_request_header_on_error) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s", "missing CR before LF in header -> 400");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-25 10:35:23 +00:00
|
|
|
enum keep_alive_set {
|
|
|
|
HTTP_CONNECTION_UNSET,
|
|
|
|
HTTP_CONNECTION_KEEPALIVE,
|
|
|
|
HTTP_CONNECTION_CLOSE,
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
enum keep_alive_set keep_alive_set;
|
|
|
|
char con_length_set;
|
|
|
|
char *reqline_host;
|
|
|
|
int reqline_hostlen;
|
|
|
|
} parse_header_state;
|
|
|
|
|
|
|
|
static void init_parse_header_state(parse_header_state* state) {
|
|
|
|
state->keep_alive_set = HTTP_CONNECTION_UNSET;
|
|
|
|
state->con_length_set = 0;
|
|
|
|
state->reqline_host = NULL;
|
|
|
|
state->reqline_hostlen = 0;
|
|
|
|
}
|
|
|
|
|
2018-09-09 05:50:33 +00:00
|
|
|
/* add header to list of headers
|
|
|
|
* certain headers are also parsed
|
|
|
|
* might drop a header if deemed unnecessary/broken
|
2018-08-25 10:35:23 +00:00
|
|
|
*
|
|
|
|
* returns 0 on error
|
|
|
|
*/
|
2018-09-08 20:31:01 +00:00
|
|
|
static int parse_single_header(server *srv, connection *con, parse_header_state *state, char *k, size_t klen, char *v, size_t vlen) {
|
2018-09-09 05:50:33 +00:00
|
|
|
const enum http_header_e id = http_header_hkey_get(k, klen);
|
|
|
|
buffer **saveb = NULL;
|
2018-08-25 10:35:23 +00:00
|
|
|
|
2018-09-09 05:50:33 +00:00
|
|
|
/* strip leading whitespace */
|
|
|
|
for (; vlen > 0 && (v[0] == ' ' || v[0] == '\t'); ++v, --vlen) ;
|
2018-08-25 10:35:23 +00:00
|
|
|
|
2018-09-09 05:50:33 +00:00
|
|
|
/* strip trailing whitespace */
|
|
|
|
while (vlen > 0 && (v[vlen - 1] == ' ' || v[vlen - 1] == '\t')) --vlen;
|
2018-08-25 10:35:23 +00:00
|
|
|
|
2018-09-09 05:50:33 +00:00
|
|
|
/* empty header-fields are not allowed by HTTP-RFC, we just ignore them */
|
|
|
|
if (0 == vlen) return 1; /* ignore header */
|
2018-08-25 10:35:23 +00:00
|
|
|
|
2018-09-09 05:50:33 +00:00
|
|
|
/*
|
|
|
|
* Note: k might not be '\0'-terminated
|
|
|
|
*/
|
2018-08-25 10:35:23 +00:00
|
|
|
|
2018-09-09 05:50:33 +00:00
|
|
|
switch (id) {
|
|
|
|
/*case HTTP_HEADER_OTHER:*/
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
case HTTP_HEADER_HOST:
|
|
|
|
if (!(con->request.htags & HTTP_HEADER_HOST)) {
|
|
|
|
saveb = &con->request.http_host;
|
2018-11-24 21:43:26 +00:00
|
|
|
if (vlen >= 1024) { /*(expecting < 256)*/
|
|
|
|
if (srv->srvconf.log_request_header_on_error) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s", "uri-authority too long -> 400");
|
|
|
|
}
|
|
|
|
return 0; /* invalid header */
|
|
|
|
}
|
2018-09-09 05:50:33 +00:00
|
|
|
}
|
|
|
|
else if (state->reqline_host) {
|
|
|
|
/* ignore all Host: headers as we got Host in request line */
|
|
|
|
return 1; /* ignore header */
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (srv->srvconf.log_request_header_on_error) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s",
|
|
|
|
"duplicate Host-header -> 400");
|
|
|
|
}
|
|
|
|
return 0; /* invalid header */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case HTTP_HEADER_CONNECTION:
|
|
|
|
{
|
|
|
|
array * const vals = srv->split_vals;
|
2018-10-23 00:28:53 +00:00
|
|
|
array_reset_data_strings(vals);
|
2018-09-09 05:50:33 +00:00
|
|
|
http_request_split_value(vals, v, vlen); /* split on , */
|
|
|
|
for (size_t vi = 0; vi < vals->used; ++vi) {
|
|
|
|
data_string *dsv = (data_string *)vals->data[vi];
|
|
|
|
if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value),
|
|
|
|
CONST_STR_LEN("keep-alive"))) {
|
|
|
|
state->keep_alive_set = HTTP_CONNECTION_KEEPALIVE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value),
|
|
|
|
CONST_STR_LEN("close"))) {
|
|
|
|
state->keep_alive_set = HTTP_CONNECTION_CLOSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case HTTP_HEADER_CONTENT_TYPE:
|
|
|
|
if (con->request.htags & HTTP_HEADER_CONTENT_TYPE) {
|
|
|
|
if (srv->srvconf.log_request_header_on_error) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s",
|
|
|
|
"duplicate Content-Type-header -> 400");
|
|
|
|
}
|
|
|
|
return 0; /* invalid header */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case HTTP_HEADER_IF_NONE_MATCH:
|
|
|
|
/* if dup, only the first one will survive */
|
|
|
|
if (con->request.htags & HTTP_HEADER_IF_NONE_MATCH) {
|
|
|
|
return 1; /* ignore header */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case HTTP_HEADER_CONTENT_LENGTH:
|
|
|
|
if (!(con->request.htags & HTTP_HEADER_CONTENT_LENGTH)) {
|
|
|
|
char *err;
|
|
|
|
off_t r = strtoll(v, &err, 10);
|
|
|
|
|
|
|
|
if (*err == '\0' && r >= 0) {
|
|
|
|
con->request.content_length = r;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sss",
|
|
|
|
"content-length broken:", v, "-> 400");
|
|
|
|
return 0; /* invalid header */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (srv->srvconf.log_request_header_on_error) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s",
|
|
|
|
"duplicate Content-Length-header -> 400");
|
|
|
|
}
|
|
|
|
return 0; /* invalid header */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case HTTP_HEADER_IF_MODIFIED_SINCE:
|
|
|
|
if (con->request.htags & HTTP_HEADER_IF_MODIFIED_SINCE) {
|
|
|
|
/* Proxies sometimes send dup headers
|
|
|
|
* if they are the same we ignore the second
|
|
|
|
* if not, we raise an error */
|
|
|
|
buffer *vb =
|
|
|
|
http_header_request_get(con, HTTP_HEADER_IF_MODIFIED_SINCE,
|
|
|
|
CONST_STR_LEN("If-Modified-Since"));
|
|
|
|
if (vb && buffer_is_equal_caseless_string(vb, v, vlen)) {
|
|
|
|
/* ignore it if they are the same */
|
|
|
|
return 1; /* ignore header */
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (srv->srvconf.log_request_header_on_error) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s",
|
|
|
|
"duplicate If-Modified-Since header -> 400");
|
|
|
|
}
|
|
|
|
return 0; /* invalid header */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2018-08-25 10:35:23 +00:00
|
|
|
|
2018-09-09 05:50:33 +00:00
|
|
|
con->request.htags |= id;
|
|
|
|
http_header_request_append(con, id, k, klen, v, vlen);
|
2018-08-25 10:35:23 +00:00
|
|
|
|
2018-09-09 05:50:33 +00:00
|
|
|
if (saveb) {
|
|
|
|
*saveb = http_header_request_get(con, id, k, klen);
|
|
|
|
}
|
2018-08-25 10:35:23 +00:00
|
|
|
|
2018-09-09 05:50:33 +00:00
|
|
|
return 1;
|
2018-08-25 10:35:23 +00:00
|
|
|
}
|
|
|
|
|
2019-02-09 00:38:43 +00:00
|
|
|
static size_t http_request_parse_reqline(server *srv, connection *con, buffer *hdrs, parse_header_state *state) {
|
|
|
|
char * const ptr = hdrs->ptr;
|
2018-08-25 10:35:23 +00:00
|
|
|
char *uri = NULL, *proto = NULL, *method = NULL;
|
2005-02-20 14:27:00 +00:00
|
|
|
int line = 0;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
int request_line_stage = 0;
|
2015-02-08 19:10:44 +00:00
|
|
|
size_t i, first, ilen;
|
[config] opts for http header parsing strictness (fixes #551, fixes #1086, fixes #1184, fixes #2143, #2258, #2281, fixes #946, fixes #1330, fixes #602, #1016)
server.http-parseopt-header-strict = "enable"
server.http-parseopt-host-strict = "enable" (implies host-normalize)
server.http-parseopt-host-normalize = "disable"
defaults retain current behavior, which is strict header parsing
and strict host parsing, with enhancement to normalize IPv4 address
and port number strings.
For lighttpd tests, these need to be enabled (and are by default)
For marginally faster HTTP header parsing for benchmarks, disable these.
To allow
- underscores in hostname
- hypen ('-') at beginning of hostname
- all-numeric TLDs
server.http-parseopt-host-strict = "disable"
x-ref:
"lighttpd doesn't allow underscores in host names"
https://redmine.lighttpd.net/issues/551
"hyphen in hostname"
https://redmine.lighttpd.net/issues/1086
"a numeric tld"
https://redmine.lighttpd.net/issues/1184
"Numeric tld's"
https://redmine.lighttpd.net/issues/2143
"Bad Request"
https://redmine.lighttpd.net/issues/2258
"400 Bad Request when using Numeric TLDs"
https://redmine.lighttpd.net/issues/2281
To allow a variety of numerical formats to be converted to IP addresses
server.http-parseopt-host-strict = "disable"
server.http-parseopt-host-normalize = "enable"
x-ref:
"URL encoding leads to "400 - Bad Request""
https://redmine.lighttpd.net/issues/946
"400 Bad Request when using IP's numeric value ("ip2long()")"
https://redmine.lighttpd.net/issues/1330
To allow most 8-bit and 7-bit chars in headers
server.http-parseopt-header-strict = "disable" (not recommended)
x-ref:
"Russian letters not alowed?"
https://redmine.lighttpd.net/issues/602
"header Content-Disposition with russian '?' (CP1251, ascii code 255) causes error"
https://redmine.lighttpd.net/issues/1016
2016-05-18 09:42:42 +00:00
|
|
|
const unsigned int http_header_strict = (con->conf.http_parseopts & HTTP_PARSEOPT_HEADER_STRICT);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Request: "^(GET|POST|HEAD) ([^ ]+(\\?[^ ]+|)) (HTTP/1\\.[01])$"
|
|
|
|
* Option : "^([-a-zA-Z]+): (.+)$"
|
2005-02-20 14:27:00 +00:00
|
|
|
* End : "^$"
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* parse the first line of the request
|
|
|
|
*
|
|
|
|
* should be:
|
|
|
|
*
|
|
|
|
* <method> <uri> <protocol>\r\n
|
|
|
|
* */
|
2019-02-09 00:38:43 +00:00
|
|
|
ilen = buffer_string_length(hdrs);
|
2015-02-08 19:10:44 +00:00
|
|
|
for (i = 0, first = 0; i < ilen && line == 0; i++) {
|
2019-02-09 00:38:43 +00:00
|
|
|
switch(ptr[i]) {
|
2006-10-04 13:26:23 +00:00
|
|
|
case '\r':
|
2019-02-09 00:38:43 +00:00
|
|
|
if (ptr[i+1] != '\n') break;
|
2017-10-16 06:17:07 +00:00
|
|
|
/* fall through */
|
|
|
|
case '\n':
|
|
|
|
{
|
2005-02-20 14:27:00 +00:00
|
|
|
http_method_t r;
|
|
|
|
char *nuri = NULL;
|
2015-02-08 19:10:44 +00:00
|
|
|
size_t j, jlen;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2019-02-09 00:38:43 +00:00
|
|
|
buffer_copy_string_len(con->request.request_line, ptr, i);
|
2017-10-16 06:17:07 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* \r\n -> \0\0 */
|
2019-02-09 00:38:43 +00:00
|
|
|
if (ptr[i] == '\r') {
|
|
|
|
ptr[i] = '\0';
|
2017-10-16 06:17:07 +00:00
|
|
|
++i;
|
|
|
|
} else if (http_header_strict) { /* '\n' */
|
2018-08-25 10:43:03 +00:00
|
|
|
http_request_missing_CR_before_LF(srv, con);
|
2018-09-09 06:13:08 +00:00
|
|
|
return 0;
|
2017-10-16 06:17:07 +00:00
|
|
|
}
|
2019-02-09 00:38:43 +00:00
|
|
|
ptr[i] = '\0';
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (request_line_stage != 2) {
|
|
|
|
if (srv->srvconf.log_request_header_on_error) {
|
2006-02-02 11:51:10 +00:00
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s", "incomplete request line -> 400");
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2018-09-09 06:13:08 +00:00
|
|
|
return 0;
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2019-02-09 00:38:43 +00:00
|
|
|
proto = ptr + first;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
*(uri - 1) = '\0';
|
|
|
|
*(proto - 1) = '\0';
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* we got the first one :) */
|
2013-06-29 10:53:22 +00:00
|
|
|
if (HTTP_METHOD_UNSET == (r = get_http_method_key(method))) {
|
2005-02-20 14:27:00 +00:00
|
|
|
con->http_status = 501;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (srv->srvconf.log_request_header_on_error) {
|
2006-02-02 11:51:10 +00:00
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s", "unknown http-method -> 501");
|
2006-02-01 11:33:54 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2018-09-09 06:13:08 +00:00
|
|
|
return 0;
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
con->request.http_method = r;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
|
|
|
/*
|
2006-03-04 17:10:47 +00:00
|
|
|
* RFC2616 says:
|
|
|
|
*
|
|
|
|
* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
|
|
|
|
*
|
2006-10-04 13:26:23 +00:00
|
|
|
* */
|
2006-03-04 17:10:47 +00:00
|
|
|
if (0 == strncmp(proto, "HTTP/", sizeof("HTTP/") - 1)) {
|
|
|
|
char * major = proto + sizeof("HTTP/") - 1;
|
|
|
|
char * minor = strchr(major, '.');
|
|
|
|
char *err = NULL;
|
|
|
|
int major_num = 0, minor_num = 0;
|
|
|
|
|
|
|
|
int invalid_version = 0;
|
|
|
|
|
|
|
|
if (NULL == minor || /* no dot */
|
|
|
|
minor == major || /* no major */
|
|
|
|
*(minor + 1) == '\0' /* no minor */) {
|
|
|
|
invalid_version = 1;
|
|
|
|
} else {
|
|
|
|
*minor = '\0';
|
|
|
|
major_num = strtol(major, &err, 10);
|
|
|
|
|
|
|
|
if (*err != '\0') invalid_version = 1;
|
|
|
|
|
|
|
|
*minor++ = '.';
|
|
|
|
minor_num = strtol(minor, &err, 10);
|
|
|
|
|
|
|
|
if (*err != '\0') invalid_version = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (invalid_version) {
|
|
|
|
if (srv->srvconf.log_request_header_on_error) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s", "unknown protocol -> 400");
|
|
|
|
}
|
2018-09-09 06:13:08 +00:00
|
|
|
return 0;
|
2006-03-04 17:10:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (major_num == 1 && minor_num == 1) {
|
2005-02-20 14:27:00 +00:00
|
|
|
con->request.http_version = con->conf.allow_http11 ? HTTP_VERSION_1_1 : HTTP_VERSION_1_0;
|
2006-03-04 17:10:47 +00:00
|
|
|
} else if (major_num == 1 && minor_num == 0) {
|
2005-02-20 14:27:00 +00:00
|
|
|
con->request.http_version = HTTP_VERSION_1_0;
|
2006-10-04 13:26:23 +00:00
|
|
|
} else {
|
2005-02-20 14:27:00 +00:00
|
|
|
con->http_status = 505;
|
|
|
|
|
|
|
|
if (srv->srvconf.log_request_header_on_error) {
|
2006-02-02 11:51:10 +00:00
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s", "unknown HTTP version -> 505");
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2018-09-09 06:13:08 +00:00
|
|
|
return 0;
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (srv->srvconf.log_request_header_on_error) {
|
2006-02-02 11:51:10 +00:00
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s", "unknown protocol -> 400");
|
2006-02-01 11:33:54 +00:00
|
|