Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.15.12 ]​[ nginx-1.16.0 ]​

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