comparison src/stream/ngx_stream_geo_module.c @ 6631:80875b75d27e

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