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