upstream nginx-0.7.34
[nginx.git] / nginx / src / http / modules / ngx_http_geo_module.c
1
2 /*
3  * Copyright (C) Igor Sysoev
4  */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_http.h>
10
11
12 typedef struct {
13     u_short                          start;
14     u_short                          end;
15     ngx_http_variable_value_t       *value;
16 } ngx_http_geo_range_t;
17
18
19 typedef struct {
20     ngx_http_geo_range_t            *ranges;
21     ngx_uint_t                       n;
22 } ngx_http_geo_low_ranges_t;
23
24
25 typedef struct {
26     ngx_http_geo_low_ranges_t        low[0x10000];
27     ngx_http_variable_value_t       *default_value;
28 } ngx_http_geo_high_ranges_t;
29
30
31 typedef struct {
32     ngx_http_variable_value_t       *value;
33     ngx_str_t                       *net;
34     ngx_http_geo_high_ranges_t      *high;
35     ngx_radix_tree_t                *tree;
36     ngx_rbtree_t                     rbtree;
37     ngx_rbtree_node_t                sentinel;
38     ngx_pool_t                      *pool;
39     ngx_pool_t                      *temp_pool;
40 } ngx_http_geo_conf_ctx_t;
41
42
43 typedef struct {
44     union {
45         ngx_radix_tree_t            *tree;
46         ngx_http_geo_high_ranges_t  *high;
47     } u;
48
49     ngx_int_t                        index;
50 } ngx_http_geo_ctx_t;
51
52
53 static in_addr_t ngx_http_geo_addr(ngx_http_request_t *r,
54     ngx_http_geo_ctx_t *ctx);
55 static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
56 static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
57 static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
58     ngx_str_t *value);
59 static char *ngx_http_geo_add_range(ngx_conf_t *cf,
60     ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
61 static ngx_uint_t ngx_http_geo_delete_range(ngx_conf_t *cf,
62     ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
63 static char *ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
64     ngx_str_t *value);
65 static ngx_http_variable_value_t *ngx_http_geo_value(ngx_conf_t *cf,
66     ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value);
67
68
69 static ngx_command_t  ngx_http_geo_commands[] = {
70
71     { ngx_string("geo"),
72       NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,
73       ngx_http_geo_block,
74       NGX_HTTP_MAIN_CONF_OFFSET,
75       0,
76       NULL },
77
78       ngx_null_command
79 };
80
81
82 static ngx_http_module_t  ngx_http_geo_module_ctx = {
83     NULL,                                  /* preconfiguration */
84     NULL,                                  /* postconfiguration */
85
86     NULL,                                  /* create main configuration */
87     NULL,                                  /* init main configuration */
88
89     NULL,                                  /* create server configuration */
90     NULL,                                  /* merge server configuration */
91
92     NULL,                                  /* create location configuration */
93     NULL                                   /* merge location configuration */
94 };
95
96
97 ngx_module_t  ngx_http_geo_module = {
98     NGX_MODULE_V1,
99     &ngx_http_geo_module_ctx,              /* module context */
100     ngx_http_geo_commands,                 /* module directives */
101     NGX_HTTP_MODULE,                       /* module type */
102     NULL,                                  /* init master */
103     NULL,                                  /* init module */
104     NULL,                                  /* init process */
105     NULL,                                  /* init thread */
106     NULL,                                  /* exit thread */
107     NULL,                                  /* exit process */
108     NULL,                                  /* exit master */
109     NGX_MODULE_V1_PADDING
110 };
111
112
113 /* AF_INET only */
114
115 static ngx_int_t
116 ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
117     uintptr_t data)
118 {
119     ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
120
121     ngx_http_variable_value_t  *vv;
122
123     vv = (ngx_http_variable_value_t *)
124               ngx_radix32tree_find(ctx->u.tree, ngx_http_geo_addr(r, ctx));
125
126     *v = *vv;
127
128     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
129                    "http geo: %v", v);
130
131     return NGX_OK;
132 }
133
134
135 static ngx_int_t
136 ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
137     uintptr_t data)
138 {
139     ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
140
141     in_addr_t              addr;
142     ngx_uint_t             i, n;
143     ngx_http_geo_range_t  *range;
144
145     *v = *ctx->u.high->default_value;
146
147     addr = ngx_http_geo_addr(r, ctx);
148
149     range = ctx->u.high->low[addr >> 16].ranges;
150
151     n = addr & 0xffff;
152
153     for (i = 0; i < ctx->u.high->low[addr >> 16].n; i++) {
154         if (n >= (ngx_uint_t) range[i].start
155             && n <= (ngx_uint_t) range[i].end)
156         {
157             *v = *range[i].value;
158         }
159     }
160
161     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
162                    "http geo: %v", v);
163
164     return NGX_OK;
165 }
166
167
168 static in_addr_t
169 ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx)
170 {
171     struct sockaddr_in         *sin;
172     ngx_http_variable_value_t  *v;
173
174     if (ctx->index == -1) {
175         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
176                        "http geo started: %V", &r->connection->addr_text);
177
178         sin = (struct sockaddr_in *) r->connection->sockaddr;
179         return ntohl(sin->sin_addr.s_addr);
180     }
181
182     v = ngx_http_get_flushed_variable(r, ctx->index);
183
184     if (v == NULL || v->not_found) {
185         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
186                        "http geo not found");
187
188         return 0;
189     }
190
191     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
192                    "http geo started: %v", v);
193
194     return ntohl(ngx_inet_addr(v->data, v->len));
195 }
196
197
198 static char *
199 ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
200 {
201     char                     *rv;
202     size_t                    len;
203     ngx_str_t                *value, name;
204     ngx_uint_t                i;
205     ngx_conf_t                save;
206     ngx_pool_t               *pool;
207     ngx_array_t              *a;
208     ngx_http_variable_t      *var;
209     ngx_http_geo_ctx_t       *geo;
210     ngx_http_geo_conf_ctx_t   ctx;
211
212     value = cf->args->elts;
213
214     geo = ngx_palloc(cf->pool, sizeof(ngx_http_geo_ctx_t));
215     if (geo == NULL) {
216         return NGX_CONF_ERROR;
217     }
218
219     name = value[1];
220     name.len--;
221     name.data++;
222
223     if (cf->args->nelts == 3) {
224
225         geo->index = ngx_http_get_variable_index(cf, &name);
226         if (geo->index == NGX_ERROR) {
227             return NGX_CONF_ERROR;
228         }
229
230         name = value[2];
231         name.len--;
232         name.data++;
233
234     } else {
235         geo->index = -1;
236     }
237
238     var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
239     if (var == NULL) {
240         return NGX_CONF_ERROR;
241     }
242
243     pool = ngx_create_pool(16384, cf->log);
244     if (pool == NULL) {
245         return NGX_CONF_ERROR;
246     }
247
248     ctx.temp_pool = ngx_create_pool(16384, cf->log);
249     if (ctx.temp_pool == NULL) {
250         return NGX_CONF_ERROR;
251     }
252
253     ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel,
254                     ngx_http_variable_value_rbtree_insert);
255
256     ctx.high = NULL;
257     ctx.tree = NULL;
258     ctx.pool = cf->pool;
259
260     save = *cf;
261     cf->pool = pool;
262     cf->ctx = &ctx;
263     cf->handler = ngx_http_geo;
264     cf->handler_conf = conf;
265
266     rv = ngx_conf_parse(cf, NULL);
267
268     *cf = save;
269
270     if (ctx.high) {
271
272         for (i = 0; i < 0x10000; i++) {
273             a = (ngx_array_t *) ctx.high->low[i].ranges;
274
275             if (a == NULL || a->nelts == 0) {
276                 continue;
277             }
278
279             ctx.high->low[i].n = a->nelts;
280
281             len = a->nelts * sizeof(ngx_http_geo_range_t);
282
283             ctx.high->low[i].ranges = ngx_palloc(cf->pool, len);
284             if (ctx.high->low[i].ranges == NULL ){
285                 return NGX_CONF_ERROR;
286             }
287
288             ngx_memcpy(ctx.high->low[i].ranges, a->elts, len);
289         }
290
291         geo->u.high = ctx.high;
292
293         var->get_handler = ngx_http_geo_range_variable;
294         var->data = (uintptr_t) geo;
295
296         ngx_destroy_pool(ctx.temp_pool);
297         ngx_destroy_pool(pool);
298
299         if (ctx.high->default_value == NULL) {
300             ctx.high->default_value = &ngx_http_variable_null_value;
301         }
302
303     } else {
304         if (ctx.tree == NULL) {
305             ctx.tree = ngx_radix_tree_create(cf->pool, -1);
306             if (ctx.tree == NULL) {
307                 return NGX_CONF_ERROR;
308             }
309         }
310
311         geo->u.tree = ctx.tree;
312
313         var->get_handler = ngx_http_geo_cidr_variable;
314         var->data = (uintptr_t) geo;
315
316         ngx_destroy_pool(ctx.temp_pool);
317         ngx_destroy_pool(pool);
318
319         if (ngx_radix32tree_find(ctx.tree, 0) != NGX_RADIX_NO_VALUE) {
320             return rv;
321         }
322
323         if (ngx_radix32tree_insert(ctx.tree, 0, 0,
324                                    (uintptr_t) &ngx_http_variable_null_value)
325             == NGX_ERROR)
326         {
327             return NGX_CONF_ERROR;
328         }
329     }
330
331     return rv;
332 }
333
334
335 static char *
336 ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
337 {
338     char                     *rv;
339     ngx_str_t                *value, file;
340     ngx_http_geo_conf_ctx_t  *ctx;
341
342     ctx = cf->ctx;
343
344     value = cf->args->elts;
345
346     if (cf->args->nelts == 1) {
347
348         if (ngx_strcmp(value[0].data, "ranges") == 0) {
349
350             if (ctx->tree) {
351                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
352                                    "the \"ranges\" directive must be "
353                                    "the first directive inside \"geo\" block");
354                 goto failed;
355             }
356
357             ctx->high = ngx_pcalloc(ctx->pool,
358                                     sizeof(ngx_http_geo_high_ranges_t));
359             if (ctx->high == NULL) {
360                 goto failed;
361             }
362
363             rv = NGX_CONF_OK;
364
365             goto done;
366         }
367     }
368
369     if (cf->args->nelts != 2) {
370         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
371                            "invalid number of the geo parameters");
372         goto failed;
373     }
374
375     if (ngx_strcmp(value[0].data, "include") == 0) {
376
377         file.len = value[1].len++;
378
379         file.data = ngx_pstrdup(ctx->temp_pool, &value[1]);
380         if (file.data == NULL) {
381             goto failed;
382         }
383
384         if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK){
385             goto failed;
386         }
387
388         ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
389
390         rv = ngx_conf_parse(cf, &file);
391
392         goto done;
393     }
394
395     if (ctx->high) {
396         rv = ngx_http_geo_range(cf, ctx, value);
397
398     } else {
399         rv = ngx_http_geo_cidr(cf, ctx, value);
400     }
401
402 done:
403
404     ngx_reset_pool(cf->pool);
405
406     return rv;
407
408 failed:
409
410     ngx_reset_pool(cf->pool);
411
412     return NGX_CONF_ERROR;
413 }
414
415
416 static char *
417 ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
418     ngx_str_t *value)
419 {
420     u_char                     *p, *last;
421     in_addr_t                   start, end;
422     ngx_str_t                  *net;
423     ngx_uint_t                  del;
424     ngx_http_variable_value_t  *old;
425
426     if (ngx_strcmp(value[0].data, "default") == 0) {
427
428         old = ctx->high->default_value;
429
430         ctx->high->default_value = ngx_http_geo_value(cf, ctx, &value[1]);
431         if (ctx->high->default_value == NULL) {
432             return NGX_CONF_ERROR;
433         }
434
435         if (old) {
436             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
437                     "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
438                     &value[0], ctx->high->default_value, old);
439         }
440
441         return NGX_CONF_OK;
442     }
443
444     if (ngx_strcmp(value[0].data, "delete") == 0) {
445         net = &value[1];
446         del = 1;
447
448     } else {
449         net = &value[0];
450         del = 0;
451     }
452
453     last = net->data + net->len;
454
455     p = ngx_strlchr(net->data, last, '-');
456
457     if (p == NULL) {
458         goto invalid;
459     }
460
461     start = ngx_inet_addr(net->data, p - net->data);
462
463     if (start == INADDR_NONE) {
464         goto invalid;
465     }
466
467     start = ntohl(start);
468
469     p++;
470
471     end = ngx_inet_addr(p, last - p);
472
473     if (end == INADDR_NONE) {
474         goto invalid;
475     }
476
477     end = ntohl(end);
478
479     if (start > end) {
480         goto invalid;
481     }
482
483     if (del) {
484         if (ngx_http_geo_delete_range(cf, ctx, start, end)) {
485             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
486                                "no address range \"%V\" to delete", net);
487         }
488
489         return NGX_CONF_OK;
490     }
491
492     ctx->value = ngx_http_geo_value(cf, ctx, &value[1]);
493
494     if (ctx->value == NULL) {
495         return NGX_CONF_ERROR;
496     }
497
498     ctx->net = net;
499
500     return ngx_http_geo_add_range(cf, ctx, start, end);
501
502 invalid:
503
504     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid range \"%V\"", net);
505
506     return NGX_CONF_ERROR;
507 }
508
509
510 /* the add procedure is optimized to add a growing up sequence */
511
512 static char *
513 ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
514     in_addr_t start, in_addr_t end)
515 {
516     in_addr_t              n;
517     ngx_uint_t             h, i, s, e;
518     ngx_array_t           *a;
519     ngx_http_geo_range_t  *range;
520
521     for (n = start; n <= end; n += 0x10000) {
522
523         h = n >> 16;
524
525         if (n == start) {
526             s = n & 0xffff;
527         } else {
528             s = 0;
529         }
530
531         if ((n | 0xffff) > end) {
532             e = end & 0xffff;
533
534         } else {
535             e = 0xffff;
536         }
537
538         a = (ngx_array_t *) ctx->high->low[h].ranges;
539
540         if (a == NULL) {
541             a = ngx_array_create(ctx->temp_pool, 64,
542                                  sizeof(ngx_http_geo_range_t));
543             if (a == NULL) {
544                 return NGX_CONF_ERROR;
545             }
546
547             ctx->high->low[h].ranges = (ngx_http_geo_range_t *) a;
548         }
549
550         i = a->nelts;
551         range = a->elts;
552
553         while (i) {
554
555             i--;
556
557             if (e < (ngx_uint_t) range[i].start) {
558                 continue;
559             }
560
561             if (s > (ngx_uint_t) range[i].end) {
562
563                 /* add after the range */
564
565                 range = ngx_array_push(a);
566                 if (range == NULL) {
567                     return NGX_CONF_ERROR;
568                 }
569
570                 range = a->elts;
571
572                 ngx_memcpy(&range[i + 2], &range[i + 1],
573                            (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));
574
575                 range[i + 1].start = (u_short) s;
576                 range[i + 1].end = (u_short) e;
577                 range[i + 1].value = ctx->value;
578
579                 goto next;
580             }
581
582             if (s == (ngx_uint_t) range[i].start
583                 && e == (ngx_uint_t) range[i].end)
584             {
585                 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
586                     "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
587                     ctx->net, ctx->value, range[i].value);
588
589                 range[i].value = ctx->value;
590
591                 goto next;
592             }
593
594             if (s > (ngx_uint_t) range[i].start
595                 && e < (ngx_uint_t) range[i].end)
596             {
597                 /* split the range and insert the new one */
598
599                 range = ngx_array_push(a);
600                 if (range == NULL) {
601                     return NGX_CONF_ERROR;
602                 }
603
604                 range = ngx_array_push(a);
605                 if (range == NULL) {
606                     return NGX_CONF_ERROR;
607                 }
608
609                 range = a->elts;
610
611                 ngx_memcpy(&range[i + 3], &range[i + 1],
612                            (a->nelts - 3 - i) * sizeof(ngx_http_geo_range_t));
613
614                 range[i + 2].start = (u_short) (e + 1);
615                 range[i + 2].end = range[i].end;
616                 range[i + 2].value = range[i].value;
617
618                 range[i + 1].start = (u_short) s;
619                 range[i + 1].end = (u_short) e;
620                 range[i + 1].value = ctx->value;
621
622                 range[i].end = (u_short) (s - 1);
623
624                 goto next;
625             }
626
627             if (s == (ngx_uint_t) range[i].start
628                 && e < (ngx_uint_t) range[i].end)
629             {
630                 /* shift the range start and insert the new range */
631
632                 range = ngx_array_push(a);
633                 if (range == NULL) {
634                     return NGX_CONF_ERROR;
635                 }
636
637                 range = a->elts;
638
639                 ngx_memcpy(&range[i + 1], &range[i],
640                            (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));
641
642                 range[i + 1].start = (u_short) (e + 1);
643
644                 range[i].start = (u_short) s;
645                 range[i].end = (u_short) e;
646                 range[i].value = ctx->value;
647
648                 goto next;
649             }
650
651             if (s > (ngx_uint_t) range[i].start
652                 && e == (ngx_uint_t) range[i].end)
653             {
654                 /* shift the range end and insert the new range */
655
656                 range = ngx_array_push(a);
657                 if (range == NULL) {
658                     return NGX_CONF_ERROR;
659                 }
660
661                 range = a->elts;
662
663                 ngx_memcpy(&range[i + 2], &range[i + 1],
664                            (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));
665
666                 range[i + 1].start = (u_short) s;
667                 range[i + 1].end = (u_short) e;
668                 range[i + 1].value = ctx->value;
669
670                 range[i].end = (u_short) (s - 1);
671
672                 goto next;
673             }
674
675             s = (ngx_uint_t) range[i].start;
676             e = (ngx_uint_t) range[i].end;
677
678             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
679                          "range \"%V\" overlaps \"%d.%d.%d.%d-%d.%d.%d.%d\"",
680                          ctx->net,
681                          h >> 8, h & 0xff, s >> 8, s & 0xff,
682                          h >> 8, h & 0xff, e >> 8, e & 0xff);
683
684             return NGX_CONF_ERROR;
685         }
686
687         /* add the first range */
688
689         range = ngx_array_push(a);
690         if (range == NULL) {
691             return NGX_CONF_ERROR;
692         }
693
694         range->start = (u_short) s;
695         range->end = (u_short) e;
696         range->value = ctx->value;
697
698     next:
699
700         continue;
701     }
702
703     return NGX_CONF_OK;
704 }
705
706
707 static ngx_uint_t
708 ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
709     in_addr_t start, in_addr_t end)
710 {
711     in_addr_t              n;
712     ngx_uint_t             h, i, s, e, warn;
713     ngx_array_t           *a;
714     ngx_http_geo_range_t  *range;
715
716     warn = 0;
717
718     for (n = start; n <= end; n += 0x10000) {
719
720         h = n >> 16;
721
722         if (n == start) {
723             s = n & 0xffff;
724         } else {
725             s = 0;
726         }
727
728         if ((n | 0xffff) > end) {
729             e = end & 0xffff;
730
731         } else {
732             e = 0xffff;
733         }
734
735         a = (ngx_array_t *) ctx->high->low[h].ranges;
736
737         if (a == NULL) {
738             warn = 1;
739             continue;
740         }
741
742         range = a->elts;
743         for (i = 0; i < a->nelts; i++) {
744
745             if (s == (ngx_uint_t) range[i].start
746                 && e == (ngx_uint_t) range[i].end)
747             {
748                 ngx_memcpy(&range[i], &range[i + 1],
749                            (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));
750
751                 a->nelts--;
752
753                 break;
754             }
755
756             if (s != (ngx_uint_t) range[i].start
757                 && e != (ngx_uint_t) range[i].end)
758             {
759                 continue;
760             }
761
762             warn = 1;
763         }
764     }
765
766     return warn;
767 }
768
769
770 static char *
771 ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
772     ngx_str_t *value)
773 {
774     ngx_int_t                        rc, del;
775     ngx_str_t                       *net;
776     ngx_uint_t                       i;
777     ngx_inet_cidr_t                  cidrin;
778     ngx_http_variable_value_t       *val, *old;
779
780     if (ctx->tree == NULL) {
781         ctx->tree = ngx_radix_tree_create(ctx->pool, -1);
782         if (ctx->tree == NULL) {
783             return NGX_CONF_ERROR;
784         }
785     }
786
787     if (ngx_strcmp(value[0].data, "default") == 0) {
788         cidrin.addr = 0;
789         cidrin.mask = 0;
790         net = &value[0];
791
792     } else {
793         if (ngx_strcmp(value[0].data, "delete") == 0) {
794             net = &value[1];
795             del = 1;
796
797         } else {
798             net = &value[0];
799             del = 0;
800         }
801
802         if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
803             cidrin.addr = 0xffffffff;
804             cidrin.mask = 0xffffffff;
805
806         } else {
807             rc = ngx_ptocidr(net, &cidrin);
808
809             if (rc == NGX_ERROR) {
810                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
811                                    "invalid network \"%V\"", net);
812                 return NGX_CONF_ERROR;
813             }
814
815             if (rc == NGX_DONE) {
816                 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
817                                    "low address bits of %V are meaningless",
818                                    net);
819             }
820
821             cidrin.addr = ntohl(cidrin.addr);
822             cidrin.mask = ntohl(cidrin.mask);
823         }
824
825         if (del) {
826             if (ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask)
827                 != NGX_OK)
828             {
829                 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
830                                    "no network \"%V\" to delete", net);
831             }
832
833             return NGX_CONF_OK;
834         }
835     }
836
837     val = ngx_http_geo_value(cf, ctx, &value[1]);
838
839     if (val == NULL) {
840         return NGX_CONF_ERROR;
841     }
842
843     for (i = 2; i; i--) {
844         rc = ngx_radix32tree_insert(ctx->tree, cidrin.addr, cidrin.mask,
845                                     (uintptr_t) val);
846         if (rc == NGX_OK) {
847             return NGX_CONF_OK;
848         }
849
850         if (rc == NGX_ERROR) {
851             return NGX_CONF_ERROR;
852         }
853
854         /* rc == NGX_BUSY */
855
856         old  = (ngx_http_variable_value_t *)
857                    ngx_radix32tree_find(ctx->tree, cidrin.addr & cidrin.mask);
858
859         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
860                 "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
861                 net, val, old);
862
863         rc = ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask);
864
865         if (rc == NGX_ERROR) {
866             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
867             return NGX_CONF_ERROR;
868         }
869     }
870
871     return NGX_CONF_ERROR;
872 }
873
874
875 static ngx_http_variable_value_t *
876 ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
877     ngx_str_t *value)
878 {
879     uint32_t                         hash;
880     ngx_http_variable_value_t       *val;
881     ngx_http_variable_value_node_t  *vvn;
882
883     hash = ngx_crc32_long(value->data, value->len);
884
885     val = ngx_http_variable_value_lookup(&ctx->rbtree, value, hash);
886
887     if (val) {
888         return val;
889     }
890
891     val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));
892     if (val == NULL) {
893         return NULL;
894     }
895
896     val->len = value->len;
897     val->data = ngx_pstrdup(ctx->pool, value);
898     if (val->data == NULL) {
899         return NULL;
900     }
901
902     val->valid = 1;
903     val->no_cacheable = 0;
904     val->not_found = 0;
905
906     vvn = ngx_palloc(ctx->temp_pool, sizeof(ngx_http_variable_value_node_t));
907     if (vvn == NULL) {
908         return NULL;
909     }
910
911     vvn->node.key = hash;
912     vvn->len = val->len;
913     vvn->value = val;
914
915     ngx_rbtree_insert(&ctx->rbtree, &vvn->node);
916
917     return val;
918 }