diff src/http/modules/ngx_http_referer_module.c @ 326:f70f2f565fe0 NGINX_0_5_33

nginx 0.5.33 *) Change: now by default the "echo" SSI command uses entity encoding. *) Feature: the "encoding" parameter in the "echo" SSI command. *) Change: mail proxy was split on three modules: pop3, imap and smtp. *) Feature: the --without-mail_pop3_module, --without-mail_imap_module, and --without-mail_smtp_module configuration parameters. *) Feature: the "smtp_greeting_delay" and "smtp_client_buffer" directives of the ngx_mail_smtp_module. *) Feature: the "server_name" and "valid_referers" directives support regular expressions. *) Feature: the "server_name", "map", and "valid_referers" directives support the "www.example.*" wildcards. *) Bugfix: sub_filter did not work with empty substitution. *) Bugfix: in sub_filter parsing. *) Bugfix: a worker process may got caught in an endless loop, if the memcached was used. *) Bugfix: nginx supported low case only "close" and "keep-alive" values in the "Connection" request header line; bug appeared in 0.5.32. *) Bugfix: nginx could not start on Solaris if the shared PCRE library located in non-standard place was used.
author Igor Sysoev <http://sysoev.ru>
date Wed, 07 Nov 2007 00:00:00 +0300
parents 95d92ec39071
children 26ff8d6b618d
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,26 @@
 
 #define NGX_HTTP_REFERER_NO_URI_PART  ((void *) 4)
 
+#if (NGX_PCRE)
+
 typedef struct {
-    ngx_hash_t               hash;
-    ngx_hash_wildcard_t     *dns_wildcards;
+    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;
@@ -29,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);
 
@@ -81,16 +100,28 @@ 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.buckets == NULL && rlcf->dns_wildcards == NULL) {
+    if (rlcf->hash.hash.buckets == NULL
+        && rlcf->hash.wc_head == NULL
+        && rlcf->hash.wc_tail == NULL
+#if (NGX_PCRE)
+        && rlcf->regex == NULL
+#endif
+       )
+    {
         goto valid;
     }
 
@@ -133,21 +164,43 @@ ngx_http_referer_variable(ngx_http_reque
         }
     }
 
-    len = p - ref;
+    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;
 
-    if (rlcf->hash.buckets) {
-        uri = ngx_hash_find(&rlcf->hash, key, buf, len);
-        if (uri) {
-            goto uri;
+        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;
         }
     }
 
-    if (rlcf->dns_wildcards) {
-        uri = ngx_hash_find_wildcard(rlcf->dns_wildcards, buf, len);
-        if (uri) {
-            goto uri;
-        }
-    }
+#endif
 
 invalid:
 
@@ -208,7 +261,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 +269,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 +287,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 +297,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) {
@@ -342,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)
                 {
@@ -352,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) {
@@ -373,23 +471,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 +486,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);
@@ -418,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)
 {