3 * Copyright (C) Igor Sysoev
7 #include <ngx_config.h>
12 #define NGX_HTTP_REFERER_NO_URI_PART ((void *) 4)
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
,
28 static char *ngx_http_valid_referers(ngx_conf_t
*cf
, ngx_command_t
*cmd
,
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
,
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
,
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
= {
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 */
81 ngx_http_referer_variable(ngx_http_request_t
*r
, ngx_http_variable_value_t
*v
,
84 u_char
*p
, *ref
, *last
;
88 ngx_http_referer_conf_t
*rlcf
;
91 rlcf
= ngx_http_get_module_loc_conf(r
, ngx_http_referer_module
);
93 if (rlcf
->hash
.buckets
== NULL
&& rlcf
->dns_wildcards
== NULL
) {
97 if (r
->headers_in
.referer
== NULL
) {
98 if (rlcf
->no_referer
) {
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
) {
123 for (p
= ref
; p
< last
; p
++) {
124 if (*p
== '/' || *p
== ':') {
128 buf
[i
] = ngx_tolower(*p
);
129 key
= ngx_hash(key
, buf
[i
++]);
138 if (rlcf
->hash
.buckets
) {
139 uri
= ngx_hash_find(&rlcf
->hash
, key
, buf
, len
);
145 if (rlcf
->dns_wildcards
) {
146 uri
= ngx_hash_find_wildcard(rlcf
->dns_wildcards
, buf
, len
);
154 *v
= ngx_http_variable_true_value
;
160 for ( /* void */ ; p
< last
; p
++) {
168 if (uri
== NGX_HTTP_REFERER_NO_URI_PART
) {
172 if (len
< uri
->len
|| ngx_strncmp(uri
->data
, p
, uri
->len
) != 0) {
178 *v
= ngx_http_variable_null_value
;
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
));
191 return NGX_CONF_ERROR
;
194 conf
->no_referer
= NGX_CONF_UNSET
;
195 conf
->blocked_referer
= NGX_CONF_UNSET
;
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);
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
)
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
);
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
)
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;
281 ngx_http_valid_referers(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
)
283 ngx_http_referer_conf_t
*rlcf
= conf
;
286 ngx_str_t
*value
, uri
, name
;
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
);
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;
331 if (ngx_strcmp(value
[i
].data
, "blocked") == 0) {
332 rlcf
->blocked_referer
= 1;
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
)
348 return NGX_CONF_ERROR
;
355 p
= (u_char
*) ngx_strstr(value
[i
].data
, "/");
358 uri
.len
= (value
[i
].data
+ value
[i
].len
) - 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
;
373 ngx_http_add_referer(ngx_conf_t
*cf
, ngx_hash_keys_arrays_t
*keys
,
374 ngx_str_t
*value
, ngx_str_t
*uri
)
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;
395 u
= NGX_HTTP_REFERER_NO_URI_PART
;
398 u
= ngx_palloc(cf
->pool
, sizeof(ngx_str_t
));
400 return NGX_CONF_ERROR
;
406 rc
= ngx_hash_add_key(keys
, value
, u
, flags
);
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
);