nginx 0.3.37
[nginx-catap.git] / src / http / modules / ngx_http_referer_module.c
blobeb01857aba958711ceed570adcbaf8734081262f
2 /*
3 * Copyright (C) Igor Sysoev
4 */
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_http.h>
12 #define NGX_HTTP_REFERER_NO_URI_PART ((void *) 4)
14 typedef struct {
15 ngx_hash_t hash;
16 ngx_hash_wildcard_t *dns_wildcards;
18 ngx_flag_t no_referer;
19 ngx_flag_t blocked_referer;
21 ngx_hash_keys_arrays_t *keys;
22 } ngx_http_referer_conf_t;
25 static void * ngx_http_referer_create_conf(ngx_conf_t *cf);
26 static char * ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent,
27 void *child);
28 static char *ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd,
29 void *conf);
30 static char *ngx_http_add_referer(ngx_conf_t *cf, ngx_hash_keys_arrays_t *keys,
31 ngx_str_t *value, ngx_str_t *uri);
32 static int ngx_libc_cdecl ngx_http_cmp_referer_wildcards(const void *one,
33 const void *two);
36 static ngx_command_t ngx_http_referer_commands[] = {
38 { ngx_string("valid_referers"),
39 NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
40 ngx_http_valid_referers,
41 NGX_HTTP_LOC_CONF_OFFSET,
43 NULL },
45 ngx_null_command
49 static ngx_http_module_t ngx_http_referer_module_ctx = {
50 NULL, /* preconfiguration */
51 NULL, /* postconfiguration */
53 NULL, /* create main configuration */
54 NULL, /* init main configuration */
56 NULL, /* create server configuration */
57 NULL, /* merge server configuration */
59 ngx_http_referer_create_conf, /* create location configuration */
60 ngx_http_referer_merge_conf /* merge location configuration */
64 ngx_module_t ngx_http_referer_module = {
65 NGX_MODULE_V1,
66 &ngx_http_referer_module_ctx, /* module context */
67 ngx_http_referer_commands, /* module directives */
68 NGX_HTTP_MODULE, /* module type */
69 NULL, /* init master */
70 NULL, /* init module */
71 NULL, /* init process */
72 NULL, /* init thread */
73 NULL, /* exit thread */
74 NULL, /* exit process */
75 NULL, /* exit master */
76 NGX_MODULE_V1_PADDING
80 static ngx_int_t
81 ngx_http_referer_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
82 uintptr_t data)
84 u_char *p, *ref, *last;
85 size_t len;
86 ngx_str_t *uri;
87 ngx_uint_t i, key;
88 ngx_http_referer_conf_t *rlcf;
89 u_char buf[256];
91 rlcf = ngx_http_get_module_loc_conf(r, ngx_http_referer_module);
93 if (rlcf->hash.buckets == NULL && rlcf->dns_wildcards == NULL) {
94 goto valid;
97 if (r->headers_in.referer == NULL) {
98 if (rlcf->no_referer) {
99 goto valid;
102 goto invalid;
105 len = r->headers_in.referer->value.len;
106 ref = r->headers_in.referer->value.data;
108 if (len < sizeof("http://i.ru") - 1
109 || (ngx_strncasecmp(ref, "http://", 7) != 0))
111 if (rlcf->blocked_referer) {
112 goto valid;
115 goto invalid;
118 last = ref + len;
119 ref += 7;
120 i = 0;
121 key = 0;
123 for (p = ref; p < last; p++) {
124 if (*p == '/' || *p == ':') {
125 break;
128 buf[i] = ngx_tolower(*p);
129 key = ngx_hash(key, buf[i++]);
131 if (i == 256) {
132 goto invalid;
136 len = p - ref;
138 if (rlcf->hash.buckets) {
139 uri = ngx_hash_find(&rlcf->hash, key, buf, len);
140 if (uri) {
141 goto uri;
145 if (rlcf->dns_wildcards) {
146 uri = ngx_hash_find_wildcard(rlcf->dns_wildcards, buf, len);
147 if (uri) {
148 goto uri;
152 invalid:
154 *v = ngx_http_variable_true_value;
156 return NGX_OK;
158 uri:
160 for ( /* void */ ; p < last; p++) {
161 if (*p == '/') {
162 break;
166 len = last - p;
168 if (uri == NGX_HTTP_REFERER_NO_URI_PART) {
169 goto valid;
172 if (len < uri->len || ngx_strncmp(uri->data, p, uri->len) != 0) {
173 goto invalid;
176 valid:
178 *v = ngx_http_variable_null_value;
180 return NGX_OK;
184 static void *
185 ngx_http_referer_create_conf(ngx_conf_t *cf)
187 ngx_http_referer_conf_t *conf;
189 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_referer_conf_t));
190 if (conf == NULL) {
191 return NGX_CONF_ERROR;
194 conf->no_referer = NGX_CONF_UNSET;
195 conf->blocked_referer = NGX_CONF_UNSET;
197 return conf;
201 static char *
202 ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent, void *child)
204 ngx_http_referer_conf_t *prev = parent;
205 ngx_http_referer_conf_t *conf = child;
207 ngx_hash_init_t hash;
209 if (conf->keys == NULL) {
210 conf->hash = prev->hash;
211 conf->dns_wildcards = prev->dns_wildcards;
213 ngx_conf_merge_value(conf->no_referer, prev->no_referer, 0);
214 ngx_conf_merge_value(conf->blocked_referer, prev->blocked_referer, 0);
216 return NGX_CONF_OK;
219 if ((conf->no_referer == 1 || conf->blocked_referer == 1)
220 && conf->keys->keys.nelts == 0 && conf->keys->dns_wildcards.nelts == 0)
222 ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
223 "the \"none\" or \"blocked\" referers are specified "
224 "in the \"valid_referers\" directive "
225 "without any valid referer");
226 return NGX_CONF_ERROR;
229 hash.key = ngx_hash_key_lc;
230 hash.max_size = 2048; /* TODO: referer_hash_max_size; */
231 hash.bucket_size = 64; /* TODO: referer_hash_bucket_size; */
232 hash.name = "referers_hash";
233 hash.pool = cf->pool;
235 if (conf->keys->keys.nelts) {
236 hash.hash = &conf->hash;
237 hash.temp_pool = NULL;
239 if (ngx_hash_init(&hash, conf->keys->keys.elts, conf->keys->keys.nelts)
240 != NGX_OK)
242 return NGX_CONF_ERROR;
246 if (conf->keys->dns_wildcards.nelts) {
248 ngx_qsort(conf->keys->dns_wildcards.elts,
249 (size_t) conf->keys->dns_wildcards.nelts,
250 sizeof(ngx_hash_key_t),
251 ngx_http_cmp_referer_wildcards);
253 hash.hash = NULL;
254 hash.temp_pool = cf->temp_pool;
256 if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wildcards.elts,
257 conf->keys->dns_wildcards.nelts)
258 != NGX_OK)
260 return NGX_CONF_ERROR;
263 conf->dns_wildcards = (ngx_hash_wildcard_t *) hash.hash;
266 if (conf->no_referer == NGX_CONF_UNSET) {
267 conf->no_referer = 0;
270 if (conf->blocked_referer == NGX_CONF_UNSET) {
271 conf->blocked_referer = 0;
274 conf->keys = NULL;
276 return NGX_CONF_OK;
280 static char *
281 ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
283 ngx_http_referer_conf_t *rlcf = conf;
285 u_char *p;
286 ngx_str_t *value, uri, name;
287 ngx_uint_t i, n;
288 ngx_http_variable_t *var;
289 ngx_http_server_name_t *sn;
290 ngx_http_core_srv_conf_t *cscf;
292 name.len = sizeof("invalid_referer") - 1;
293 name.data = (u_char *) "invalid_referer";
295 var = ngx_http_add_variable(cf, &name,
296 NGX_HTTP_VAR_CHANGABLE|NGX_HTTP_VAR_NOHASH);
297 if (var == NULL) {
298 return NGX_CONF_ERROR;
301 var->handler = ngx_http_referer_variable;
303 if (rlcf->keys == NULL) {
304 rlcf->keys = ngx_pcalloc(cf->temp_pool, sizeof(ngx_hash_keys_arrays_t));
305 if (rlcf->keys == NULL) {
306 return NGX_CONF_ERROR;
309 rlcf->keys->pool = cf->pool;
310 rlcf->keys->temp_pool = cf->pool;
312 if (ngx_hash_keys_array_init(rlcf->keys, NGX_HASH_SMALL) != NGX_OK) {
313 return NGX_CONF_ERROR;
317 value = cf->args->elts;
319 for (i = 1; i < cf->args->nelts; i++) {
320 if (value[i].len == 0) {
321 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
322 "invalid referer \"%V\"", &value[i]);
323 return NGX_CONF_ERROR;
326 if (ngx_strcmp(value[i].data, "none") == 0) {
327 rlcf->no_referer = 1;
328 continue;
331 if (ngx_strcmp(value[i].data, "blocked") == 0) {
332 rlcf->blocked_referer = 1;
333 continue;
336 uri.len = 0;
337 uri.data = NULL;
339 if (ngx_strcmp(value[i].data, "server_names") == 0) {
341 cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module);
343 sn = cscf->server_names.elts;
344 for (n = 0; n < cscf->server_names.nelts; n++) {
345 if (ngx_http_add_referer(cf, rlcf->keys, &sn[n].name, &uri)
346 != NGX_OK)
348 return NGX_CONF_ERROR;
352 continue;
355 p = (u_char *) ngx_strstr(value[i].data, "/");
357 if (p) {
358 uri.len = (value[i].data + value[i].len) - p;
359 uri.data = p;
360 value[i].len = p - value[i].data;
363 if (ngx_http_add_referer(cf, rlcf->keys, &value[i], &uri) != NGX_OK) {
364 return NGX_CONF_ERROR;
368 return NGX_CONF_OK;
372 static char *
373 ngx_http_add_referer(ngx_conf_t *cf, ngx_hash_keys_arrays_t *keys,
374 ngx_str_t *value, ngx_str_t *uri)
376 u_char ch;
377 ngx_int_t rc;
378 ngx_str_t *u;
379 ngx_uint_t flags;
381 ch = value->data[0];
383 if ((ch == '*' && (value->len < 3 || value->data[1] != '.'))
384 || (ch == '.' && value->len < 2))
386 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
387 "invalid DNS wildcard \"%V\"", value);
389 return NGX_CONF_ERROR;
392 flags = (ch == '*' || ch == '.') ? NGX_HASH_WILDCARD_KEY : 0;
394 if (uri->len == 0) {
395 u = NGX_HTTP_REFERER_NO_URI_PART;
397 } else {
398 u = ngx_palloc(cf->pool, sizeof(ngx_str_t));
399 if (u == NULL) {
400 return NGX_CONF_ERROR;
403 *u = *uri;
406 rc = ngx_hash_add_key(keys, value, u, flags);
408 if (rc == NGX_OK) {
409 return NGX_CONF_OK;
412 if (rc == NGX_BUSY) {
413 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
414 "conflicting parameter \"%V\"", value);
417 return NGX_CONF_ERROR;
421 static int ngx_libc_cdecl
422 ngx_http_cmp_referer_wildcards(const void *one, const void *two)
424 ngx_hash_key_t *first, *second;
426 first = (ngx_hash_key_t *) one;
427 second = (ngx_hash_key_t *) two;
429 return ngx_strcmp(first->key.data, second->key.data);