~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Nginx/http/modules/ngx_http_referer_module.c

Version: ~ [ nginx-1.4.1 ] ~ [ nginx-1.5.0 ] ~

  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 {
 23     ngx_hash_combined_t      hash;
 24 
 25 #if (NGX_PCRE)
 26     ngx_array_t             *regex;
 27 #endif
 28 
 29     ngx_flag_t               no_referer;
 30     ngx_flag_t               blocked_referer;
 31 
 32     ngx_hash_keys_arrays_t  *keys;
 33 
 34     ngx_uint_t               referer_hash_max_size;
 35     ngx_uint_t               referer_hash_bucket_size;
 36 } ngx_http_referer_conf_t;
 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"),
 55       NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
 56       ngx_http_valid_referers,
 57       NGX_HTTP_LOC_CONF_OFFSET,
 58       0,
 59       NULL },
 60 
 61     { ngx_string("referer_hash_max_size"),
 62       NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
 63       ngx_conf_set_num_slot,
 64       NGX_HTTP_LOC_CONF_OFFSET,
 65       offsetof(ngx_http_referer_conf_t, referer_hash_max_size),
 66       NULL },
 67 
 68     { ngx_string("referer_hash_bucket_size"),
 69       NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
 70       ngx_conf_set_num_slot,
 71       NGX_HTTP_LOC_CONF_OFFSET,
 72       offsetof(ngx_http_referer_conf_t, referer_hash_bucket_size),
 73       NULL },
 74 
 75       ngx_null_command
 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 
 94 ngx_module_t  ngx_http_referer_module = {
 95     NGX_MODULE_V1,
 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 */
106     NGX_MODULE_V1_PADDING
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;
118     ngx_http_referer_conf_t   *rlcf;
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 
214     *v = ngx_http_variable_true_value;
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 
238     *v = ngx_http_variable_null_value;
239 
240     return NGX_OK;
241 }
242 
243 
244 static void *
245 ngx_http_referer_create_conf(ngx_conf_t *cf)
246 {
247     ngx_http_referer_conf_t  *conf;
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;
259     conf->blocked_referer = NGX_CONF_UNSET;
260     conf->referer_hash_max_size = NGX_CONF_UNSET_UINT;
261     conf->referer_hash_bucket_size = NGX_CONF_UNSET_UINT;
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);
282         ngx_conf_merge_value(conf->blocked_referer, prev->blocked_referer, 0);
283         ngx_conf_merge_uint_value(conf->referer_hash_max_size,
284                                   prev->referer_hash_max_size, 2048);
285         ngx_conf_merge_uint_value(conf->referer_hash_bucket_size,
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     {
296         ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
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 
303     ngx_conf_merge_uint_value(conf->referer_hash_max_size,
304                               prev->referer_hash_max_size, 2048);
305     ngx_conf_merge_uint_value(conf->referer_hash_bucket_size,
306                               prev->referer_hash_bucket_size, 64);
307     conf->referer_hash_bucket_size = ngx_align(conf->referer_hash_bucket_size,
308                                                ngx_cacheline_size);
309 
310     hash.key = ngx_hash_key_lc;
311     hash.max_size = conf->referer_hash_max_size;
312     hash.bucket_size = conf->referer_hash_bucket_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 
329         ngx_qsort(conf->keys->dns_wc_head.elts,
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 
349         ngx_qsort(conf->keys->dns_wc_tail.elts,
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;
394     ngx_http_server_name_t    *sn;
395     ngx_http_core_srv_conf_t  *cscf;
396 
397     ngx_str_set(&name, "invalid_referer");
398 
399     var = ngx_http_add_variable(cf, &name,
400                                 NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOHASH);
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 
416         if (ngx_hash_keys_array_init(rlcf->keys, NGX_HASH_SMALL) != NGX_OK) {
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) {
425             ngx_conf_log_error(NGX_LOG_EMERG, cf, 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 
444             cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module);
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) {
507         u = NGX_HTTP_REFERER_NO_URI_PART;
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) {
525         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
526                            "invalid hostname or wildcard \"%V\"", value);
527     }
528 
529     if (rc == NGX_BUSY) {
530         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
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;
544     ngx_regex_compile_t   rc;
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;
578     rc.options = NGX_REGEX_CASELESS;
579     rc.err.len = NGX_MAX_CONF_ERRSTR;
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 
594     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
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 }
614 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.