Mercurial > hg > nginx-quic
annotate src/http/modules/ngx_http_referer_module.c @ 1179:6e2216ad2c87
$request_time has millisecond precision
author | Igor Sysoev <igor@sysoev.ru> |
---|---|
date | Sat, 21 Apr 2007 07:50:19 +0000 |
parents | db7c468c447d |
children | ae555e0549ca |
rev | line source |
---|---|
577 | 1 |
2 /* | |
3 * Copyright (C) Igor Sysoev | |
4 */ | |
5 | |
6 | |
7 #include <ngx_config.h> | |
8 #include <ngx_core.h> | |
9 #include <ngx_http.h> | |
10 | |
11 | |
595 | 12 #define NGX_HTTP_REFERER_NO_URI_PART ((void *) 4) |
13 | |
577 | 14 typedef struct { |
593 | 15 ngx_hash_t hash; |
16 ngx_hash_wildcard_t *dns_wildcards; | |
577 | 17 |
593 | 18 ngx_flag_t no_referer; |
19 ngx_flag_t blocked_referer; | |
577 | 20 |
593 | 21 ngx_hash_keys_arrays_t *keys; |
577 | 22 } ngx_http_referer_conf_t; |
23 | |
24 | |
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, | |
27 void *child); | |
28 static char *ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd, | |
29 void *conf); | |
593 | 30 static char *ngx_http_add_referer(ngx_conf_t *cf, ngx_hash_keys_arrays_t *keys, |
595 | 31 ngx_str_t *value, ngx_str_t *uri); |
593 | 32 static int ngx_libc_cdecl ngx_http_cmp_referer_wildcards(const void *one, |
33 const void *two); | |
577 | 34 |
35 | |
36 static ngx_command_t ngx_http_referer_commands[] = { | |
37 | |
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, | |
42 0, | |
43 NULL }, | |
44 | |
45 ngx_null_command | |
46 }; | |
47 | |
48 | |
49 static ngx_http_module_t ngx_http_referer_module_ctx = { | |
50 NULL, /* preconfiguration */ | |
51 NULL, /* postconfiguration */ | |
52 | |
53 NULL, /* create main configuration */ | |
54 NULL, /* init main configuration */ | |
55 | |
56 NULL, /* create server configuration */ | |
57 NULL, /* merge server configuration */ | |
58 | |
59 ngx_http_referer_create_conf, /* create location configuration */ | |
60 ngx_http_referer_merge_conf /* merge location configuration */ | |
61 }; | |
62 | |
63 | |
64 ngx_module_t ngx_http_referer_module = { | |
65 NGX_MODULE_V1, | |
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 */ | |
76 NGX_MODULE_V1_PADDING | |
77 }; | |
78 | |
79 | |
80 static ngx_int_t | |
81 ngx_http_referer_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, | |
82 uintptr_t data) | |
83 { | |
595 | 84 u_char *p, *ref, *last; |
593 | 85 size_t len; |
595 | 86 ngx_str_t *uri; |
87 ngx_uint_t i, key; | |
593 | 88 ngx_http_referer_conf_t *rlcf; |
595 | 89 u_char buf[256]; |
577 | 90 |
593 | 91 rlcf = ngx_http_get_module_loc_conf(r, ngx_http_referer_module); |
577 | 92 |
601 | 93 if (rlcf->hash.buckets == NULL && rlcf->dns_wildcards == NULL) { |
595 | 94 goto valid; |
577 | 95 } |
96 | |
97 if (r->headers_in.referer == NULL) { | |
593 | 98 if (rlcf->no_referer) { |
595 | 99 goto valid; |
100 } | |
577 | 101 |
595 | 102 goto invalid; |
577 | 103 } |
104 | |
105 len = r->headers_in.referer->value.len; | |
106 ref = r->headers_in.referer->value.data; | |
107 | |
108 if (len < sizeof("http://i.ru") - 1 | |
1107
db7c468c447d
ngx_strcasecmp()/ngx_strncasecmp()
Igor Sysoev <igor@sysoev.ru>
parents:
637
diff
changeset
|
109 || (ngx_strncasecmp(ref, (u_char *) "http://", 7) != 0)) |
577 | 110 { |
593 | 111 if (rlcf->blocked_referer) { |
595 | 112 goto valid; |
113 } | |
577 | 114 |
595 | 115 goto invalid; |
577 | 116 } |
117 | |
595 | 118 last = ref + len; |
577 | 119 ref += 7; |
595 | 120 i = 0; |
121 key = 0; | |
577 | 122 |
595 | 123 for (p = ref; p < last; p++) { |
593 | 124 if (*p == '/' || *p == ':') { |
125 break; | |
577 | 126 } |
595 | 127 |
128 buf[i] = ngx_tolower(*p); | |
129 key = ngx_hash(key, buf[i++]); | |
130 | |
131 if (i == 256) { | |
132 goto invalid; | |
133 } | |
593 | 134 } |
577 | 135 |
593 | 136 len = p - ref; |
577 | 137 |
593 | 138 if (rlcf->hash.buckets) { |
595 | 139 uri = ngx_hash_find(&rlcf->hash, key, buf, len); |
140 if (uri) { | |
141 goto uri; | |
593 | 142 } |
143 } | |
577 | 144 |
601 | 145 if (rlcf->dns_wildcards) { |
595 | 146 uri = ngx_hash_find_wildcard(rlcf->dns_wildcards, buf, len); |
147 if (uri) { | |
148 goto uri; | |
577 | 149 } |
150 } | |
151 | |
595 | 152 invalid: |
153 | |
577 | 154 *v = ngx_http_variable_true_value; |
155 | |
156 return NGX_OK; | |
595 | 157 |
158 uri: | |
159 | |
160 for ( /* void */ ; p < last; p++) { | |
161 if (*p == '/') { | |
162 break; | |
163 } | |
164 } | |
165 | |
166 len = last - p; | |
167 | |
168 if (uri == NGX_HTTP_REFERER_NO_URI_PART) { | |
169 goto valid; | |
170 } | |
171 | |
172 if (len < uri->len || ngx_strncmp(uri->data, p, uri->len) != 0) { | |
173 goto invalid; | |
174 } | |
175 | |
176 valid: | |
177 | |
178 *v = ngx_http_variable_null_value; | |
179 | |
180 return NGX_OK; | |
577 | 181 } |
182 | |
183 | |
184 static void * | |
185 ngx_http_referer_create_conf(ngx_conf_t *cf) | |
186 { | |
187 ngx_http_referer_conf_t *conf; | |
188 | |
593 | 189 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_referer_conf_t)); |
577 | 190 if (conf == NULL) { |
191 return NGX_CONF_ERROR; | |
192 } | |
193 | |
194 conf->no_referer = NGX_CONF_UNSET; | |
195 conf->blocked_referer = NGX_CONF_UNSET; | |
196 | |
197 return conf; | |
198 } | |
199 | |
200 | |
201 static char * | |
202 ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent, void *child) | |
203 { | |
204 ngx_http_referer_conf_t *prev = parent; | |
205 ngx_http_referer_conf_t *conf = child; | |
206 | |
593 | 207 ngx_hash_init_t hash; |
208 | |
209 if (conf->keys == NULL) { | |
210 conf->hash = prev->hash; | |
211 conf->dns_wildcards = prev->dns_wildcards; | |
212 | |
577 | 213 ngx_conf_merge_value(conf->no_referer, prev->no_referer, 0); |
214 ngx_conf_merge_value(conf->blocked_referer, prev->blocked_referer, 0); | |
593 | 215 |
216 return NGX_CONF_OK; | |
217 } | |
218 | |
603 | 219 if ((conf->no_referer == 1 || conf->blocked_referer == 1) |
631 | 220 && conf->keys->keys.nelts == 0 && conf->keys->dns_wildcards.nelts == 0) |
603 | 221 { |
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; | |
227 } | |
228 | |
593 | 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; | |
234 | |
235 if (conf->keys->keys.nelts) { | |
236 hash.hash = &conf->hash; | |
237 hash.temp_pool = NULL; | |
238 | |
239 if (ngx_hash_init(&hash, conf->keys->keys.elts, conf->keys->keys.nelts) | |
240 != NGX_OK) | |
241 { | |
242 return NGX_CONF_ERROR; | |
243 } | |
244 } | |
245 | |
246 if (conf->keys->dns_wildcards.nelts) { | |
247 | |
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); | |
252 | |
253 hash.hash = NULL; | |
254 hash.temp_pool = cf->temp_pool; | |
255 | |
256 if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wildcards.elts, | |
257 conf->keys->dns_wildcards.nelts) | |
258 != NGX_OK) | |
259 { | |
260 return NGX_CONF_ERROR; | |
261 } | |
262 | |
263 conf->dns_wildcards = (ngx_hash_wildcard_t *) hash.hash; | |
577 | 264 } |
265 | |
266 if (conf->no_referer == NGX_CONF_UNSET) { | |
267 conf->no_referer = 0; | |
268 } | |
269 | |
270 if (conf->blocked_referer == NGX_CONF_UNSET) { | |
271 conf->blocked_referer = 0; | |
272 } | |
273 | |
611 | 274 conf->keys = NULL; |
275 | |
577 | 276 return NGX_CONF_OK; |
277 } | |
278 | |
279 | |
280 static char * | |
281 ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
282 { | |
593 | 283 ngx_http_referer_conf_t *rlcf = conf; |
577 | 284 |
593 | 285 u_char *p; |
595 | 286 ngx_str_t *value, uri, name; |
593 | 287 ngx_uint_t i, n; |
577 | 288 ngx_http_variable_t *var; |
289 ngx_http_server_name_t *sn; | |
290 ngx_http_core_srv_conf_t *cscf; | |
291 | |
292 name.len = sizeof("invalid_referer") - 1; | |
293 name.data = (u_char *) "invalid_referer"; | |
294 | |
583 | 295 var = ngx_http_add_variable(cf, &name, |
296 NGX_HTTP_VAR_CHANGABLE|NGX_HTTP_VAR_NOHASH); | |
577 | 297 if (var == NULL) { |
298 return NGX_CONF_ERROR; | |
299 } | |
300 | |
637 | 301 var->get_handler = ngx_http_referer_variable; |
577 | 302 |
593 | 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; | |
307 } | |
577 | 308 |
593 | 309 rlcf->keys->pool = cf->pool; |
310 rlcf->keys->temp_pool = cf->pool; | |
311 | |
312 if (ngx_hash_keys_array_init(rlcf->keys, NGX_HASH_SMALL) != NGX_OK) { | |
577 | 313 return NGX_CONF_ERROR; |
314 } | |
315 } | |
316 | |
317 value = cf->args->elts; | |
318 | |
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; | |
324 } | |
325 | |
326 if (ngx_strcmp(value[i].data, "none") == 0) { | |
593 | 327 rlcf->no_referer = 1; |
577 | 328 continue; |
329 } | |
330 | |
331 if (ngx_strcmp(value[i].data, "blocked") == 0) { | |
593 | 332 rlcf->blocked_referer = 1; |
577 | 333 continue; |
334 } | |
335 | |
595 | 336 uri.len = 0; |
613 | 337 uri.data = NULL; |
595 | 338 |
577 | 339 if (ngx_strcmp(value[i].data, "server_names") == 0) { |
593 | 340 |
341 cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module); | |
577 | 342 |
593 | 343 sn = cscf->server_names.elts; |
344 for (n = 0; n < cscf->server_names.nelts; n++) { | |
595 | 345 if (ngx_http_add_referer(cf, rlcf->keys, &sn[n].name, &uri) |
346 != NGX_OK) | |
347 { | |
593 | 348 return NGX_CONF_ERROR; |
349 } | |
350 } | |
577 | 351 |
352 continue; | |
353 } | |
354 | |
593 | 355 p = (u_char *) ngx_strstr(value[i].data, "/"); |
577 | 356 |
593 | 357 if (p) { |
595 | 358 uri.len = (value[i].data + value[i].len) - p; |
359 uri.data = p; | |
593 | 360 value[i].len = p - value[i].data; |
577 | 361 } |
362 | |
595 | 363 if (ngx_http_add_referer(cf, rlcf->keys, &value[i], &uri) != NGX_OK) { |
577 | 364 return NGX_CONF_ERROR; |
365 } | |
366 } | |
367 | |
368 return NGX_CONF_OK; | |
369 } | |
593 | 370 |
371 | |
372 static char * | |
373 ngx_http_add_referer(ngx_conf_t *cf, ngx_hash_keys_arrays_t *keys, | |
595 | 374 ngx_str_t *value, ngx_str_t *uri) |
593 | 375 { |
595 | 376 u_char ch; |
377 ngx_int_t rc; | |
378 ngx_str_t *u; | |
379 ngx_uint_t flags; | |
593 | 380 |
381 ch = value->data[0]; | |
382 | |
383 if ((ch == '*' && (value->len < 3 || value->data[1] != '.')) | |
384 || (ch == '.' && value->len < 2)) | |
385 { | |
386 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
387 "invalid DNS wildcard \"%V\"", value); | |
388 | |
389 return NGX_CONF_ERROR; | |
390 } | |
391 | |
392 flags = (ch == '*' || ch == '.') ? NGX_HASH_WILDCARD_KEY : 0; | |
393 | |
595 | 394 if (uri->len == 0) { |
395 u = NGX_HTTP_REFERER_NO_URI_PART; | |
396 | |
397 } else { | |
398 u = ngx_palloc(cf->pool, sizeof(ngx_str_t)); | |
399 if (u == NULL) { | |
400 return NGX_CONF_ERROR; | |
401 } | |
402 | |
403 *u = *uri; | |
404 } | |
405 | |
406 rc = ngx_hash_add_key(keys, value, u, flags); | |
593 | 407 |
408 if (rc == NGX_OK) { | |
409 return NGX_CONF_OK; | |
410 } | |
411 | |
412 if (rc == NGX_BUSY) { | |
413 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
414 "conflicting parameter \"%V\"", value); | |
415 } | |
416 | |
417 return NGX_CONF_ERROR; | |
418 } | |
419 | |
420 | |
421 static int ngx_libc_cdecl | |
422 ngx_http_cmp_referer_wildcards(const void *one, const void *two) | |
423 { | |
424 ngx_hash_key_t *first, *second; | |
425 | |
426 first = (ngx_hash_key_t *) one; | |
427 second = (ngx_hash_key_t *) two; | |
428 | |
429 return ngx_strcmp(first->key.data, second->key.data); | |
430 } |