~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Nginx/http/modules/ngx_http_geo_module.c

Version: ~ [ nginx-1.4.1 ] ~ [ nginx-1.5.0 ] ~

  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 }
1645 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.