version 1.1.1.2, 2014/06/15 20:20:06
|
version 1.1.1.3, 2016/11/02 10:35:00
|
Line 1
|
Line 1
|
|
#include "first.h" |
|
|
#include "request.h" |
#include "request.h" |
#include "keyvalue.h" |
#include "keyvalue.h" |
#include "log.h" |
#include "log.h" |
Line 10
|
Line 12
|
#include <stdio.h> |
#include <stdio.h> |
#include <ctype.h> |
#include <ctype.h> |
|
|
static int request_check_hostname(server *srv, connection *con, buffer *host) { | static int request_check_hostname(buffer *host) { |
enum { DOMAINLABEL, TOPLABEL } stage = TOPLABEL; |
enum { DOMAINLABEL, TOPLABEL } stage = TOPLABEL; |
size_t i; |
size_t i; |
int label_len = 0; |
int label_len = 0; |
size_t host_len; | size_t host_len, hostport_len; |
char *colon; |
char *colon; |
int is_ip = -1; /* -1 don't know yet, 0 no, 1 yes */ |
int is_ip = -1; /* -1 don't know yet, 0 no, 1 yes */ |
int level = 0; |
int level = 0; |
|
|
UNUSED(srv); |
|
UNUSED(con); |
|
|
|
/* |
/* |
* hostport = host [ ":" port ] |
* hostport = host [ ":" port ] |
* host = hostname | IPv4address | IPv6address |
* host = hostname | IPv4address | IPv6address |
Line 33 static int request_check_hostname(server *srv, connect
|
Line 32 static int request_check_hostname(server *srv, connect
|
* port = *digit |
* port = *digit |
*/ |
*/ |
|
|
/* no Host: */ |
|
if (!host || host->used == 0) return 0; |
|
|
|
host_len = host->used - 1; |
|
|
|
/* IPv6 adress */ |
/* IPv6 adress */ |
if (host->ptr[0] == '[') { |
if (host->ptr[0] == '[') { |
char *c = host->ptr + 1; |
char *c = host->ptr + 1; |
Line 74 static int request_check_hostname(server *srv, connect
|
Line 68 static int request_check_hostname(server *srv, connect
|
return 0; |
return 0; |
} |
} |
|
|
|
hostport_len = host_len = buffer_string_length(host); |
|
|
if (NULL != (colon = memchr(host->ptr, ':', host_len))) { |
if (NULL != (colon = memchr(host->ptr, ':', host_len))) { |
char *c = colon + 1; |
char *c = colon + 1; |
|
|
Line 92 static int request_check_hostname(server *srv, connect
|
Line 88 static int request_check_hostname(server *srv, connect
|
/* if the hostname ends in a "." strip it */ |
/* if the hostname ends in a "." strip it */ |
if (host->ptr[host_len-1] == '.') { |
if (host->ptr[host_len-1] == '.') { |
/* shift port info one left */ |
/* shift port info one left */ |
if (NULL != colon) memmove(colon-1, colon, host->used - host_len); | if (NULL != colon) memmove(colon-1, colon, hostport_len - host_len); |
else host->ptr[host_len-1] = '\0'; | buffer_string_set_length(host, --hostport_len); |
host_len -= 1; | if (--host_len == 0) return -1; |
host->used -= 1; | |
} |
} |
|
|
if (host_len == 0) return -1; |
|
|
|
/* scan from the right and skip the \0 */ |
/* scan from the right and skip the \0 */ |
for (i = host_len; i-- > 0; ) { |
for (i = host_len; i-- > 0; ) { |
Line 208 static int request_check_hostname(server *srv, connect
|
Line 202 static int request_check_hostname(server *srv, connect
|
return 0; |
return 0; |
} |
} |
|
|
|
int http_request_host_normalize(buffer *b) { |
|
/* |
|
* 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)*/ |
|
buffer_string_set_length(b, (size_t)(colon - p)); /*(remove port str)*/ |
|
} |
|
|
|
if (light_isdigit(*p)) { |
|
/* (IPv4 address literal or domain starting w/ digit (e.g. 3com))*/ |
|
struct in_addr addr; |
|
#if defined(HAVE_INET_ATON) /*(Windows does not provide inet_aton())*/ |
|
if (0 != inet_aton(p, &addr)) |
|
#else |
|
if ((addr.s_addr = inet_addr(p)) != INADDR_NONE) |
|
#endif |
|
{ |
|
#if defined(HAVE_INET_PTON)/*(expect inet_ntop() if inet_pton())*/ |
|
#ifndef INET_ADDRSTRLEN |
|
#define INET_ADDRSTRLEN 16 |
|
#endif |
|
char buf[INET_ADDRSTRLEN]; |
|
inet_ntop(AF_INET, (const void *)&addr, buf, sizeof(buf)); |
|
buffer_copy_string(b, buf); |
|
#else |
|
buffer_copy_string(b, inet_ntoa(addr)); /*(not thread-safe)*/ |
|
#endif |
|
} |
|
} |
|
} else { /* IPv6 addr */ |
|
#if defined(HAVE_IPV6) && defined(HAVE_INET_PTON) |
|
|
|
struct in6_addr addr; |
|
char *bracket = b->ptr+blen-1; |
|
char *percent = strchr(b->ptr+1, '%'); |
|
size_t len; |
|
int rc; |
|
char buf[INET6_ADDRSTRLEN+16]; /*(+16 for potential %interface name)*/ |
|
if (blen <= 2) return -1; /*(invalid "[]")*/ |
|
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; |
|
} |
|
} |
|
} |
|
|
|
*bracket = '\0';/*(terminate IPv6 string)*/ |
|
if (percent) *percent = '\0'; /*(remove %interface from address)*/ |
|
rc = inet_pton(AF_INET6, b->ptr+1, &addr); |
|
if (percent) *percent = '%'; /*(restore %interface)*/ |
|
*bracket = ']'; /*(restore bracket)*/ |
|
if (1 != rc) return -1; |
|
|
|
inet_ntop(AF_INET6,(const void *)&addr, buf, sizeof(buf)); |
|
len = strlen(buf); |
|
if (percent) { |
|
if (percent > bracket) return -1; |
|
if (len + (size_t)(bracket - percent) >= sizeof(buf)) return -1; |
|
memcpy(buf+len, percent, (size_t)(bracket - percent)); |
|
len += (size_t)(bracket - percent); |
|
} |
|
buffer_string_set_length(b, 1); /* truncate after '[' */ |
|
buffer_append_string_len(b, buf, len); |
|
buffer_append_string_len(b, CONST_STR_LEN("]")); |
|
|
|
#else |
|
|
|
return -1; |
|
|
|
#endif |
|
} |
|
|
|
if (port) { |
|
buffer_append_string_len(b, CONST_STR_LEN(":")); |
|
buffer_append_int(b, (int)port); |
|
} |
|
|
|
return 0; |
|
} |
|
|
#if 0 |
#if 0 |
#define DUMP_HEADER |
#define DUMP_HEADER |
#endif |
#endif |
|
|
static int http_request_split_value(array *vals, buffer *b) { |
static int http_request_split_value(array *vals, buffer *b) { |
size_t i; | size_t i, len; |
int state = 0; |
int state = 0; |
|
|
const char *current; |
const char *current; |
Line 226 static int http_request_split_value(array *vals, buffe
|
Line 349 static int http_request_split_value(array *vals, buffe
|
* into a array (more or less a explode() incl. striping of whitespaces |
* into a array (more or less a explode() incl. striping of whitespaces |
*/ |
*/ |
|
|
if (b->used == 0) return 0; | if (buffer_string_is_empty(b)) return 0; |
|
|
current = b->ptr; |
current = b->ptr; |
for (i = 0; i < b->used; ++i, ++current) { | len = buffer_string_length(b); |
| for (i = 0; i <= len; ++i, ++current) { |
data_string *ds; |
data_string *ds; |
|
|
switch (state) { |
switch (state) { |
Line 297 int http_request_parse(server *srv, connection *con) {
|
Line 421 int http_request_parse(server *srv, connection *con) {
|
int line = 0; |
int line = 0; |
|
|
int request_line_stage = 0; |
int request_line_stage = 0; |
size_t i, first; | size_t i, first, ilen; |
|
|
int done = 0; |
int done = 0; |
|
const unsigned int http_header_strict = (con->conf.http_parseopts & HTTP_PARSEOPT_HEADER_STRICT); |
|
|
/* |
/* |
* Request: "^(GET|POST|HEAD) ([^ ]+(\\?[^ ]+|)) (HTTP/1\\.[01])$" |
* Request: "^(GET|POST|HEAD) ([^ ]+(\\?[^ ]+|)) (HTTP/1\\.[01])$" |
Line 310 int http_request_parse(server *srv, connection *con) {
|
Line 435 int http_request_parse(server *srv, connection *con) {
|
if (con->conf.log_request_header) { |
if (con->conf.log_request_header) { |
log_error_write(srv, __FILE__, __LINE__, "sdsdSb", |
log_error_write(srv, __FILE__, __LINE__, "sdsdSb", |
"fd:", con->fd, |
"fd:", con->fd, |
"request-len:", con->request.request->used, | "request-len:", buffer_string_length(con->request.request), |
"\n", con->request.request); |
"\n", con->request.request); |
} |
} |
|
|
Line 319 int http_request_parse(server *srv, connection *con) {
|
Line 444 int http_request_parse(server *srv, connection *con) {
|
con->request.request->ptr[1] == '\n') { |
con->request.request->ptr[1] == '\n') { |
/* we are in keep-alive and might get \r\n after a previous POST request.*/ |
/* we are in keep-alive and might get \r\n after a previous POST request.*/ |
|
|
buffer_copy_string_len(con->parse_request, con->request.request->ptr + 2, con->request.request->used - 1 - 2); | buffer_copy_string_len(con->parse_request, con->request.request->ptr + 2, buffer_string_length(con->request.request) - 2); |
} else { |
} else { |
/* fill the local request buffer */ |
/* fill the local request buffer */ |
buffer_copy_string_buffer(con->parse_request, con->request.request); | buffer_copy_buffer(con->parse_request, con->request.request); |
} |
} |
|
|
keep_alive_set = 0; |
keep_alive_set = 0; |
Line 334 int http_request_parse(server *srv, connection *con) {
|
Line 459 int http_request_parse(server *srv, connection *con) {
|
* |
* |
* <method> <uri> <protocol>\r\n |
* <method> <uri> <protocol>\r\n |
* */ |
* */ |
for (i = 0, first = 0; i < con->parse_request->used && line == 0; i++) { | ilen = buffer_string_length(con->parse_request); |
char *cur = con->parse_request->ptr + i; | for (i = 0, first = 0; i < ilen && line == 0; i++) { |
| switch(con->parse_request->ptr[i]) { |
switch(*cur) { | |
case '\r': |
case '\r': |
if (con->parse_request->ptr[i+1] == '\n') { |
if (con->parse_request->ptr[i+1] == '\n') { |
http_method_t r; |
http_method_t r; |
char *nuri = NULL; |
char *nuri = NULL; |
size_t j; | size_t j, jlen; |
|
|
/* \r\n -> \0\0 */ |
/* \r\n -> \0\0 */ |
con->parse_request->ptr[i] = '\0'; |
con->parse_request->ptr[i] = '\0'; |
Line 476 int http_request_parse(server *srv, connection *con) {
|
Line 600 int http_request_parse(server *srv, connection *con) {
|
} |
} |
|
|
/* check uri for invalid characters */ |
/* check uri for invalid characters */ |
for (j = 0; j < con->request.uri->used - 1; j++) { | jlen = buffer_string_length(con->request.uri); |
if (!request_uri_is_valid_char(con->request.uri->ptr[j])) { | if (http_header_strict) { |
unsigned char buf[2]; | for (j = 0; j < jlen && request_uri_is_valid_char(con->request.uri->ptr[j]); j++) ; |
| } else { |
| char *z = memchr(con->request.uri->ptr, '\0', jlen); |
| j = (NULL == z) ? jlen : (size_t)(z - con->request.uri->ptr); |
| } |
| if (j < jlen) { |
con->http_status = 400; |
con->http_status = 400; |
con->keep_alive = 0; |
con->keep_alive = 0; |
|
|
if (srv->srvconf.log_request_header_on_error) { |
if (srv->srvconf.log_request_header_on_error) { |
|
unsigned char buf[2]; |
buf[0] = con->request.uri->ptr[j]; |
buf[0] = con->request.uri->ptr[j]; |
buf[1] = '\0'; |
buf[1] = '\0'; |
|
|
Line 505 int http_request_parse(server *srv, connection *con) {
|
Line 635 int http_request_parse(server *srv, connection *con) {
|
} |
} |
|
|
return 0; |
return 0; |
} |
|
} |
} |
|
|
buffer_copy_string_buffer(con->request.orig_uri, con->request.uri); | buffer_copy_buffer(con->request.orig_uri, con->request.uri); |
|
|
con->http_status = 0; |
con->http_status = 0; |
|
|
Line 551 int http_request_parse(server *srv, connection *con) {
|
Line 680 int http_request_parse(server *srv, connection *con) {
|
|
|
in_folding = 0; |
in_folding = 0; |
|
|
if (con->request.uri->used == 1) { | if (buffer_string_is_empty(con->request.uri)) { |
con->http_status = 400; |
con->http_status = 400; |
con->response.keep_alive = 0; |
con->response.keep_alive = 0; |
con->keep_alive = 0; |
con->keep_alive = 0; |
Line 579 int http_request_parse(server *srv, connection *con) {
|
Line 708 int http_request_parse(server *srv, connection *con) {
|
con->request.http_host = ds->value; |
con->request.http_host = ds->value; |
} |
} |
|
|
for (; i < con->parse_request->used && !done; i++) { | for (; i <= ilen && !done; i++) { |
char *cur = con->parse_request->ptr + i; |
char *cur = con->parse_request->ptr + i; |
|
|
if (is_key) { |
if (is_key) { |
Line 703 int http_request_parse(server *srv, connection *con) {
|
Line 832 int http_request_parse(server *srv, connection *con) {
|
} |
} |
break; |
break; |
default: |
default: |
if (*cur < 32 || ((unsigned char)*cur) >= 127) { | if (http_header_strict ? (*cur < 32 || ((unsigned char)*cur) >= 127) : *cur == '\0') { |
con->http_status = 400; |
con->http_status = 400; |
con->keep_alive = 0; |
con->keep_alive = 0; |
con->response.keep_alive = 0; |
con->response.keep_alive = 0; |
Line 825 int http_request_parse(server *srv, connection *con) {
|
Line 954 int http_request_parse(server *srv, connection *con) {
|
} else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Length")))) { |
} else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Length")))) { |
char *err; |
char *err; |
unsigned long int r; |
unsigned long int r; |
size_t j; | size_t j, jlen; |
|
|
if (con_length_set) { |
if (con_length_set) { |
con->http_status = 400; |
con->http_status = 400; |
Line 842 int http_request_parse(server *srv, connection *con) {
|
Line 971 int http_request_parse(server *srv, connection *con) {
|
return 0; |
return 0; |
} |
} |
|
|
if (ds->value->used == 0) SEGFAULT(); | jlen = buffer_string_length(ds->value); |
| for (j = 0; j < jlen; j++) { |
for (j = 0; j < ds->value->used - 1; j++) { | |
char c = ds->value->ptr[j]; |
char c = ds->value->ptr[j]; |
if (!isdigit((unsigned char)c)) { |
if (!isdigit((unsigned char)c)) { |
log_error_write(srv, __FILE__, __LINE__, "sbs", |
log_error_write(srv, __FILE__, __LINE__, "sbs", |
Line 1027 int http_request_parse(server *srv, connection *con) {
|
Line 1155 int http_request_parse(server *srv, connection *con) {
|
case '\t': |
case '\t': |
/* strip leading WS */ |
/* strip leading WS */ |
if (value == cur) value = cur+1; |
if (value == cur) value = cur+1; |
/* fallthrough */ | break; |
default: |
default: |
if (*cur >= 0 && *cur < 32 && *cur != '\t') { | if (http_header_strict ? (*cur >= 0 && *cur < 32) : *cur == '\0') { |
if (srv->srvconf.log_request_header_on_error) { |
if (srv->srvconf.log_request_header_on_error) { |
log_error_write(srv, __FILE__, __LINE__, "sds", |
log_error_write(srv, __FILE__, __LINE__, "sds", |
"invalid char in header", (int)*cur, "-> 400"); |
"invalid char in header", (int)*cur, "-> 400"); |
Line 1061 int http_request_parse(server *srv, connection *con) {
|
Line 1189 int http_request_parse(server *srv, connection *con) {
|
|
|
/* RFC 2616, 14.23 */ |
/* RFC 2616, 14.23 */ |
if (con->request.http_host == NULL || |
if (con->request.http_host == NULL || |
buffer_is_empty(con->request.http_host)) { | buffer_string_is_empty(con->request.http_host)) { |
con->http_status = 400; |
con->http_status = 400; |
con->response.keep_alive = 0; |
con->response.keep_alive = 0; |
con->keep_alive = 0; |
con->keep_alive = 0; |
Line 1086 int http_request_parse(server *srv, connection *con) {
|
Line 1214 int http_request_parse(server *srv, connection *con) {
|
} |
} |
|
|
/* check hostname field if it is set */ |
/* check hostname field if it is set */ |
if (NULL != con->request.http_host && | if (!buffer_is_empty(con->request.http_host) && |
0 != request_check_hostname(srv, con, con->request.http_host)) { | (((con->conf.http_parseopts & HTTP_PARSEOPT_HOST_STRICT) && |
| 0 != request_check_hostname(con->request.http_host)) |
| || ((con->conf.http_parseopts & HTTP_PARSEOPT_HOST_NORMALIZE) && |
| 0 != http_request_host_normalize(con->request.http_host)))) { |
|
|
if (srv->srvconf.log_request_header_on_error) { |
if (srv->srvconf.log_request_header_on_error) { |
log_error_write(srv, __FILE__, __LINE__, "s", |
log_error_write(srv, __FILE__, __LINE__, "s", |
Line 1132 int http_request_parse(server *srv, connection *con) {
|
Line 1263 int http_request_parse(server *srv, connection *con) {
|
} |
} |
break; |
break; |
default: |
default: |
/* the may have a content-length */ | /* require Content-Length if request contains request body */ |
| if (array_get_element(con->request.headers, "Transfer-Encoding")) { |
| /* presence of Transfer-Encoding in request headers requires "chunked" |
| * be final encoding in HTTP/1.1. Return 411 Length Required as |
| * lighttpd does not support request input transfer-encodings */ |
| con->keep_alive = 0; |
| con->http_status = 411; /* 411 Length Required */ |
| return 0; |
| } |
break; |
break; |
} |
} |
|
|
Line 1149 int http_request_parse(server *srv, connection *con) {
|
Line 1288 int http_request_parse(server *srv, connection *con) {
|
return 0; |
return 0; |
} |
} |
|
|
/* divide by 1024 as srvconf.max_request_size is in kBytes */ |
|
if (srv->srvconf.max_request_size != 0 && |
|
(con->request.content_length >> 10) > srv->srvconf.max_request_size) { |
|
/* the request body itself is larger then |
|
* our our max_request_size |
|
*/ |
|
|
|
con->http_status = 413; |
|
con->keep_alive = 0; |
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sos", |
|
"request-size too long:", (off_t) con->request.content_length, "-> 413"); |
|
return 0; |
|
} |
|
|
|
|
|
/* we have content */ |
/* we have content */ |
if (con->request.content_length != 0) { |
if (con->request.content_length != 0) { |
return 1; |
return 1; |
Line 1177 int http_request_parse(server *srv, connection *con) {
|
Line 1300 int http_request_parse(server *srv, connection *con) {
|
int http_request_header_finished(server *srv, connection *con) { |
int http_request_header_finished(server *srv, connection *con) { |
UNUSED(srv); |
UNUSED(srv); |
|
|
if (con->request.request->used < 5) return 0; | if (buffer_string_length(con->request.request) < 4) return 0; |
|
|
if (0 == memcmp(con->request.request->ptr + con->request.request->used - 5, "\r\n\r\n", 4)) return 1; | if (0 == memcmp(con->request.request->ptr + buffer_string_length(con->request.request) - 4, CONST_STR_LEN("\r\n\r\n"))) return 1; |
if (NULL != strstr(con->request.request->ptr, "\r\n\r\n")) return 1; |
if (NULL != strstr(con->request.request->ptr, "\r\n\r\n")) return 1; |
|
|
return 0; |
return 0; |