Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ngx_http_referer_module.c
Go to the documentation of this file.
1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) Nginx, Inc.
5  */
6 
7 
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_http.h>
11 
12 
13 #define NGX_HTTP_REFERER_NO_URI_PART ((void *) 4)
14 
15 #if !(NGX_PCRE)
16 
17 #define ngx_regex_t void
18 
19 #endif
20 
21 
22 typedef struct {
24 
25 #if (NGX_PCRE)
26  ngx_array_t *regex;
27 #endif
28 
31 
33 
37 
38 
39 static void * ngx_http_referer_create_conf(ngx_conf_t *cf);
40 static char * ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent,
41  void *child);
42 static char *ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd,
43  void *conf);
44 static char *ngx_http_add_referer(ngx_conf_t *cf, ngx_hash_keys_arrays_t *keys,
45  ngx_str_t *value, ngx_str_t *uri);
46 static char *ngx_http_add_regex_referer(ngx_conf_t *cf,
47  ngx_http_referer_conf_t *rlcf, ngx_str_t *name, ngx_regex_t *regex);
48 static int ngx_libc_cdecl ngx_http_cmp_referer_wildcards(const void *one,
49  const void *two);
50 
51 
52 static ngx_command_t ngx_http_referer_commands[] = {
53 
54  { ngx_string("valid_referers"),
56  ngx_http_valid_referers,
58  0,
59  NULL },
60 
61  { ngx_string("referer_hash_max_size"),
65  offsetof(ngx_http_referer_conf_t, referer_hash_max_size),
66  NULL },
67 
68  { ngx_string("referer_hash_bucket_size"),
72  offsetof(ngx_http_referer_conf_t, referer_hash_bucket_size),
73  NULL },
74 
76 };
77 
78 
79 static ngx_http_module_t ngx_http_referer_module_ctx = {
80  NULL, /* preconfiguration */
81  NULL, /* postconfiguration */
82 
83  NULL, /* create main configuration */
84  NULL, /* init main configuration */
85 
86  NULL, /* create server configuration */
87  NULL, /* merge server configuration */
88 
89  ngx_http_referer_create_conf, /* create location configuration */
90  ngx_http_referer_merge_conf /* merge location configuration */
91 };
92 
93 
96  &ngx_http_referer_module_ctx, /* module context */
97  ngx_http_referer_commands, /* module directives */
98  NGX_HTTP_MODULE, /* module type */
99  NULL, /* init master */
100  NULL, /* init module */
101  NULL, /* init process */
102  NULL, /* init thread */
103  NULL, /* exit thread */
104  NULL, /* exit process */
105  NULL, /* exit master */
107 };
108 
109 
110 static ngx_int_t
111 ngx_http_referer_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
112  uintptr_t data)
113 {
114  u_char *p, *ref, *last;
115  size_t len;
116  ngx_str_t *uri;
117  ngx_uint_t i, key;
119  u_char buf[256];
120 
121  rlcf = ngx_http_get_module_loc_conf(r, ngx_http_referer_module);
122 
123  if (rlcf->hash.hash.buckets == NULL
124  && rlcf->hash.wc_head == NULL
125  && rlcf->hash.wc_tail == NULL
126 #if (NGX_PCRE)
127  && rlcf->regex == NULL
128 #endif
129  )
130  {
131  goto valid;
132  }
133 
134  if (r->headers_in.referer == NULL) {
135  if (rlcf->no_referer) {
136  goto valid;
137  }
138 
139  goto invalid;
140  }
141 
142  len = r->headers_in.referer->value.len;
143  ref = r->headers_in.referer->value.data;
144 
145  if (len >= sizeof("http://i.ru") - 1) {
146  last = ref + len;
147 
148  if (ngx_strncasecmp(ref, (u_char *) "http://", 7) == 0) {
149  ref += 7;
150  goto valid_scheme;
151 
152  } else if (ngx_strncasecmp(ref, (u_char *) "https://", 8) == 0) {
153  ref += 8;
154  goto valid_scheme;
155  }
156  }
157 
158  if (rlcf->blocked_referer) {
159  goto valid;
160  }
161 
162  goto invalid;
163 
164 valid_scheme:
165 
166  i = 0;
167  key = 0;
168 
169  for (p = ref; p < last; p++) {
170  if (*p == '/' || *p == ':') {
171  break;
172  }
173 
174  buf[i] = ngx_tolower(*p);
175  key = ngx_hash(key, buf[i++]);
176 
177  if (i == 256) {
178  goto invalid;
179  }
180  }
181 
182  uri = ngx_hash_find_combined(&rlcf->hash, key, buf, p - ref);
183 
184  if (uri) {
185  goto uri;
186  }
187 
188 #if (NGX_PCRE)
189 
190  if (rlcf->regex) {
191  ngx_int_t rc;
192  ngx_str_t referer;
193 
194  referer.len = len - 7;
195  referer.data = ref;
196 
197  rc = ngx_regex_exec_array(rlcf->regex, &referer, r->connection->log);
198 
199  if (rc == NGX_OK) {
200  goto valid;
201  }
202 
203  if (rc == NGX_ERROR) {
204  return rc;
205  }
206 
207  /* NGX_DECLINED */
208  }
209 
210 #endif
211 
212 invalid:
213 
215 
216  return NGX_OK;
217 
218 uri:
219 
220  for ( /* void */ ; p < last; p++) {
221  if (*p == '/') {
222  break;
223  }
224  }
225 
226  len = last - p;
227 
228  if (uri == NGX_HTTP_REFERER_NO_URI_PART) {
229  goto valid;
230  }
231 
232  if (len < uri->len || ngx_strncmp(uri->data, p, uri->len) != 0) {
233  goto invalid;
234  }
235 
236 valid:
237 
239 
240  return NGX_OK;
241 }
242 
243 
244 static void *
245 ngx_http_referer_create_conf(ngx_conf_t *cf)
246 {
248 
249  conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_referer_conf_t));
250  if (conf == NULL) {
251  return NULL;
252  }
253 
254 #if (NGX_PCRE)
255  conf->regex = NGX_CONF_UNSET_PTR;
256 #endif
257 
258  conf->no_referer = NGX_CONF_UNSET;
262 
263  return conf;
264 }
265 
266 
267 static char *
268 ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent, void *child)
269 {
270  ngx_http_referer_conf_t *prev = parent;
271  ngx_http_referer_conf_t *conf = child;
272 
273  ngx_hash_init_t hash;
274 
275  if (conf->keys == NULL) {
276  conf->hash = prev->hash;
277 
278 #if (NGX_PCRE)
279  ngx_conf_merge_ptr_value(conf->regex, prev->regex, NULL);
280 #endif
281  ngx_conf_merge_value(conf->no_referer, prev->no_referer, 0);
284  prev->referer_hash_max_size, 2048);
286  prev->referer_hash_bucket_size, 64);
287 
288  return NGX_CONF_OK;
289  }
290 
291  if ((conf->no_referer == 1 || conf->blocked_referer == 1)
292  && conf->keys->keys.nelts == 0
293  && conf->keys->dns_wc_head.nelts == 0
294  && conf->keys->dns_wc_tail.nelts == 0)
295  {
297  "the \"none\" or \"blocked\" referers are specified "
298  "in the \"valid_referers\" directive "
299  "without any valid referer");
300  return NGX_CONF_ERROR;
301  }
302 
304  prev->referer_hash_max_size, 2048);
306  prev->referer_hash_bucket_size, 64);
309 
310  hash.key = ngx_hash_key_lc;
311  hash.max_size = conf->referer_hash_max_size;
313  hash.name = "referer_hash";
314  hash.pool = cf->pool;
315 
316  if (conf->keys->keys.nelts) {
317  hash.hash = &conf->hash.hash;
318  hash.temp_pool = NULL;
319 
320  if (ngx_hash_init(&hash, conf->keys->keys.elts, conf->keys->keys.nelts)
321  != NGX_OK)
322  {
323  return NGX_CONF_ERROR;
324  }
325  }
326 
327  if (conf->keys->dns_wc_head.nelts) {
328 
330  (size_t) conf->keys->dns_wc_head.nelts,
331  sizeof(ngx_hash_key_t),
332  ngx_http_cmp_referer_wildcards);
333 
334  hash.hash = NULL;
335  hash.temp_pool = cf->temp_pool;
336 
337  if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wc_head.elts,
338  conf->keys->dns_wc_head.nelts)
339  != NGX_OK)
340  {
341  return NGX_CONF_ERROR;
342  }
343 
344  conf->hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;
345  }
346 
347  if (conf->keys->dns_wc_tail.nelts) {
348 
350  (size_t) conf->keys->dns_wc_tail.nelts,
351  sizeof(ngx_hash_key_t),
352  ngx_http_cmp_referer_wildcards);
353 
354  hash.hash = NULL;
355  hash.temp_pool = cf->temp_pool;
356 
357  if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wc_tail.elts,
358  conf->keys->dns_wc_tail.nelts)
359  != NGX_OK)
360  {
361  return NGX_CONF_ERROR;
362  }
363 
364  conf->hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;
365  }
366 
367 #if (NGX_PCRE)
368  ngx_conf_merge_ptr_value(conf->regex, prev->regex, NULL);
369 #endif
370 
371  if (conf->no_referer == NGX_CONF_UNSET) {
372  conf->no_referer = 0;
373  }
374 
375  if (conf->blocked_referer == NGX_CONF_UNSET) {
376  conf->blocked_referer = 0;
377  }
378 
379  conf->keys = NULL;
380 
381  return NGX_CONF_OK;
382 }
383 
384 
385 static char *
386 ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
387 {
388  ngx_http_referer_conf_t *rlcf = conf;
389 
390  u_char *p;
391  ngx_str_t *value, uri, name;
392  ngx_uint_t i, n;
393  ngx_http_variable_t *var;
396 
397  ngx_str_set(&name, "invalid_referer");
398 
399  var = ngx_http_add_variable(cf, &name,
401  if (var == NULL) {
402  return NGX_CONF_ERROR;
403  }
404 
405  var->get_handler = ngx_http_referer_variable;
406 
407  if (rlcf->keys == NULL) {
408  rlcf->keys = ngx_pcalloc(cf->temp_pool, sizeof(ngx_hash_keys_arrays_t));
409  if (rlcf->keys == NULL) {
410  return NGX_CONF_ERROR;
411  }
412 
413  rlcf->keys->pool = cf->pool;
414  rlcf->keys->temp_pool = cf->pool;
415 
417  return NGX_CONF_ERROR;
418  }
419  }
420 
421  value = cf->args->elts;
422 
423  for (i = 1; i < cf->args->nelts; i++) {
424  if (value[i].len == 0) {
426  "invalid referer \"%V\"", &value[i]);
427  return NGX_CONF_ERROR;
428  }
429 
430  if (ngx_strcmp(value[i].data, "none") == 0) {
431  rlcf->no_referer = 1;
432  continue;
433  }
434 
435  if (ngx_strcmp(value[i].data, "blocked") == 0) {
436  rlcf->blocked_referer = 1;
437  continue;
438  }
439 
440  ngx_str_null(&uri);
441 
442  if (ngx_strcmp(value[i].data, "server_names") == 0) {
443 
445 
446  sn = cscf->server_names.elts;
447  for (n = 0; n < cscf->server_names.nelts; n++) {
448 
449 #if (NGX_PCRE)
450  if (sn[n].regex) {
451 
452  if (ngx_http_add_regex_referer(cf, rlcf, &sn[n].name,
453  sn[n].regex->regex)
454  != NGX_OK)
455  {
456  return NGX_CONF_ERROR;
457  }
458 
459  continue;
460  }
461 #endif
462 
463  if (ngx_http_add_referer(cf, rlcf->keys, &sn[n].name, &uri)
464  != NGX_OK)
465  {
466  return NGX_CONF_ERROR;
467  }
468  }
469 
470  continue;
471  }
472 
473  if (value[i].data[0] == '~') {
474  if (ngx_http_add_regex_referer(cf, rlcf, &value[i], NULL) != NGX_OK)
475  {
476  return NGX_CONF_ERROR;
477  }
478 
479  continue;
480  }
481 
482  p = (u_char *) ngx_strchr(value[i].data, '/');
483 
484  if (p) {
485  uri.len = (value[i].data + value[i].len) - p;
486  uri.data = p;
487  value[i].len = p - value[i].data;
488  }
489 
490  if (ngx_http_add_referer(cf, rlcf->keys, &value[i], &uri) != NGX_OK) {
491  return NGX_CONF_ERROR;
492  }
493  }
494 
495  return NGX_CONF_OK;
496 }
497 
498 
499 static char *
500 ngx_http_add_referer(ngx_conf_t *cf, ngx_hash_keys_arrays_t *keys,
501  ngx_str_t *value, ngx_str_t *uri)
502 {
503  ngx_int_t rc;
504  ngx_str_t *u;
505 
506  if (uri->len == 0) {
508 
509  } else {
510  u = ngx_palloc(cf->pool, sizeof(ngx_str_t));
511  if (u == NULL) {
512  return NGX_CONF_ERROR;
513  }
514 
515  *u = *uri;
516  }
517 
518  rc = ngx_hash_add_key(keys, value, u, NGX_HASH_WILDCARD_KEY);
519 
520  if (rc == NGX_OK) {
521  return NGX_CONF_OK;
522  }
523 
524  if (rc == NGX_DECLINED) {
526  "invalid hostname or wildcard \"%V\"", value);
527  }
528 
529  if (rc == NGX_BUSY) {
531  "conflicting parameter \"%V\"", value);
532  }
533 
534  return NGX_CONF_ERROR;
535 }
536 
537 
538 static char *
539 ngx_http_add_regex_referer(ngx_conf_t *cf, ngx_http_referer_conf_t *rlcf,
540  ngx_str_t *name, ngx_regex_t *regex)
541 {
542 #if (NGX_PCRE)
543  ngx_regex_elt_t *re;
545  u_char errstr[NGX_MAX_CONF_ERRSTR];
546 
547  if (name->len == 1) {
548  ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty regex in \"%V\"", name);
549  return NGX_CONF_ERROR;
550  }
551 
552  if (rlcf->regex == NGX_CONF_UNSET_PTR) {
553  rlcf->regex = ngx_array_create(cf->pool, 2, sizeof(ngx_regex_elt_t));
554  if (rlcf->regex == NULL) {
555  return NGX_CONF_ERROR;
556  }
557  }
558 
559  re = ngx_array_push(rlcf->regex);
560  if (re == NULL) {
561  return NGX_CONF_ERROR;
562  }
563 
564  if (regex) {
565  re->regex = regex;
566  re->name = name->data;
567 
568  return NGX_CONF_OK;
569  }
570 
571  name->len--;
572  name->data++;
573 
574  ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
575 
576  rc.pattern = *name;
577  rc.pool = cf->pool;
580  rc.err.data = errstr;
581 
582  if (ngx_regex_compile(&rc) != NGX_OK) {
583  ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);
584  return NGX_CONF_ERROR;
585  }
586 
587  re->regex = rc.regex;
588  re->name = name->data;
589 
590  return NGX_CONF_OK;
591 
592 #else
593 
595  "the using of the regex \"%V\" requires PCRE library",
596  name);
597 
598  return NGX_CONF_ERROR;
599 
600 #endif
601 }
602 
603 
604 static int ngx_libc_cdecl
605 ngx_http_cmp_referer_wildcards(const void *one, const void *two)
606 {
607  ngx_hash_key_t *first, *second;
608 
609  first = (ngx_hash_key_t *) one;
610  second = (ngx_hash_key_t *) two;
611 
612  return ngx_dns_strcmp(first->key.data, second->key.data);
613 }