Back to home page

Nginx displayed by LXR

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