3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
8 #include <ngx_config.h>
13 #define NGX_HTTP_REFERER_NO_URI_PART ((void *) 4)
17 #define ngx_regex_t void
23 ngx_hash_combined_t hash
;
29 ngx_flag_t no_referer
;
30 ngx_flag_t blocked_referer
;
32 ngx_hash_keys_arrays_t
*keys
;
34 ngx_uint_t referer_hash_max_size
;
35 ngx_uint_t referer_hash_bucket_size
;
36 } ngx_http_referer_conf_t
;
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
,
42 static char *ngx_http_valid_referers(ngx_conf_t
*cf
, ngx_command_t
*cmd
,
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
,
52 static ngx_command_t ngx_http_referer_commands
[] = {
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
,
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
),
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
),
79 static ngx_http_module_t ngx_http_referer_module_ctx
= {
80 NULL
, /* preconfiguration */
81 NULL
, /* postconfiguration */
83 NULL
, /* create main configuration */
84 NULL
, /* init main configuration */
86 NULL
, /* create server configuration */
87 NULL
, /* merge server configuration */
89 ngx_http_referer_create_conf
, /* create location configuration */
90 ngx_http_referer_merge_conf
/* merge location configuration */
94 ngx_module_t ngx_http_referer_module
= {
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
111 ngx_http_referer_variable(ngx_http_request_t
*r
, ngx_http_variable_value_t
*v
,
114 u_char
*p
, *ref
, *last
;
118 ngx_http_referer_conf_t
*rlcf
;
121 rlcf
= ngx_http_get_module_loc_conf(r
, ngx_http_referer_module
);
123 if (rlcf
->hash
.hash
.buckets
== NULL
124 && rlcf
->hash
.wc_head
== NULL
125 && rlcf
->hash
.wc_tail
== NULL
127 && rlcf
->regex
== NULL
134 if (r
->headers_in
.referer
== NULL
) {
135 if (rlcf
->no_referer
) {
142 len
= r
->headers_in
.referer
->value
.len
;
143 ref
= r
->headers_in
.referer
->value
.data
;
145 if (len
>= sizeof("http://i.ru") - 1) {
148 if (ngx_strncasecmp(ref
, (u_char
*) "http://", 7) == 0) {
152 } else if (ngx_strncasecmp(ref
, (u_char
*) "https://", 8) == 0) {
158 if (rlcf
->blocked_referer
) {
169 for (p
= ref
; p
< last
; p
++) {
170 if (*p
== '/' || *p
== ':') {
174 buf
[i
] = ngx_tolower(*p
);
175 key
= ngx_hash(key
, buf
[i
++]);
182 uri
= ngx_hash_find_combined(&rlcf
->hash
, key
, buf
, p
- ref
);
194 referer
.len
= len
- 7;
197 rc
= ngx_regex_exec_array(rlcf
->regex
, &referer
, r
->connection
->log
);
203 if (rc
== NGX_ERROR
) {
214 *v
= ngx_http_variable_true_value
;
220 for ( /* void */ ; p
< last
; p
++) {
228 if (uri
== NGX_HTTP_REFERER_NO_URI_PART
) {
232 if (len
< uri
->len
|| ngx_strncmp(uri
->data
, p
, uri
->len
) != 0) {
238 *v
= ngx_http_variable_null_value
;
245 ngx_http_referer_create_conf(ngx_conf_t
*cf
)
247 ngx_http_referer_conf_t
*conf
;
249 conf
= ngx_pcalloc(cf
->pool
, sizeof(ngx_http_referer_conf_t
));
255 conf
->regex
= NGX_CONF_UNSET_PTR
;
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
;
268 ngx_http_referer_merge_conf(ngx_conf_t
*cf
, void *parent
, void *child
)
270 ngx_http_referer_conf_t
*prev
= parent
;
271 ngx_http_referer_conf_t
*conf
= child
;
273 ngx_hash_init_t hash
;
275 if (conf
->keys
== NULL
) {
276 conf
->hash
= prev
->hash
;
279 ngx_conf_merge_ptr_value(conf
->regex
, prev
->regex
, NULL
);
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);
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)
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
;
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
,
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
;
316 if (conf
->keys
->keys
.nelts
) {
317 hash
.hash
= &conf
->hash
.hash
;
318 hash
.temp_pool
= NULL
;
320 if (ngx_hash_init(&hash
, conf
->keys
->keys
.elts
, conf
->keys
->keys
.nelts
)
323 return NGX_CONF_ERROR
;
327 if (conf
->keys
->dns_wc_head
.nelts
) {
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
);
335 hash
.temp_pool
= cf
->temp_pool
;
337 if (ngx_hash_wildcard_init(&hash
, conf
->keys
->dns_wc_head
.elts
,
338 conf
->keys
->dns_wc_head
.nelts
)
341 return NGX_CONF_ERROR
;
344 conf
->hash
.wc_head
= (ngx_hash_wildcard_t
*) hash
.hash
;
347 if (conf
->keys
->dns_wc_tail
.nelts
) {
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
);
355 hash
.temp_pool
= cf
->temp_pool
;
357 if (ngx_hash_wildcard_init(&hash
, conf
->keys
->dns_wc_tail
.elts
,
358 conf
->keys
->dns_wc_tail
.nelts
)
361 return NGX_CONF_ERROR
;
364 conf
->hash
.wc_tail
= (ngx_hash_wildcard_t
*) hash
.hash
;
368 ngx_conf_merge_ptr_value(conf
->regex
, prev
->regex
, NULL
);
371 if (conf
->no_referer
== NGX_CONF_UNSET
) {
372 conf
->no_referer
= 0;
375 if (conf
->blocked_referer
== NGX_CONF_UNSET
) {
376 conf
->blocked_referer
= 0;
386 ngx_http_valid_referers(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
)
388 ngx_http_referer_conf_t
*rlcf
= conf
;
391 ngx_str_t
*value
, uri
, name
;
393 ngx_http_variable_t
*var
;
394 ngx_http_server_name_t
*sn
;
395 ngx_http_core_srv_conf_t
*cscf
;
397 ngx_str_set(&name
, "invalid_referer");
399 var
= ngx_http_add_variable(cf
, &name
,
400 NGX_HTTP_VAR_CHANGEABLE
|NGX_HTTP_VAR_NOHASH
);
402 return NGX_CONF_ERROR
;
405 var
->get_handler
= ngx_http_referer_variable
;
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
;
413 rlcf
->keys
->pool
= cf
->pool
;
414 rlcf
->keys
->temp_pool
= cf
->pool
;
416 if (ngx_hash_keys_array_init(rlcf
->keys
, NGX_HASH_SMALL
) != NGX_OK
) {
417 return NGX_CONF_ERROR
;
421 value
= cf
->args
->elts
;
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
;
430 if (ngx_strcmp(value
[i
].data
, "none") == 0) {
431 rlcf
->no_referer
= 1;
435 if (ngx_strcmp(value
[i
].data
, "blocked") == 0) {
436 rlcf
->blocked_referer
= 1;
442 if (ngx_strcmp(value
[i
].data
, "server_names") == 0) {
444 cscf
= ngx_http_conf_get_module_srv_conf(cf
, ngx_http_core_module
);
446 sn
= cscf
->server_names
.elts
;
447 for (n
= 0; n
< cscf
->server_names
.nelts
; n
++) {
452 if (ngx_http_add_regex_referer(cf
, rlcf
, &sn
[n
].name
,
456 return NGX_CONF_ERROR
;
463 if (ngx_http_add_referer(cf
, rlcf
->keys
, &sn
[n
].name
, &uri
)
466 return NGX_CONF_ERROR
;
473 if (value
[i
].data
[0] == '~') {
474 if (ngx_http_add_regex_referer(cf
, rlcf
, &value
[i
], NULL
) != NGX_OK
)
476 return NGX_CONF_ERROR
;
482 p
= (u_char
*) ngx_strchr(value
[i
].data
, '/');
485 uri
.len
= (value
[i
].data
+ value
[i
].len
) - p
;
487 value
[i
].len
= p
- value
[i
].data
;
490 if (ngx_http_add_referer(cf
, rlcf
->keys
, &value
[i
], &uri
) != NGX_OK
) {
491 return NGX_CONF_ERROR
;
500 ngx_http_add_referer(ngx_conf_t
*cf
, ngx_hash_keys_arrays_t
*keys
,
501 ngx_str_t
*value
, ngx_str_t
*uri
)
507 u
= NGX_HTTP_REFERER_NO_URI_PART
;
510 u
= ngx_palloc(cf
->pool
, sizeof(ngx_str_t
));
512 return NGX_CONF_ERROR
;
518 rc
= ngx_hash_add_key(keys
, value
, u
, NGX_HASH_WILDCARD_KEY
);
524 if (rc
== NGX_DECLINED
) {
525 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
526 "invalid hostname or wildcard \"%V\"", value
);
529 if (rc
== NGX_BUSY
) {
530 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
531 "conflicting parameter \"%V\"", value
);
534 return NGX_CONF_ERROR
;
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
)
544 ngx_regex_compile_t rc
;
545 u_char errstr
[NGX_MAX_CONF_ERRSTR
];
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
;
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
;
559 re
= ngx_array_push(rlcf
->regex
);
561 return NGX_CONF_ERROR
;
566 re
->name
= name
->data
;
574 ngx_memzero(&rc
, sizeof(ngx_regex_compile_t
));
578 rc
.options
= NGX_REGEX_CASELESS
;
579 rc
.err
.len
= NGX_MAX_CONF_ERRSTR
;
580 rc
.err
.data
= errstr
;
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
;
587 re
->regex
= rc
.regex
;
588 re
->name
= name
->data
;
594 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
595 "the using of the regex \"%V\" requires PCRE library",
598 return NGX_CONF_ERROR
;
604 static int ngx_libc_cdecl
605 ngx_http_cmp_referer_wildcards(const void *one
, const void *two
)
607 ngx_hash_key_t
*first
, *second
;
609 first
= (ngx_hash_key_t
*) one
;
610 second
= (ngx_hash_key_t
*) two
;
612 return ngx_dns_strcmp(first
->key
.data
, second
->key
.data
);