changeset 312:429900ca25ee NGINX_0_6_0

nginx 0.6.0 *) Feature: the "server_name", "map", and "valid_referers" directives support the "www.example.*" wildcards.
author Igor Sysoev <http://sysoev.ru>
date Thu, 14 Jun 2007 00:00:00 +0400
parents fcb663e92663
children a42b0aafacc1
files CHANGES CHANGES.ru src/core/nginx.h src/core/ngx_hash.c src/core/ngx_hash.h src/http/modules/ngx_http_map_module.c src/http/modules/ngx_http_referer_module.c src/http/modules/perl/nginx.pm src/http/ngx_http.c src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_request.c src/http/ngx_http_request.h
diffstat 13 files changed, 471 insertions(+), 286 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,10 @@
 
+Changes with nginx 0.6.0                                         14 Jun 2007
+
+    *) Feature: the "server_name", "map", and "valid_referers" directives 
+       supports the "www.example.*" wildcards.
+
+
 Changes with nginx 0.5.25                                        11 Jun 2007
 
     *) Bugfix: nginx could not be built with the 
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,10 @@
 
+Изменения в nginx 0.6.0                                           14.06.2007
+
+    *) Добавление: директивы "server_name", "map", and "valid_referers" 
+       поддерживают маски вида "www.example.*".
+
+
 Изменения в nginx 0.5.25                                          11.06.2007
 
     *) Исправление: nginx не собирался с параметром 
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VERSION      "0.5.25"
+#define NGINX_VERSION      "0.6.0"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_hash.c
+++ b/src/core/ngx_hash.c
@@ -53,7 +53,7 @@ ngx_hash_find(ngx_hash_t *hash, ngx_uint
 
 
 void *
-ngx_hash_find_wildcard(ngx_hash_wildcard_t *hwc, u_char *name, size_t len)
+ngx_hash_find_wc_head(ngx_hash_wildcard_t *hwc, u_char *name, size_t len)
 {
     void        *value;
     ngx_uint_t   i, n, key;
@@ -63,7 +63,7 @@ ngx_hash_find_wildcard(ngx_hash_wildcard
 
     line.len = len;
     line.data = name;
-    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "wc:\"%V\"", &line);
+    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "wch:\"%V\"", &line);
 #endif
 
     n = len;
@@ -112,7 +112,7 @@ ngx_hash_find_wildcard(ngx_hash_wildcard
                 }
             }
 
-            value = ngx_hash_find_wildcard(hwc, name, n - 1);
+            value = ngx_hash_find_wc_head(hwc, name, n - 1);
 
             if (value) {
                 return value;
@@ -128,6 +128,104 @@ ngx_hash_find_wildcard(ngx_hash_wildcard
 }
 
 
+void *
+ngx_hash_find_wc_tail(ngx_hash_wildcard_t *hwc, u_char *name, size_t len)
+{
+    void        *value;
+    ngx_uint_t   i, key;
+
+#if 0
+    ngx_str_t  line;
+
+    line.len = len;
+    line.data = name;
+    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "wct:\"%V\"", &line);
+#endif
+
+    key = 0;
+
+    for (i = 0; i < len; i++) {
+        if (name[i] == '.') {
+            break;
+        }
+
+        key = ngx_hash(key, name[i]);
+    }
+
+    if (i == len) {
+        return NULL;
+    }
+
+#if 0
+    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "key:\"%ui\"", key);
+#endif
+
+    value = ngx_hash_find(&hwc->hash, key, name, i);
+
+    if (value) {
+
+        /*
+         * the 2 low bits of value have the special meaning:
+         *     00 - value is data pointer,
+         *     01 - value is pointer to wildcard hash allowing "example.*".
+         */
+
+        if ((uintptr_t) value & 1) {
+
+            i++;
+
+            hwc = (ngx_hash_wildcard_t *) ((uintptr_t) value & (uintptr_t) ~3);
+
+            value = ngx_hash_find_wc_tail(hwc, &name[i], len - i);
+
+            if (value) {
+                return value;
+            }
+
+            return hwc->value;
+        }
+
+        return value;
+    }
+
+    return hwc->value;
+}
+
+
+void *
+ngx_hash_find_combined(ngx_hash_combined_t *hash, ngx_uint_t key, u_char *name,
+    size_t len)
+{
+    void  *value;
+
+    if (hash->hash.buckets) {
+        value = ngx_hash_find(&hash->hash, key, name, len);
+
+        if (value) {
+            return value;
+        }
+    }
+
+    if (hash->wc_head && hash->wc_head->hash.buckets) {
+        value = ngx_hash_find_wc_head(hash->wc_head, name, len);
+
+        if (value) {
+            return value;
+        }
+    }
+
+    if (hash->wc_tail && hash->wc_tail->hash.buckets) {
+        value = ngx_hash_find_wc_tail(hash->wc_tail, name, len);
+
+        if (value) {
+            return value;
+        }
+    }
+
+    return NULL;
+}
+
+
 #define NGX_HASH_ELT_SIZE(name)                                               \
     (sizeof(void *) + ngx_align((name)->key.len + 1, sizeof(void *)))
 
@@ -544,7 +642,14 @@ ngx_hash_keys_array_init(ngx_hash_keys_a
         return NGX_ERROR;
     }
 
-    if (ngx_array_init(&ha->dns_wildcards, ha->temp_pool, asize,
+    if (ngx_array_init(&ha->dns_wc_head, ha->temp_pool, asize,
+                       sizeof(ngx_hash_key_t))
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
+    if (ngx_array_init(&ha->dns_wc_tail, ha->temp_pool, asize,
                        sizeof(ngx_hash_key_t))
         != NGX_OK)
     {
@@ -556,9 +661,15 @@ ngx_hash_keys_array_init(ngx_hash_keys_a
         return NGX_ERROR;
     }
 
-    ha->dns_wildcards_hash = ngx_pcalloc(ha->temp_pool,
-                                         sizeof(ngx_array_t) * ha->hsize);
-    if (ha->dns_wildcards_hash == NULL) {
+    ha->dns_wc_head_hash = ngx_pcalloc(ha->temp_pool,
+                                       sizeof(ngx_array_t) * ha->hsize);
+    if (ha->dns_wc_head_hash == NULL) {
+        return NGX_ERROR;
+    }
+
+    ha->dns_wc_tail_hash = ngx_pcalloc(ha->temp_pool,
+                                       sizeof(ngx_array_t) * ha->hsize);
+    if (ha->dns_wc_tail_hash == NULL) {
         return NGX_ERROR;
     }
 
@@ -571,37 +682,144 @@ ngx_hash_add_key(ngx_hash_keys_arrays_t 
     ngx_uint_t flags)
 {
     size_t           len;
-    u_char          *reverse;
+    u_char          *p;
     ngx_str_t       *name;
-    ngx_uint_t       i, k, n, skip;
+    ngx_uint_t       i, k, n, skip, last;
+    ngx_array_t     *keys, *hwc;
     ngx_hash_key_t  *hk;
 
-    if (!(flags & NGX_HASH_WILDCARD_KEY)) {
+    last = key->len;
+
+    if (flags & NGX_HASH_WILDCARD_KEY) {
 
-        /* exact hash */
+        /*
+         * supported wildcards:
+         *     "*.example.com", ".example.com", and "www.example.*"
+         */
 
-        k = 0;
+        n = 0;
 
         for (i = 0; i < key->len; i++) {
-            if (!(flags & NGX_HASH_READONLY_KEY)) {
-                key->data[i] = ngx_tolower(key->data[i]);
+
+            if (key->data[i] == '*') {
+                if (++n > 1) {
+                    return NGX_DECLINED;
+                }
+            }
+
+            if (key->data[i] == '.' && key->data[i + 1] == '.') {
+                return NGX_DECLINED;
             }
-            k = ngx_hash(k, key->data[i]);
+        }
+
+        if (key->len > 1 && key->data[0] == '.') {
+            skip = 1;
+            goto wildcard;
+        }
+
+        if (key->len > 2) {
+
+            if (key->data[0] == '*' && key->data[1] == '.') {
+                skip = 2;
+                goto wildcard;
+            }
+
+            if (key->data[i - 2] == '.' && key->data[i - 1] == '*') {
+                skip = 0;
+                last -= 2;
+                goto wildcard;
+            }
         }
 
-        k %= ha->hsize;
+        if (n) {
+            return NGX_DECLINED;
+        }
+    }
+
+    /* exact hash */
+
+    k = 0;
+
+    for (i = 0; i < last; i++) {
+        if (!(flags & NGX_HASH_READONLY_KEY)) {
+            key->data[i] = ngx_tolower(key->data[i]);
+        }
+        k = ngx_hash(k, key->data[i]);
+    }
+
+    k %= ha->hsize;
+
+    /* check conflicts in exact hash */
+
+    name = ha->keys_hash[k].elts;
+
+    if (name) {
+        for (i = 0; i < ha->keys_hash[k].nelts; i++) {
+            if (last != name[i].len) {
+                continue;
+            }
+
+            if (ngx_strncmp(key->data, name[i].data, last) == 0) {
+                return NGX_BUSY;
+            }
+        }
 
-        /* check conflicts in exact hash */
+    } else {
+        if (ngx_array_init(&ha->keys_hash[k], ha->temp_pool, 4,
+                           sizeof(ngx_str_t))
+            != NGX_OK)
+        {
+            return NGX_ERROR;
+        }
+    }
+
+    name = ngx_array_push(&ha->keys_hash[k]);
+    if (name == NULL) {
+        return NGX_ERROR;
+    }
+
+    *name = *key;
+
+    hk = ngx_array_push(&ha->keys);
+    if (hk == NULL) {
+        return NGX_ERROR;
+    }
+
+    hk->key = *key;
+    hk->key_hash = ngx_hash_key(key->data, last);
+    hk->value = value;
+
+    return NGX_OK;
+
+
+wildcard:
+
+    /* wildcard hash */
+
+    k = 0;
+
+    for (i = skip; i < last; i++) {
+        key->data[i] = ngx_tolower(key->data[i]);
+        k = ngx_hash(k, key->data[i]);
+    }
+
+    k %= ha->hsize;
+
+    if (skip == 1) {
+
+        /* check conflicts in exact hash for ".example.com" */
 
         name = ha->keys_hash[k].elts;
 
         if (name) {
+            len = last - skip;
+
             for (i = 0; i < ha->keys_hash[k].nelts; i++) {
-                if (key->len != name[i].len) {
+                if (len != name[i].len) {
                     continue;
                 }
 
-                if (ngx_strncmp(key->data, name[i].data, key->len) == 0) {
+                if (ngx_strncmp(&key->data[1], name[i].data, len) == 0) {
                     return NGX_BUSY;
                 }
             }
@@ -620,92 +838,36 @@ ngx_hash_add_key(ngx_hash_keys_arrays_t 
             return NGX_ERROR;
         }
 
-        *name = *key;
-
-        hk = ngx_array_push(&ha->keys);
-        if (hk == NULL) {
+        name->len = last - 1;
+        name->data = ngx_palloc(ha->temp_pool, name->len);
+        if (name->data == NULL) {
             return NGX_ERROR;
         }
 
-        hk->key = *key;
-        hk->key_hash = ngx_hash_key(key->data, key->len);
-        hk->value = value;
-
-    } else {
-
-        /* wildcard hash */
-
-        skip = (key->data[0] == '*') ? 2 : 1;
-        k = 0;
-
-        for (i = skip; i < key->len; i++) {
-            key->data[i] = ngx_tolower(key->data[i]);
-            k = ngx_hash(k, key->data[i]);
-        }
-
-        k %= ha->hsize;
-
-        if (skip == 1) {
-
-            /* check conflicts in exact hash for ".example.com" */
-
-            name = ha->keys_hash[k].elts;
-
-            if (name) {
-                len = key->len - skip;
+        ngx_memcpy(name->data, &key->data[1], name->len);
+    }
 
-                for (i = 0; i < ha->keys_hash[k].nelts; i++) {
-                    if (len != name[i].len) {
-                        continue;
-                    }
 
-                    if (ngx_strncmp(&key->data[1], name[i].data, len) == 0) {
-                        return NGX_BUSY;
-                    }
-                }
-
-            } else {
-                if (ngx_array_init(&ha->keys_hash[k], ha->temp_pool, 4,
-                                   sizeof(ngx_str_t))
-                    != NGX_OK)
-                {
-                    return NGX_ERROR;
-                }
-            }
-
-            name = ngx_array_push(&ha->keys_hash[k]);
-            if (name == NULL) {
-                return NGX_ERROR;
-            }
-
-            name->len = key->len - 1;
-            name->data = ngx_palloc(ha->temp_pool, name->len);
-            if (name->data == NULL) {
-                return NGX_ERROR;
-            }
-
-            ngx_memcpy(name->data, &key->data[1], name->len);
-        }
-
+    if (skip) {
 
         /*
          * convert "*.example.com" to "com.example.\0"
          *      and ".example.com" to "com.example\0"
          */
 
-        reverse = ngx_palloc(ha->temp_pool, key->len);
-        if (reverse == NULL) {
+        p = ngx_palloc(ha->temp_pool, last);
+        if (p == NULL) {
             return NGX_ERROR;
         }
 
         len = 0;
         n = 0;
 
-        for (i = key->len - 1; i; i--) {
+        for (i = last - 1; i; i--) {
             if (key->data[i] == '.') {
-                ngx_memcpy(&reverse[n], &key->data[i + 1], len);
+                ngx_memcpy(&p[n], &key->data[i + 1], len);
                 n += len;
-                reverse[n++] = '.';
+                p[n++] = '.';
                 len = 0;
                 continue;
             }
@@ -714,63 +876,75 @@ ngx_hash_add_key(ngx_hash_keys_arrays_t 
         }
 
         if (len) {
-            ngx_memcpy(&reverse[n], &key->data[1], len);
+            ngx_memcpy(&p[n], &key->data[1], len);
             n += len;
         }
 
-        reverse[n] = '\0';
+        p[n] = '\0';
 
+        hwc = &ha->dns_wc_head;
+        keys = &ha->dns_wc_head_hash[k];
+
+    } else {
 
-        hk = ngx_array_push(&ha->dns_wildcards);
-        if (hk == NULL) {
-            return NGX_ERROR;
-        }
+        /* convert "www.example.*" to "www.example\0" */
 
-        hk->key.len = key->len - 1;
-        hk->key.data = reverse;
-        hk->key_hash = 0;
-        hk->value = value;
+        p = key->data;
+        key->data[last] = '\0';
+        last++;
+
+        hwc = &ha->dns_wc_tail;
+        keys = &ha->dns_wc_tail_hash[k];
+    }
 
 
-        /* check conflicts in wildcard hash */
+    hk = ngx_array_push(hwc);
+    if (hk == NULL) {
+        return NGX_ERROR;
+    }
 
-        name = ha->dns_wildcards_hash[k].elts;
+    hk->key.len = last - 1;
+    hk->key.data = p;
+    hk->key_hash = 0;
+    hk->value = value;
 
-        if (name) {
-            len = key->len - skip;
 
-            for (i = 0; i < ha->dns_wildcards_hash[k].nelts; i++) {
-                if (len != name[i].len) {
-                    continue;
-                }
+    /* check conflicts in wildcard hash */
+
+    name = keys->elts;
 
-                if (ngx_strncmp(key->data + skip, name[i].data, len) == 0) {
-                    return NGX_BUSY;
-                }
+    if (name) {
+        len = last - skip;
+
+        for (i = 0; i < keys->nelts; i++) {
+            if (len != name[i].len) {
+                continue;
             }
 
-        } else {
-            if (ngx_array_init(&ha->dns_wildcards_hash[k], ha->temp_pool, 4,
-                               sizeof(ngx_str_t))
-                != NGX_OK)
-            {
-                return NGX_ERROR;
+            if (ngx_strncmp(key->data + skip, name[i].data, len) == 0) {
+                return NGX_BUSY;
             }
         }
 
-        name = ngx_array_push(&ha->dns_wildcards_hash[k]);
-        if (name == NULL) {
+    } else {
+        if (ngx_array_init(keys, ha->temp_pool, 4, sizeof(ngx_str_t)) != NGX_OK)
+        {
             return NGX_ERROR;
         }
+    }
 
-        name->len = key->len - skip;
-        name->data = ngx_palloc(ha->temp_pool, name->len);
-        if (name->data == NULL) {
-            return NGX_ERROR;
-        }
+    name = ngx_array_push(keys);
+    if (name == NULL) {
+        return NGX_ERROR;
+    }
 
-        ngx_memcpy(name->data, key->data + skip, name->len);
+    name->len = last - skip;
+    name->data = ngx_palloc(ha->temp_pool, name->len);
+    if (name->data == NULL) {
+        return NGX_ERROR;
     }
 
+    ngx_memcpy(name->data, key->data + skip, name->len);
+
     return NGX_OK;
 }
--- a/src/core/ngx_hash.h
+++ b/src/core/ngx_hash.h
@@ -42,6 +42,13 @@ typedef ngx_uint_t (*ngx_hash_key_pt) (u
 
 
 typedef struct {
+    ngx_hash_t            hash;
+    ngx_hash_wildcard_t  *wc_head;
+    ngx_hash_wildcard_t  *wc_tail;
+} ngx_hash_combined_t;
+
+
+typedef struct {
     ngx_hash_t       *hash;
     ngx_hash_key_pt   key;
 
@@ -73,8 +80,11 @@ typedef struct {
     ngx_array_t       keys;
     ngx_array_t      *keys_hash;
 
-    ngx_array_t       dns_wildcards;
-    ngx_array_t      *dns_wildcards_hash;
+    ngx_array_t       dns_wc_head;
+    ngx_array_t      *dns_wc_head_hash;
+
+    ngx_array_t       dns_wc_tail;
+    ngx_array_t      *dns_wc_tail_hash;
 } ngx_hash_keys_arrays_t;
 
 
@@ -87,8 +97,10 @@ typedef struct {
 
 
 void *ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len);
-void *ngx_hash_find_wildcard(ngx_hash_wildcard_t *hwc, u_char *name,
-    size_t len);
+void *ngx_hash_find_wc_head(ngx_hash_wildcard_t *hwc, u_char *name, size_t len);
+void *ngx_hash_find_wc_tail(ngx_hash_wildcard_t *hwc, u_char *name, size_t len);
+void *ngx_hash_find_combined(ngx_hash_combined_t *hash, ngx_uint_t key,
+    u_char *name, size_t len);
 
 ngx_int_t ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names,
     ngx_uint_t nelts);
--- a/src/http/modules/ngx_http_map_module.c
+++ b/src/http/modules/ngx_http_map_module.c
@@ -26,8 +26,7 @@ typedef struct {
 
 
 typedef struct {
-    ngx_hash_t                  hash;
-    ngx_hash_wildcard_t        *dns_wildcards;
+    ngx_hash_combined_t         hash;
     ngx_int_t                   index;
     ngx_http_variable_value_t  *default_value;
     ngx_uint_t                  hostnames;      /* unsigned  hostnames:1 */
@@ -142,28 +141,13 @@ ngx_http_map_variable(ngx_http_request_t
         key = ngx_hash(key, name[i]);
     }
 
-    value = NULL;
-
-    if (map->hash.buckets) {
-        value = ngx_hash_find(&map->hash, key, name, len);
-    }
+    value = ngx_hash_find_combined(&map->hash, key, name, len);
 
     if (value) {
         *v = *value;
 
     } else {
-        if (map->dns_wildcards && map->dns_wildcards->hash.buckets) {
-            value = ngx_hash_find_wildcard(map->dns_wildcards, name, len);
-            if (value) {
-                *v = *value;
-
-            } else {
-                *v = *map->default_value;
-            }
-
-        } else {
-            *v = *map->default_value;
-        }
+        *v = *map->default_value;
     }
 
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -282,6 +266,9 @@ ngx_http_map_block(ngx_conf_t *cf, ngx_c
         return rv;
     }
 
+    map->default_value = ctx.default_value ? ctx.default_value:
+                                             &ngx_http_variable_null_value;
+
     hash.key = ngx_hash_key_lc;
     hash.max_size = mcf->hash_max_size;
     hash.bucket_size = mcf->hash_bucket_size;
@@ -289,7 +276,7 @@ ngx_http_map_block(ngx_conf_t *cf, ngx_c
     hash.pool = cf->pool;
 
     if (ctx.keys.keys.nelts) {
-        hash.hash = &map->hash;
+        hash.hash = &map->hash.hash;
         hash.temp_pool = NULL;
 
         if (ngx_hash_init(&hash, ctx.keys.keys.elts, ctx.keys.keys.nelts)
@@ -300,27 +287,44 @@ ngx_http_map_block(ngx_conf_t *cf, ngx_c
         }
     }
 
-    map->default_value = ctx.default_value ? ctx.default_value:
-                                             &ngx_http_variable_null_value;
+    if (ctx.keys.dns_wc_head.nelts) {
 
-    if (ctx.keys.dns_wildcards.nelts) {
-
-        ngx_qsort(ctx.keys.dns_wildcards.elts,
-                  (size_t) ctx.keys.dns_wildcards.nelts,
+        ngx_qsort(ctx.keys.dns_wc_head.elts,
+                  (size_t) ctx.keys.dns_wc_head.nelts,
                   sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards);
 
         hash.hash = NULL;
         hash.temp_pool = pool;
 
-        if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wildcards.elts,
-                                   ctx.keys.dns_wildcards.nelts)
+        if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_head.elts,
+                                   ctx.keys.dns_wc_head.nelts)
             != NGX_OK)
         {
             ngx_destroy_pool(pool);
             return NGX_CONF_ERROR;
         }
 
-        map->dns_wildcards = (ngx_hash_wildcard_t *) hash.hash;
+        map->hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;
+    }
+
+    if (ctx.keys.dns_wc_tail.nelts) {
+
+        ngx_qsort(ctx.keys.dns_wc_tail.elts,
+                  (size_t) ctx.keys.dns_wc_tail.nelts,
+                  sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards);
+
+        hash.hash = NULL;
+        hash.temp_pool = pool;
+
+        if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_tail.elts,
+                                   ctx.keys.dns_wc_tail.nelts)
+            != NGX_OK)
+        {
+            ngx_destroy_pool(pool);
+            return NGX_CONF_ERROR;
+        }
+
+        map->hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;
     }
 
     ngx_destroy_pool(pool);
@@ -344,10 +348,9 @@ ngx_http_map_cmp_dns_wildcards(const voi
 static char *
 ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
 {
-    u_char                      ch;
     ngx_int_t                   rc;
     ngx_str_t                  *value, file;
-    ngx_uint_t                  i, key, flags;
+    ngx_uint_t                  i, key;
     ngx_http_map_conf_ctx_t    *ctx;
     ngx_http_variable_value_t  *var, **vp;
 
@@ -439,50 +442,36 @@ ngx_http_map(ngx_conf_t *cf, ngx_command
 
 found:
 
-    ch = value[0].data[0];
-
-    if ((ch != '*' && ch != '.') || ctx->hostnames == 0) {
-
-        if (ngx_strcmp(value[0].data, "default") == 0) {
-
-            if (ctx->default_value) {
-                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                                   "duplicate default map parameter");
-                return NGX_CONF_ERROR;
-            }
-
-            ctx->default_value = var;
+    if (ngx_strcmp(value[0].data, "default") == 0) {
 
-            return NGX_CONF_OK;
-        }
-
-        if (value[0].len && ch == '!') {
-            value[0].len--;
-            value[0].data++;
-        }
-
-        flags = 0;
-
-    } else {
-
-        if ((ch == '*' && (value[0].len < 3 || value[0].data[1] != '.'))
-            || (ch == '.' && value[0].len < 2))
-        {
+        if (ctx->default_value) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "invalid DNS wildcard \"%V\"", &value[0]);
-
+                               "duplicate default map parameter");
             return NGX_CONF_ERROR;
         }
 
-        flags = NGX_HASH_WILDCARD_KEY;
+        ctx->default_value = var;
+
+        return NGX_CONF_OK;
     }
 
-    rc = ngx_hash_add_key(&ctx->keys, &value[0], var, flags);
+    if (value[0].len && value[0].data[0] == '!') {
+        value[0].len--;
+        value[0].data++;
+    }
+
+    rc = ngx_hash_add_key(&ctx->keys, &value[0], var,
+                          (ctx->hostnames) ? NGX_HASH_WILDCARD_KEY : 0);
 
     if (rc == NGX_OK) {
         return NGX_CONF_OK;
     }
 
+    if (rc == NGX_DECLINED) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid hostname or wildcard \"%V\"", &value[0]);
+    }
+
     if (rc == NGX_BUSY) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "conflicting parameter \"%V\"", &value[0]);
--- a/src/http/modules/ngx_http_referer_module.c
+++ b/src/http/modules/ngx_http_referer_module.c
@@ -12,8 +12,7 @@
 #define NGX_HTTP_REFERER_NO_URI_PART  ((void *) 4)
 
 typedef struct {
-    ngx_hash_t               hash;
-    ngx_hash_wildcard_t     *dns_wildcards;
+    ngx_hash_combined_t      hash;
 
     ngx_flag_t               no_referer;
     ngx_flag_t               blocked_referer;
@@ -90,7 +89,10 @@ ngx_http_referer_variable(ngx_http_reque
 
     rlcf = ngx_http_get_module_loc_conf(r, ngx_http_referer_module);
 
-    if (rlcf->hash.buckets == NULL && rlcf->dns_wildcards == NULL) {
+    if (rlcf->hash.hash.buckets == NULL
+        && rlcf->hash.wc_head == NULL
+        && rlcf->hash.wc_tail == NULL)
+    {
         goto valid;
     }
 
@@ -135,18 +137,10 @@ ngx_http_referer_variable(ngx_http_reque
 
     len = p - ref;
 
-    if (rlcf->hash.buckets) {
-        uri = ngx_hash_find(&rlcf->hash, key, buf, len);
-        if (uri) {
-            goto uri;
-        }
-    }
+    uri = ngx_hash_find_combined(&rlcf->hash, key, buf, len);
 
-    if (rlcf->dns_wildcards) {
-        uri = ngx_hash_find_wildcard(rlcf->dns_wildcards, buf, len);
-        if (uri) {
-            goto uri;
-        }
+    if (uri) {
+        goto uri;
     }
 
 invalid:
@@ -208,7 +202,6 @@ ngx_http_referer_merge_conf(ngx_conf_t *
 
     if (conf->keys == NULL) {
         conf->hash = prev->hash;
-        conf->dns_wildcards = prev->dns_wildcards;
 
         ngx_conf_merge_value(conf->no_referer, prev->no_referer, 0);
         ngx_conf_merge_value(conf->blocked_referer, prev->blocked_referer, 0);
@@ -217,7 +210,9 @@ ngx_http_referer_merge_conf(ngx_conf_t *
     }
 
     if ((conf->no_referer == 1 || conf->blocked_referer == 1)
-        && conf->keys->keys.nelts == 0 && conf->keys->dns_wildcards.nelts == 0)
+        && conf->keys->keys.nelts == 0
+        && conf->keys->dns_wc_head.nelts == 0
+        && conf->keys->dns_wc_tail.nelts == 0)
     {
         ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                       "the \"none\" or \"blocked\" referers are specified "
@@ -233,7 +228,7 @@ ngx_http_referer_merge_conf(ngx_conf_t *
     hash.pool = cf->pool;
 
     if (conf->keys->keys.nelts) {
-        hash.hash = &conf->hash;
+        hash.hash = &conf->hash.hash;
         hash.temp_pool = NULL;
 
         if (ngx_hash_init(&hash, conf->keys->keys.elts, conf->keys->keys.nelts)
@@ -243,24 +238,44 @@ ngx_http_referer_merge_conf(ngx_conf_t *
         }
     }
 
-    if (conf->keys->dns_wildcards.nelts) {
+    if (conf->keys->dns_wc_head.nelts) {
 
-        ngx_qsort(conf->keys->dns_wildcards.elts,
-                  (size_t) conf->keys->dns_wildcards.nelts,
+        ngx_qsort(conf->keys->dns_wc_head.elts,
+                  (size_t) conf->keys->dns_wc_head.nelts,
                   sizeof(ngx_hash_key_t),
                   ngx_http_cmp_referer_wildcards);
 
         hash.hash = NULL;
         hash.temp_pool = cf->temp_pool;
 
-        if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wildcards.elts,
-                                   conf->keys->dns_wildcards.nelts)
+        if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wc_head.elts,
+                                   conf->keys->dns_wc_head.nelts)
             != NGX_OK)
         {
             return NGX_CONF_ERROR;
         }
 
-        conf->dns_wildcards = (ngx_hash_wildcard_t *) hash.hash;
+        conf->hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;
+    }
+
+    if (conf->keys->dns_wc_tail.nelts) {
+
+        ngx_qsort(conf->keys->dns_wc_tail.elts,
+                  (size_t) conf->keys->dns_wc_tail.nelts,
+                  sizeof(ngx_hash_key_t),
+                  ngx_http_cmp_referer_wildcards);
+
+        hash.hash = NULL;
+        hash.temp_pool = cf->temp_pool;
+
+        if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wc_tail.elts,
+                                   conf->keys->dns_wc_tail.nelts)
+            != NGX_OK)
+        {
+            return NGX_CONF_ERROR;
+        }
+
+        conf->hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;
     }
 
     if (conf->no_referer == NGX_CONF_UNSET) {
@@ -373,23 +388,8 @@ static char *
 ngx_http_add_referer(ngx_conf_t *cf, ngx_hash_keys_arrays_t *keys,
     ngx_str_t *value, ngx_str_t *uri)
 {
-    u_char       ch;
-    ngx_int_t    rc;
-    ngx_str_t   *u;
-    ngx_uint_t   flags;
-
-    ch = value->data[0];
-
-    if ((ch == '*' && (value->len < 3 || value->data[1] != '.'))
-        || (ch == '.' && value->len < 2))
-    {
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                           "invalid DNS wildcard \"%V\"", value);
-
-        return NGX_CONF_ERROR;
-    }
-
-    flags = (ch == '*' || ch == '.') ? NGX_HASH_WILDCARD_KEY : 0;
+    ngx_int_t   rc;
+    ngx_str_t  *u;
 
     if (uri->len == 0) {
         u = NGX_HTTP_REFERER_NO_URI_PART;
@@ -403,12 +403,17 @@ ngx_http_add_referer(ngx_conf_t *cf, ngx
         *u = *uri;
     }
 
-    rc = ngx_hash_add_key(keys, value, u, flags);
+    rc = ngx_hash_add_key(keys, value, u, NGX_HASH_WILDCARD_KEY);
 
     if (rc == NGX_OK) {
         return NGX_CONF_OK;
     }
 
+    if (rc == NGX_DECLINED) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid hostname or wildcard \"%V\"", value);
+    }
+
     if (rc == NGX_BUSY) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "conflicting parameter \"%V\"", value);
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -47,7 +47,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.5.25';
+our $VERSION = '0.6.0';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -73,7 +73,6 @@ static char *
 ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     char                        *rv;
-    u_char                       ch;
     ngx_int_t                    rc, j;
     ngx_uint_t                   mi, m, s, l, p, a, i, n;
     ngx_uint_t                   find_config_index, use_rewrite, use_access;
@@ -648,40 +647,20 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
             }
 
             name = in_addr[a].names.elts;
+
             for (s = 0; s < in_addr[a].names.nelts; s++) {
 
-                ch = name[s].name.data[0];
-
-                if (ch == '*' || ch == '.') {
-                    continue;
-                }
-
                 rc = ngx_hash_add_key(&ha, &name[s].name, name[s].core_srv_conf,
-                                      0);
+                                      NGX_HASH_WILDCARD_KEY);
 
                 if (rc == NGX_ERROR) {
                     return NGX_CONF_ERROR;
                 }
 
-                if (rc == NGX_BUSY) {
-                    ngx_log_error(NGX_LOG_WARN, cf->log, 0,
-                                "conflicting server name \"%V\" on %s, ignored",
+                if (rc == NGX_DECLINED) {
+                    ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                                "invalid server name or wildcard \"%V\" on %s",
                                 &name[s].name, in_addr[a].listen_conf->addr);
-                }
-            }
-
-            for (s = 0; s < in_addr[a].names.nelts; s++) {
-
-                ch = name[s].name.data[0];
-
-                if (ch != '*' && ch != '.') {
-                    continue;
-                }
-
-                rc = ngx_hash_add_key(&ha, &name[s].name, name[s].core_srv_conf,
-                                      NGX_HASH_WILDCARD_KEY);
-
-                if (rc == NGX_ERROR) {
                     return NGX_CONF_ERROR;
                 }
 
@@ -709,25 +688,46 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
                 }
             }
 
-            if (ha.dns_wildcards.nelts) {
+            if (ha.dns_wc_head.nelts) {
 
-                ngx_qsort(ha.dns_wildcards.elts,
-                          (size_t) ha.dns_wildcards.nelts,
+                ngx_qsort(ha.dns_wc_head.elts,
+                          (size_t) ha.dns_wc_head.nelts,
                           sizeof(ngx_hash_key_t),
                           ngx_http_cmp_dns_wildcards);
 
                 hash.hash = NULL;
                 hash.temp_pool = ha.temp_pool;
 
-                if (ngx_hash_wildcard_init(&hash, ha.dns_wildcards.elts,
-                                           ha.dns_wildcards.nelts)
+                if (ngx_hash_wildcard_init(&hash, ha.dns_wc_head.elts,
+                                           ha.dns_wc_head.nelts)
                     != NGX_OK)
                 {
                     ngx_destroy_pool(ha.temp_pool);
                     return NGX_CONF_ERROR;
                 }
 
-                in_addr[a].dns_wildcards = (ngx_hash_wildcard_t *) hash.hash;
+                in_addr[a].wc_head = (ngx_hash_wildcard_t *) hash.hash;
+            }
+
+            if (ha.dns_wc_tail.nelts) {
+
+                ngx_qsort(ha.dns_wc_tail.elts,
+                          (size_t) ha.dns_wc_tail.nelts,
+                          sizeof(ngx_hash_key_t),
+                          ngx_http_cmp_dns_wildcards);
+
+                hash.hash = NULL;
+                hash.temp_pool = ha.temp_pool;
+
+                if (ngx_hash_wildcard_init(&hash, ha.dns_wc_tail.elts,
+                                           ha.dns_wc_tail.nelts)
+                    != NGX_OK)
+                {
+                    ngx_destroy_pool(ha.temp_pool);
+                    return NGX_CONF_ERROR;
+                }
+
+                in_addr[a].wc_tail = (ngx_hash_wildcard_t *) hash.hash;
             }
 
             ngx_destroy_pool(ha.temp_pool);
@@ -848,8 +848,10 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
                 hip->addrs[i].core_srv_conf = in_addr[i].core_srv_conf;
 
                 if (in_addr[i].hash.buckets == NULL
-                    && (in_addr[i].dns_wildcards == NULL
-                        || in_addr[i].dns_wildcards->hash.buckets == NULL))
+                    && (in_addr[i].wc_head == NULL
+                        || in_addr[i].wc_head->hash.buckets == NULL)
+                    && (in_addr[i].wc_head == NULL
+                        || in_addr[i].wc_head->hash.buckets == NULL))
                 {
                     continue;
                 }
@@ -861,7 +863,8 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
                 hip->addrs[i].virtual_names = vn;
 
                 vn->hash = in_addr[i].hash;
-                vn->dns_wildcards = in_addr[i].dns_wildcards;
+                vn->wc_head = in_addr[i].wc_head;
+                vn->wc_tail = in_addr[i].wc_tail;
             }
 
             if (done) {
@@ -934,7 +937,8 @@ ngx_http_add_address(ngx_conf_t *cf, ngx
     in_addr->addr = lscf->addr;
     in_addr->hash.buckets = NULL;
     in_addr->hash.size = 0;
-    in_addr->dns_wildcards = NULL;
+    in_addr->wc_head = NULL;
+    in_addr->wc_tail = NULL;
     in_addr->names.elts = NULL;
     in_addr->core_srv_conf = cscf;
     in_addr->default_server = lscf->conf.default_server;
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -2618,7 +2618,7 @@ ngx_http_core_server_name(ngx_conf_t *cf
     ch = value[1].data[0];
 
     if (cscf->server_name.data == NULL && value[1].len) {
-        if (ch == '*') {
+        if (ngx_strchr(value[1].data, '*')) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                "first server name \"%V\" must not be wildcard",
                                &value[1]);
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -173,7 +173,8 @@ typedef struct {
     in_addr_t                  addr;
 
     ngx_hash_t                 hash;
-    ngx_hash_wildcard_t       *dns_wildcards;
+    ngx_hash_wildcard_t       *wc_head;
+    ngx_hash_wildcard_t       *wc_tail;
 
     ngx_array_t                names;      /* array of ngx_http_server_name_t */
 
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -1459,19 +1459,10 @@ ngx_http_find_virtual_server(ngx_http_re
 
     vn = r->virtual_names;
 
-    if (vn->hash.buckets) {
-        cscf = ngx_hash_find(&vn->hash, hash, host, len);
-        if (cscf) {
-            goto found;
-        }
-    }
-
-    if (vn->dns_wildcards && vn->dns_wildcards->hash.buckets) {
-        cscf = ngx_hash_find_wildcard(vn->dns_wildcards, host, len);
-
-        if (cscf) {
-            goto found;
-        }
+    cscf = ngx_hash_find_combined(vn, hash, host, len);
+
+    if (cscf) {
+        goto found;
     }
 
     cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -275,10 +275,7 @@ typedef struct {
 } ngx_http_connection_t;
 
 
-typedef struct {
-    ngx_hash_t                        hash;
-    ngx_hash_wildcard_t              *dns_wildcards;
-} ngx_http_virtual_names_t;
+typedef ngx_hash_combined_t  ngx_http_virtual_names_t;
 
 
 typedef void (*ngx_http_cleanup_pt)(void *data);