Annotation of embedaddon/nginx/src/http/modules/ngx_http_referer_module.c, revision 1.1.1.1
1.1 misho 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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>