|
|
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: typedef struct {
14: ngx_http_variable_value_t *value;
15: u_short start;
16: u_short end;
17: } ngx_http_geo_range_t;
18:
19:
20: typedef struct {
21: ngx_radix_tree_t *tree;
22: #if (NGX_HAVE_INET6)
23: ngx_radix_tree_t *tree6;
24: #endif
25: } ngx_http_geo_trees_t;
26:
27:
28: typedef struct {
29: ngx_http_geo_range_t **low;
30: ngx_http_variable_value_t *default_value;
31: } ngx_http_geo_high_ranges_t;
32:
33:
34: typedef struct {
35: ngx_str_node_t sn;
36: ngx_http_variable_value_t *value;
37: size_t offset;
38: } ngx_http_geo_variable_value_node_t;
39:
40:
41: typedef struct {
42: ngx_http_variable_value_t *value;
43: ngx_str_t *net;
44: ngx_http_geo_high_ranges_t high;
45: ngx_radix_tree_t *tree;
46: #if (NGX_HAVE_INET6)
47: ngx_radix_tree_t *tree6;
48: #endif
49: ngx_rbtree_t rbtree;
50: ngx_rbtree_node_t sentinel;
51: ngx_array_t *proxies;
52: ngx_pool_t *pool;
53: ngx_pool_t *temp_pool;
54:
55: size_t data_size;
56:
57: ngx_str_t include_name;
58: ngx_uint_t includes;
59: ngx_uint_t entries;
60:
61: unsigned ranges:1;
62: unsigned outside_entries:1;
63: unsigned allow_binary_include:1;
64: unsigned binary_include:1;
65: unsigned proxy_recursive:1;
66: } ngx_http_geo_conf_ctx_t;
67:
68:
69: typedef struct {
70: union {
71: ngx_http_geo_trees_t trees;
72: ngx_http_geo_high_ranges_t high;
73: } u;
74:
75: ngx_array_t *proxies;
76: unsigned proxy_recursive:1;
77:
78: ngx_int_t index;
79: } ngx_http_geo_ctx_t;
80:
81:
82: static ngx_int_t ngx_http_geo_addr(ngx_http_request_t *r,
83: ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr);
84: static ngx_int_t ngx_http_geo_real_addr(ngx_http_request_t *r,
85: ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr);
86: static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
87: static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
88: static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
89: ngx_str_t *value);
90: static char *ngx_http_geo_add_range(ngx_conf_t *cf,
91: ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
92: static ngx_uint_t ngx_http_geo_delete_range(ngx_conf_t *cf,
93: ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
94: static char *ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
95: ngx_str_t *value);
96: static char *ngx_http_geo_cidr_add(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
97: ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net);
98: static ngx_http_variable_value_t *ngx_http_geo_value(ngx_conf_t *cf,
99: ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value);
100: static char *ngx_http_geo_add_proxy(ngx_conf_t *cf,
101: ngx_http_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr);
102: static ngx_int_t ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net,
103: ngx_cidr_t *cidr);
104: static char *ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
105: ngx_str_t *name);
106: static ngx_int_t ngx_http_geo_include_binary_base(ngx_conf_t *cf,
107: ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *name);
108: static void ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx);
109: static u_char *ngx_http_geo_copy_values(u_char *base, u_char *p,
110: ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
111:
112:
113: static ngx_command_t ngx_http_geo_commands[] = {
114:
115: { ngx_string("geo"),
116: NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,
117: ngx_http_geo_block,
118: NGX_HTTP_MAIN_CONF_OFFSET,
119: 0,
120: NULL },
121:
122: ngx_null_command
123: };
124:
125:
126: static ngx_http_module_t ngx_http_geo_module_ctx = {
127: NULL, /* preconfiguration */
128: NULL, /* postconfiguration */
129:
130: NULL, /* create main configuration */
131: NULL, /* init main configuration */
132:
133: NULL, /* create server configuration */
134: NULL, /* merge server configuration */
135:
136: NULL, /* create location configuration */
137: NULL /* merge location configuration */
138: };
139:
140:
141: ngx_module_t ngx_http_geo_module = {
142: NGX_MODULE_V1,
143: &ngx_http_geo_module_ctx, /* module context */
144: ngx_http_geo_commands, /* module directives */
145: NGX_HTTP_MODULE, /* module type */
146: NULL, /* init master */
147: NULL, /* init module */
148: NULL, /* init process */
149: NULL, /* init thread */
150: NULL, /* exit thread */
151: NULL, /* exit process */
152: NULL, /* exit master */
153: NGX_MODULE_V1_PADDING
154: };
155:
156:
157: typedef struct {
158: u_char GEORNG[6];
159: u_char version;
160: u_char ptr_size;
161: uint32_t endianness;
162: uint32_t crc32;
163: } ngx_http_geo_header_t;
164:
165:
166: static ngx_http_geo_header_t ngx_http_geo_header = {
167: { 'G', 'E', 'O', 'R', 'N', 'G' }, 0, sizeof(void *), 0x12345678, 0
168: };
169:
170:
171: /* geo range is AF_INET only */
172:
173: static ngx_int_t
174: ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
175: uintptr_t data)
176: {
177: ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
178:
179: in_addr_t inaddr;
180: ngx_addr_t addr;
181: struct sockaddr_in *sin;
182: ngx_http_variable_value_t *vv;
183: #if (NGX_HAVE_INET6)
184: u_char *p;
185: struct in6_addr *inaddr6;
186: #endif
187:
188: if (ngx_http_geo_addr(r, ctx, &addr) != NGX_OK) {
189: vv = (ngx_http_variable_value_t *)
190: ngx_radix32tree_find(ctx->u.trees.tree, INADDR_NONE);
191: goto done;
192: }
193:
194: switch (addr.sockaddr->sa_family) {
195:
196: #if (NGX_HAVE_INET6)
197: case AF_INET6:
198: inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;
199: p = inaddr6->s6_addr;
200:
201: if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
202: inaddr = p[12] << 24;
203: inaddr += p[13] << 16;
204: inaddr += p[14] << 8;
205: inaddr += p[15];
206:
207: vv = (ngx_http_variable_value_t *)
208: ngx_radix32tree_find(ctx->u.trees.tree, inaddr);
209:
210: } else {
211: vv = (ngx_http_variable_value_t *)
212: ngx_radix128tree_find(ctx->u.trees.tree6, p);
213: }
214:
215: break;
216: #endif
217:
218: default: /* AF_INET */
219: sin = (struct sockaddr_in *) addr.sockaddr;
220: inaddr = ntohl(sin->sin_addr.s_addr);
221:
222: vv = (ngx_http_variable_value_t *)
223: ngx_radix32tree_find(ctx->u.trees.tree, inaddr);
224:
225: break;
226: }
227:
228: done:
229:
230: *v = *vv;
231:
232: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
233: "http geo: %v", v);
234:
235: return NGX_OK;
236: }
237:
238:
239: static ngx_int_t
240: ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
241: uintptr_t data)
242: {
243: ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
244:
245: in_addr_t inaddr;
246: ngx_addr_t addr;
247: ngx_uint_t n;
248: struct sockaddr_in *sin;
249: ngx_http_geo_range_t *range;
250: #if (NGX_HAVE_INET6)
251: u_char *p;
252: struct in6_addr *inaddr6;
253: #endif
254:
255: *v = *ctx->u.high.default_value;
256:
257: if (ngx_http_geo_addr(r, ctx, &addr) == NGX_OK) {
258:
259: switch (addr.sockaddr->sa_family) {
260:
261: #if (NGX_HAVE_INET6)
262: case AF_INET6:
263: inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;
264:
265: if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
266: p = inaddr6->s6_addr;
267:
268: inaddr = p[12] << 24;
269: inaddr += p[13] << 16;
270: inaddr += p[14] << 8;
271: inaddr += p[15];
272:
273: } else {
274: inaddr = INADDR_NONE;
275: }
276:
277: break;
278: #endif
279:
280: default: /* AF_INET */
281: sin = (struct sockaddr_in *) addr.sockaddr;
282: inaddr = ntohl(sin->sin_addr.s_addr);
283: break;
284: }
285:
286: } else {
287: inaddr = INADDR_NONE;
288: }
289:
290: if (ctx->u.high.low) {
291: range = ctx->u.high.low[inaddr >> 16];
292:
293: if (range) {
294: n = inaddr & 0xffff;
295: do {
296: if (n >= (ngx_uint_t) range->start
297: && n <= (ngx_uint_t) range->end)
298: {
299: *v = *range->value;
300: break;
301: }
302: } while ((++range)->value);
303: }
304: }
305:
306: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
307: "http geo: %v", v);
308:
309: return NGX_OK;
310: }
311:
312:
313: static ngx_int_t
314: ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx,
315: ngx_addr_t *addr)
316: {
317: ngx_array_t *xfwd;
318:
319: if (ngx_http_geo_real_addr(r, ctx, addr) != NGX_OK) {
320: return NGX_ERROR;
321: }
322:
323: xfwd = &r->headers_in.x_forwarded_for;
324:
325: if (xfwd->nelts > 0 && ctx->proxies != NULL) {
326: (void) ngx_http_get_forwarded_addr(r, addr, xfwd, NULL,
327: ctx->proxies, ctx->proxy_recursive);
328: }
329:
330: return NGX_OK;
331: }
332:
333:
334: static ngx_int_t
335: ngx_http_geo_real_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx,
336: ngx_addr_t *addr)
337: {
338: ngx_http_variable_value_t *v;
339:
340: if (ctx->index == -1) {
341: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
342: "http geo started: %V", &r->connection->addr_text);
343:
344: addr->sockaddr = r->connection->sockaddr;
345: addr->socklen = r->connection->socklen;
346: /* addr->name = r->connection->addr_text; */
347:
348: return NGX_OK;
349: }
350:
351: v = ngx_http_get_flushed_variable(r, ctx->index);
352:
353: if (v == NULL || v->not_found) {
354: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
355: "http geo not found");
356:
357: return NGX_ERROR;
358: }
359:
360: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
361: "http geo started: %v", v);
362:
363: if (ngx_parse_addr(r->pool, addr, v->data, v->len) == NGX_OK) {
364: return NGX_OK;
365: }
366:
367: return NGX_ERROR;
368: }
369:
370:
371: static char *
372: ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
373: {
374: char *rv;
375: size_t len;
376: ngx_str_t *value, name;
377: ngx_uint_t i;
378: ngx_conf_t save;
379: ngx_pool_t *pool;
380: ngx_array_t *a;
381: ngx_http_variable_t *var;
382: ngx_http_geo_ctx_t *geo;
383: ngx_http_geo_conf_ctx_t ctx;
384: #if (NGX_HAVE_INET6)
385: static struct in6_addr zero;
386: #endif
387:
388: value = cf->args->elts;
389:
390: geo = ngx_palloc(cf->pool, sizeof(ngx_http_geo_ctx_t));
391: if (geo == NULL) {
392: return NGX_CONF_ERROR;
393: }
394:
395: name = value[1];
396:
397: if (name.data[0] != '$') {
398: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
399: "invalid variable name \"%V\"", &name);
400: return NGX_CONF_ERROR;
401: }
402:
403: name.len--;
404: name.data++;
405:
406: if (cf->args->nelts == 3) {
407:
408: geo->index = ngx_http_get_variable_index(cf, &name);
409: if (geo->index == NGX_ERROR) {
410: return NGX_CONF_ERROR;
411: }
412:
413: name = value[2];
414:
415: if (name.data[0] != '$') {
416: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
417: "invalid variable name \"%V\"", &name);
418: return NGX_CONF_ERROR;
419: }
420:
421: name.len--;
422: name.data++;
423:
424: } else {
425: geo->index = -1;
426: }
427:
428: var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
429: if (var == NULL) {
430: return NGX_CONF_ERROR;
431: }
432:
433: pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
434: if (pool == NULL) {
435: return NGX_CONF_ERROR;
436: }
437:
438: ngx_memzero(&ctx, sizeof(ngx_http_geo_conf_ctx_t));
439:
440: ctx.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
441: if (ctx.temp_pool == NULL) {
442: return NGX_CONF_ERROR;
443: }
444:
445: ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, ngx_str_rbtree_insert_value);
446:
447: ctx.pool = cf->pool;
448: ctx.data_size = sizeof(ngx_http_geo_header_t)
449: + sizeof(ngx_http_variable_value_t)
450: + 0x10000 * sizeof(ngx_http_geo_range_t *);
451: ctx.allow_binary_include = 1;
452:
453: save = *cf;
454: cf->pool = pool;
455: cf->ctx = &ctx;
456: cf->handler = ngx_http_geo;
457: cf->handler_conf = conf;
458:
459: rv = ngx_conf_parse(cf, NULL);
460:
461: *cf = save;
462:
463: geo->proxies = ctx.proxies;
464: geo->proxy_recursive = ctx.proxy_recursive;
465:
466: if (ctx.ranges) {
467:
468: if (ctx.high.low && !ctx.binary_include) {
469: for (i = 0; i < 0x10000; i++) {
470: a = (ngx_array_t *) ctx.high.low[i];
471:
472: if (a == NULL || a->nelts == 0) {
473: continue;
474: }
475:
476: len = a->nelts * sizeof(ngx_http_geo_range_t);
477:
478: ctx.high.low[i] = ngx_palloc(cf->pool, len + sizeof(void *));
479: if (ctx.high.low[i] == NULL) {
480: return NGX_CONF_ERROR;
481: }
482:
483: ngx_memcpy(ctx.high.low[i], a->elts, len);
484: ctx.high.low[i][a->nelts].value = NULL;
485: ctx.data_size += len + sizeof(void *);
486: }
487:
488: if (ctx.allow_binary_include
489: && !ctx.outside_entries
490: && ctx.entries > 100000
491: && ctx.includes == 1)
492: {
493: ngx_http_geo_create_binary_base(&ctx);
494: }
495: }
496:
497: if (ctx.high.default_value == NULL) {
498: ctx.high.default_value = &ngx_http_variable_null_value;
499: }
500:
501: geo->u.high = ctx.high;
502:
503: var->get_handler = ngx_http_geo_range_variable;
504: var->data = (uintptr_t) geo;
505:
506: ngx_destroy_pool(ctx.temp_pool);
507: ngx_destroy_pool(pool);
508:
509: } else {
510: if (ctx.tree == NULL) {
511: ctx.tree = ngx_radix_tree_create(cf->pool, -1);
512: if (ctx.tree == NULL) {
513: return NGX_CONF_ERROR;
514: }
515: }
516:
517: geo->u.trees.tree = ctx.tree;
518:
519: #if (NGX_HAVE_INET6)
520: if (ctx.tree6 == NULL) {
521: ctx.tree6 = ngx_radix_tree_create(cf->pool, -1);
522: if (ctx.tree6 == NULL) {
523: return NGX_CONF_ERROR;
524: }
525: }
526:
527: geo->u.trees.tree6 = ctx.tree6;
528: #endif
529:
530: var->get_handler = ngx_http_geo_cidr_variable;
531: var->data = (uintptr_t) geo;
532:
533: ngx_destroy_pool(ctx.temp_pool);
534: ngx_destroy_pool(pool);
535:
536: if (ngx_radix32tree_insert(ctx.tree, 0, 0,
537: (uintptr_t) &ngx_http_variable_null_value)
538: == NGX_ERROR)
539: {
540: return NGX_CONF_ERROR;
541: }
542:
543: /* NGX_BUSY is okay (default was set explicitly) */
544:
545: #if (NGX_HAVE_INET6)
546: if (ngx_radix128tree_insert(ctx.tree6, zero.s6_addr, zero.s6_addr,
547: (uintptr_t) &ngx_http_variable_null_value)
548: == NGX_ERROR)
549: {
550: return NGX_CONF_ERROR;
551: }
552: #endif
553: }
554:
555: return rv;
556: }
557:
558:
559: static char *
560: ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
561: {
562: char *rv;
563: ngx_str_t *value;
564: ngx_cidr_t cidr;
565: ngx_http_geo_conf_ctx_t *ctx;
566:
567: ctx = cf->ctx;
568:
569: value = cf->args->elts;
570:
571: if (cf->args->nelts == 1) {
572:
573: if (ngx_strcmp(value[0].data, "ranges") == 0) {
574:
575: if (ctx->tree
576: #if (NGX_HAVE_INET6)
577: || ctx->tree6
578: #endif
579: )
580: {
581: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
582: "the \"ranges\" directive must be "
583: "the first directive inside \"geo\" block");
584: goto failed;
585: }
586:
587: ctx->ranges = 1;
588:
589: rv = NGX_CONF_OK;
590:
591: goto done;
592: }
593:
594: else if (ngx_strcmp(value[0].data, "proxy_recursive") == 0) {
595: ctx->proxy_recursive = 1;
596: rv = NGX_CONF_OK;
597: goto done;
598: }
599: }
600:
601: if (cf->args->nelts != 2) {
602: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
603: "invalid number of the geo parameters");
604: goto failed;
605: }
606:
607: if (ngx_strcmp(value[0].data, "include") == 0) {
608:
609: rv = ngx_http_geo_include(cf, ctx, &value[1]);
610:
611: goto done;
612:
613: } else if (ngx_strcmp(value[0].data, "proxy") == 0) {
614:
615: if (ngx_http_geo_cidr_value(cf, &value[1], &cidr) != NGX_OK) {
616: goto failed;
617: }
618:
619: rv = ngx_http_geo_add_proxy(cf, ctx, &cidr);
620:
621: goto done;
622: }
623:
624: if (ctx->ranges) {
625: rv = ngx_http_geo_range(cf, ctx, value);
626:
627: } else {
628: rv = ngx_http_geo_cidr(cf, ctx, value);
629: }
630:
631: done:
632:
633: ngx_reset_pool(cf->pool);
634:
635: return rv;
636:
637: failed:
638:
639: ngx_reset_pool(cf->pool);
640:
641: return NGX_CONF_ERROR;
642: }
643:
644:
645: static char *
646: ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
647: ngx_str_t *value)
648: {
649: u_char *p, *last;
650: in_addr_t start, end;
651: ngx_str_t *net;
652: ngx_uint_t del;
653:
654: if (ngx_strcmp(value[0].data, "default") == 0) {
655:
656: if (ctx->high.default_value) {
657: ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
658: "duplicate default geo range value: \"%V\", old value: \"%v\"",
659: &value[1], ctx->high.default_value);
660: }
661:
662: ctx->high.default_value = ngx_http_geo_value(cf, ctx, &value[1]);
663: if (ctx->high.default_value == NULL) {
664: return NGX_CONF_ERROR;
665: }
666:
667: return NGX_CONF_OK;
668: }
669:
670: if (ctx->binary_include) {
671: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
672: "binary geo range base \"%s\" cannot be mixed with usual entries",
673: ctx->include_name.data);
674: return NGX_CONF_ERROR;
675: }
676:
677: if (ctx->high.low == NULL) {
678: ctx->high.low = ngx_pcalloc(ctx->pool,
679: 0x10000 * sizeof(ngx_http_geo_range_t *));
680: if (ctx->high.low == NULL) {
681: return NGX_CONF_ERROR;
682: }
683: }
684:
685: ctx->entries++;
686: ctx->outside_entries = 1;
687:
688: if (ngx_strcmp(value[0].data, "delete") == 0) {
689: net = &value[1];
690: del = 1;
691:
692: } else {
693: net = &value[0];
694: del = 0;
695: }
696:
697: last = net->data + net->len;
698:
699: p = ngx_strlchr(net->data, last, '-');
700:
701: if (p == NULL) {
702: goto invalid;
703: }
704:
705: start = ngx_inet_addr(net->data, p - net->data);
706:
707: if (start == INADDR_NONE) {
708: goto invalid;
709: }
710:
711: start = ntohl(start);
712:
713: p++;
714:
715: end = ngx_inet_addr(p, last - p);
716:
717: if (end == INADDR_NONE) {
718: goto invalid;
719: }
720:
721: end = ntohl(end);
722:
723: if (start > end) {
724: goto invalid;
725: }
726:
727: if (del) {
728: if (ngx_http_geo_delete_range(cf, ctx, start, end)) {
729: ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
730: "no address range \"%V\" to delete", net);
731: }
732:
733: return NGX_CONF_OK;
734: }
735:
736: ctx->value = ngx_http_geo_value(cf, ctx, &value[1]);
737:
738: if (ctx->value == NULL) {
739: return NGX_CONF_ERROR;
740: }
741:
742: ctx->net = net;
743:
744: return ngx_http_geo_add_range(cf, ctx, start, end);
745:
746: invalid:
747:
748: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid range \"%V\"", net);
749:
750: return NGX_CONF_ERROR;
751: }
752:
753:
754: /* the add procedure is optimized to add a growing up sequence */
755:
756: static char *
757: ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
758: in_addr_t start, in_addr_t end)
759: {
760: in_addr_t n;
761: ngx_uint_t h, i, s, e;
762: ngx_array_t *a;
763: ngx_http_geo_range_t *range;
764:
765: for (n = start; n <= end; n = (n + 0x10000) & 0xffff0000) {
766:
767: h = n >> 16;
768:
769: if (n == start) {
770: s = n & 0xffff;
771: } else {
772: s = 0;
773: }
774:
775: if ((n | 0xffff) > end) {
776: e = end & 0xffff;
777:
778: } else {
779: e = 0xffff;
780: }
781:
782: a = (ngx_array_t *) ctx->high.low[h];
783:
784: if (a == NULL) {
785: a = ngx_array_create(ctx->temp_pool, 64,
786: sizeof(ngx_http_geo_range_t));
787: if (a == NULL) {
788: return NGX_CONF_ERROR;
789: }
790:
791: ctx->high.low[h] = (ngx_http_geo_range_t *) a;
792: }
793:
794: i = a->nelts;
795: range = a->elts;
796:
797: while (i) {
798:
799: i--;
800:
801: if (e < (ngx_uint_t) range[i].start) {
802: continue;
803: }
804:
805: if (s > (ngx_uint_t) range[i].end) {
806:
807: /* add after the range */
808:
809: range = ngx_array_push(a);
810: if (range == NULL) {
811: return NGX_CONF_ERROR;
812: }
813:
814: range = a->elts;
815:
816: ngx_memmove(&range[i + 2], &range[i + 1],
817: (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));
818:
819: range[i + 1].start = (u_short) s;
820: range[i + 1].end = (u_short) e;
821: range[i + 1].value = ctx->value;
822:
823: goto next;
824: }
825:
826: if (s == (ngx_uint_t) range[i].start
827: && e == (ngx_uint_t) range[i].end)
828: {
829: ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
830: "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
831: ctx->net, ctx->value, range[i].value);
832:
833: range[i].value = ctx->value;
834:
835: goto next;
836: }
837:
838: if (s > (ngx_uint_t) range[i].start
839: && e < (ngx_uint_t) range[i].end)
840: {
841: /* split the range and insert the new one */
842:
843: range = ngx_array_push(a);
844: if (range == NULL) {
845: return NGX_CONF_ERROR;
846: }
847:
848: range = ngx_array_push(a);
849: if (range == NULL) {
850: return NGX_CONF_ERROR;
851: }
852:
853: range = a->elts;
854:
855: ngx_memmove(&range[i + 3], &range[i + 1],
856: (a->nelts - 3 - i) * sizeof(ngx_http_geo_range_t));
857:
858: range[i + 2].start = (u_short) (e + 1);
859: range[i + 2].end = range[i].end;
860: range[i + 2].value = range[i].value;
861:
862: range[i + 1].start = (u_short) s;
863: range[i + 1].end = (u_short) e;
864: range[i + 1].value = ctx->value;
865:
866: range[i].end = (u_short) (s - 1);
867:
868: goto next;
869: }
870:
871: if (s == (ngx_uint_t) range[i].start
872: && e < (ngx_uint_t) range[i].end)
873: {
874: /* shift the range start and insert the new range */
875:
876: range = ngx_array_push(a);
877: if (range == NULL) {
878: return NGX_CONF_ERROR;
879: }
880:
881: range = a->elts;
882:
883: ngx_memmove(&range[i + 1], &range[i],
884: (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));
885:
886: range[i + 1].start = (u_short) (e + 1);
887:
888: range[i].start = (u_short) s;
889: range[i].end = (u_short) e;
890: range[i].value = ctx->value;
891:
892: goto next;
893: }
894:
895: if (s > (ngx_uint_t) range[i].start
896: && e == (ngx_uint_t) range[i].end)
897: {
898: /* shift the range end and insert the new range */
899:
900: range = ngx_array_push(a);
901: if (range == NULL) {
902: return NGX_CONF_ERROR;
903: }
904:
905: range = a->elts;
906:
907: ngx_memmove(&range[i + 2], &range[i + 1],
908: (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));
909:
910: range[i + 1].start = (u_short) s;
911: range[i + 1].end = (u_short) e;
912: range[i + 1].value = ctx->value;
913:
914: range[i].end = (u_short) (s - 1);
915:
916: goto next;
917: }
918:
919: s = (ngx_uint_t) range[i].start;
920: e = (ngx_uint_t) range[i].end;
921:
922: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
923: "range \"%V\" overlaps \"%d.%d.%d.%d-%d.%d.%d.%d\"",
924: ctx->net,
925: h >> 8, h & 0xff, s >> 8, s & 0xff,
926: h >> 8, h & 0xff, e >> 8, e & 0xff);
927:
928: return NGX_CONF_ERROR;
929: }
930:
931: /* add the first range */
932:
933: range = ngx_array_push(a);
934: if (range == NULL) {
935: return NGX_CONF_ERROR;
936: }
937:
938: range->start = (u_short) s;
939: range->end = (u_short) e;
940: range->value = ctx->value;
941:
942: next:
943:
944: continue;
945: }
946:
947: return NGX_CONF_OK;
948: }
949:
950:
951: static ngx_uint_t
952: ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
953: in_addr_t start, in_addr_t end)
954: {
955: in_addr_t n;
956: ngx_uint_t h, i, s, e, warn;
957: ngx_array_t *a;
958: ngx_http_geo_range_t *range;
959:
960: warn = 0;
961:
962: for (n = start; n <= end; n += 0x10000) {
963:
964: h = n >> 16;
965:
966: if (n == start) {
967: s = n & 0xffff;
968: } else {
969: s = 0;
970: }
971:
972: if ((n | 0xffff) > end) {
973: e = end & 0xffff;
974:
975: } else {
976: e = 0xffff;
977: }
978:
979: a = (ngx_array_t *) ctx->high.low[h];
980:
981: if (a == NULL) {
982: warn = 1;
983: continue;
984: }
985:
986: range = a->elts;
987: for (i = 0; i < a->nelts; i++) {
988:
989: if (s == (ngx_uint_t) range[i].start
990: && e == (ngx_uint_t) range[i].end)
991: {
992: ngx_memmove(&range[i], &range[i + 1],
993: (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));
994:
995: a->nelts--;
996:
997: break;
998: }
999:
1000: if (s != (ngx_uint_t) range[i].start
1001: && e != (ngx_uint_t) range[i].end)
1002: {
1003: continue;
1004: }
1005:
1006: warn = 1;
1007: }
1008: }
1009:
1010: return warn;
1011: }
1012:
1013:
1014: static char *
1015: ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
1016: ngx_str_t *value)
1017: {
1018: char *rv;
1019: ngx_int_t rc, del;
1020: ngx_str_t *net;
1021: ngx_cidr_t cidr;
1022:
1023: if (ctx->tree == NULL) {
1024: ctx->tree = ngx_radix_tree_create(ctx->pool, -1);
1025: if (ctx->tree == NULL) {
1026: return NGX_CONF_ERROR;
1027: }
1028: }
1029:
1030: #if (NGX_HAVE_INET6)
1031: if (ctx->tree6 == NULL) {
1032: ctx->tree6 = ngx_radix_tree_create(ctx->pool, -1);
1033: if (ctx->tree6 == NULL) {
1034: return NGX_CONF_ERROR;
1035: }
1036: }
1037: #endif
1038:
1039: if (ngx_strcmp(value[0].data, "default") == 0) {
1040: cidr.family = AF_INET;
1041: cidr.u.in.addr = 0;
1042: cidr.u.in.mask = 0;
1043:
1044: rv = ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]);
1045:
1046: if (rv != NGX_CONF_OK) {
1047: return rv;
1048: }
1049:
1050: #if (NGX_HAVE_INET6)
1051: cidr.family = AF_INET6;
1052: ngx_memzero(&cidr.u.in6, sizeof(ngx_in6_cidr_t));
1053:
1054: rv = ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]);
1055:
1056: if (rv != NGX_CONF_OK) {
1057: return rv;
1058: }
1059: #endif
1060:
1061: return NGX_CONF_OK;
1062: }
1063:
1064: if (ngx_strcmp(value[0].data, "delete") == 0) {
1065: net = &value[1];
1066: del = 1;
1067:
1068: } else {
1069: net = &value[0];
1070: del = 0;
1071: }
1072:
1073: if (ngx_http_geo_cidr_value(cf, net, &cidr) != NGX_OK) {
1074: return NGX_CONF_ERROR;
1075: }
1076:
1077: if (cidr.family == AF_INET) {
1078: cidr.u.in.addr = ntohl(cidr.u.in.addr);
1079: cidr.u.in.mask = ntohl(cidr.u.in.mask);
1080: }
1081:
1082: if (del) {
1083: switch (cidr.family) {
1084:
1085: #if (NGX_HAVE_INET6)
1086: case AF_INET6:
1087: rc = ngx_radix128tree_delete(ctx->tree6,
1088: cidr.u.in6.addr.s6_addr,
1089: cidr.u.in6.mask.s6_addr);
1090: break;
1091: #endif
1092:
1093: default: /* AF_INET */
1094: rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr,
1095: cidr.u.in.mask);
1096: break;
1097: }
1098:
1099: if (rc != NGX_OK) {
1100: ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1101: "no network \"%V\" to delete", net);
1102: }
1103:
1104: return NGX_CONF_OK;
1105: }
1106:
1107: return ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], net);
1108: }
1109:
1110:
1111: static char *
1112: ngx_http_geo_cidr_add(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
1113: ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net)
1114: {
1115: ngx_int_t rc;
1116: ngx_http_variable_value_t *val, *old;
1117:
1118: val = ngx_http_geo_value(cf, ctx, value);
1119:
1120: if (val == NULL) {
1121: return NGX_CONF_ERROR;
1122: }
1123:
1124: switch (cidr->family) {
1125:
1126: #if (NGX_HAVE_INET6)
1127: case AF_INET6:
1128: rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,
1129: cidr->u.in6.mask.s6_addr,
1130: (uintptr_t) val);
1131:
1132: if (rc == NGX_OK) {
1133: return NGX_CONF_OK;
1134: }
1135:
1136: if (rc == NGX_ERROR) {
1137: return NGX_CONF_ERROR;
1138: }
1139:
1140: /* rc == NGX_BUSY */
1141:
1142: old = (ngx_http_variable_value_t *)
1143: ngx_radix128tree_find(ctx->tree6,
1144: cidr->u.in6.addr.s6_addr);
1145:
1146: ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1147: "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
1148: net, val, old);
1149:
1150: rc = ngx_radix128tree_delete(ctx->tree6,
1151: cidr->u.in6.addr.s6_addr,
1152: cidr->u.in6.mask.s6_addr);
1153:
1154: if (rc == NGX_ERROR) {
1155: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
1156: return NGX_CONF_ERROR;
1157: }
1158:
1159: rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,
1160: cidr->u.in6.mask.s6_addr,
1161: (uintptr_t) val);
1162:
1163: break;
1164: #endif
1165:
1166: default: /* AF_INET */
1167: rc = ngx_radix32tree_insert(ctx->tree, cidr->u.in.addr,
1168: cidr->u.in.mask, (uintptr_t) val);
1169:
1170: if (rc == NGX_OK) {
1171: return NGX_CONF_OK;
1172: }
1173:
1174: if (rc == NGX_ERROR) {
1175: return NGX_CONF_ERROR;
1176: }
1177:
1178: /* rc == NGX_BUSY */
1179:
1180: old = (ngx_http_variable_value_t *)
1181: ngx_radix32tree_find(ctx->tree, cidr->u.in.addr);
1182:
1183: ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1184: "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
1185: net, val, old);
1186:
1187: rc = ngx_radix32tree_delete(ctx->tree,
1188: cidr->u.in.addr, cidr->u.in.mask);
1189:
1190: if (rc == NGX_ERROR) {
1191: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
1192: return NGX_CONF_ERROR;
1193: }
1194:
1195: rc = ngx_radix32tree_insert(ctx->tree, cidr->u.in.addr,
1196: cidr->u.in.mask, (uintptr_t) val);
1197:
1198: break;
1199: }
1200:
1201: if (rc == NGX_OK) {
1202: return NGX_CONF_OK;
1203: }
1204:
1205: return NGX_CONF_ERROR;
1206: }
1207:
1208:
1209: static ngx_http_variable_value_t *
1210: ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
1211: ngx_str_t *value)
1212: {
1213: uint32_t hash;
1214: ngx_http_variable_value_t *val;
1215: ngx_http_geo_variable_value_node_t *gvvn;
1216:
1217: hash = ngx_crc32_long(value->data, value->len);
1218:
1219: gvvn = (ngx_http_geo_variable_value_node_t *)
1220: ngx_str_rbtree_lookup(&ctx->rbtree, value, hash);
1221:
1222: if (gvvn) {
1223: return gvvn->value;
1224: }
1225:
1226: val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));
1227: if (val == NULL) {
1228: return NULL;
1229: }
1230:
1231: val->len = value->len;
1232: val->data = ngx_pstrdup(ctx->pool, value);
1233: if (val->data == NULL) {
1234: return NULL;
1235: }
1236:
1237: val->valid = 1;
1238: val->no_cacheable = 0;
1239: val->not_found = 0;
1240:
1241: gvvn = ngx_palloc(ctx->temp_pool,
1242: sizeof(ngx_http_geo_variable_value_node_t));
1243: if (gvvn == NULL) {
1244: return NULL;
1245: }
1246:
1247: gvvn->sn.node.key = hash;
1248: gvvn->sn.str.len = val->len;
1249: gvvn->sn.str.data = val->data;
1250: gvvn->value = val;
1251: gvvn->offset = 0;
1252:
1253: ngx_rbtree_insert(&ctx->rbtree, &gvvn->sn.node);
1254:
1255: ctx->data_size += ngx_align(sizeof(ngx_http_variable_value_t) + value->len,
1256: sizeof(void *));
1257:
1258: return val;
1259: }
1260:
1261:
1262: static char *
1263: ngx_http_geo_add_proxy(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
1264: ngx_cidr_t *cidr)
1265: {
1266: ngx_cidr_t *c;
1267:
1268: if (ctx->proxies == NULL) {
1269: ctx->proxies = ngx_array_create(ctx->pool, 4, sizeof(ngx_cidr_t));
1270: if (ctx->proxies == NULL) {
1271: return NGX_CONF_ERROR;
1272: }
1273: }
1274:
1275: c = ngx_array_push(ctx->proxies);
1276: if (c == NULL) {
1277: return NGX_CONF_ERROR;
1278: }
1279:
1280: *c = *cidr;
1281:
1282: return NGX_CONF_OK;
1283: }
1284:
1285:
1286: static ngx_int_t
1287: ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr)
1288: {
1289: ngx_int_t rc;
1290:
1291: if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
1292: cidr->family = AF_INET;
1293: cidr->u.in.addr = 0xffffffff;
1294: cidr->u.in.mask = 0xffffffff;
1295:
1296: return NGX_OK;
1297: }
1298:
1299: rc = ngx_ptocidr(net, cidr);
1300:
1301: if (rc == NGX_ERROR) {
1302: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net);
1303: return NGX_ERROR;
1304: }
1305:
1306: if (rc == NGX_DONE) {
1307: ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1308: "low address bits of %V are meaningless", net);
1309: }
1310:
1311: return NGX_OK;
1312: }
1313:
1314:
1315: static char *
1316: ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
1317: ngx_str_t *name)
1318: {
1319: char *rv;
1320: ngx_str_t file;
1321:
1322: file.len = name->len + 4;
1323: file.data = ngx_pnalloc(ctx->temp_pool, name->len + 5);
1324: if (file.data == NULL) {
1325: return NGX_CONF_ERROR;
1326: }
1327:
1328: ngx_sprintf(file.data, "%V.bin%Z", name);
1329:
1330: if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {
1331: return NGX_CONF_ERROR;
1332: }
1333:
1334: if (ctx->ranges) {
1335: ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
1336:
1337: switch (ngx_http_geo_include_binary_base(cf, ctx, &file)) {
1338: case NGX_OK:
1339: return NGX_CONF_OK;
1340: case NGX_ERROR:
1341: return NGX_CONF_ERROR;
1342: default:
1343: break;
1344: }
1345: }
1346:
1347: file.len -= 4;
1348: file.data[file.len] = '\0';
1349:
1350: ctx->include_name = file;
1351:
1352: if (ctx->outside_entries) {
1353: ctx->allow_binary_include = 0;
1354: }
1355:
1356: ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
1357:
1358: rv = ngx_conf_parse(cf, &file);
1359:
1360: ctx->includes++;
1361: ctx->outside_entries = 0;
1362:
1363: return rv;
1364: }
1365:
1366:
1367: static ngx_int_t
1368: ngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
1369: ngx_str_t *name)
1370: {
1371: u_char *base, ch;
1372: time_t mtime;
1373: size_t size, len;
1374: ssize_t n;
1375: uint32_t crc32;
1376: ngx_err_t err;
1377: ngx_int_t rc;
1378: ngx_uint_t i;
1379: ngx_file_t file;
1380: ngx_file_info_t fi;
1381: ngx_http_geo_range_t *range, **ranges;
1382: ngx_http_geo_header_t *header;
1383: ngx_http_variable_value_t *vv;
1384:
1385: ngx_memzero(&file, sizeof(ngx_file_t));
1386: file.name = *name;
1387: file.log = cf->log;
1388:
1389: file.fd = ngx_open_file(name->data, NGX_FILE_RDONLY, 0, 0);
1390: if (file.fd == NGX_INVALID_FILE) {
1391: err = ngx_errno;
1392: if (err != NGX_ENOENT) {
1393: ngx_conf_log_error(NGX_LOG_CRIT, cf, err,
1394: ngx_open_file_n " \"%s\" failed", name->data);
1395: }
1396: return NGX_DECLINED;
1397: }
1398:
1399: if (ctx->outside_entries) {
1400: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1401: "binary geo range base \"%s\" cannot be mixed with usual entries",
1402: name->data);
1403: rc = NGX_ERROR;
1404: goto done;
1405: }
1406:
1407: if (ctx->binary_include) {
1408: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1409: "second binary geo range base \"%s\" cannot be mixed with \"%s\"",
1410: name->data, ctx->include_name.data);
1411: rc = NGX_ERROR;
1412: goto done;
1413: }
1414:
1415: if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
1416: ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
1417: ngx_fd_info_n " \"%s\" failed", name->data);
1418: goto failed;
1419: }
1420:
1421: size = (size_t) ngx_file_size(&fi);
1422: mtime = ngx_file_mtime(&fi);
1423:
1424: ch = name->data[name->len - 4];
1425: name->data[name->len - 4] = '\0';
1426:
1427: if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {
1428: ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
1429: ngx_file_info_n " \"%s\" failed", name->data);
1430: goto failed;
1431: }
1432:
1433: name->data[name->len - 4] = ch;
1434:
1435: if (mtime < ngx_file_mtime(&fi)) {
1436: ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1437: "stale binary geo range base \"%s\"", name->data);
1438: goto failed;
1439: }
1440:
1441: base = ngx_palloc(ctx->pool, size);
1442: if (base == NULL) {
1443: goto failed;
1444: }
1445:
1446: n = ngx_read_file(&file, base, size, 0);
1447:
1448: if (n == NGX_ERROR) {
1449: ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
1450: ngx_read_file_n " \"%s\" failed", name->data);
1451: goto failed;
1452: }
1453:
1454: if ((size_t) n != size) {
1455: ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,
1456: ngx_read_file_n " \"%s\" returned only %z bytes instead of %z",
1457: name->data, n, size);
1458: goto failed;
1459: }
1460:
1461: header = (ngx_http_geo_header_t *) base;
1462:
1463: if (size < 16 || ngx_memcmp(&ngx_http_geo_header, header, 12) != 0) {
1464: ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1465: "incompatible binary geo range base \"%s\"", name->data);
1466: goto failed;
1467: }
1468:
1469: ngx_crc32_init(crc32);
1470:
1471: vv = (ngx_http_variable_value_t *) (base + sizeof(ngx_http_geo_header_t));
1472:
1473: while(vv->data) {
1474: len = ngx_align(sizeof(ngx_http_variable_value_t) + vv->len,
1475: sizeof(void *));
1476: ngx_crc32_update(&crc32, (u_char *) vv, len);
1477: vv->data += (size_t) base;
1478: vv = (ngx_http_variable_value_t *) ((u_char *) vv + len);
1479: }
1480: ngx_crc32_update(&crc32, (u_char *) vv, sizeof(ngx_http_variable_value_t));
1481: vv++;
1482:
1483: ranges = (ngx_http_geo_range_t **) vv;
1484:
1485: for (i = 0; i < 0x10000; i++) {
1486: ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *));
1487: if (ranges[i]) {
1488: ranges[i] = (ngx_http_geo_range_t *)
1489: ((u_char *) ranges[i] + (size_t) base);
1490: }
1491: }
1492:
1493: range = (ngx_http_geo_range_t *) &ranges[0x10000];
1494:
1495: while ((u_char *) range < base + size) {
1496: while (range->value) {
1497: ngx_crc32_update(&crc32, (u_char *) range,
1498: sizeof(ngx_http_geo_range_t));
1499: range->value = (ngx_http_variable_value_t *)
1500: ((u_char *) range->value + (size_t) base);
1501: range++;
1502: }
1503: ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *));
1504: range = (ngx_http_geo_range_t *) ((u_char *) range + sizeof(void *));
1505: }
1506:
1507: ngx_crc32_final(crc32);
1508:
1509: if (crc32 != header->crc32) {
1510: ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1511: "CRC32 mismatch in binary geo range base \"%s\"", name->data);
1512: goto failed;
1513: }
1514:
1515: ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0,
1516: "using binary geo range base \"%s\"", name->data);
1517:
1518: ctx->include_name = *name;
1519: ctx->binary_include = 1;
1520: ctx->high.low = ranges;
1521: rc = NGX_OK;
1522:
1523: goto done;
1524:
1525: failed:
1526:
1527: rc = NGX_DECLINED;
1528:
1529: done:
1530:
1531: if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
1532: ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
1533: ngx_close_file_n " \"%s\" failed", name->data);
1534: }
1535:
1536: return rc;
1537: }
1538:
1539:
1540: static void
1541: ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx)
1542: {
1543: u_char *p;
1544: uint32_t hash;
1545: ngx_str_t s;
1546: ngx_uint_t i;
1547: ngx_file_mapping_t fm;
1548: ngx_http_geo_range_t *r, *range, **ranges;
1549: ngx_http_geo_header_t *header;
1550: ngx_http_geo_variable_value_node_t *gvvn;
1551:
1552: fm.name = ngx_pnalloc(ctx->temp_pool, ctx->include_name.len + 5);
1553: if (fm.name == NULL) {
1554: return;
1555: }
1556:
1557: ngx_sprintf(fm.name, "%V.bin%Z", &ctx->include_name);
1558:
1559: fm.size = ctx->data_size;
1560: fm.log = ctx->pool->log;
1561:
1562: ngx_log_error(NGX_LOG_NOTICE, fm.log, 0,
1563: "creating binary geo range base \"%s\"", fm.name);
1564:
1565: if (ngx_create_file_mapping(&fm) != NGX_OK) {
1566: return;
1567: }
1568:
1569: p = ngx_cpymem(fm.addr, &ngx_http_geo_header,
1570: sizeof(ngx_http_geo_header_t));
1571:
1572: p = ngx_http_geo_copy_values(fm.addr, p, ctx->rbtree.root,
1573: ctx->rbtree.sentinel);
1574:
1575: p += sizeof(ngx_http_variable_value_t);
1576:
1577: ranges = (ngx_http_geo_range_t **) p;
1578:
1579: p += 0x10000 * sizeof(ngx_http_geo_range_t *);
1580:
1581: for (i = 0; i < 0x10000; i++) {
1582: r = ctx->high.low[i];
1583: if (r == NULL) {
1584: continue;
1585: }
1586:
1587: range = (ngx_http_geo_range_t *) p;
1588: ranges[i] = (ngx_http_geo_range_t *) (p - (u_char *) fm.addr);
1589:
1590: do {
1591: s.len = r->value->len;
1592: s.data = r->value->data;
1593: hash = ngx_crc32_long(s.data, s.len);
1594: gvvn = (ngx_http_geo_variable_value_node_t *)
1595: ngx_str_rbtree_lookup(&ctx->rbtree, &s, hash);
1596:
1597: range->value = (ngx_http_variable_value_t *) gvvn->offset;
1598: range->start = r->start;
1599: range->end = r->end;
1600: range++;
1601:
1602: } while ((++r)->value);
1603:
1604: range->value = NULL;
1605:
1606: p = (u_char *) range + sizeof(void *);
1607: }
1608:
1609: header = fm.addr;
1610: header->crc32 = ngx_crc32_long((u_char *) fm.addr
1611: + sizeof(ngx_http_geo_header_t),
1612: fm.size - sizeof(ngx_http_geo_header_t));
1613:
1614: ngx_close_file_mapping(&fm);
1615: }
1616:
1617:
1618: static u_char *
1619: ngx_http_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node,
1620: ngx_rbtree_node_t *sentinel)
1621: {
1622: ngx_http_variable_value_t *vv;
1623: ngx_http_geo_variable_value_node_t *gvvn;
1624:
1625: if (node == sentinel) {
1626: return p;
1627: }
1628:
1629: gvvn = (ngx_http_geo_variable_value_node_t *) node;
1630: gvvn->offset = p - base;
1631:
1632: vv = (ngx_http_variable_value_t *) p;
1633: *vv = *gvvn->value;
1634: p += sizeof(ngx_http_variable_value_t);
1635: vv->data = (u_char *) (p - base);
1636:
1637: p = ngx_cpymem(p, gvvn->sn.str.data, gvvn->sn.str.len);
1638:
1639: p = ngx_align_ptr(p, sizeof(void *));
1640:
1641: p = ngx_http_geo_copy_values(base, p, node->left, sentinel);
1642:
1643: return ngx_http_geo_copy_values(base, p, node->right, sentinel);
1644: }