changeset 1618:fbf94b8341bf stable-0.5

r1386, r1388, r1389, r1580, r1581 merge: regex in server_name and valid_referers
author Igor Sysoev <igor@sysoev.ru>
date Wed, 07 Nov 2007 13:46:29 +0000
parents cacb565c554e
children e2ab812358bd
files src/http/modules/ngx_http_referer_module.c 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 6 files changed, 316 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- a/src/http/modules/ngx_http_referer_module.c
+++ b/src/http/modules/ngx_http_referer_module.c
@@ -11,9 +11,27 @@
 
 #define NGX_HTTP_REFERER_NO_URI_PART  ((void *) 4)
 
+#if (NGX_PCRE)
+
+typedef struct {
+    ngx_regex_t             *regex;
+    ngx_str_t                name;
+} ngx_http_referer_regex_t;
+
+#else
+
+#define ngx_regex_t          void
+
+#endif
+
+
 typedef struct {
     ngx_hash_combined_t      hash;
 
+#if (NGX_PCRE)
+    ngx_array_t             *regex;
+#endif
+
     ngx_flag_t               no_referer;
     ngx_flag_t               blocked_referer;
 
@@ -28,6 +46,8 @@ static char *ngx_http_valid_referers(ngx
     void *conf);
 static char *ngx_http_add_referer(ngx_conf_t *cf, ngx_hash_keys_arrays_t *keys,
     ngx_str_t *value, ngx_str_t *uri);
+static char *ngx_http_add_regex_referer(ngx_conf_t *cf,
+    ngx_http_referer_conf_t *rlcf, ngx_str_t *name, ngx_regex_t *regex);
 static int ngx_libc_cdecl ngx_http_cmp_referer_wildcards(const void *one,
     const void *two);
 
@@ -80,18 +100,27 @@ static ngx_int_t
 ngx_http_referer_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
      uintptr_t data)
 {
-    u_char                   *p, *ref, *last;
-    size_t                    len;
-    ngx_str_t                *uri;
-    ngx_uint_t                i, key;
-    ngx_http_referer_conf_t  *rlcf;
-    u_char                    buf[256];
+    u_char                    *p, *ref, *last;
+    size_t                     len;
+    ngx_str_t                 *uri;
+    ngx_uint_t                 i, key;
+    ngx_http_referer_conf_t   *rlcf;
+    u_char                     buf[256];
+#if (NGX_PCRE)
+    ngx_int_t                  n;
+    ngx_str_t                  referer;
+    ngx_http_referer_regex_t  *regex;
+#endif
 
     rlcf = ngx_http_get_module_loc_conf(r, ngx_http_referer_module);
 
     if (rlcf->hash.hash.buckets == NULL
         && rlcf->hash.wc_head == NULL
-        && rlcf->hash.wc_tail == NULL)
+        && rlcf->hash.wc_tail == NULL
+#if (NGX_PCRE)
+        && rlcf->regex == NULL
+#endif
+       )
     {
         goto valid;
     }
@@ -135,14 +164,44 @@ ngx_http_referer_variable(ngx_http_reque
         }
     }
 
-    len = p - ref;
-
-    uri = ngx_hash_find_combined(&rlcf->hash, key, buf, len);
+    uri = ngx_hash_find_combined(&rlcf->hash, key, buf, p - ref);
 
     if (uri) {
         goto uri;
     }
 
+#if (NGX_PCRE)
+
+    if (rlcf->regex) {
+
+        referer.len = len - 7;
+        referer.data = ref;
+
+        regex = rlcf->regex->elts;
+
+        for (i = 0; i < rlcf->regex->nelts; i++) {
+            n = ngx_regex_exec(regex[i].regex, &referer, NULL, 0);
+
+            if (n == NGX_REGEX_NO_MATCHED) {
+                continue;
+            }
+
+            if (n < 0) {
+                ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                              ngx_regex_exec_n
+                              " failed: %d on \"%V\" using \"%V\"",
+                              n, &referer, &regex[i].name);
+                return NGX_ERROR;
+            }
+
+            /* match */
+
+            goto valid;
+        }
+    }
+
+#endif
+
 invalid:
 
     *v = ngx_http_variable_true_value;
@@ -357,6 +416,21 @@ ngx_http_valid_referers(ngx_conf_t *cf, 
 
             sn = cscf->server_names.elts;
             for (n = 0; n < cscf->server_names.nelts; n++) {
+
+#if (NGX_PCRE)
+                if (sn[n].regex) {
+
+                    if (ngx_http_add_regex_referer(cf, rlcf, &sn[n].name,
+                                                   sn[n].regex)
+                        != NGX_OK)
+                    {
+                        return NGX_CONF_ERROR;
+                    }
+
+                    continue;
+                }
+#endif
+
                 if (ngx_http_add_referer(cf, rlcf->keys, &sn[n].name, &uri)
                     != NGX_OK)
                 {
@@ -367,6 +441,15 @@ ngx_http_valid_referers(ngx_conf_t *cf, 
             continue;
         }
 
+        if (value[i].data[0] == '~') {
+            if (ngx_http_add_regex_referer(cf, rlcf, &value[i], NULL) != NGX_OK)
+            {
+                return NGX_CONF_ERROR;
+            }
+
+            continue;
+        }
+
         p = (u_char *) ngx_strchr(value[i].data, '/');
 
         if (p) {
@@ -423,6 +506,64 @@ ngx_http_add_referer(ngx_conf_t *cf, ngx
 }
 
 
+static char *
+ngx_http_add_regex_referer(ngx_conf_t *cf, ngx_http_referer_conf_t *rlcf,
+    ngx_str_t *name, ngx_regex_t *regex)
+{
+#if (NGX_PCRE)
+    ngx_str_t                  err;
+    ngx_http_referer_regex_t  *rr;
+    u_char                     errstr[NGX_MAX_CONF_ERRSTR];
+
+    if (rlcf->regex == NULL) {
+        rlcf->regex = ngx_array_create(cf->pool, 2,
+                                       sizeof(ngx_http_referer_regex_t));
+        if (rlcf->regex == NULL) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    rr = ngx_array_push(rlcf->regex);
+    if (rr == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    if (regex) {
+        rr->regex = regex;
+        rr->name = *name;
+
+        return NGX_CONF_OK;
+    }
+
+    err.len = NGX_MAX_CONF_ERRSTR;
+    err.data = errstr;
+
+    name->len--;
+    name->data++;
+
+    rr->regex = ngx_regex_compile(name, NGX_REGEX_CASELESS, cf->pool, &err);
+
+    if (rr->regex == NULL) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
+        return NGX_CONF_ERROR;
+    }
+
+    rr->name = *name;
+
+    return NGX_CONF_OK;
+
+#else
+
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                       "the using of the regex \"%V\" requires PCRE library",
+                       name);
+
+    return NGX_CONF_ERROR;
+
+#endif
+}
+
+
 static int ngx_libc_cdecl
 ngx_http_cmp_referer_wildcards(const void *one, const void *two)
 {
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -98,6 +98,9 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
     ngx_http_core_loc_conf_t    *clcf;
     ngx_http_phase_handler_pt    checker;
     ngx_http_core_main_conf_t   *cmcf;
+#if (NGX_PCRE)
+    ngx_uint_t                   regex;
+#endif
 #if (NGX_WIN32)
     ngx_iocp_conf_t             *iocpcf;
 #endif
@@ -655,10 +658,21 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
                 return NGX_CONF_ERROR;
             }
 
+#if (NGX_PCRE)
+            regex = 0;
+#endif
+
             name = in_addr[a].names.elts;
 
             for (s = 0; s < in_addr[a].names.nelts; s++) {
 
+#if (NGX_PCRE)
+                if (name[s].regex) {
+                    regex++;
+                    continue;
+                }
+#endif
+
                 rc = ngx_hash_add_key(&ha, &name[s].name, name[s].core_srv_conf,
                                       NGX_HASH_WILDCARD_KEY);
 
@@ -740,6 +754,27 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
             }
 
             ngx_destroy_pool(ha.temp_pool);
+
+#if (NGX_PCRE)
+
+            if (regex == 0) {
+                continue;
+            }
+
+            in_addr[a].nregex = regex;
+            in_addr[a].regex = ngx_palloc(cf->pool,
+                                       regex * sizeof(ngx_http_server_name_t));
+
+            if (in_addr[a].regex == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            for (i = 0, s = 0; s < in_addr[a].names.nelts; s++) {
+                if (name[s].regex) {
+                    in_addr[a].regex[i++] = name[s];
+                }
+            }
+#endif
         }
 
         in_addr = in_port[p].addrs.elts;
@@ -871,9 +906,13 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
                 }
                 hip->addrs[i].virtual_names = vn;
 
-                vn->hash = in_addr[i].hash;
-                vn->wc_head = in_addr[i].wc_head;
-                vn->wc_tail = in_addr[i].wc_tail;
+                vn->names.hash = in_addr[i].hash;
+                vn->names.wc_head = in_addr[i].wc_head;
+                vn->names.wc_tail = in_addr[i].wc_tail;
+#if (NGX_PCRE)
+                vn->nregex = in_addr[i].nregex;
+                vn->regex = in_addr[i].regex;
+#endif
             }
 
             if (done) {
@@ -932,7 +971,8 @@ ngx_http_add_address(ngx_conf_t *cf, ngx
 
     if (in_port->addrs.elts == NULL) {
         if (ngx_array_init(&in_port->addrs, cf->temp_pool, 4,
-                           sizeof(ngx_http_conf_in_addr_t)) != NGX_OK)
+                           sizeof(ngx_http_conf_in_addr_t))
+            != NGX_OK)
         {
             return NGX_ERROR;
         }
@@ -949,6 +989,10 @@ ngx_http_add_address(ngx_conf_t *cf, ngx
     in_addr->wc_head = NULL;
     in_addr->wc_tail = NULL;
     in_addr->names.elts = NULL;
+#if (NGX_PCRE)
+    in_addr->nregex = 0;
+    in_addr->regex = NULL;
+#endif
     in_addr->core_srv_conf = cscf;
     in_addr->default_server = lscf->conf.default_server;
     in_addr->bind = lscf->conf.bind;
@@ -981,13 +1025,15 @@ ngx_http_add_names(ngx_conf_t *cf, ngx_h
 
     if (in_addr->names.elts == NULL) {
         if (ngx_array_init(&in_addr->names, cf->temp_pool, 4,
-                           sizeof(ngx_http_server_name_t)) != NGX_OK)
+                           sizeof(ngx_http_server_name_t))
+            != NGX_OK)
         {
             return NGX_ERROR;
         }
     }
 
     server_names = cscf->server_names.elts;
+
     for (i = 0; i < cscf->server_names.nelts; i++) {
 
         for (n = 0; n < server_names[i].name.len; n++) {
@@ -998,7 +1044,6 @@ ngx_http_add_names(ngx_conf_t *cf, ngx_h
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, cf->log, 0,
                        "name: %V", &server_names[i].name);
 
-
         name = ngx_array_push(&in_addr->names);
         if (name == NULL) {
             return NGX_ERROR;
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -2272,9 +2272,12 @@ ngx_http_core_merge_srv_conf(ngx_conf_t 
             return NGX_CONF_ERROR;
         }
 
+#if (NGX_PCRE)
+        sn->regex = NULL;
+#endif
+        sn->core_srv_conf = conf;
         sn->name.len = conf->server_name.len;
         sn->name.data = conf->server_name.data;
-        sn->core_srv_conf = conf;
     }
 
     ngx_conf_merge_size_value(conf->connection_pool_size,
@@ -2719,6 +2722,10 @@ ngx_http_core_server_name(ngx_conf_t *cf
     ngx_str_t               *value, name;
     ngx_uint_t               i;
     ngx_http_server_name_t  *sn;
+#if (NGX_PCRE)
+    ngx_str_t                err;
+    u_char                   errstr[NGX_MAX_CONF_ERRSTR];
+#endif
 
     value = cf->args->elts;
 
@@ -2732,6 +2739,13 @@ ngx_http_core_server_name(ngx_conf_t *cf
             return NGX_CONF_ERROR;
         }
 
+        if (value[1].data[0] == '~') {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "first server name \"%V\" "
+                               "must not be regular expression", &value[1]);
+            return NGX_CONF_ERROR;
+        }
+
         name = value[1];
 
         if (ch == '.') {
@@ -2775,9 +2789,42 @@ ngx_http_core_server_name(ngx_conf_t *cf
             return NGX_CONF_ERROR;
         }
 
+#if (NGX_PCRE)
+        sn->regex = NULL;
+#endif
+        sn->core_srv_conf = cscf;
         sn->name.len = value[i].len;
         sn->name.data = value[i].data;
-        sn->core_srv_conf = cscf;
+
+        if (value[i].data[0] != '~') {
+            continue;
+        }
+
+#if (NGX_PCRE)
+        err.len = NGX_MAX_CONF_ERRSTR;
+        err.data = errstr;
+
+        value[i].len--;
+        value[i].data++;
+
+        sn->regex = ngx_regex_compile(&value[i], NGX_REGEX_CASELESS, cf->pool,
+                                      &err);
+
+        if (sn->regex == NULL) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
+            return NGX_CONF_ERROR;
+        }
+
+        sn->name.len = value[i].len;
+        sn->name.data = value[i].data;
+
+#else
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "the using of the regex \"%V\" "
+                           "requires PCRE library", &value[i]);
+
+        return NGX_CONF_ERROR;
+#endif
     }
 
     return NGX_CONF_OK;
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -151,8 +151,10 @@ typedef struct {
 
 typedef struct {
     in_addr_t                  addr;
+
     /* the default server configuration for this address:port */
     ngx_http_core_srv_conf_t  *core_srv_conf;
+
     ngx_http_virtual_names_t  *virtual_names;
 } ngx_http_in_addr_t;
 
@@ -180,6 +182,11 @@ typedef struct {
 
     ngx_array_t                names;      /* array of ngx_http_server_name_t */
 
+#if (NGX_PCRE)
+    ngx_uint_t                 nregex;
+    ngx_http_server_name_t    *regex;
+#endif
+
     /* the default server configuration for this address:port */
     ngx_http_core_srv_conf_t  *core_srv_conf;
 
@@ -190,10 +197,13 @@ typedef struct {
 } ngx_http_conf_in_addr_t;
 
 
-typedef struct {
+struct ngx_http_server_name_s {
+#if (NGX_PCRE)
+    ngx_regex_t               *regex;
+#endif
+    ngx_http_core_srv_conf_t  *core_srv_conf; /* virtual name server conf */
     ngx_str_t                  name;
-    ngx_http_core_srv_conf_t  *core_srv_conf; /* virtual name server conf */
-} ngx_http_server_name_t;
+};
 
 
 typedef struct {
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -1457,18 +1457,56 @@ static void
 ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, size_t len,
     ngx_uint_t hash)
 {
-    ngx_http_virtual_names_t  *vn;
     ngx_http_core_loc_conf_t  *clcf;
     ngx_http_core_srv_conf_t  *cscf;
-
-    vn = r->virtual_names;
-
-    cscf = ngx_hash_find_combined(vn, hash, host, len);
+#if (NGX_PCRE)
+    ngx_int_t                  n;
+    ngx_uint_t                 i;
+    ngx_str_t                  name;
+    ngx_http_server_name_t    *sn;
+#endif
+
+    cscf = ngx_hash_find_combined(&r->virtual_names->names, hash, host, len);
 
     if (cscf) {
         goto found;
     }
 
+#if (NGX_PCRE)
+
+    if (r->virtual_names->nregex) {
+
+        name.len = len;
+        name.data = host;
+
+        sn = r->virtual_names->regex;
+
+        for (i = 0; i < r->virtual_names->nregex; i++) {
+
+            n = ngx_regex_exec(sn[i].regex, &name, NULL, 0);
+
+            if (n == NGX_REGEX_NO_MATCHED) {
+                continue;
+            }
+
+            if (n < 0) {
+                ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                              ngx_regex_exec_n
+                              " failed: %d on \"%V\" using \"%V\"",
+                              n, &name, &sn[i].name);
+                return;
+            }
+
+            /* match */
+
+            cscf = sn[i].core_srv_conf;
+
+            goto found;
+        }
+    }
+
+#endif
+
     cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
 
     if (cscf->wildcard) {
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -275,7 +275,15 @@ typedef struct {
 } ngx_http_connection_t;
 
 
-typedef ngx_hash_combined_t  ngx_http_virtual_names_t;
+typedef struct ngx_http_server_name_s  ngx_http_server_name_t;
+
+
+typedef struct {
+     ngx_hash_combined_t              names;
+
+     ngx_uint_t                       nregex;
+     ngx_http_server_name_t          *regex;
+} ngx_http_virtual_names_t;
 
 
 typedef void (*ngx_http_cleanup_pt)(void *data);