]> git.kaiwu.me - nginx.git/commitdiff
Added support for IP-literal in the Host header and request line (ticket #1).
authorValentin Bartenev <vbart@nginx.com>
Mon, 28 Nov 2011 09:15:33 +0000 (09:15 +0000)
committerValentin Bartenev <vbart@nginx.com>
Mon, 28 Nov 2011 09:15:33 +0000 (09:15 +0000)
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.

src/http/ngx_http_parse.c
src/http/ngx_http_request.c

index a7a4c6ee639910e260de58055ff31890226d1808..28842cf9d88bd57b15d8fb8db6eae2c513b0d818 100644 (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;
index aea2228fdd84d95d7c21a3ed8e39a49f453ea5e2..fbb7915b3408d07e3d353905ede15035f8c460db 100644 (file)
@@ -1674,56 +1674,85 @@ static ssize_t
 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;
+    u_char  *h, ch;
+    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;
 
-            dot = 1;
-            continue;
-        }
+        case ':':
+            if (state == sw_usual) {
+                host_len = i;
+                state = sw_rest;
+            }
+            break;
 
-        dot = 0;
+        case '[':
+            if (i == 0) {
+                state = sw_literal;
+            }
+            break;
 
-        if (ch == ':') {
-            last = i;
-            continue;
-        }
+        case ']':
+            if (state == sw_literal) {
+                host_len = i + 1;
+                state = sw_rest;
+            }
+            break;
 
-        if (ngx_path_separator(ch) || ch == '\0') {
+        case '\0':
             return 0;
-        }
 
-        if (ch >= 'A' || ch < 'Z') {
-            alloc = 1;
+        default:
+
+            if (ngx_path_separator(ch)) {
+                return 0;
+            }
+
+            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;
 }