diff options
Diffstat (limited to 'src/request.c')
-rw-r--r-- | src/request.c | 599 |
1 files changed, 0 insertions, 599 deletions
diff --git a/src/request.c b/src/request.c deleted file mode 100644 index 816b5368..00000000 --- a/src/request.c +++ /dev/null @@ -1,599 +0,0 @@ -#include <sys/stat.h> - -#include <limits.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <ctype.h> -#include <errno.h> - -#include "request.h" -#include "keyvalue.h" -#include "log.h" -#include "http_req.h" - -#include "sys-strings.h" - -static int request_check_hostname(buffer *host) { - enum { DOMAINLABEL, TOPLABEL } stage = TOPLABEL; - size_t i; - int label_len = 0; - size_t host_len; - char *colon; - int is_ip = -1; /* -1 don't know yet, 0 no, 1 yes */ - int level = 0; - - /* - * 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 - */ - - /* no Host: */ - if (buffer_is_empty(host)) return 0; - if (host->used < 1) return 0; - - host_len = host->used - 1; - - /* IPv6 adress */ - if (host->ptr[0] == '[') { - char *c = host->ptr + 1; - int colon_cnt = 0; - - /* check portnumber */ - for (; *c && *c != ']'; c++) { - if (*c == ':') { - if (++colon_cnt > 7) { - return -1; - } - } else if (!light_isxdigit(*c)) { - return -1; - } - } - - /* missing ] */ - if (!*c) { - return -1; - } - - /* check port */ - if (*(c+1) == ':') { - for (c += 2; *c; c++) { - if (!light_isdigit(*c)) { - return -1; - } - } - } - return 0; - } - - if (NULL != (colon = memchr(host->ptr, ':', host_len))) { - char *c = colon + 1; - - /* check portnumber */ - for (; *c; c++) { - if (!light_isdigit(*c)) return -1; - } - - /* remove the port from the host-len */ - host_len = colon - host->ptr; - } - - /* Host is empty */ - if (host_len == 0) return -1; - - /* if the hostname ends in a "." strip it */ - if (host->ptr[host_len-1] == '.') { - /* shift port info one left */ - if (NULL != colon) memmove(colon-1, colon, host->used - host_len); - else host->ptr[host_len-1] = '\0'; - host_len -= 1; - host->used -= 1; - } - - if (host_len == 0) return -1; - - /* scan from the right and skip the \0 */ - for (i = host_len; i-- > 0; ) { - const char c = host->ptr[i]; - - switch (stage) { - case TOPLABEL: - if (c == '.') { - /* only switch stage, if this is not the last character */ - if (i != host_len - 1) { - if (label_len == 0) { - return -1; - } - - /* check the first character at right of the dot */ - if (is_ip == 0) { - if (!light_isalnum(host->ptr[i+1])) { - return -1; - } - } 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; - } - - stage = DOMAINLABEL; - - 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 */ - if (!light_isalnum(c)) { - 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++; - } - - break; - case DOMAINLABEL: - if (is_ip == 1) { - if (c == '.') { - if (label_len == 0) { - return -1; - } - - label_len = 0; - level++; - } else if (!light_isdigit(c)) { - return -1; - } else { - label_len++; - } - } else { - if (c == '.') { - if (label_len == 0) { - return -1; - } - - /* c is either - or alphanum here */ - if ('-' == host->ptr[i+1]) { - return -1; - } - - 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++; - } - } - - break; - } - } - - /* a IP has to consist of 4 parts */ - if (is_ip == 1 && level != 3) { - return -1; - } - - if (label_len == 0) { - return -1; - } - - return 0; -} - -#if 0 -#define DUMP_HEADER -#endif - -static int http_request_split_value(array *vals, buffer *b) { - char *s; - size_t i; - int state = 0; - /* - * parse - * - * val1, val2, val3, val4 - * - * into a array (more or less a explode() incl. striping of whitespaces - */ - - if (b->used == 0) return 0; - - s = b->ptr; - - for (i =0; i < b->used - 1; ) { - char *start = NULL, *end = NULL; - data_string *ds; - - switch (state) { - case 0: /* ws */ - - /* skip ws */ - for (; (*s == ' ' || *s == '\t') && i < b->used - 1; i++, s++); - - - state = 1; - break; - case 1: /* value */ - start = s; - - for (; *s != ',' && i < b->used - 1; i++, s++); - end = s - 1; - - for (; (*end == ' ' || *end == '\t') && end > start; end--); - - if (NULL == (ds = (data_string *)array_get_unused_element(vals, TYPE_STRING))) { - ds = data_string_init(); - } - - buffer_copy_string_len(ds->value, start, end-start+1); - array_insert_unique(vals, (data_unset *)ds); - - if (*s == ',') { - state = 0; - i++; - s++; - } else { - /* end of string */ - - state = 2; - } - break; - default: - i++; - break; - } - } - return 0; -} - -int http_request_parse(server *srv, connection *con, http_req *req) { - size_t i; - enum { HTTP_CONNECTION_UNSET, HTTP_CONNECTION_CLOSE, HTTP_CONNECTION_KEEPALIVE } keep_alive_set = HTTP_CONNECTION_UNSET; - - if (req->protocol == HTTP_VERSION_UNSET) { - con->http_status = 505; /* Version not Supported */ - return 0; - } - - if (req->method == HTTP_METHOD_UNSET) { - con->http_status = 405; /* Method not allowed */ - return 0; - } - - if (buffer_is_empty(req->uri_raw)) { - con->http_status = 400; - return 0; - } - - /* strip absolute URLs - * */ - - buffer_copy_string_buffer(con->request.orig_uri, req->uri_raw); - if (req->uri_raw->ptr[0] == '/') { - buffer_copy_string_buffer(con->request.uri, req->uri_raw); - } else if (req->uri_raw->ptr[0] == '*') { - if (req->method != HTTP_METHOD_OPTIONS) { - con->http_status = 400; - return 0; - } - buffer_copy_string_buffer(con->request.uri, req->uri_raw); - } else { - /* GET http://www.example.org/foobar */ - char *sl; - int l; - - if (0 == strncmp(BUF_STR(req->uri_raw), "https://", 8)) { - l = 8; - } else if (0 == strncmp(BUF_STR(req->uri_raw), "http://", 7)) { - l = 7; - } else { - con->http_status = 400; - return 0; - } - - if (NULL == (sl = strchr(BUF_STR(req->uri_raw) + l, '/'))) { - con->http_status = 400; - return 0; - } - - buffer_copy_string(con->request.uri, sl); - buffer_copy_string_len(con->request.http_host, BUF_STR(req->uri_raw) + l, sl - BUF_STR(req->uri_raw) - l); - - if (request_check_hostname(con->request.http_host)) { - if (srv->srvconf.log_request_header_on_error) { - TRACE("Host header is invalid (Status: 400), was %s", SAFE_BUF_STR(con->request.http_host)); - } - con->http_status = 400; - con->keep_alive = 0; - - buffer_reset(con->request.http_host); - - return 0; - } - } - - con->request.http_method = req->method; - con->request.http_version = req->protocol; - - for (i = 0; i < req->headers->used; i++) { - data_string *ds = (data_string *)req->headers->data[i]; - data_string *hdr; - int cmp; - - /* this list is sorted */ - if (0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Connection")))) { - array *vals; - size_t vi; - /* Connection: Keep-Alive, ... */ - - vals = srv->split_vals; - - array_reset(vals); - http_request_split_value(vals, ds->value); - - for (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"))) { - keep_alive_set = HTTP_CONNECTION_KEEPALIVE; - - break; - } else if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value), CONST_STR_LEN("close"))) { - keep_alive_set = HTTP_CONNECTION_CLOSE; - - break; - } - } - } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Length")))) { - char *err; - off_t r; - - /* ignore the header, if it is a duplicate */ - if (con->request.content_length != -1) continue; - - r = str_to_off_t(ds->value->ptr, &err, 10); - - if (*err != '\0') { - TRACE("content-length is not a number: %s (Status: 400)", err); - - con->http_status = 400; - - return 0; - } - - /** - * check if we had a over- or underrun in the string conversion - */ - if (r == STR_OFF_T_MIN || - r == STR_OFF_T_MAX) { - if (errno == ERANGE) { - con->http_status = 413; - - return 0; - } - } - - /** - * negative content-length is not supported - * and is a bad request - */ - if (r < 0) { - con->http_status = 400; - - return 0; - } - - /* don't handle more the SSIZE_MAX bytes in content-length */ - if (r > SSIZE_MAX) { - con->http_status = 413; - - ERROR("request-size too long: %s (Status: 413)", SAFE_BUF_STR(ds->value)); - - return 0; - } - - con->request.content_length = r; - } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Expect")))) { - /* HTTP 2616 8.2.3 - * Expect: 100-continue - * - * -> (10.1.1) 100 (read content, process request, send final status-code) - * -> (10.4.18) 417 (close) - * - * - */ - - if (0 != buffer_caseless_compare(CONST_BUF_LEN(ds->value), CONST_STR_LEN("100-continue"))) { - /* we only support 100-continue */ - con->http_status = 417; - return 0; - } - - if (con->request.http_version != HTTP_VERSION_1_1) { - /* only HTTP/1.1 clients can send us this header */ - con->http_status = 417; - return 0; - } - } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Host")))) { - if (request_check_hostname(ds->value)) { - TRACE("Host header is invalid (Status: 400), was %s", SAFE_BUF_STR(ds->value)); - con->http_status = 400; - con->keep_alive = 0; - - return 0; - } - - if (!buffer_is_empty(con->request.http_host) && !buffer_is_equal(con->request.http_host, ds->value)) { - TRACE("%s", "Host header is duplicate (Status: 400)"); - con->http_status = 400; - - return 0; - } - - buffer_copy_string_buffer(con->request.http_host, ds->value); - } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-Modified-Since")))) { - data_string *old; - - if (NULL != (old = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("If-Modified-Since")))) { - if (0 != buffer_caseless_compare(CONST_BUF_LEN(old->value), CONST_BUF_LEN(ds->value))) { - /* duplicate header and different timestamps */ - con->http_status = 400; - - TRACE("%s", "If-Modified-Since is duplicate (Status: 400)"); - - return 0; - } - } - } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-None-Match")))) { - data_string *old; - /* if dup, only the first one will survive */ - if (NULL != (old = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("If-None-Match")))) { - continue; - } - } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Range")))) { - if (NULL != array_get_element(con->request.headers, CONST_STR_LEN("Range"))) { - /* duplicate Range header */ - - TRACE("%s", "Range: header is duplicate (Status: 400)"); - - con->http_status = 400; - con->keep_alive = 0; - - return 0; - } - } - - if (NULL == (hdr = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) { - hdr = data_string_init(); - } - - buffer_copy_string_buffer(hdr->key, ds->key); - buffer_copy_string_buffer(hdr->value, ds->value); - - array_insert_unique(con->request.headers, (data_unset *)hdr); - } - - - con->header_len = i; - - /* do some post-processing */ - - if (con->request.http_version == HTTP_VERSION_1_1) { - if (keep_alive_set != HTTP_CONNECTION_CLOSE) { - /* no Connection-Header sent */ - - /* HTTP/1.1 -> keep-alive default TRUE */ - con->keep_alive = 1; - } else { - con->keep_alive = 0; - } - - /* RFC 2616, 14.23 */ - if (buffer_is_empty(con->request.http_host)) { - con->http_status = 400; - con->response.keep_alive = 0; - con->keep_alive = 0; - - if (srv->srvconf.log_request_header_on_error) { - log_error_write(srv, __FILE__, __LINE__, "s", "HTTP/1.1 but Host missing -> 400"); - log_error_write(srv, __FILE__, __LINE__, "Sb", - "request-header:\n", - con->request.request); - } - return 0; - } - } else { - if (keep_alive_set == HTTP_CONNECTION_KEEPALIVE) { - /* no Connection-Header sent */ - - /* HTTP/1.0 -> keep-alive default FALSE */ - con->keep_alive = 1; - } else { - con->keep_alive = 0; - } - } - - switch(con->request.http_method) { - case HTTP_METHOD_GET: - case HTTP_METHOD_HEAD: - /* content-length is forbidden for those */ - if (con->request.content_length != -1) { - /* content-length is missing */ - if (srv->srvconf.log_request_header_on_error) { - ERROR("GET/HEAD with content-length: %d", 400); - } - - con->keep_alive = 0; - con->http_status = 400; - return 0; - } - break; - case HTTP_METHOD_POST: - /* content-length is required for them */ - if (con->request.content_length == -1) { - /* content-length is missing */ - if (srv->srvconf.log_request_header_on_error) { - log_error_write(srv, __FILE__, __LINE__, "s", - "POST-request, but content-length missing -> 411"); - } - - con->keep_alive = 0; - con->http_status = 411; - return 0; - - } - break; - default: - /* the may have a content-length */ - break; - } - - - /* check if we have read post data */ - if (con->request.content_length != -1) { - /* divide by 1024 as srvconf.max_request_size is in kBytes */ - if (srv->srvconf.max_request_size != 0 && - ((size_t)(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:", con->request.content_length, "-> 413"); - return 0; - } - } - - return 0; -} - - |