3 * Copyright (C) Igor Sysoev
7 #include <ngx_config.h>
12 #define NGX_HTTP_REFERER_NO_URI_PART ((void *) 4)
16 #define ngx_regex_t void
22 ngx_hash_combined_t hash
;
28 ngx_flag_t no_referer
;
29 ngx_flag_t blocked_referer
;
31 ngx_hash_keys_arrays_t
*keys
;
33 ngx_uint_t referer_hash_max_size
;
34 ngx_uint_t referer_hash_bucket_size
;
35 } ngx_http_referer_conf_t
;
38 static void * ngx_http_referer_create_conf(ngx_conf_t
*cf
);
39 static char * ngx_http_referer_merge_conf(ngx_conf_t
*cf
, void *parent
,
41 static char *ngx_http_valid_referers(ngx_conf_t
*cf
, ngx_command_t
*cmd
,
43 static char *ngx_http_add_referer(ngx_conf_t
*cf
, ngx_hash_keys_arrays_t
*keys
,
44 ngx_str_t
*value
, ngx_str_t
*uri
);
45 static char *ngx_http_add_regex_referer(ngx_conf_t
*cf
,
46 ngx_http_referer_conf_t
*rlcf
, ngx_str_t
*name
, ngx_regex_t
*regex
);
47 static int ngx_libc_cdecl
ngx_http_cmp_referer_wildcards(const void *one
,
51 static ngx_command_t ngx_http_referer_commands
[] = {
53 { ngx_string("valid_referers"),
54 NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_1MORE
,
55 ngx_http_valid_referers
,
56 NGX_HTTP_LOC_CONF_OFFSET
,
60 { ngx_string("referer_hash_max_size"),
61 NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_TAKE1
,
62 ngx_conf_set_num_slot
,
63 NGX_HTTP_LOC_CONF_OFFSET
,
64 offsetof(ngx_http_referer_conf_t
, referer_hash_max_size
),
67 { ngx_string("referer_hash_bucket_size"),
68 NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_TAKE1
,
69 ngx_conf_set_num_slot
,
70 NGX_HTTP_LOC_CONF_OFFSET
,
71 offsetof(ngx_http_referer_conf_t
, referer_hash_bucket_size
),
78 static ngx_http_module_t ngx_http_referer_module_ctx
= {
79 NULL
, /* preconfiguration */
80 NULL
, /* postconfiguration */
82 NULL
, /* create main configuration */
83 NULL
, /* init main configuration */
85 NULL
, /* create server configuration */
86 NULL
, /* merge server configuration */
88 ngx_http_referer_create_conf
, /* create location configuration */
89 ngx_http_referer_merge_conf
/* merge location configuration */
93 ngx_module_t ngx_http_referer_module
= {
95 &ngx_http_referer_module_ctx
, /* module context */
96 ngx_http_referer_commands
, /* module directives */
97 NGX_HTTP_MODULE
, /* module type */
98 NULL
, /* init master */
99 NULL
, /* init module */
100 NULL
, /* init process */
101 NULL
, /* init thread */
102 NULL
, /* exit thread */
103 NULL
, /* exit process */
104 NULL
, /* exit master */
105 NGX_MODULE_V1_PADDING
110 ngx_http_referer_variable(ngx_http_request_t
*r
, ngx_http_variable_value_t
*v
,
113 u_char
*p
, *ref
, *last
;
117 ngx_http_referer_conf_t
*rlcf
;
120 rlcf
= ngx_http_get_module_loc_conf(r
, ngx_http_referer_module
);
122 if (rlcf
->hash
.hash
.buckets
== NULL
123 && rlcf
->hash
.wc_head
== NULL
124 && rlcf
->hash
.wc_tail
== NULL
126 && rlcf
->regex
== NULL
133 if (r
->headers_in
.referer
== NULL
) {
134 if (rlcf
->no_referer
) {
141 len
= r
->headers_in
.referer
->value
.len
;
142 ref
= r
->headers_in
.referer
->value
.data
;
144 if (len
>= sizeof("http://i.ru") - 1) {
147 if (ngx_strncasecmp(ref
, (u_char
*) "http://", 7) == 0) {
151 } else if (ngx_strncasecmp(ref
, (u_char
*) "https://", 8) == 0) {
157 if (rlcf
->blocked_referer
) {
168 for (p
= ref
; p
< last
; p
++) {
169 if (*p
== '/' || *p
== ':') {
173 buf
[i
] = ngx_tolower(*p
);
174 key
= ngx_hash(key
, buf
[i
++]);
181 uri
= ngx_hash_find_combined(&rlcf
->hash
, key
, buf
, p
- ref
);
193 referer
.len
= len
- 7;
196 rc
= ngx_regex_exec_array(rlcf
->regex
, &referer
, r
->connection
->log
);
202 if (rc
== NGX_ERROR
) {
213 *v
= ngx_http_variable_true_value
;
219 for ( /* void */ ; p
< last
; p
++) {
227 if (uri
== NGX_HTTP_REFERER_NO_URI_PART
) {
231 if (len
< uri
->len
|| ngx_strncmp(uri
->data
, p
, uri
->len
) != 0) {
237 *v
= ngx_http_variable_null_value
;
244 ngx_http_referer_create_conf(ngx_conf_t
*cf
)
246 ngx_http_referer_conf_t
*conf
;
248 conf
= ngx_pcalloc(cf
->pool
, sizeof(ngx_http_referer_conf_t
));
254 conf
->regex
= NGX_CONF_UNSET_PTR
;
257 conf
->no_referer
= NGX_CONF_UNSET
;
258 conf
->blocked_referer
= NGX_CONF_UNSET
;
259 conf
->referer_hash_max_size
= NGX_CONF_UNSET_UINT
;
260 conf
->referer_hash_bucket_size
= NGX_CONF_UNSET_UINT
;
267 ngx_http_referer_merge_conf(ngx_conf_t
*cf
, void *parent
, void *child
)
269 ngx_http_referer_conf_t
*prev
= parent
;
270 ngx_http_referer_conf_t
*conf
= child
;
272 ngx_hash_init_t hash
;
274 if (conf
->keys
== NULL
) {
275 conf
->hash
= prev
->hash
;
278 ngx_conf_merge_ptr_value(conf
->regex
, prev
->regex
, NULL
);
280 ngx_conf_merge_value(conf
->no_referer
, prev
->no_referer
, 0);
281 ngx_conf_merge_value(conf
->blocked_referer
, prev
->blocked_referer
, 0);
282 ngx_conf_merge_uint_value(conf
->referer_hash_max_size
,
283 prev
->referer_hash_max_size
, 2048);
284 ngx_conf_merge_uint_value(conf
->referer_hash_bucket_size
,
285 prev
->referer_hash_bucket_size
, 64);
290 if ((conf
->no_referer
== 1 || conf
->blocked_referer
== 1)
291 && conf
->keys
->keys
.nelts
== 0
292 && conf
->keys
->dns_wc_head
.nelts
== 0
293 && conf
->keys
->dns_wc_tail
.nelts
== 0)
295 ngx_log_error(NGX_LOG_EMERG
, cf
->log
, 0,
296 "the \"none\" or \"blocked\" referers are specified "
297 "in the \"valid_referers\" directive "
298 "without any valid referer");
299 return NGX_CONF_ERROR
;
302 ngx_conf_merge_uint_value(conf
->referer_hash_max_size
,
303 prev
->referer_hash_max_size
, 2048);
304 ngx_conf_merge_uint_value(conf
->referer_hash_bucket_size
,
305 prev
->referer_hash_bucket_size
, 64);
306 conf
->referer_hash_bucket_size
= ngx_align(conf
->referer_hash_bucket_size
,
309 hash
.key
= ngx_hash_key_lc
;
310 hash
.max_size
= conf
->referer_hash_max_size
;
311 hash
.bucket_size
= conf
->referer_hash_bucket_size
;
312 hash
.name
= "referer_hash";
313 hash
.pool
= cf
->pool
;
315 if (conf
->keys
->keys
.nelts
) {
316 hash
.hash
= &conf
->hash
.hash
;
317 hash
.temp_pool
= NULL
;
319 if (ngx_hash_init(&hash
, conf
->keys
->keys
.elts
, conf
->keys
->keys
.nelts
)
322 return NGX_CONF_ERROR
;
326 if (conf
->keys
->dns_wc_head
.nelts
) {
328 ngx_qsort(conf
->keys
->dns_wc_head
.elts
,
329 (size_t) conf
->keys
->dns_wc_head
.nelts
,
330 sizeof(ngx_hash_key_t
),
331 ngx_http_cmp_referer_wildcards
);
334 hash
.temp_pool
= cf
->temp_pool
;
336 if (ngx_hash_wildcard_init(&hash
, conf
->keys
->dns_wc_head
.elts
,
337 conf
->keys
->dns_wc_head
.nelts
)
340 return NGX_CONF_ERROR
;
343 conf
->hash
.wc_head
= (ngx_hash_wildcard_t
*) hash
.hash
;
346 if (conf
->keys
->dns_wc_tail
.nelts
) {
348 ngx_qsort(conf
->keys
->dns_wc_tail
.elts
,
349 (size_t) conf
->keys
->dns_wc_tail
.nelts
,
350 sizeof(ngx_hash_key_t
),
351 ngx_http_cmp_referer_wildcards
);
354 hash
.temp_pool
= cf
->temp_pool
;
356 if (ngx_hash_wildcard_init(&hash
, conf
->keys
->dns_wc_tail
.elts
,
357 conf
->keys
->dns_wc_tail
.nelts
)
360 return NGX_CONF_ERROR
;
363 conf
->hash
.wc_tail
= (ngx_hash_wildcard_t
*) hash
.hash
;
367 ngx_conf_merge_ptr_value(conf
->regex
, prev
->regex
, NULL
);
370 if (conf
->no_referer
== NGX_CONF_UNSET
) {
371 conf
->no_referer
= 0;
374 if (conf
->blocked_referer
== NGX_CONF_UNSET
) {
375 conf
->blocked_referer
= 0;
385 ngx_http_valid_referers(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
)
387 ngx_http_referer_conf_t
*rlcf
= conf
;
390 ngx_str_t
*value
, uri
, name
;
392 ngx_http_variable_t
*var
;
393 ngx_http_server_name_t
*sn
;
394 ngx_http_core_srv_conf_t
*cscf
;
396 ngx_str_set(&name
, "invalid_referer");
398 var
= ngx_http_add_variable(cf
, &name
,
399 NGX_HTTP_VAR_CHANGEABLE
|NGX_HTTP_VAR_NOHASH
);
401 return NGX_CONF_ERROR
;
404 var
->get_handler
= ngx_http_referer_variable
;
406 if (rlcf
->keys
== NULL
) {
407 rlcf
->keys
= ngx_pcalloc(cf
->temp_pool
, sizeof(ngx_hash_keys_arrays_t
));
408 if (rlcf
->keys
== NULL
) {
409 return NGX_CONF_ERROR
;
412 rlcf
->keys
->pool
= cf
->pool
;
413 rlcf
->keys
->temp_pool
= cf
->pool
;
415 if (ngx_hash_keys_array_init(rlcf
->keys
, NGX_HASH_SMALL
) != NGX_OK
) {
416 return NGX_CONF_ERROR
;
420 value
= cf
->args
->elts
;
422 for (i
= 1; i
< cf
->args
->nelts
; i
++) {
423 if (value
[i
].len
== 0) {
424 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
425 "invalid referer \"%V\"", &value
[i
]);
426 return NGX_CONF_ERROR
;
429 if (ngx_strcmp(value
[i
].data
, "none") == 0) {
430 rlcf
->no_referer
= 1;
434 if (ngx_strcmp(value
[i
].data
, "blocked") == 0) {
435 rlcf
->blocked_referer
= 1;
441 if (ngx_strcmp(value
[i
].data
, "server_names") == 0) {
443 cscf
= ngx_http_conf_get_module_srv_conf(cf
, ngx_http_core_module
);
445 sn
= cscf
->server_names
.elts
;
446 for (n
= 0; n
< cscf
->server_names
.nelts
; n
++) {
451 if (ngx_http_add_regex_referer(cf
, rlcf
, &sn
[n
].name
,
455 return NGX_CONF_ERROR
;
462 if (ngx_http_add_referer(cf
, rlcf
->keys
, &sn
[n
].name
, &uri
)
465 return NGX_CONF_ERROR
;
472 if (value
[i
].data
[0] == '~') {
473 if (ngx_http_add_regex_referer(cf
, rlcf
, &value
[i
], NULL
) != NGX_OK
)
475 return NGX_CONF_ERROR
;
481 p
= (u_char
*) ngx_strchr(value
[i
].data
, '/');
484 uri
.len
= (value
[i
].data
+ value
[i
].len
) - p
;
486 value
[i
].len
= p
- value
[i
].data
;
489 if (ngx_http_add_referer(cf
, rlcf
->keys
, &value
[i
], &uri
) != NGX_OK
) {
490 return NGX_CONF_ERROR
;
499 ngx_http_add_referer(ngx_conf_t
*cf
, ngx_hash_keys_arrays_t
*keys
,
500 ngx_str_t
*value
, ngx_str_t
*uri
)
506 u
= NGX_HTTP_REFERER_NO_URI_PART
;
509 u
= ngx_palloc(cf
->pool
, sizeof(ngx_str_t
));
511 return NGX_CONF_ERROR
;
517 rc
= ngx_hash_add_key(keys
, value
, u
, NGX_HASH_WILDCARD_KEY
);
523 if (rc
== NGX_DECLINED
) {
524 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
525 "invalid hostname or wildcard \"%V\"", value
);
528 if (rc
== NGX_BUSY
) {
529 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
530 "conflicting parameter \"%V\"", value
);
533 return NGX_CONF_ERROR
;
538 ngx_http_add_regex_referer(ngx_conf_t
*cf
, ngx_http_referer_conf_t
*rlcf
,
539 ngx_str_t
*name
, ngx_regex_t
*regex
)
543 ngx_regex_compile_t rc
;
544 u_char errstr
[NGX_MAX_CONF_ERRSTR
];
546 if (name
->len
== 1) {
547 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0, "empty regex in \"%V\"", name
);
548 return NGX_CONF_ERROR
;
551 if (rlcf
->regex
== NGX_CONF_UNSET_PTR
) {
552 rlcf
->regex
= ngx_array_create(cf
->pool
, 2, sizeof(ngx_regex_elt_t
));
553 if (rlcf
->regex
== NULL
) {
554 return NGX_CONF_ERROR
;
558 re
= ngx_array_push(rlcf
->regex
);
560 return NGX_CONF_ERROR
;
565 re
->name
= name
->data
;
573 ngx_memzero(&rc
, sizeof(ngx_regex_compile_t
));
577 rc
.options
= NGX_REGEX_CASELESS
;
578 rc
.err
.len
= NGX_MAX_CONF_ERRSTR
;
579 rc
.err
.data
= errstr
;
581 if (ngx_regex_compile(&rc
) != NGX_OK
) {
582 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0, "%V", &rc
.err
);
583 return NGX_CONF_ERROR
;
586 re
->regex
= rc
.regex
;
587 re
->name
= name
->data
;
593 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
594 "the using of the regex \"%V\" requires PCRE library",
597 return NGX_CONF_ERROR
;
603 static int ngx_libc_cdecl
604 ngx_http_cmp_referer_wildcards(const void *one
, const void *two
)
606 ngx_hash_key_t
*first
, *second
;
608 first
= (ngx_hash_key_t
*) one
;
609 second
= (ngx_hash_key_t
*) two
;
611 return ngx_dns_strcmp(first
->key
.data
, second
->key
.data
);