[core] tighten code in request_check_hostname()

master
Glenn Strauss 2 years ago
parent 060be714be
commit 4c12d7da08

@ -1,7 +1,7 @@
/*
* request - HTTP request processing
*
* Fully-rewritten from original EXCEPT for request_check_hostname()
* Fully-rewritten from original
* Copyright(c) 2018 Glenn Strauss gstrauss()gluelogic.com All rights reserved
* License: BSD 3-clause (same as lighttpd)
*/
@ -20,193 +20,80 @@
#include <string.h>
static int request_check_hostname(buffer * const host) {
enum { DOMAINLABEL, TOPLABEL } stage = TOPLABEL;
size_t i;
int label_len = 0;
size_t host_len, hostport_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
*/
/* IPv6 address */
if (host->ptr[0] == '[') {
char *c = host->ptr + 1;
int colon_cnt = 0;
/* check the address inside [...] */
for (; *c && *c != ']'; c++) {
if (*c == ':') {
if (++colon_cnt > 7) {
return -1;
}
} else if (!light_isxdigit(*c) && '.' != *c) {
return -1;
}
}
/* missing ] */
if (!*c) {
return -1;
}
/* check port */
if (*(c+1) == ':') {
for (c += 2; *c; c++) {
if (!light_isdigit(*c)) {
return -1;
}
}
}
else if ('\0' != *(c+1)) {
/* only a port is allowed to follow [...] */
return -1;
}
return 0;
}
hostport_len = host_len = buffer_string_length(host);
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, hostport_len - host_len);
buffer_string_set_length(host, --hostport_len);
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;
/*
* 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
*/
const char *h = host->ptr;
if (*h != '[') {
uint32_t len = buffer_string_length(host);
const char * const colon = memchr(h, ':', len);
uint32_t hlen = colon ? (colon - h) : len;
/* if hostname ends in ".", strip it */
if (__builtin_expect( (0 == hlen), 0)) return -1;
if (__builtin_expect( (h[hlen-1] == '.'), 0)) {
/* shift port info one left */
if (--hlen == 0) return -1;
--len;
if (NULL != colon)
memmove(host->ptr+hlen, colon, len - hlen);
buffer_string_set_length(host, len);
}
int label_len = 0;
int allnumeric = 1;
int numeric = 1;
int level = 0;
for (uint32_t i = 0; i < hlen; ++i) {
const int ch = h[i];
++label_len;
if (light_isdigit(ch))
continue;
else if ((light_isalpha(ch) || (ch == '-' && i != 0)))
numeric = 0;
else if (ch == '.' && 1 != label_len && '-' != h[i+1]) {
allnumeric &= numeric;
numeric = 1;
label_len = 0;
++level;
}
else
return -1;
}
/* (if last segment numeric, then IPv4 and must have 4 numeric parts) */
if (0 == label_len || (numeric && (level != 3 || !allnumeric)))
return -1;
h += hlen;
}
else { /* IPv6 address */
/* check the address inside [...]; note: not fully validating */
/* (note: not allowing scoped literals, e.g. %eth0 suffix) */
++h; /* step past '[' */
int cnt = 0;
while (light_isxdigit(*h) || *h == '.' || (*h == ':' && ++cnt < 8)) ++h;
/*(invalid char, too many ':', missing ']', or empty "[]")*/
if (*h != ']' || h - host->ptr == 1) return -1;
++h; /* step past ']' */
}
/* check numerical port, if present */
if (*h == ':') {
if (__builtin_expect( (h[1] == '\0'), 0)) /*(remove trailing colon)*/
buffer_string_set_length(host, h - host->ptr);
do { ++h; } while (light_isdigit(*h));
}
return (*h == '\0') ? 0 : -1;
}
int http_request_host_normalize(buffer * const b, const int scheme_port) {

Loading…
Cancel
Save