Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.13.12 ]​[ nginx-1.12.2 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_http.h>
0011 
0012 
0013 typedef struct {
0014     ngx_http_variable_value_t       *value;
0015     u_short                          start;
0016     u_short                          end;
0017 } ngx_http_geo_range_t;
0018 
0019 
0020 typedef struct {
0021     ngx_radix_tree_t                *tree;
0022 #if (NGX_HAVE_INET6)
0023     ngx_radix_tree_t                *tree6;
0024 #endif
0025 } ngx_http_geo_trees_t;
0026 
0027 
0028 typedef struct {
0029     ngx_http_geo_range_t           **low;
0030     ngx_http_variable_value_t       *default_value;
0031 } ngx_http_geo_high_ranges_t;
0032 
0033 
0034 typedef struct {
0035     ngx_str_node_t                   sn;
0036     ngx_http_variable_value_t       *value;
0037     size_t                           offset;
0038 } ngx_http_geo_variable_value_node_t;
0039 
0040 
0041 typedef struct {
0042     ngx_http_variable_value_t       *value;
0043     ngx_str_t                       *net;
0044     ngx_http_geo_high_ranges_t       high;
0045     ngx_radix_tree_t                *tree;
0046 #if (NGX_HAVE_INET6)
0047     ngx_radix_tree_t                *tree6;
0048 #endif
0049     ngx_rbtree_t                     rbtree;
0050     ngx_rbtree_node_t                sentinel;
0051     ngx_array_t                     *proxies;
0052     ngx_pool_t                      *pool;
0053     ngx_pool_t                      *temp_pool;
0054 
0055     size_t                           data_size;
0056 
0057     ngx_str_t                        include_name;
0058     ngx_uint_t                       includes;
0059     ngx_uint_t                       entries;
0060 
0061     unsigned                         ranges:1;
0062     unsigned                         outside_entries:1;
0063     unsigned                         allow_binary_include:1;
0064     unsigned                         binary_include:1;
0065     unsigned                         proxy_recursive:1;
0066 } ngx_http_geo_conf_ctx_t;
0067 
0068 
0069 typedef struct {
0070     union {
0071         ngx_http_geo_trees_t         trees;
0072         ngx_http_geo_high_ranges_t   high;
0073     } u;
0074 
0075     ngx_array_t                     *proxies;
0076     unsigned                         proxy_recursive:1;
0077 
0078     ngx_int_t                        index;
0079 } ngx_http_geo_ctx_t;
0080 
0081 
0082 static ngx_int_t ngx_http_geo_addr(ngx_http_request_t *r,
0083     ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr);
0084 static ngx_int_t ngx_http_geo_real_addr(ngx_http_request_t *r,
0085     ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr);
0086 static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
0087 static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
0088 static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
0089     ngx_str_t *value);
0090 static char *ngx_http_geo_add_range(ngx_conf_t *cf,
0091     ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
0092 static ngx_uint_t ngx_http_geo_delete_range(ngx_conf_t *cf,
0093     ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
0094 static char *ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
0095     ngx_str_t *value);
0096 static char *ngx_http_geo_cidr_add(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
0097     ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net);
0098 static ngx_http_variable_value_t *ngx_http_geo_value(ngx_conf_t *cf,
0099     ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value);
0100 static char *ngx_http_geo_add_proxy(ngx_conf_t *cf,
0101     ngx_http_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr);
0102 static ngx_int_t ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net,
0103     ngx_cidr_t *cidr);
0104 static char *ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
0105     ngx_str_t *name);
0106 static ngx_int_t ngx_http_geo_include_binary_base(ngx_conf_t *cf,
0107     ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *name);
0108 static void ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx);
0109 static u_char *ngx_http_geo_copy_values(u_char *base, u_char *p,
0110     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
0111 
0112 
0113 static ngx_command_t  ngx_http_geo_commands[] = {
0114 
0115     { ngx_string("geo"),
0116       NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,
0117       ngx_http_geo_block,
0118       NGX_HTTP_MAIN_CONF_OFFSET,
0119       0,
0120       NULL },
0121 
0122       ngx_null_command
0123 };
0124 
0125 
0126 static ngx_http_module_t  ngx_http_geo_module_ctx = {
0127     NULL,                                  /* preconfiguration */
0128     NULL,                                  /* postconfiguration */
0129 
0130     NULL,                                  /* create main configuration */
0131     NULL,                                  /* init main configuration */
0132 
0133     NULL,                                  /* create server configuration */
0134     NULL,                                  /* merge server configuration */
0135 
0136     NULL,                                  /* create location configuration */
0137     NULL                                   /* merge location configuration */
0138 };
0139 
0140 
0141 ngx_module_t  ngx_http_geo_module = {
0142     NGX_MODULE_V1,
0143     &ngx_http_geo_module_ctx,              /* module context */
0144     ngx_http_geo_commands,                 /* module directives */
0145     NGX_HTTP_MODULE,                       /* module type */
0146     NULL,                                  /* init master */
0147     NULL,                                  /* init module */
0148     NULL,                                  /* init process */
0149     NULL,                                  /* init thread */
0150     NULL,                                  /* exit thread */
0151     NULL,                                  /* exit process */
0152     NULL,                                  /* exit master */
0153     NGX_MODULE_V1_PADDING
0154 };
0155 
0156 
0157 typedef struct {
0158     u_char    GEORNG[6];
0159     u_char    version;
0160     u_char    ptr_size;
0161     uint32_t  endianness;
0162     uint32_t  crc32;
0163 } ngx_http_geo_header_t;
0164 
0165 
0166 static ngx_http_geo_header_t  ngx_http_geo_header = {
0167     { 'G', 'E', 'O', 'R', 'N', 'G' }, 0, sizeof(void *), 0x12345678, 0
0168 };
0169 
0170 
0171 /* geo range is AF_INET only */
0172 
0173 static ngx_int_t
0174 ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
0175     uintptr_t data)
0176 {
0177     ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
0178 
0179     in_addr_t                   inaddr;
0180     ngx_addr_t                  addr;
0181     struct sockaddr_in         *sin;
0182     ngx_http_variable_value_t  *vv;
0183 #if (NGX_HAVE_INET6)
0184     u_char                     *p;
0185     struct in6_addr            *inaddr6;
0186 #endif
0187 
0188     if (ngx_http_geo_addr(r, ctx, &addr) != NGX_OK) {
0189         vv = (ngx_http_variable_value_t *)
0190                   ngx_radix32tree_find(ctx->u.trees.tree, INADDR_NONE);
0191         goto done;
0192     }
0193 
0194     switch (addr.sockaddr->sa_family) {
0195 
0196 #if (NGX_HAVE_INET6)
0197     case AF_INET6:
0198         inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;
0199         p = inaddr6->s6_addr;
0200 
0201         if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
0202             inaddr = p[12] << 24;
0203             inaddr += p[13] << 16;
0204             inaddr += p[14] << 8;
0205             inaddr += p[15];
0206 
0207             vv = (ngx_http_variable_value_t *)
0208                       ngx_radix32tree_find(ctx->u.trees.tree, inaddr);
0209 
0210         } else {
0211             vv = (ngx_http_variable_value_t *)
0212                       ngx_radix128tree_find(ctx->u.trees.tree6, p);
0213         }
0214 
0215         break;
0216 #endif
0217 
0218     default: /* AF_INET */
0219         sin = (struct sockaddr_in *) addr.sockaddr;
0220         inaddr = ntohl(sin->sin_addr.s_addr);
0221 
0222         vv = (ngx_http_variable_value_t *)
0223                   ngx_radix32tree_find(ctx->u.trees.tree, inaddr);
0224 
0225         break;
0226     }
0227 
0228 done:
0229 
0230     *v = *vv;
0231 
0232     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0233                    "http geo: %v", v);
0234 
0235     return NGX_OK;
0236 }
0237 
0238 
0239 static ngx_int_t
0240 ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
0241     uintptr_t data)
0242 {
0243     ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
0244 
0245     in_addr_t              inaddr;
0246     ngx_addr_t             addr;
0247     ngx_uint_t             n;
0248     struct sockaddr_in    *sin;
0249     ngx_http_geo_range_t  *range;
0250 #if (NGX_HAVE_INET6)
0251     u_char                *p;
0252     struct in6_addr       *inaddr6;
0253 #endif
0254 
0255     *v = *ctx->u.high.default_value;
0256 
0257     if (ngx_http_geo_addr(r, ctx, &addr) == NGX_OK) {
0258 
0259         switch (addr.sockaddr->sa_family) {
0260 
0261 #if (NGX_HAVE_INET6)
0262         case AF_INET6:
0263             inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;
0264 
0265             if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
0266                 p = inaddr6->s6_addr;
0267 
0268                 inaddr = p[12] << 24;
0269                 inaddr += p[13] << 16;
0270                 inaddr += p[14] << 8;
0271                 inaddr += p[15];
0272 
0273             } else {
0274                 inaddr = INADDR_NONE;
0275             }
0276 
0277             break;
0278 #endif
0279 
0280         default: /* AF_INET */
0281             sin = (struct sockaddr_in *) addr.sockaddr;
0282             inaddr = ntohl(sin->sin_addr.s_addr);
0283             break;
0284         }
0285 
0286     } else {
0287         inaddr = INADDR_NONE;
0288     }
0289 
0290     if (ctx->u.high.low) {
0291         range = ctx->u.high.low[inaddr >> 16];
0292 
0293         if (range) {
0294             n = inaddr & 0xffff;
0295             do {
0296                 if (n >= (ngx_uint_t) range->start
0297                     && n <= (ngx_uint_t) range->end)
0298                 {
0299                     *v = *range->value;
0300                     break;
0301                 }
0302             } while ((++range)->value);
0303         }
0304     }
0305 
0306     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0307                    "http geo: %v", v);
0308 
0309     return NGX_OK;
0310 }
0311 
0312 
0313 static ngx_int_t
0314 ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx,
0315     ngx_addr_t *addr)
0316 {
0317     ngx_array_t  *xfwd;
0318 
0319     if (ngx_http_geo_real_addr(r, ctx, addr) != NGX_OK) {
0320         return NGX_ERROR;
0321     }
0322 
0323     xfwd = &r->headers_in.x_forwarded_for;
0324 
0325     if (xfwd->nelts > 0 && ctx->proxies != NULL) {
0326         (void) ngx_http_get_forwarded_addr(r, addr, xfwd, NULL,
0327                                            ctx->proxies, ctx->proxy_recursive);
0328     }
0329 
0330     return NGX_OK;
0331 }
0332 
0333 
0334 static ngx_int_t
0335 ngx_http_geo_real_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx,
0336     ngx_addr_t *addr)
0337 {
0338     ngx_http_variable_value_t  *v;
0339 
0340     if (ctx->index == -1) {
0341         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0342                        "http geo started: %V", &r->connection->addr_text);
0343 
0344         addr->sockaddr = r->connection->sockaddr;
0345         addr->socklen = r->connection->socklen;
0346         /* addr->name = r->connection->addr_text; */
0347 
0348         return NGX_OK;
0349     }
0350 
0351     v = ngx_http_get_flushed_variable(r, ctx->index);
0352 
0353     if (v == NULL || v->not_found) {
0354         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0355                        "http geo not found");
0356 
0357         return NGX_ERROR;
0358     }
0359 
0360     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0361                    "http geo started: %v", v);
0362 
0363     if (ngx_parse_addr(r->pool, addr, v->data, v->len) == NGX_OK) {
0364         return NGX_OK;
0365     }
0366 
0367     return NGX_ERROR;
0368 }
0369 
0370 
0371 static char *
0372 ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0373 {
0374     char                     *rv;
0375     size_t                    len;
0376     ngx_str_t                *value, name;
0377     ngx_uint_t                i;
0378     ngx_conf_t                save;
0379     ngx_pool_t               *pool;
0380     ngx_array_t              *a;
0381     ngx_http_variable_t      *var;
0382     ngx_http_geo_ctx_t       *geo;
0383     ngx_http_geo_conf_ctx_t   ctx;
0384 #if (NGX_HAVE_INET6)
0385     static struct in6_addr    zero;
0386 #endif
0387 
0388     value = cf->args->elts;
0389 
0390     geo = ngx_palloc(cf->pool, sizeof(ngx_http_geo_ctx_t));
0391     if (geo == NULL) {
0392         return NGX_CONF_ERROR;
0393     }
0394 
0395     name = value[1];
0396 
0397     if (name.data[0] != '$') {
0398         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0399                            "invalid variable name \"%V\"", &name);
0400         return NGX_CONF_ERROR;
0401     }
0402 
0403     name.len--;
0404     name.data++;
0405 
0406     if (cf->args->nelts == 3) {
0407 
0408         geo->index = ngx_http_get_variable_index(cf, &name);
0409         if (geo->index == NGX_ERROR) {
0410             return NGX_CONF_ERROR;
0411         }
0412 
0413         name = value[2];
0414 
0415         if (name.data[0] != '$') {
0416             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0417                                "invalid variable name \"%V\"", &name);
0418             return NGX_CONF_ERROR;
0419         }
0420 
0421         name.len--;
0422         name.data++;
0423 
0424     } else {
0425         geo->index = -1;
0426     }
0427 
0428     var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
0429     if (var == NULL) {
0430         return NGX_CONF_ERROR;
0431     }
0432 
0433     pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
0434     if (pool == NULL) {
0435         return NGX_CONF_ERROR;
0436     }
0437 
0438     ngx_memzero(&ctx, sizeof(ngx_http_geo_conf_ctx_t));
0439 
0440     ctx.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
0441     if (ctx.temp_pool == NULL) {
0442         ngx_destroy_pool(pool);
0443         return NGX_CONF_ERROR;
0444     }
0445 
0446     ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, ngx_str_rbtree_insert_value);
0447 
0448     ctx.pool = cf->pool;
0449     ctx.data_size = sizeof(ngx_http_geo_header_t)
0450                   + sizeof(ngx_http_variable_value_t)
0451                   + 0x10000 * sizeof(ngx_http_geo_range_t *);
0452     ctx.allow_binary_include = 1;
0453 
0454     save = *cf;
0455     cf->pool = pool;
0456     cf->ctx = &ctx;
0457     cf->handler = ngx_http_geo;
0458     cf->handler_conf = conf;
0459 
0460     rv = ngx_conf_parse(cf, NULL);
0461 
0462     *cf = save;
0463 
0464     if (rv != NGX_CONF_OK) {
0465         goto failed;
0466     }
0467 
0468     geo->proxies = ctx.proxies;
0469     geo->proxy_recursive = ctx.proxy_recursive;
0470 
0471     if (ctx.ranges) {
0472 
0473         if (ctx.high.low && !ctx.binary_include) {
0474             for (i = 0; i < 0x10000; i++) {
0475                 a = (ngx_array_t *) ctx.high.low[i];
0476 
0477                 if (a == NULL) {
0478                     continue;
0479                 }
0480 
0481                 if (a->nelts == 0) {
0482                     ctx.high.low[i] = NULL;
0483                     continue;
0484                 }
0485 
0486                 len = a->nelts * sizeof(ngx_http_geo_range_t);
0487 
0488                 ctx.high.low[i] = ngx_palloc(cf->pool, len + sizeof(void *));
0489                 if (ctx.high.low[i] == NULL) {
0490                     goto failed;
0491                 }
0492 
0493                 ngx_memcpy(ctx.high.low[i], a->elts, len);
0494                 ctx.high.low[i][a->nelts].value = NULL;
0495                 ctx.data_size += len + sizeof(void *);
0496             }
0497 
0498             if (ctx.allow_binary_include
0499                 && !ctx.outside_entries
0500                 && ctx.entries > 100000
0501                 && ctx.includes == 1)
0502             {
0503                 ngx_http_geo_create_binary_base(&ctx);
0504             }
0505         }
0506 
0507         if (ctx.high.default_value == NULL) {
0508             ctx.high.default_value = &ngx_http_variable_null_value;
0509         }
0510 
0511         geo->u.high = ctx.high;
0512 
0513         var->get_handler = ngx_http_geo_range_variable;
0514         var->data = (uintptr_t) geo;
0515 
0516     } else {
0517         if (ctx.tree == NULL) {
0518             ctx.tree = ngx_radix_tree_create(cf->pool, -1);
0519             if (ctx.tree == NULL) {
0520                 goto failed;
0521             }
0522         }
0523 
0524         geo->u.trees.tree = ctx.tree;
0525 
0526 #if (NGX_HAVE_INET6)
0527         if (ctx.tree6 == NULL) {
0528             ctx.tree6 = ngx_radix_tree_create(cf->pool, -1);
0529             if (ctx.tree6 == NULL) {
0530                 goto failed;
0531             }
0532         }
0533 
0534         geo->u.trees.tree6 = ctx.tree6;
0535 #endif
0536 
0537         var->get_handler = ngx_http_geo_cidr_variable;
0538         var->data = (uintptr_t) geo;
0539 
0540         if (ngx_radix32tree_insert(ctx.tree, 0, 0,
0541                                    (uintptr_t) &ngx_http_variable_null_value)
0542             == NGX_ERROR)
0543         {
0544             goto failed;
0545         }
0546 
0547         /* NGX_BUSY is okay (default was set explicitly) */
0548 
0549 #if (NGX_HAVE_INET6)
0550         if (ngx_radix128tree_insert(ctx.tree6, zero.s6_addr, zero.s6_addr,
0551                                     (uintptr_t) &ngx_http_variable_null_value)
0552             == NGX_ERROR)
0553         {
0554             goto failed;
0555         }
0556 #endif
0557     }
0558 
0559     ngx_destroy_pool(ctx.temp_pool);
0560     ngx_destroy_pool(pool);
0561 
0562     return NGX_CONF_OK;
0563 
0564 failed:
0565 
0566     ngx_destroy_pool(ctx.temp_pool);
0567     ngx_destroy_pool(pool);
0568 
0569     return NGX_CONF_ERROR;
0570 }
0571 
0572 
0573 static char *
0574 ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
0575 {
0576     char                     *rv;
0577     ngx_str_t                *value;
0578     ngx_cidr_t                cidr;
0579     ngx_http_geo_conf_ctx_t  *ctx;
0580 
0581     ctx = cf->ctx;
0582 
0583     value = cf->args->elts;
0584 
0585     if (cf->args->nelts == 1) {
0586 
0587         if (ngx_strcmp(value[0].data, "ranges") == 0) {
0588 
0589             if (ctx->tree
0590 #if (NGX_HAVE_INET6)
0591                 || ctx->tree6
0592 #endif
0593                )
0594             {
0595                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0596                                    "the \"ranges\" directive must be "
0597                                    "the first directive inside \"geo\" block");
0598                 goto failed;
0599             }
0600 
0601             ctx->ranges = 1;
0602 
0603             rv = NGX_CONF_OK;
0604 
0605             goto done;
0606         }
0607 
0608         else if (ngx_strcmp(value[0].data, "proxy_recursive") == 0) {
0609             ctx->proxy_recursive = 1;
0610             rv = NGX_CONF_OK;
0611             goto done;
0612         }
0613     }
0614 
0615     if (cf->args->nelts != 2) {
0616         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0617                            "invalid number of the geo parameters");
0618         goto failed;
0619     }
0620 
0621     if (ngx_strcmp(value[0].data, "include") == 0) {
0622 
0623         rv = ngx_http_geo_include(cf, ctx, &value[1]);
0624 
0625         goto done;
0626 
0627     } else if (ngx_strcmp(value[0].data, "proxy") == 0) {
0628 
0629         if (ngx_http_geo_cidr_value(cf, &value[1], &cidr) != NGX_OK) {
0630             goto failed;
0631         }
0632 
0633         rv = ngx_http_geo_add_proxy(cf, ctx, &cidr);
0634 
0635         goto done;
0636     }
0637 
0638     if (ctx->ranges) {
0639         rv = ngx_http_geo_range(cf, ctx, value);
0640 
0641     } else {
0642         rv = ngx_http_geo_cidr(cf, ctx, value);
0643     }
0644 
0645 done:
0646 
0647     ngx_reset_pool(cf->pool);
0648 
0649     return rv;
0650 
0651 failed:
0652 
0653     ngx_reset_pool(cf->pool);
0654 
0655     return NGX_CONF_ERROR;
0656 }
0657 
0658 
0659 static char *
0660 ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
0661     ngx_str_t *value)
0662 {
0663     u_char      *p, *last;
0664     in_addr_t    start, end;
0665     ngx_str_t   *net;
0666     ngx_uint_t   del;
0667 
0668     if (ngx_strcmp(value[0].data, "default") == 0) {
0669 
0670         if (ctx->high.default_value) {
0671             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
0672                 "duplicate default geo range value: \"%V\", old value: \"%v\"",
0673                 &value[1], ctx->high.default_value);
0674         }
0675 
0676         ctx->high.default_value = ngx_http_geo_value(cf, ctx, &value[1]);
0677         if (ctx->high.default_value == NULL) {
0678             return NGX_CONF_ERROR;
0679         }
0680 
0681         return NGX_CONF_OK;
0682     }
0683 
0684     if (ctx->binary_include) {
0685         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0686             "binary geo range base \"%s\" cannot be mixed with usual entries",
0687             ctx->include_name.data);
0688         return NGX_CONF_ERROR;
0689     }
0690 
0691     if (ctx->high.low == NULL) {
0692         ctx->high.low = ngx_pcalloc(ctx->pool,
0693                                     0x10000 * sizeof(ngx_http_geo_range_t *));
0694         if (ctx->high.low == NULL) {
0695             return NGX_CONF_ERROR;
0696         }
0697     }
0698 
0699     ctx->entries++;
0700     ctx->outside_entries = 1;
0701 
0702     if (ngx_strcmp(value[0].data, "delete") == 0) {
0703         net = &value[1];
0704         del = 1;
0705 
0706     } else {
0707         net = &value[0];
0708         del = 0;
0709     }
0710 
0711     last = net->data + net->len;
0712 
0713     p = ngx_strlchr(net->data, last, '-');
0714 
0715     if (p == NULL) {
0716         goto invalid;
0717     }
0718 
0719     start = ngx_inet_addr(net->data, p - net->data);
0720 
0721     if (start == INADDR_NONE) {
0722         goto invalid;
0723     }
0724 
0725     start = ntohl(start);
0726 
0727     p++;
0728 
0729     end = ngx_inet_addr(p, last - p);
0730 
0731     if (end == INADDR_NONE) {
0732         goto invalid;
0733     }
0734 
0735     end = ntohl(end);
0736 
0737     if (start > end) {
0738         goto invalid;
0739     }
0740 
0741     if (del) {
0742         if (ngx_http_geo_delete_range(cf, ctx, start, end)) {
0743             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
0744                                "no address range \"%V\" to delete", net);
0745         }
0746 
0747         return NGX_CONF_OK;
0748     }
0749 
0750     ctx->value = ngx_http_geo_value(cf, ctx, &value[1]);
0751 
0752     if (ctx->value == NULL) {
0753         return NGX_CONF_ERROR;
0754     }
0755 
0756     ctx->net = net;
0757 
0758     return ngx_http_geo_add_range(cf, ctx, start, end);
0759 
0760 invalid:
0761 
0762     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid range \"%V\"", net);
0763 
0764     return NGX_CONF_ERROR;
0765 }
0766 
0767 
0768 /* the add procedure is optimized to add a growing up sequence */
0769 
0770 static char *
0771 ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
0772     in_addr_t start, in_addr_t end)
0773 {
0774     in_addr_t              n;
0775     ngx_uint_t             h, i, s, e;
0776     ngx_array_t           *a;
0777     ngx_http_geo_range_t  *range;
0778 
0779     for (n = start; n <= end; n = (n + 0x10000) & 0xffff0000) {
0780 
0781         h = n >> 16;
0782 
0783         if (n == start) {
0784             s = n & 0xffff;
0785         } else {
0786             s = 0;
0787         }
0788 
0789         if ((n | 0xffff) > end) {
0790             e = end & 0xffff;
0791 
0792         } else {
0793             e = 0xffff;
0794         }
0795 
0796         a = (ngx_array_t *) ctx->high.low[h];
0797 
0798         if (a == NULL) {
0799             a = ngx_array_create(ctx->temp_pool, 64,
0800                                  sizeof(ngx_http_geo_range_t));
0801             if (a == NULL) {
0802                 return NGX_CONF_ERROR;
0803             }
0804 
0805             ctx->high.low[h] = (ngx_http_geo_range_t *) a;
0806         }
0807 
0808         i = a->nelts;
0809         range = a->elts;
0810 
0811         while (i) {
0812 
0813             i--;
0814 
0815             if (e < (ngx_uint_t) range[i].start) {
0816                 continue;
0817             }
0818 
0819             if (s > (ngx_uint_t) range[i].end) {
0820 
0821                 /* add after the range */
0822 
0823                 range = ngx_array_push(a);
0824                 if (range == NULL) {
0825                     return NGX_CONF_ERROR;
0826                 }
0827 
0828                 range = a->elts;
0829 
0830                 ngx_memmove(&range[i + 2], &range[i + 1],
0831                             (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));
0832 
0833                 range[i + 1].start = (u_short) s;
0834                 range[i + 1].end = (u_short) e;
0835                 range[i + 1].value = ctx->value;
0836 
0837                 goto next;
0838             }
0839 
0840             if (s == (ngx_uint_t) range[i].start
0841                 && e == (ngx_uint_t) range[i].end)
0842             {
0843                 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
0844                     "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
0845                     ctx->net, ctx->value, range[i].value);
0846 
0847                 range[i].value = ctx->value;
0848 
0849                 goto next;
0850             }
0851 
0852             if (s > (ngx_uint_t) range[i].start
0853                 && e < (ngx_uint_t) range[i].end)
0854             {
0855                 /* split the range and insert the new one */
0856 
0857                 range = ngx_array_push(a);
0858                 if (range == NULL) {
0859                     return NGX_CONF_ERROR;
0860                 }
0861 
0862                 range = ngx_array_push(a);
0863                 if (range == NULL) {
0864                     return NGX_CONF_ERROR;
0865                 }
0866 
0867                 range = a->elts;
0868 
0869                 ngx_memmove(&range[i + 3], &range[i + 1],
0870                             (a->nelts - 3 - i) * sizeof(ngx_http_geo_range_t));
0871 
0872                 range[i + 2].start = (u_short) (e + 1);
0873                 range[i + 2].end = range[i].end;
0874                 range[i + 2].value = range[i].value;
0875 
0876                 range[i + 1].start = (u_short) s;
0877                 range[i + 1].end = (u_short) e;
0878                 range[i + 1].value = ctx->value;
0879 
0880                 range[i].end = (u_short) (s - 1);
0881 
0882                 goto next;
0883             }
0884 
0885             if (s == (ngx_uint_t) range[i].start
0886                 && e < (ngx_uint_t) range[i].end)
0887             {
0888                 /* shift the range start and insert the new range */
0889 
0890                 range = ngx_array_push(a);
0891                 if (range == NULL) {
0892                     return NGX_CONF_ERROR;
0893                 }
0894 
0895                 range = a->elts;
0896 
0897                 ngx_memmove(&range[i + 1], &range[i],
0898                             (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));
0899 
0900                 range[i + 1].start = (u_short) (e + 1);
0901 
0902                 range[i].start = (u_short) s;
0903                 range[i].end = (u_short) e;
0904                 range[i].value = ctx->value;
0905 
0906                 goto next;
0907             }
0908 
0909             if (s > (ngx_uint_t) range[i].start
0910                 && e == (ngx_uint_t) range[i].end)
0911             {
0912                 /* shift the range end and insert the new range */
0913 
0914                 range = ngx_array_push(a);
0915                 if (range == NULL) {
0916                     return NGX_CONF_ERROR;
0917                 }
0918 
0919                 range = a->elts;
0920 
0921                 ngx_memmove(&range[i + 2], &range[i + 1],
0922                             (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));
0923 
0924                 range[i + 1].start = (u_short) s;
0925                 range[i + 1].end = (u_short) e;
0926                 range[i + 1].value = ctx->value;
0927 
0928                 range[i].end = (u_short) (s - 1);
0929 
0930                 goto next;
0931             }
0932 
0933             s = (ngx_uint_t) range[i].start;
0934             e = (ngx_uint_t) range[i].end;
0935 
0936             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0937                          "range \"%V\" overlaps \"%d.%d.%d.%d-%d.%d.%d.%d\"",
0938                          ctx->net,
0939                          h >> 8, h & 0xff, s >> 8, s & 0xff,
0940                          h >> 8, h & 0xff, e >> 8, e & 0xff);
0941 
0942             return NGX_CONF_ERROR;
0943         }
0944 
0945         /* add the first range */
0946 
0947         range = ngx_array_push(a);
0948         if (range == NULL) {
0949             return NGX_CONF_ERROR;
0950         }
0951 
0952         range = a->elts;
0953 
0954         ngx_memmove(&range[1], &range[0],
0955                     (a->nelts - 1) * sizeof(ngx_http_geo_range_t));
0956 
0957         range[0].start = (u_short) s;
0958         range[0].end = (u_short) e;
0959         range[0].value = ctx->value;
0960 
0961     next:
0962 
0963         if (h == 0xffff) {
0964             break;
0965         }
0966     }
0967 
0968     return NGX_CONF_OK;
0969 }
0970 
0971 
0972 static ngx_uint_t
0973 ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
0974     in_addr_t start, in_addr_t end)
0975 {
0976     in_addr_t              n;
0977     ngx_uint_t             h, i, s, e, warn;
0978     ngx_array_t           *a;
0979     ngx_http_geo_range_t  *range;
0980 
0981     warn = 0;
0982 
0983     for (n = start; n <= end; n = (n + 0x10000) & 0xffff0000) {
0984 
0985         h = n >> 16;
0986 
0987         if (n == start) {
0988             s = n & 0xffff;
0989         } else {
0990             s = 0;
0991         }
0992 
0993         if ((n | 0xffff) > end) {
0994             e = end & 0xffff;
0995 
0996         } else {
0997             e = 0xffff;
0998         }
0999 
1000         a = (ngx_array_t *) ctx->high.low[h];
1001 
1002         if (a == NULL || a->nelts == 0) {
1003             warn = 1;
1004             goto next;
1005         }
1006 
1007         range = a->elts;
1008         for (i = 0; i < a->nelts; i++) {
1009 
1010             if (s == (ngx_uint_t) range[i].start
1011                 && e == (ngx_uint_t) range[i].end)
1012             {
1013                 ngx_memmove(&range[i], &range[i + 1],
1014                             (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));
1015 
1016                 a->nelts--;
1017 
1018                 break;
1019             }
1020 
1021             if (i == a->nelts - 1) {
1022                 warn = 1;
1023             }
1024         }
1025 
1026     next:
1027 
1028         if (h == 0xffff) {
1029             break;
1030         }
1031     }
1032 
1033     return warn;
1034 }
1035 
1036 
1037 static char *
1038 ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
1039     ngx_str_t *value)
1040 {
1041     char        *rv;
1042     ngx_int_t    rc, del;
1043     ngx_str_t   *net;
1044     ngx_cidr_t   cidr;
1045 
1046     if (ctx->tree == NULL) {
1047         ctx->tree = ngx_radix_tree_create(ctx->pool, -1);
1048         if (ctx->tree == NULL) {
1049             return NGX_CONF_ERROR;
1050         }
1051     }
1052 
1053 #if (NGX_HAVE_INET6)
1054     if (ctx->tree6 == NULL) {
1055         ctx->tree6 = ngx_radix_tree_create(ctx->pool, -1);
1056         if (ctx->tree6 == NULL) {
1057             return NGX_CONF_ERROR;
1058         }
1059     }
1060 #endif
1061 
1062     if (ngx_strcmp(value[0].data, "default") == 0) {
1063         cidr.family = AF_INET;
1064         cidr.u.in.addr = 0;
1065         cidr.u.in.mask = 0;
1066 
1067         rv = ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]);
1068 
1069         if (rv != NGX_CONF_OK) {
1070             return rv;
1071         }
1072 
1073 #if (NGX_HAVE_INET6)
1074         cidr.family = AF_INET6;
1075         ngx_memzero(&cidr.u.in6, sizeof(ngx_in6_cidr_t));
1076 
1077         rv = ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]);
1078 
1079         if (rv != NGX_CONF_OK) {
1080             return rv;
1081         }
1082 #endif
1083 
1084         return NGX_CONF_OK;
1085     }
1086 
1087     if (ngx_strcmp(value[0].data, "delete") == 0) {
1088         net = &value[1];
1089         del = 1;
1090 
1091     } else {
1092         net = &value[0];
1093         del = 0;
1094     }
1095 
1096     if (ngx_http_geo_cidr_value(cf, net, &cidr) != NGX_OK) {
1097         return NGX_CONF_ERROR;
1098     }
1099 
1100     if (cidr.family == AF_INET) {
1101         cidr.u.in.addr = ntohl(cidr.u.in.addr);
1102         cidr.u.in.mask = ntohl(cidr.u.in.mask);
1103     }
1104 
1105     if (del) {
1106         switch (cidr.family) {
1107 
1108 #if (NGX_HAVE_INET6)
1109         case AF_INET6:
1110             rc = ngx_radix128tree_delete(ctx->tree6,
1111                                          cidr.u.in6.addr.s6_addr,
1112                                          cidr.u.in6.mask.s6_addr);
1113             break;
1114 #endif
1115 
1116         default: /* AF_INET */
1117             rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr,
1118                                         cidr.u.in.mask);
1119             break;
1120         }
1121 
1122         if (rc != NGX_OK) {
1123             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1124                                "no network \"%V\" to delete", net);
1125         }
1126 
1127         return NGX_CONF_OK;
1128     }
1129 
1130     return ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], net);
1131 }
1132 
1133 
1134 static char *
1135 ngx_http_geo_cidr_add(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
1136     ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net)
1137 {
1138     ngx_int_t                   rc;
1139     ngx_http_variable_value_t  *val, *old;
1140 
1141     val = ngx_http_geo_value(cf, ctx, value);
1142 
1143     if (val == NULL) {
1144         return NGX_CONF_ERROR;
1145     }
1146 
1147     switch (cidr->family) {
1148 
1149 #if (NGX_HAVE_INET6)
1150     case AF_INET6:
1151         rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,
1152                                      cidr->u.in6.mask.s6_addr,
1153                                      (uintptr_t) val);
1154 
1155         if (rc == NGX_OK) {
1156             return NGX_CONF_OK;
1157         }
1158 
1159         if (rc == NGX_ERROR) {
1160             return NGX_CONF_ERROR;
1161         }
1162 
1163         /* rc == NGX_BUSY */
1164 
1165         old = (ngx_http_variable_value_t *)
1166                    ngx_radix128tree_find(ctx->tree6,
1167                                          cidr->u.in6.addr.s6_addr);
1168 
1169         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1170               "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
1171               net, val, old);
1172 
1173         rc = ngx_radix128tree_delete(ctx->tree6,
1174                                      cidr->u.in6.addr.s6_addr,
1175                                      cidr->u.in6.mask.s6_addr);
1176 
1177         if (rc == NGX_ERROR) {
1178             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
1179             return NGX_CONF_ERROR;
1180         }
1181 
1182         rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,
1183                                      cidr->u.in6.mask.s6_addr,
1184                                      (uintptr_t) val);
1185 
1186         break;
1187 #endif
1188 
1189     default: /* AF_INET */
1190         rc = ngx_radix32tree_insert(ctx->tree, cidr->u.in.addr,
1191                                     cidr->u.in.mask, (uintptr_t) val);
1192 
1193         if (rc == NGX_OK) {
1194             return NGX_CONF_OK;
1195         }
1196 
1197         if (rc == NGX_ERROR) {
1198             return NGX_CONF_ERROR;
1199         }
1200 
1201         /* rc == NGX_BUSY */
1202 
1203         old = (ngx_http_variable_value_t *)
1204                    ngx_radix32tree_find(ctx->tree, cidr->u.in.addr);
1205 
1206         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1207               "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
1208               net, val, old);
1209 
1210         rc = ngx_radix32tree_delete(ctx->tree,
1211                                     cidr->u.in.addr, cidr->u.in.mask);
1212 
1213         if (rc == NGX_ERROR) {
1214             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
1215             return NGX_CONF_ERROR;
1216         }
1217 
1218         rc = ngx_radix32tree_insert(ctx->tree, cidr->u.in.addr,
1219                                     cidr->u.in.mask, (uintptr_t) val);
1220 
1221         break;
1222     }
1223 
1224     if (rc == NGX_OK) {
1225         return NGX_CONF_OK;
1226     }
1227 
1228     return NGX_CONF_ERROR;
1229 }
1230 
1231 
1232 static ngx_http_variable_value_t *
1233 ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
1234     ngx_str_t *value)
1235 {
1236     uint32_t                             hash;
1237     ngx_http_variable_value_t           *val;
1238     ngx_http_geo_variable_value_node_t  *gvvn;
1239 
1240     hash = ngx_crc32_long(value->data, value->len);
1241 
1242     gvvn = (ngx_http_geo_variable_value_node_t *)
1243                ngx_str_rbtree_lookup(&ctx->rbtree, value, hash);
1244 
1245     if (gvvn) {
1246         return gvvn->value;
1247     }
1248 
1249     val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));
1250     if (val == NULL) {
1251         return NULL;
1252     }
1253 
1254     val->len = value->len;
1255     val->data = ngx_pstrdup(ctx->pool, value);
1256     if (val->data == NULL) {
1257         return NULL;
1258     }
1259 
1260     val->valid = 1;
1261     val->no_cacheable = 0;
1262     val->not_found = 0;
1263 
1264     gvvn = ngx_palloc(ctx->temp_pool,
1265                       sizeof(ngx_http_geo_variable_value_node_t));
1266     if (gvvn == NULL) {
1267         return NULL;
1268     }
1269 
1270     gvvn->sn.node.key = hash;
1271     gvvn->sn.str.len = val->len;
1272     gvvn->sn.str.data = val->data;
1273     gvvn->value = val;
1274     gvvn->offset = 0;
1275 
1276     ngx_rbtree_insert(&ctx->rbtree, &gvvn->sn.node);
1277 
1278     ctx->data_size += ngx_align(sizeof(ngx_http_variable_value_t) + value->len,
1279                                 sizeof(void *));
1280 
1281     return val;
1282 }
1283 
1284 
1285 static char *
1286 ngx_http_geo_add_proxy(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
1287     ngx_cidr_t *cidr)
1288 {
1289     ngx_cidr_t  *c;
1290 
1291     if (ctx->proxies == NULL) {
1292         ctx->proxies = ngx_array_create(ctx->pool, 4, sizeof(ngx_cidr_t));
1293         if (ctx->proxies == NULL) {
1294             return NGX_CONF_ERROR;
1295         }
1296     }
1297 
1298     c = ngx_array_push(ctx->proxies);
1299     if (c == NULL) {
1300         return NGX_CONF_ERROR;
1301     }
1302 
1303     *c = *cidr;
1304 
1305     return NGX_CONF_OK;
1306 }
1307 
1308 
1309 static ngx_int_t
1310 ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr)
1311 {
1312     ngx_int_t  rc;
1313 
1314     if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
1315         cidr->family = AF_INET;
1316         cidr->u.in.addr = 0xffffffff;
1317         cidr->u.in.mask = 0xffffffff;
1318 
1319         return NGX_OK;
1320     }
1321 
1322     rc = ngx_ptocidr(net, cidr);
1323 
1324     if (rc == NGX_ERROR) {
1325         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net);
1326         return NGX_ERROR;
1327     }
1328 
1329     if (rc == NGX_DONE) {
1330         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1331                            "low address bits of %V are meaningless", net);
1332     }
1333 
1334     return NGX_OK;
1335 }
1336 
1337 
1338 static char *
1339 ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
1340     ngx_str_t *name)
1341 {
1342     char       *rv;
1343     ngx_str_t   file;
1344 
1345     file.len = name->len + 4;
1346     file.data = ngx_pnalloc(ctx->temp_pool, name->len + 5);
1347     if (file.data == NULL) {
1348         return NGX_CONF_ERROR;
1349     }
1350 
1351     ngx_sprintf(file.data, "%V.bin%Z", name);
1352 
1353     if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {
1354         return NGX_CONF_ERROR;
1355     }
1356 
1357     if (ctx->ranges) {
1358         ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
1359 
1360         switch (ngx_http_geo_include_binary_base(cf, ctx, &file)) {
1361         case NGX_OK:
1362             return NGX_CONF_OK;
1363         case NGX_ERROR:
1364             return NGX_CONF_ERROR;
1365         default:
1366             break;
1367         }
1368     }
1369 
1370     file.len -= 4;
1371     file.data[file.len] = '\0';
1372 
1373     ctx->include_name = file;
1374 
1375     if (ctx->outside_entries) {
1376         ctx->allow_binary_include = 0;
1377     }
1378 
1379     ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
1380 
1381     rv = ngx_conf_parse(cf, &file);
1382 
1383     ctx->includes++;
1384     ctx->outside_entries = 0;
1385 
1386     return rv;
1387 }
1388 
1389 
1390 static ngx_int_t
1391 ngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
1392     ngx_str_t *name)
1393 {
1394     u_char                     *base, ch;
1395     time_t                      mtime;
1396     size_t                      size, len;
1397     ssize_t                     n;
1398     uint32_t                    crc32;
1399     ngx_err_t                   err;
1400     ngx_int_t                   rc;
1401     ngx_uint_t                  i;
1402     ngx_file_t                  file;
1403     ngx_file_info_t             fi;
1404     ngx_http_geo_range_t       *range, **ranges;
1405     ngx_http_geo_header_t      *header;
1406     ngx_http_variable_value_t  *vv;
1407 
1408     ngx_memzero(&file, sizeof(ngx_file_t));
1409     file.name = *name;
1410     file.log = cf->log;
1411 
1412     file.fd = ngx_open_file(name->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
1413 
1414     if (file.fd == NGX_INVALID_FILE) {
1415         err = ngx_errno;
1416         if (err != NGX_ENOENT) {
1417             ngx_conf_log_error(NGX_LOG_CRIT, cf, err,
1418                                ngx_open_file_n " \"%s\" failed", name->data);
1419         }
1420         return NGX_DECLINED;
1421     }
1422 
1423     if (ctx->outside_entries) {
1424         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1425             "binary geo range base \"%s\" cannot be mixed with usual entries",
1426             name->data);
1427         rc = NGX_ERROR;
1428         goto done;
1429     }
1430 
1431     if (ctx->binary_include) {
1432         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1433             "second binary geo range base \"%s\" cannot be mixed with \"%s\"",
1434             name->data, ctx->include_name.data);
1435         rc = NGX_ERROR;
1436         goto done;
1437     }
1438 
1439     if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
1440         ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
1441                            ngx_fd_info_n " \"%s\" failed", name->data);
1442         goto failed;
1443     }
1444 
1445     size = (size_t) ngx_file_size(&fi);
1446     mtime = ngx_file_mtime(&fi);
1447 
1448     ch = name->data[name->len - 4];
1449     name->data[name->len - 4] = '\0';
1450 
1451     if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {
1452         ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
1453                            ngx_file_info_n " \"%s\" failed", name->data);
1454         goto failed;
1455     }
1456 
1457     name->data[name->len - 4] = ch;
1458 
1459     if (mtime < ngx_file_mtime(&fi)) {
1460         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1461                            "stale binary geo range base \"%s\"", name->data);
1462         goto failed;
1463     }
1464 
1465     base = ngx_palloc(ctx->pool, size);
1466     if (base == NULL) {
1467         goto failed;
1468     }
1469 
1470     n = ngx_read_file(&file, base, size, 0);
1471 
1472     if (n == NGX_ERROR) {
1473         ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
1474                            ngx_read_file_n " \"%s\" failed", name->data);
1475         goto failed;
1476     }
1477 
1478     if ((size_t) n != size) {
1479         ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,
1480             ngx_read_file_n " \"%s\" returned only %z bytes instead of %z",
1481             name->data, n, size);
1482         goto failed;
1483     }
1484 
1485     header = (ngx_http_geo_header_t *) base;
1486 
1487     if (size < 16 || ngx_memcmp(&ngx_http_geo_header, header, 12) != 0) {
1488         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1489              "incompatible binary geo range base \"%s\"", name->data);
1490         goto failed;
1491     }
1492 
1493     ngx_crc32_init(crc32);
1494 
1495     vv = (ngx_http_variable_value_t *) (base + sizeof(ngx_http_geo_header_t));
1496 
1497     while (vv->data) {
1498         len = ngx_align(sizeof(ngx_http_variable_value_t) + vv->len,
1499                         sizeof(void *));
1500         ngx_crc32_update(&crc32, (u_char *) vv, len);
1501         vv->data += (size_t) base;
1502         vv = (ngx_http_variable_value_t *) ((u_char *) vv + len);
1503     }
1504     ngx_crc32_update(&crc32, (u_char *) vv, sizeof(ngx_http_variable_value_t));
1505     vv++;
1506 
1507     ranges = (ngx_http_geo_range_t **) vv;
1508 
1509     for (i = 0; i < 0x10000; i++) {
1510         ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *));
1511         if (ranges[i]) {
1512             ranges[i] = (ngx_http_geo_range_t *)
1513                             ((u_char *) ranges[i] + (size_t) base);
1514         }
1515     }
1516 
1517     range = (ngx_http_geo_range_t *) &ranges[0x10000];
1518 
1519     while ((u_char *) range < base + size) {
1520         while (range->value) {
1521             ngx_crc32_update(&crc32, (u_char *) range,
1522                              sizeof(ngx_http_geo_range_t));
1523             range->value = (ngx_http_variable_value_t *)
1524                                ((u_char *) range->value + (size_t) base);
1525             range++;
1526         }
1527         ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *));
1528         range = (ngx_http_geo_range_t *) ((u_char *) range + sizeof(void *));
1529     }
1530 
1531     ngx_crc32_final(crc32);
1532 
1533     if (crc32 != header->crc32) {
1534         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1535                   "CRC32 mismatch in binary geo range base \"%s\"", name->data);
1536         goto failed;
1537     }
1538 
1539     ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0,
1540                        "using binary geo range base \"%s\"", name->data);
1541 
1542     ctx->include_name = *name;
1543     ctx->binary_include = 1;
1544     ctx->high.low = ranges;
1545     rc = NGX_OK;
1546 
1547     goto done;
1548 
1549 failed:
1550 
1551     rc = NGX_DECLINED;
1552 
1553 done:
1554 
1555     if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
1556         ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
1557                       ngx_close_file_n " \"%s\" failed", name->data);
1558     }
1559 
1560     return rc;
1561 }
1562 
1563 
1564 static void
1565 ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx)
1566 {
1567     u_char                              *p;
1568     uint32_t                             hash;
1569     ngx_str_t                            s;
1570     ngx_uint_t                           i;
1571     ngx_file_mapping_t                   fm;
1572     ngx_http_geo_range_t                *r, *range, **ranges;
1573     ngx_http_geo_header_t               *header;
1574     ngx_http_geo_variable_value_node_t  *gvvn;
1575 
1576     fm.name = ngx_pnalloc(ctx->temp_pool, ctx->include_name.len + 5);
1577     if (fm.name == NULL) {
1578         return;
1579     }
1580 
1581     ngx_sprintf(fm.name, "%V.bin%Z", &ctx->include_name);
1582 
1583     fm.size = ctx->data_size;
1584     fm.log = ctx->pool->log;
1585 
1586     ngx_log_error(NGX_LOG_NOTICE, fm.log, 0,
1587                   "creating binary geo range base \"%s\"", fm.name);
1588 
1589     if (ngx_create_file_mapping(&fm) != NGX_OK) {
1590         return;
1591     }
1592 
1593     p = ngx_cpymem(fm.addr, &ngx_http_geo_header,
1594                    sizeof(ngx_http_geo_header_t));
1595 
1596     p = ngx_http_geo_copy_values(fm.addr, p, ctx->rbtree.root,
1597                                  ctx->rbtree.sentinel);
1598 
1599     p += sizeof(ngx_http_variable_value_t);
1600 
1601     ranges = (ngx_http_geo_range_t **) p;
1602 
1603     p += 0x10000 * sizeof(ngx_http_geo_range_t *);
1604 
1605     for (i = 0; i < 0x10000; i++) {
1606         r = ctx->high.low[i];
1607         if (r == NULL) {
1608             continue;
1609         }
1610 
1611         range = (ngx_http_geo_range_t *) p;
1612         ranges[i] = (ngx_http_geo_range_t *) (p - (u_char *) fm.addr);
1613 
1614         do {
1615             s.len = r->value->len;
1616             s.data = r->value->data;
1617             hash = ngx_crc32_long(s.data, s.len);
1618             gvvn = (ngx_http_geo_variable_value_node_t *)
1619                         ngx_str_rbtree_lookup(&ctx->rbtree, &s, hash);
1620 
1621             range->value = (ngx_http_variable_value_t *) gvvn->offset;
1622             range->start = r->start;
1623             range->end = r->end;
1624             range++;
1625 
1626         } while ((++r)->value);
1627 
1628         range->value = NULL;
1629 
1630         p = (u_char *) range + sizeof(void *);
1631     }
1632 
1633     header = fm.addr;
1634     header->crc32 = ngx_crc32_long((u_char *) fm.addr
1635                                        + sizeof(ngx_http_geo_header_t),
1636                                    fm.size - sizeof(ngx_http_geo_header_t));
1637 
1638     ngx_close_file_mapping(&fm);
1639 }
1640 
1641 
1642 static u_char *
1643 ngx_http_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node,
1644     ngx_rbtree_node_t *sentinel)
1645 {
1646     ngx_http_variable_value_t           *vv;
1647     ngx_http_geo_variable_value_node_t  *gvvn;
1648 
1649     if (node == sentinel) {
1650         return p;
1651     }
1652 
1653     gvvn = (ngx_http_geo_variable_value_node_t *) node;
1654     gvvn->offset = p - base;
1655 
1656     vv = (ngx_http_variable_value_t *) p;
1657     *vv = *gvvn->value;
1658     p += sizeof(ngx_http_variable_value_t);
1659     vv->data = (u_char *) (p - base);
1660 
1661     p = ngx_cpymem(p, gvvn->sn.str.data, gvvn->sn.str.len);
1662 
1663     p = ngx_align_ptr(p, sizeof(void *));
1664 
1665     p = ngx_http_geo_copy_values(base, p, node->left, sentinel);
1666 
1667     return ngx_http_geo_copy_values(base, p, node->right, sentinel);
1668 }