Added support for IP-literal in the Host header and request line (ticket #1).

Additional parsing logic added to correctly handle RFC 3986 compliant IPv6 and
IPvFuture characters enclosed in square brackets.

The host validation was completely rewritten. The behavior for non IP literals
was changed in a more proper and safer way:

 - Host part is now delimited either by the first colon or by the end of string
   if there's no colon. Previously the last colon was used as delimiter which
   allowed substitution of a port number in the $host variable.
   (e.g. Host: 127.0.0.1:9000:80)

 - Fixed stripping of the ending dot in the Host header when the host was also
   followed by a port number.
   (e.g. Host: nginx.com.:80)

 - Fixed upper case characters detection. Previously it was broken which led to
   wasting memory and CPU.
This commit is contained in:
Valentin Bartenev 2011-11-28 09:15:33 +00:00
parent eaf1314ae3
commit 291486c4bd
2 changed files with 119 additions and 30 deletions

View file

@ -110,7 +110,10 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
sw_schema,
sw_schema_slash,
sw_schema_slash_slash,
sw_host_start,
sw_host,
sw_host_end,
sw_host_ip_literal,
sw_port,
sw_host_http_09,
sw_after_slash_in_uri,
@ -323,14 +326,26 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
case sw_schema_slash_slash:
switch (ch) {
case '/':
r->host_start = p + 1;
state = sw_host;
state = sw_host_start;
break;
default:
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
break;
case sw_host_start:
r->host_start = p;
if (ch == '[') {
state = sw_host_ip_literal;
break;
}
state = sw_host;
/* fall through */
case sw_host:
c = (u_char) (ch | 0x20);
@ -342,6 +357,10 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
break;
}
/* fall through */
case sw_host_end:
r->host_end = p;
switch (ch) {
@ -366,6 +385,47 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
}
break;
case sw_host_ip_literal:
if (ch >= '0' && ch <= '9') {
break;
}
c = (u_char) (ch | 0x20);
if (c >= 'a' && c <= 'z') {
break;
}
switch (ch) {
case ':':
break;
case ']':
state = sw_host_end;
break;
case '-':
case '.':
case '_':
case '~':
/* unreserved */
break;
case '!':
case '$':
case '&':
case '\'':
case '(':
case ')':
case '*':
case '+':
case ',':
case ';':
case '=':
/* sub-delims */
break;
default:
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
break;
case sw_port:
if (ch >= '0' && ch <= '9') {
break;

View file

@ -1675,55 +1675,84 @@ ngx_http_validate_host(ngx_http_request_t *r, u_char **host, size_t len,
ngx_uint_t alloc)
{
u_char *h, ch;
size_t i, last;
ngx_uint_t dot;
size_t i, dot_pos, host_len;
enum {
sw_usual = 0,
sw_literal,
sw_rest
} state;
dot_pos = len;
host_len = len;
last = len;
h = *host;
dot = 0;
state = sw_usual;
for (i = 0; i < len; i++) {
ch = h[i];
if (ch == '.') {
if (dot) {
switch (ch) {
case '.':
if (dot_pos == i - 1) {
return 0;
}
dot_pos = i;
break;
case ':':
if (state == sw_usual) {
host_len = i;
state = sw_rest;
}
break;
case '[':
if (i == 0) {
state = sw_literal;
}
break;
case ']':
if (state == sw_literal) {
host_len = i + 1;
state = sw_rest;
}
break;
case '\0':
return 0;
default:
if (ngx_path_separator(ch)) {
return 0;
}
dot = 1;
continue;
}
dot = 0;
if (ch == ':') {
last = i;
continue;
}
if (ngx_path_separator(ch) || ch == '\0') {
return 0;
}
if (ch >= 'A' || ch < 'Z') {
if (ch >= 'A' && ch <= 'Z') {
alloc = 1;
}
break;
}
}
if (dot) {
last--;
if (dot_pos == host_len - 1) {
host_len--;
}
if (alloc) {
*host = ngx_pnalloc(r->pool, last) ;
*host = ngx_pnalloc(r->pool, host_len);
if (*host == NULL) {
return -1;
}
ngx_strlow(*host, h, last);
ngx_strlow(*host, h, host_len);
}
return last;
return host_len;
}