changeset 2007:b9de93d804ea

*) host in request line has priority *) allow several Host headers *) validate host
author Igor Sysoev <igor@sysoev.ru>
date Thu, 15 May 2008 14:44:47 +0000
parents b52cb9bf2064
children 66dc85397a90
files src/http/modules/ngx_http_dav_module.c src/http/ngx_http_core_module.c src/http/ngx_http_header_filter_module.c src/http/ngx_http_request.c src/http/ngx_http_request.h src/http/ngx_http_variables.c
diffstat 6 files changed, 198 insertions(+), 130 deletions(-) [+]
line wrap: on
line diff
--- a/src/http/modules/ngx_http_dav_module.c
+++ b/src/http/modules/ngx_http_dav_module.c
@@ -512,7 +512,7 @@ ngx_http_dav_mkcol_handler(ngx_http_requ
 static ngx_int_t
 ngx_http_dav_copy_move_handler(ngx_http_request_t *r)
 {
-    u_char                   *p, *desthost, *last, ch;
+    u_char                   *p, *host, *last, ch;
     size_t                    len, root;
     ngx_err_t                 err;
     ngx_int_t                 rc, depth;
@@ -520,7 +520,7 @@ ngx_http_dav_copy_move_handler(ngx_http_
     ngx_str_t                 path, uri;
     ngx_tree_ctx_t            tree;
     ngx_file_info_t           fi;
-    ngx_table_elt_t          *host, *dest, *over;
+    ngx_table_elt_t          *dest, *over;
     ngx_http_dav_copy_ctx_t   copy;
     ngx_http_dav_loc_conf_t  *dlcf;
 
@@ -536,9 +536,9 @@ ngx_http_dav_copy_move_handler(ngx_http_
         return NGX_HTTP_BAD_REQUEST;
     }
 
-    host = r->headers_in.host;
+    len = r->headers_in.server.len;
 
-    if (host == NULL) {
+    if (len == 0) {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                       "client sent no \"Host\" header");
         return NGX_HTTP_BAD_REQUEST;
@@ -553,7 +553,7 @@ ngx_http_dav_copy_move_handler(ngx_http_
             goto invalid_destination;
         }
 
-        desthost = dest->value.data + sizeof("https://") - 1;
+        host = dest->value.data + sizeof("https://") - 1;
 
     } else
 #endif
@@ -564,12 +564,10 @@ ngx_http_dav_copy_move_handler(ngx_http_
             goto invalid_destination;
         }
 
-        desthost = dest->value.data + sizeof("http://") - 1;
+        host = dest->value.data + sizeof("http://") - 1;
     }
 
-    len = r->headers_in.host_name_len;
-
-    if (ngx_strncmp(desthost, host->value.data, len) != 0) {
+    if (ngx_strncmp(host, r->headers_in.server.data, len) != 0) {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                       "\"Destination\" URI \"%V\" is handled by "
                       "different repository than the source URI",
@@ -579,7 +577,7 @@ ngx_http_dav_copy_move_handler(ngx_http_
 
     last = dest->value.data + dest->value.len;
 
-    for (p = desthost + len; p < last; p++) {
+    for (p = host + len; p < last; p++) {
         if (*p == '/') {
             goto destination_done;
         }
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -3216,8 +3216,7 @@ ngx_http_core_server_name(ngx_conf_t *cf
         value[i].len--;
         value[i].data++;
 
-        sn->regex = ngx_regex_compile(&value[i], NGX_REGEX_CASELESS, cf->pool,
-                                      &err);
+        sn->regex = ngx_regex_compile(&value[i], 0, cf->pool, &err);
 
         if (sn->regex == NULL) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
--- a/src/http/ngx_http_header_filter_module.c
+++ b/src/http/ngx_http_header_filter_module.c
@@ -286,9 +286,8 @@ ngx_http_header_filter(ngx_http_request_
             cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
             host = cscf->server_name;
 
-        } else if (r->headers_in.host) {
-            host.len = r->headers_in.host_name_len;
-            host.data = r->headers_in.host->value.data;
+        } else if (r->headers_in.server.len) {
+            host = r->headers_in.server;
 
         } else {
             host.data = addr;
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -21,6 +21,8 @@ static ngx_int_t ngx_http_process_header
     ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_process_unique_header_line(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_process_host(ngx_http_request_t *r,
+    ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r,
@@ -30,8 +32,9 @@ static ngx_int_t ngx_http_process_cookie
 
 static ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r);
 static void ngx_http_process_request(ngx_http_request_t *r);
-static void ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host,
-    size_t len, ngx_uint_t hash);
+static ssize_t ngx_http_validate_host(u_char *host, size_t len);
+static ngx_int_t ngx_http_find_virtual_server(ngx_http_request_t *r,
+    u_char *host, size_t len);
 
 static void ngx_http_request_handler(ngx_event_t *ev);
 static ngx_int_t ngx_http_set_write_handler(ngx_http_request_t *r);
@@ -71,8 +74,7 @@ static char *ngx_http_client_errors[] = 
 
 
 ngx_http_header_t  ngx_http_headers_in[] = {
-    { ngx_string("Host"), offsetof(ngx_http_headers_in_t, host),
-                 ngx_http_process_unique_header_line },
+    { ngx_string("Host"), 0, ngx_http_process_host },
 
     { ngx_string("Connection"), 0, ngx_http_process_connection },
 
@@ -562,8 +564,6 @@ ngx_http_ssl_handshake_handler(ngx_conne
 int
 ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
 {
-    u_char                   *p;
-    ngx_uint_t                hash;
     const char               *servername;
     ngx_connection_t         *c;
     ngx_http_request_t       *r;
@@ -582,21 +582,13 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *
 
     r = c->data;
 
-    if (r->virtual_names == NULL) {
+    if (ngx_http_find_virtual_server(r, (u_char *) servername,
+                                     ngx_strlen(servername))
+        != NGX_OK)
+    {
         return SSL_TLSEXT_ERR_NOACK;
     }
 
-    /* it seems browsers send low case server name */
-
-    hash = 0;
-
-    for (p = (u_char *) servername; *p; p++) {
-        hash = ngx_hash(hash, *p);
-    }
-
-    ngx_http_find_virtual_server(r, (u_char *) servername,
-                                 p - (u_char *) servername, hash);
-
     sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);
 
     SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx);
@@ -726,7 +718,31 @@ ngx_http_process_request_line(ngx_event_
             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                            "http exten: \"%V\"", &r->exten);
 
+            if (r->host_start && r->host_end) {
+                n = ngx_http_validate_host(r->host_start,
+                                           r->host_end - r->host_start);
+
+                if (n <= 0) {
+                    ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                                  "client sent invalid host in request line");
+                    ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+                    return;
+                }
+
+                r->headers_in.server.len = n;
+                r->headers_in.server.data = r->host_start;
+            }
+
             if (r->http_version < NGX_HTTP_VERSION_10) {
+
+                if (ngx_http_find_virtual_server(r, r->headers_in.server.data,
+                                                 r->headers_in.server.len)
+                    == NGX_ERROR)
+                {
+                    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+                    return;
+                }
+
                 ngx_http_process_request(r);
                 return;
             }
@@ -1217,6 +1233,36 @@ ngx_http_process_unique_header_line(ngx_
 
 
 static ngx_int_t
+ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h,
+    ngx_uint_t offset)
+{
+    ssize_t  len;
+
+    if (r->headers_in.host == NULL) {
+        r->headers_in.host = h;
+    }
+
+    len = ngx_http_validate_host(h->value.data, h->value.len);
+
+    if (len <= 0) {
+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+                      "client sent invalid host header");
+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+        return NGX_ERROR;
+    }
+
+    if (r->headers_in.server.len) {
+        return NGX_OK;
+    }
+
+    r->headers_in.server.len = len;
+    r->headers_in.server.data = h->value.data;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h,
     ngx_uint_t offset)
 {
@@ -1232,24 +1278,6 @@ ngx_http_process_connection(ngx_http_req
 
 
 static ngx_int_t
-ngx_http_process_cookie(ngx_http_request_t *r, ngx_table_elt_t *h,
-    ngx_uint_t offset)
-{
-    ngx_table_elt_t  **cookie;
-
-    cookie = ngx_array_push(&r->headers_in.cookies);
-    if (cookie) {
-        *cookie = h;
-        return NGX_OK;
-    }
-
-    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
-
-    return NGX_ERROR;
-}
-
-
-static ngx_int_t
 ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h,
     ngx_uint_t offset)
 {
@@ -1304,59 +1332,39 @@ ngx_http_process_user_agent(ngx_http_req
 
 
 static ngx_int_t
+ngx_http_process_cookie(ngx_http_request_t *r, ngx_table_elt_t *h,
+    ngx_uint_t offset)
+{
+    ngx_table_elt_t  **cookie;
+
+    cookie = ngx_array_push(&r->headers_in.cookies);
+    if (cookie) {
+        *cookie = h;
+        return NGX_OK;
+    }
+
+    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+
+    return NGX_ERROR;
+}
+
+
+static ngx_int_t
 ngx_http_process_request_header(ngx_http_request_t *r)
 {
-    size_t       len;
-    u_char      *host, ch;
-    ngx_uint_t   hash;
-
-    if (r->headers_in.host) {
-
-        hash = 0;
-
-        for (len = 0; len < r->headers_in.host->value.len; len++) {
-            ch = r->headers_in.host->value.data[len];
-
-            if (ch == ':') {
-                break;
-            }
-
-            ch = ngx_tolower(ch);
-            r->headers_in.host->value.data[len] = ch;
-            hash = ngx_hash(hash, ch);
-        }
-
-        if (len && r->headers_in.host->value.data[len - 1] == '.') {
-            len--;
-            hash = ngx_hash_key(r->headers_in.host->value.data, len);
-        }
-
-        r->headers_in.host_name_len = len;
-
-        if (r->virtual_names) {
-
-            host = r->host_start;
-
-            if (host == NULL) {
-                host = r->headers_in.host->value.data;
-                len = r->headers_in.host_name_len;
-
-            } else {
-                len = r->host_end - host;
-            }
-
-            ngx_http_find_virtual_server(r, host, len, hash);
-        }
-
-    } else {
-        if (r->http_version > NGX_HTTP_VERSION_10) {
-            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
-                       "client sent HTTP/1.1 request without \"Host\" header");
-            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
-            return NGX_ERROR;
-        }
-
-        r->headers_in.host_name_len = 0;
+    if (ngx_http_find_virtual_server(r, r->headers_in.server.data,
+                                     r->headers_in.server.len)
+        == NGX_ERROR)
+    {
+        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        return NGX_ERROR;
+    }
+
+    if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10) {
+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+                   "client sent HTTP/1.1 request without \"Host\" header");
+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+        return NGX_ERROR;
     }
 
     if (r->headers_in.content_length) {
@@ -1489,14 +1497,89 @@ ngx_http_process_request(ngx_http_reques
 }
 
 
-static void
-ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, size_t len,
-    ngx_uint_t hash)
+static ssize_t
+ngx_http_validate_host(u_char *host, size_t len)
 {
+    u_char      ch;
+    size_t      i, last;
+    ngx_uint_t  dot;
+
+    last = len;
+    dot = 0;
+
+    for (i = 0; i < len; i++) {
+        ch = host[i];
+
+        if (ch == '.') {
+            if (dot) {
+                return -1;
+            }
+
+            dot = 1;
+            continue;
+        }
+
+        dot = 0;
+
+        if (ch == ':') {
+            last = i;
+            continue;
+        }
+
+        if (ch == '/' || ch == '\0') {
+            return -1;
+        }
+
+#if (NGX_WIN32)
+        if (ch == '\\') {
+            return -1;
+        }
+#endif
+    }
+
+    if (dot) {
+        last--;
+    }
+
+    return last;
+}
+
+
+static ngx_int_t
+ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, size_t len)
+{
+    u_char                    *server, ch;
+    ngx_uint_t                 i, hash;
     ngx_http_core_loc_conf_t  *clcf;
     ngx_http_core_srv_conf_t  *cscf;
-
-    cscf = ngx_hash_find_combined(&r->virtual_names->names, hash, host, len);
+    u_char                     buf[32];
+
+    if (len == 0 || r->virtual_names == NULL) {
+        return NGX_DECLINED;
+    }
+
+    if (len <= 32) {
+        server = buf;
+
+    } else {
+        server = ngx_palloc(r->pool, len);
+        if (server == NULL) {
+            return NGX_ERROR;
+        }
+    }
+
+    hash = 0;
+
+    for (i = 0; i < len; i++) {
+        ch = host[i];
+
+        ch = ngx_tolower(ch);
+        server[i] = ch;
+
+        hash = ngx_hash(hash, ch);
+    }
+
+    cscf = ngx_hash_find_combined(&r->virtual_names->names, hash, server, len);
 
     if (cscf) {
         goto found;
@@ -1511,7 +1594,7 @@ ngx_http_find_virtual_server(ngx_http_re
         ngx_http_server_name_t  *sn;
 
         name.len = len;
-        name.data = host;
+        name.data = server;
 
         sn = r->virtual_names->regex;
 
@@ -1528,7 +1611,7 @@ ngx_http_find_virtual_server(ngx_http_re
                               ngx_regex_exec_n
                               " failed: %d on \"%V\" using \"%V\"",
                               n, &name, &sn[i].name);
-                return;
+                return NGX_ERROR;
             }
 
             /* match */
@@ -1541,7 +1624,7 @@ ngx_http_find_virtual_server(ngx_http_re
 
 #endif
 
-    return;
+    return NGX_OK;
 
 found:
 
@@ -1555,7 +1638,7 @@ found:
         r->connection->log->log_level = clcf->err_log->log_level;
     }
 
-    return;
+    return NGX_OK;
 }
 
 
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -206,7 +206,7 @@ typedef struct {
 
     ngx_array_t                       cookies;
 
-    size_t                            host_name_len;
+    ngx_str_t                         server;
     off_t                             content_length_n;
     time_t                            keep_alive_n;
 
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -712,26 +712,15 @@ ngx_http_variable_host(ngx_http_request_
 {
     ngx_http_core_srv_conf_t  *cscf;
 
-    if (r->host_start == NULL) {
-
-        if (r->headers_in.host) {
-            v->len = r->headers_in.host_name_len;
-            v->data = r->headers_in.host->value.data;
-
-        } else {
-            cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
-
-            v->len = cscf->server_name.len;
-            v->data = cscf->server_name.data;
-        }
-
-    } else if (r->host_end) {
-        v->len = r->host_end - r->host_start;
-        v->data = r->host_start;
+    if (r->headers_in.server.len) {
+        v->len = r->headers_in.server.len;
+        v->data = r->headers_in.server.data;
 
     } else {
-        v->not_found = 1;
-        return NGX_OK;
+        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+        v->len = cscf->server_name.len;
+        v->data = cscf->server_name.data;
     }
 
     v->valid = 1;