src/http/modules/ngx_http_geo_module.c - nginx source code

Global variables defined

Data types defined

Functions defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_http.h>


  8. typedef struct {
  9.     ngx_http_variable_value_t       *value;
  10.     u_short                          start;
  11.     u_short                          end;
  12. } ngx_http_geo_range_t;


  13. typedef struct {
  14.     ngx_radix_tree_t                *tree;
  15. #if (NGX_HAVE_INET6)
  16.     ngx_radix_tree_t                *tree6;
  17. #endif
  18. } ngx_http_geo_trees_t;


  19. typedef struct {
  20.     ngx_http_geo_range_t           **low;
  21.     ngx_http_variable_value_t       *default_value;
  22. } ngx_http_geo_high_ranges_t;


  23. typedef struct {
  24.     ngx_str_node_t                   sn;
  25.     ngx_http_variable_value_t       *value;
  26.     size_t                           offset;
  27. } ngx_http_geo_variable_value_node_t;


  28. typedef struct {
  29.     ngx_http_variable_value_t       *value;
  30.     ngx_str_t                       *net;
  31.     ngx_http_geo_high_ranges_t       high;
  32.     ngx_radix_tree_t                *tree;
  33. #if (NGX_HAVE_INET6)
  34.     ngx_radix_tree_t                *tree6;
  35. #endif
  36.     ngx_rbtree_t                     rbtree;
  37.     ngx_rbtree_node_t                sentinel;
  38.     ngx_array_t                     *proxies;
  39.     ngx_pool_t                      *pool;
  40.     ngx_pool_t                      *temp_pool;

  41.     size_t                           data_size;

  42.     ngx_str_t                        include_name;
  43.     ngx_uint_t                       includes;
  44.     ngx_uint_t                       entries;

  45.     unsigned                         ranges:1;
  46.     unsigned                         outside_entries:1;
  47.     unsigned                         allow_binary_include:1;
  48.     unsigned                         binary_include:1;
  49.     unsigned                         proxy_recursive:1;
  50. } ngx_http_geo_conf_ctx_t;


  51. typedef struct {
  52.     union {
  53.         ngx_http_geo_trees_t         trees;
  54.         ngx_http_geo_high_ranges_t   high;
  55.     } u;

  56.     ngx_array_t                     *proxies;
  57.     unsigned                         proxy_recursive:1;

  58.     ngx_int_t                        index;
  59. } ngx_http_geo_ctx_t;


  60. static ngx_int_t ngx_http_geo_addr(ngx_http_request_t *r,
  61.     ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr);
  62. static ngx_int_t ngx_http_geo_real_addr(ngx_http_request_t *r,
  63.     ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr);
  64. static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
  65. static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
  66. static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  67.     ngx_str_t *value);
  68. static char *ngx_http_geo_add_range(ngx_conf_t *cf,
  69.     ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
  70. static ngx_uint_t ngx_http_geo_delete_range(ngx_conf_t *cf,
  71.     ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
  72. static char *ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  73.     ngx_str_t *value);
  74. static char *ngx_http_geo_cidr_add(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  75.     ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net);
  76. static ngx_http_variable_value_t *ngx_http_geo_value(ngx_conf_t *cf,
  77.     ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value);
  78. static char *ngx_http_geo_add_proxy(ngx_conf_t *cf,
  79.     ngx_http_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr);
  80. static ngx_int_t ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net,
  81.     ngx_cidr_t *cidr);
  82. static char *ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  83.     ngx_str_t *name);
  84. static ngx_int_t ngx_http_geo_include_binary_base(ngx_conf_t *cf,
  85.     ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *name);
  86. static void ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx);
  87. static u_char *ngx_http_geo_copy_values(u_char *base, u_char *p,
  88.     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);


  89. static ngx_command_t  ngx_http_geo_commands[] = {

  90.     { ngx_string("geo"),
  91.       NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,
  92.       ngx_http_geo_block,
  93.       NGX_HTTP_MAIN_CONF_OFFSET,
  94.       0,
  95.       NULL },

  96.       ngx_null_command
  97. };


  98. static ngx_http_module_t  ngx_http_geo_module_ctx = {
  99.     NULL,                                  /* preconfiguration */
  100.     NULL,                                  /* postconfiguration */

  101.     NULL,                                  /* create main configuration */
  102.     NULL,                                  /* init main configuration */

  103.     NULL,                                  /* create server configuration */
  104.     NULL,                                  /* merge server configuration */

  105.     NULL,                                  /* create location configuration */
  106.     NULL                                   /* merge location configuration */
  107. };


  108. ngx_module_t  ngx_http_geo_module = {
  109.     NGX_MODULE_V1,
  110.     &ngx_http_geo_module_ctx,              /* module context */
  111.     ngx_http_geo_commands,                 /* module directives */
  112.     NGX_HTTP_MODULE,                       /* module type */
  113.     NULL,                                  /* init master */
  114.     NULL,                                  /* init module */
  115.     NULL,                                  /* init process */
  116.     NULL,                                  /* init thread */
  117.     NULL,                                  /* exit thread */
  118.     NULL,                                  /* exit process */
  119.     NULL,                                  /* exit master */
  120.     NGX_MODULE_V1_PADDING
  121. };


  122. typedef struct {
  123.     u_char    GEORNG[6];
  124.     u_char    version;
  125.     u_char    ptr_size;
  126.     uint32_t  endianness;
  127.     uint32_t  crc32;
  128. } ngx_http_geo_header_t;


  129. static ngx_http_geo_header_t  ngx_http_geo_header = {
  130.     { 'G', 'E', 'O', 'R', 'N', 'G' }, 0, sizeof(void *), 0x12345678, 0
  131. };


  132. /* geo range is AF_INET only */

  133. static ngx_int_t
  134. ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
  135.     uintptr_t data)
  136. {
  137.     ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;

  138.     in_addr_t                   inaddr;
  139.     ngx_addr_t                  addr;
  140.     struct sockaddr_in         *sin;
  141.     ngx_http_variable_value_t  *vv;
  142. #if (NGX_HAVE_INET6)
  143.     u_char                     *p;
  144.     struct in6_addr            *inaddr6;
  145. #endif

  146.     if (ngx_http_geo_addr(r, ctx, &addr) != NGX_OK) {
  147.         vv = (ngx_http_variable_value_t *)
  148.                   ngx_radix32tree_find(ctx->u.trees.tree, INADDR_NONE);
  149.         goto done;
  150.     }

  151.     switch (addr.sockaddr->sa_family) {

  152. #if (NGX_HAVE_INET6)
  153.     case AF_INET6:
  154.         inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;
  155.         p = inaddr6->s6_addr;

  156.         if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
  157.             inaddr = (in_addr_t) p[12] << 24;
  158.             inaddr += p[13] << 16;
  159.             inaddr += p[14] << 8;
  160.             inaddr += p[15];

  161.             vv = (ngx_http_variable_value_t *)
  162.                       ngx_radix32tree_find(ctx->u.trees.tree, inaddr);

  163.         } else {
  164.             vv = (ngx_http_variable_value_t *)
  165.                       ngx_radix128tree_find(ctx->u.trees.tree6, p);
  166.         }

  167.         break;
  168. #endif

  169. #if (NGX_HAVE_UNIX_DOMAIN)
  170.     case AF_UNIX:
  171.         vv = (ngx_http_variable_value_t *)
  172.                   ngx_radix32tree_find(ctx->u.trees.tree, INADDR_NONE);
  173.         break;
  174. #endif

  175.     default: /* AF_INET */
  176.         sin = (struct sockaddr_in *) addr.sockaddr;
  177.         inaddr = ntohl(sin->sin_addr.s_addr);

  178.         vv = (ngx_http_variable_value_t *)
  179.                   ngx_radix32tree_find(ctx->u.trees.tree, inaddr);

  180.         break;
  181.     }

  182. done:

  183.     *v = *vv;

  184.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  185.                    "http geo: %v", v);

  186.     return NGX_OK;
  187. }


  188. static ngx_int_t
  189. ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
  190.     uintptr_t data)
  191. {
  192.     ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;

  193.     in_addr_t              inaddr;
  194.     ngx_addr_t             addr;
  195.     ngx_uint_t             n;
  196.     struct sockaddr_in    *sin;
  197.     ngx_http_geo_range_t  *range;
  198. #if (NGX_HAVE_INET6)
  199.     u_char                *p;
  200.     struct in6_addr       *inaddr6;
  201. #endif

  202.     *v = *ctx->u.high.default_value;

  203.     if (ngx_http_geo_addr(r, ctx, &addr) == NGX_OK) {

  204.         switch (addr.sockaddr->sa_family) {

  205. #if (NGX_HAVE_INET6)
  206.         case AF_INET6:
  207.             inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;

  208.             if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
  209.                 p = inaddr6->s6_addr;

  210.                 inaddr = (in_addr_t) p[12] << 24;
  211.                 inaddr += p[13] << 16;
  212.                 inaddr += p[14] << 8;
  213.                 inaddr += p[15];

  214.             } else {
  215.                 inaddr = INADDR_NONE;
  216.             }

  217.             break;
  218. #endif

  219. #if (NGX_HAVE_UNIX_DOMAIN)
  220.         case AF_UNIX:
  221.             inaddr = INADDR_NONE;
  222.             break;
  223. #endif

  224.         default: /* AF_INET */
  225.             sin = (struct sockaddr_in *) addr.sockaddr;
  226.             inaddr = ntohl(sin->sin_addr.s_addr);
  227.             break;
  228.         }

  229.     } else {
  230.         inaddr = INADDR_NONE;
  231.     }

  232.     if (ctx->u.high.low) {
  233.         range = ctx->u.high.low[inaddr >> 16];

  234.         if (range) {
  235.             n = inaddr & 0xffff;
  236.             do {
  237.                 if (n >= (ngx_uint_t) range->start
  238.                     && n <= (ngx_uint_t) range->end)
  239.                 {
  240.                     *v = *range->value;
  241.                     break;
  242.                 }
  243.             } while ((++range)->value);
  244.         }
  245.     }

  246.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  247.                    "http geo: %v", v);

  248.     return NGX_OK;
  249. }


  250. static ngx_int_t
  251. ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx,
  252.     ngx_addr_t *addr)
  253. {
  254.     ngx_table_elt_t  *xfwd;

  255.     if (ngx_http_geo_real_addr(r, ctx, addr) != NGX_OK) {
  256.         return NGX_ERROR;
  257.     }

  258.     xfwd = r->headers_in.x_forwarded_for;

  259.     if (xfwd != NULL && ctx->proxies != NULL) {
  260.         (void) ngx_http_get_forwarded_addr(r, addr, xfwd, NULL,
  261.                                            ctx->proxies, ctx->proxy_recursive);
  262.     }

  263.     return NGX_OK;
  264. }


  265. static ngx_int_t
  266. ngx_http_geo_real_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx,
  267.     ngx_addr_t *addr)
  268. {
  269.     ngx_http_variable_value_t  *v;

  270.     if (ctx->index == -1) {
  271.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  272.                        "http geo started: %V", &r->connection->addr_text);

  273.         addr->sockaddr = r->connection->sockaddr;
  274.         addr->socklen = r->connection->socklen;
  275.         /* addr->name = r->connection->addr_text; */

  276.         return NGX_OK;
  277.     }

  278.     v = ngx_http_get_flushed_variable(r, ctx->index);

  279.     if (v == NULL || v->not_found) {
  280.         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  281.                        "http geo not found");

  282.         return NGX_ERROR;
  283.     }

  284.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  285.                    "http geo started: %v", v);

  286.     if (ngx_parse_addr(r->pool, addr, v->data, v->len) == NGX_OK) {
  287.         return NGX_OK;
  288.     }

  289.     return NGX_ERROR;
  290. }


  291. static char *
  292. ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  293. {
  294.     char                     *rv;
  295.     size_t                    len;
  296.     ngx_str_t                *value, name;
  297.     ngx_uint_t                i;
  298.     ngx_conf_t                save;
  299.     ngx_pool_t               *pool;
  300.     ngx_array_t              *a;
  301.     ngx_http_variable_t      *var;
  302.     ngx_http_geo_ctx_t       *geo;
  303.     ngx_http_geo_conf_ctx_t   ctx;
  304. #if (NGX_HAVE_INET6)
  305.     static struct in6_addr    zero;
  306. #endif

  307.     value = cf->args->elts;

  308.     geo = ngx_palloc(cf->pool, sizeof(ngx_http_geo_ctx_t));
  309.     if (geo == NULL) {
  310.         return NGX_CONF_ERROR;
  311.     }

  312.     name = value[1];

  313.     if (name.data[0] != '$') {
  314.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  315.                            "invalid variable name \"%V\"", &name);
  316.         return NGX_CONF_ERROR;
  317.     }

  318.     name.len--;
  319.     name.data++;

  320.     if (cf->args->nelts == 3) {

  321.         geo->index = ngx_http_get_variable_index(cf, &name);
  322.         if (geo->index == NGX_ERROR) {
  323.             return NGX_CONF_ERROR;
  324.         }

  325.         name = value[2];

  326.         if (name.data[0] != '$') {
  327.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  328.                                "invalid variable name \"%V\"", &name);
  329.             return NGX_CONF_ERROR;
  330.         }

  331.         name.len--;
  332.         name.data++;

  333.     } else {
  334.         geo->index = -1;
  335.     }

  336.     var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
  337.     if (var == NULL) {
  338.         return NGX_CONF_ERROR;
  339.     }

  340.     pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
  341.     if (pool == NULL) {
  342.         return NGX_CONF_ERROR;
  343.     }

  344.     ngx_memzero(&ctx, sizeof(ngx_http_geo_conf_ctx_t));

  345.     ctx.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
  346.     if (ctx.temp_pool == NULL) {
  347.         ngx_destroy_pool(pool);
  348.         return NGX_CONF_ERROR;
  349.     }

  350.     ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, ngx_str_rbtree_insert_value);

  351.     ctx.pool = cf->pool;
  352.     ctx.data_size = sizeof(ngx_http_geo_header_t)
  353.                   + sizeof(ngx_http_variable_value_t)
  354.                   + 0x10000 * sizeof(ngx_http_geo_range_t *);
  355.     ctx.allow_binary_include = 1;

  356.     save = *cf;
  357.     cf->pool = pool;
  358.     cf->ctx = &ctx;
  359.     cf->handler = ngx_http_geo;
  360.     cf->handler_conf = conf;

  361.     rv = ngx_conf_parse(cf, NULL);

  362.     *cf = save;

  363.     if (rv != NGX_CONF_OK) {
  364.         goto failed;
  365.     }

  366.     geo->proxies = ctx.proxies;
  367.     geo->proxy_recursive = ctx.proxy_recursive;

  368.     if (ctx.ranges) {

  369.         if (ctx.high.low && !ctx.binary_include) {
  370.             for (i = 0; i < 0x10000; i++) {
  371.                 a = (ngx_array_t *) ctx.high.low[i];

  372.                 if (a == NULL) {
  373.                     continue;
  374.                 }

  375.                 if (a->nelts == 0) {
  376.                     ctx.high.low[i] = NULL;
  377.                     continue;
  378.                 }

  379.                 len = a->nelts * sizeof(ngx_http_geo_range_t);

  380.                 ctx.high.low[i] = ngx_palloc(cf->pool, len + sizeof(void *));
  381.                 if (ctx.high.low[i] == NULL) {
  382.                     goto failed;
  383.                 }

  384.                 ngx_memcpy(ctx.high.low[i], a->elts, len);
  385.                 ctx.high.low[i][a->nelts].value = NULL;
  386.                 ctx.data_size += len + sizeof(void *);
  387.             }

  388.             if (ctx.allow_binary_include
  389.                 && !ctx.outside_entries
  390.                 && ctx.entries > 100000
  391.                 && ctx.includes == 1)
  392.             {
  393.                 ngx_http_geo_create_binary_base(&ctx);
  394.             }
  395.         }

  396.         if (ctx.high.default_value == NULL) {
  397.             ctx.high.default_value = &ngx_http_variable_null_value;
  398.         }

  399.         geo->u.high = ctx.high;

  400.         var->get_handler = ngx_http_geo_range_variable;
  401.         var->data = (uintptr_t) geo;

  402.     } else {
  403.         if (ctx.tree == NULL) {
  404.             ctx.tree = ngx_radix_tree_create(cf->pool, -1);
  405.             if (ctx.tree == NULL) {
  406.                 goto failed;
  407.             }
  408.         }

  409.         geo->u.trees.tree = ctx.tree;

  410. #if (NGX_HAVE_INET6)
  411.         if (ctx.tree6 == NULL) {
  412.             ctx.tree6 = ngx_radix_tree_create(cf->pool, -1);
  413.             if (ctx.tree6 == NULL) {
  414.                 goto failed;
  415.             }
  416.         }

  417.         geo->u.trees.tree6 = ctx.tree6;
  418. #endif

  419.         var->get_handler = ngx_http_geo_cidr_variable;
  420.         var->data = (uintptr_t) geo;

  421.         if (ngx_radix32tree_insert(ctx.tree, 0, 0,
  422.                                    (uintptr_t) &ngx_http_variable_null_value)
  423.             == NGX_ERROR)
  424.         {
  425.             goto failed;
  426.         }

  427.         /* NGX_BUSY is okay (default was set explicitly) */

  428. #if (NGX_HAVE_INET6)
  429.         if (ngx_radix128tree_insert(ctx.tree6, zero.s6_addr, zero.s6_addr,
  430.                                     (uintptr_t) &ngx_http_variable_null_value)
  431.             == NGX_ERROR)
  432.         {
  433.             goto failed;
  434.         }
  435. #endif
  436.     }

  437.     ngx_destroy_pool(ctx.temp_pool);
  438.     ngx_destroy_pool(pool);

  439.     return NGX_CONF_OK;

  440. failed:

  441.     ngx_destroy_pool(ctx.temp_pool);
  442.     ngx_destroy_pool(pool);

  443.     return NGX_CONF_ERROR;
  444. }


  445. static char *
  446. ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
  447. {
  448.     char                     *rv;
  449.     ngx_str_t                *value;
  450.     ngx_cidr_t                cidr;
  451.     ngx_http_geo_conf_ctx_t  *ctx;

  452.     ctx = cf->ctx;

  453.     value = cf->args->elts;

  454.     if (cf->args->nelts == 1) {

  455.         if (ngx_strcmp(value[0].data, "ranges") == 0) {

  456.             if (ctx->tree
  457. #if (NGX_HAVE_INET6)
  458.                 || ctx->tree6
  459. #endif
  460.                )
  461.             {
  462.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  463.                                    "the \"ranges\" directive must be "
  464.                                    "the first directive inside \"geo\" block");
  465.                 goto failed;
  466.             }

  467.             ctx->ranges = 1;

  468.             rv = NGX_CONF_OK;

  469.             goto done;
  470.         }

  471.         else if (ngx_strcmp(value[0].data, "proxy_recursive") == 0) {
  472.             ctx->proxy_recursive = 1;
  473.             rv = NGX_CONF_OK;
  474.             goto done;
  475.         }
  476.     }

  477.     if (cf->args->nelts != 2) {
  478.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  479.                            "invalid number of the geo parameters");
  480.         goto failed;
  481.     }

  482.     if (ngx_strcmp(value[0].data, "include") == 0) {

  483.         rv = ngx_http_geo_include(cf, ctx, &value[1]);

  484.         goto done;

  485.     } else if (ngx_strcmp(value[0].data, "proxy") == 0) {

  486.         if (ngx_http_geo_cidr_value(cf, &value[1], &cidr) != NGX_OK) {
  487.             goto failed;
  488.         }

  489.         rv = ngx_http_geo_add_proxy(cf, ctx, &cidr);

  490.         goto done;
  491.     }

  492.     if (ctx->ranges) {
  493.         rv = ngx_http_geo_range(cf, ctx, value);

  494.     } else {
  495.         rv = ngx_http_geo_cidr(cf, ctx, value);
  496.     }

  497. done:

  498.     ngx_reset_pool(cf->pool);

  499.     return rv;

  500. failed:

  501.     ngx_reset_pool(cf->pool);

  502.     return NGX_CONF_ERROR;
  503. }


  504. static char *
  505. ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  506.     ngx_str_t *value)
  507. {
  508.     u_char      *p, *last;
  509.     in_addr_t    start, end;
  510.     ngx_str_t   *net;
  511.     ngx_uint_t   del;

  512.     if (ngx_strcmp(value[0].data, "default") == 0) {

  513.         if (ctx->high.default_value) {
  514.             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  515.                 "duplicate default geo range value: \"%V\", old value: \"%v\"",
  516.                 &value[1], ctx->high.default_value);
  517.         }

  518.         ctx->high.default_value = ngx_http_geo_value(cf, ctx, &value[1]);
  519.         if (ctx->high.default_value == NULL) {
  520.             return NGX_CONF_ERROR;
  521.         }

  522.         return NGX_CONF_OK;
  523.     }

  524.     if (ctx->binary_include) {
  525.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  526.             "binary geo range base \"%s\" cannot be mixed with usual entries",
  527.             ctx->include_name.data);
  528.         return NGX_CONF_ERROR;
  529.     }

  530.     if (ctx->high.low == NULL) {
  531.         ctx->high.low = ngx_pcalloc(ctx->pool,
  532.                                     0x10000 * sizeof(ngx_http_geo_range_t *));
  533.         if (ctx->high.low == NULL) {
  534.             return NGX_CONF_ERROR;
  535.         }
  536.     }

  537.     ctx->entries++;
  538.     ctx->outside_entries = 1;

  539.     if (ngx_strcmp(value[0].data, "delete") == 0) {
  540.         net = &value[1];
  541.         del = 1;

  542.     } else {
  543.         net = &value[0];
  544.         del = 0;
  545.     }

  546.     last = net->data + net->len;

  547.     p = ngx_strlchr(net->data, last, '-');

  548.     if (p == NULL) {
  549.         goto invalid;
  550.     }

  551.     start = ngx_inet_addr(net->data, p - net->data);

  552.     if (start == INADDR_NONE) {
  553.         goto invalid;
  554.     }

  555.     start = ntohl(start);

  556.     p++;

  557.     end = ngx_inet_addr(p, last - p);

  558.     if (end == INADDR_NONE) {
  559.         goto invalid;
  560.     }

  561.     end = ntohl(end);

  562.     if (start > end) {
  563.         goto invalid;
  564.     }

  565.     if (del) {
  566.         if (ngx_http_geo_delete_range(cf, ctx, start, end)) {
  567.             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  568.                                "no address range \"%V\" to delete", net);
  569.         }

  570.         return NGX_CONF_OK;
  571.     }

  572.     ctx->value = ngx_http_geo_value(cf, ctx, &value[1]);

  573.     if (ctx->value == NULL) {
  574.         return NGX_CONF_ERROR;
  575.     }

  576.     ctx->net = net;

  577.     return ngx_http_geo_add_range(cf, ctx, start, end);

  578. invalid:

  579.     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid range \"%V\"", net);

  580.     return NGX_CONF_ERROR;
  581. }


  582. /* the add procedure is optimized to add a growing up sequence */

  583. static char *
  584. ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  585.     in_addr_t start, in_addr_t end)
  586. {
  587.     in_addr_t              n;
  588.     ngx_uint_t             h, i, s, e;
  589.     ngx_array_t           *a;
  590.     ngx_http_geo_range_t  *range;

  591.     for (n = start; n <= end; n = (n + 0x10000) & 0xffff0000) {

  592.         h = n >> 16;

  593.         if (n == start) {
  594.             s = n & 0xffff;
  595.         } else {
  596.             s = 0;
  597.         }

  598.         if ((n | 0xffff) > end) {
  599.             e = end & 0xffff;

  600.         } else {
  601.             e = 0xffff;
  602.         }

  603.         a = (ngx_array_t *) ctx->high.low[h];

  604.         if (a == NULL) {
  605.             a = ngx_array_create(ctx->temp_pool, 64,
  606.                                  sizeof(ngx_http_geo_range_t));
  607.             if (a == NULL) {
  608.                 return NGX_CONF_ERROR;
  609.             }

  610.             ctx->high.low[h] = (ngx_http_geo_range_t *) a;
  611.         }

  612.         i = a->nelts;
  613.         range = a->elts;

  614.         while (i) {

  615.             i--;

  616.             if (e < (ngx_uint_t) range[i].start) {
  617.                 continue;
  618.             }

  619.             if (s > (ngx_uint_t) range[i].end) {

  620.                 /* add after the range */

  621.                 range = ngx_array_push(a);
  622.                 if (range == NULL) {
  623.                     return NGX_CONF_ERROR;
  624.                 }

  625.                 range = a->elts;

  626.                 ngx_memmove(&range[i + 2], &range[i + 1],
  627.                             (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));

  628.                 range[i + 1].start = (u_short) s;
  629.                 range[i + 1].end = (u_short) e;
  630.                 range[i + 1].value = ctx->value;

  631.                 goto next;
  632.             }

  633.             if (s == (ngx_uint_t) range[i].start
  634.                 && e == (ngx_uint_t) range[i].end)
  635.             {
  636.                 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  637.                     "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
  638.                     ctx->net, ctx->value, range[i].value);

  639.                 range[i].value = ctx->value;

  640.                 goto next;
  641.             }

  642.             if (s > (ngx_uint_t) range[i].start
  643.                 && e < (ngx_uint_t) range[i].end)
  644.             {
  645.                 /* split the range and insert the new one */

  646.                 range = ngx_array_push(a);
  647.                 if (range == NULL) {
  648.                     return NGX_CONF_ERROR;
  649.                 }

  650.                 range = ngx_array_push(a);
  651.                 if (range == NULL) {
  652.                     return NGX_CONF_ERROR;
  653.                 }

  654.                 range = a->elts;

  655.                 ngx_memmove(&range[i + 3], &range[i + 1],
  656.                             (a->nelts - 3 - i) * sizeof(ngx_http_geo_range_t));

  657.                 range[i + 2].start = (u_short) (e + 1);
  658.                 range[i + 2].end = range[i].end;
  659.                 range[i + 2].value = range[i].value;

  660.                 range[i + 1].start = (u_short) s;
  661.                 range[i + 1].end = (u_short) e;
  662.                 range[i + 1].value = ctx->value;

  663.                 range[i].end = (u_short) (s - 1);

  664.                 goto next;
  665.             }

  666.             if (s == (ngx_uint_t) range[i].start
  667.                 && e < (ngx_uint_t) range[i].end)
  668.             {
  669.                 /* shift the range start and insert the new range */

  670.                 range = ngx_array_push(a);
  671.                 if (range == NULL) {
  672.                     return NGX_CONF_ERROR;
  673.                 }

  674.                 range = a->elts;

  675.                 ngx_memmove(&range[i + 1], &range[i],
  676.                             (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));

  677.                 range[i + 1].start = (u_short) (e + 1);

  678.                 range[i].start = (u_short) s;
  679.                 range[i].end = (u_short) e;
  680.                 range[i].value = ctx->value;

  681.                 goto next;
  682.             }

  683.             if (s > (ngx_uint_t) range[i].start
  684.                 && e == (ngx_uint_t) range[i].end)
  685.             {
  686.                 /* shift the range end and insert the new range */

  687.                 range = ngx_array_push(a);
  688.                 if (range == NULL) {
  689.                     return NGX_CONF_ERROR;
  690.                 }

  691.                 range = a->elts;

  692.                 ngx_memmove(&range[i + 2], &range[i + 1],
  693.                             (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));

  694.                 range[i + 1].start = (u_short) s;
  695.                 range[i + 1].end = (u_short) e;
  696.                 range[i + 1].value = ctx->value;

  697.                 range[i].end = (u_short) (s - 1);

  698.                 goto next;
  699.             }

  700.             s = (ngx_uint_t) range[i].start;
  701.             e = (ngx_uint_t) range[i].end;

  702.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  703.                          "range \"%V\" overlaps \"%d.%d.%d.%d-%d.%d.%d.%d\"",
  704.                          ctx->net,
  705.                          h >> 8, h & 0xff, s >> 8, s & 0xff,
  706.                          h >> 8, h & 0xff, e >> 8, e & 0xff);

  707.             return NGX_CONF_ERROR;
  708.         }

  709.         /* add the first range */

  710.         range = ngx_array_push(a);
  711.         if (range == NULL) {
  712.             return NGX_CONF_ERROR;
  713.         }

  714.         range = a->elts;

  715.         ngx_memmove(&range[1], &range[0],
  716.                     (a->nelts - 1) * sizeof(ngx_http_geo_range_t));

  717.         range[0].start = (u_short) s;
  718.         range[0].end = (u_short) e;
  719.         range[0].value = ctx->value;

  720.     next:

  721.         if (h == 0xffff) {
  722.             break;
  723.         }
  724.     }

  725.     return NGX_CONF_OK;
  726. }


  727. static ngx_uint_t
  728. ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  729.     in_addr_t start, in_addr_t end)
  730. {
  731.     in_addr_t              n;
  732.     ngx_uint_t             h, i, s, e, warn;
  733.     ngx_array_t           *a;
  734.     ngx_http_geo_range_t  *range;

  735.     warn = 0;

  736.     for (n = start; n <= end; n = (n + 0x10000) & 0xffff0000) {

  737.         h = n >> 16;

  738.         if (n == start) {
  739.             s = n & 0xffff;
  740.         } else {
  741.             s = 0;
  742.         }

  743.         if ((n | 0xffff) > end) {
  744.             e = end & 0xffff;

  745.         } else {
  746.             e = 0xffff;
  747.         }

  748.         a = (ngx_array_t *) ctx->high.low[h];

  749.         if (a == NULL || a->nelts == 0) {
  750.             warn = 1;
  751.             goto next;
  752.         }

  753.         range = a->elts;
  754.         for (i = 0; i < a->nelts; i++) {

  755.             if (s == (ngx_uint_t) range[i].start
  756.                 && e == (ngx_uint_t) range[i].end)
  757.             {
  758.                 ngx_memmove(&range[i], &range[i + 1],
  759.                             (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));

  760.                 a->nelts--;

  761.                 break;
  762.             }

  763.             if (i == a->nelts - 1) {
  764.                 warn = 1;
  765.             }
  766.         }

  767.     next:

  768.         if (h == 0xffff) {
  769.             break;
  770.         }
  771.     }

  772.     return warn;
  773. }


  774. static char *
  775. ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  776.     ngx_str_t *value)
  777. {
  778.     char        *rv;
  779.     ngx_int_t    rc, del;
  780.     ngx_str_t   *net;
  781.     ngx_cidr_t   cidr;

  782.     if (ctx->tree == NULL) {
  783.         ctx->tree = ngx_radix_tree_create(ctx->pool, -1);
  784.         if (ctx->tree == NULL) {
  785.             return NGX_CONF_ERROR;
  786.         }
  787.     }

  788. #if (NGX_HAVE_INET6)
  789.     if (ctx->tree6 == NULL) {
  790.         ctx->tree6 = ngx_radix_tree_create(ctx->pool, -1);
  791.         if (ctx->tree6 == NULL) {
  792.             return NGX_CONF_ERROR;
  793.         }
  794.     }
  795. #endif

  796.     if (ngx_strcmp(value[0].data, "default") == 0) {
  797.         cidr.family = AF_INET;
  798.         cidr.u.in.addr = 0;
  799.         cidr.u.in.mask = 0;

  800.         rv = ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]);

  801.         if (rv != NGX_CONF_OK) {
  802.             return rv;
  803.         }

  804. #if (NGX_HAVE_INET6)
  805.         cidr.family = AF_INET6;
  806.         ngx_memzero(&cidr.u.in6, sizeof(ngx_in6_cidr_t));

  807.         rv = ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]);

  808.         if (rv != NGX_CONF_OK) {
  809.             return rv;
  810.         }
  811. #endif

  812.         return NGX_CONF_OK;
  813.     }

  814.     if (ngx_strcmp(value[0].data, "delete") == 0) {
  815.         net = &value[1];
  816.         del = 1;

  817.     } else {
  818.         net = &value[0];
  819.         del = 0;
  820.     }

  821.     if (ngx_http_geo_cidr_value(cf, net, &cidr) != NGX_OK) {
  822.         return NGX_CONF_ERROR;
  823.     }

  824.     if (cidr.family == AF_INET) {
  825.         cidr.u.in.addr = ntohl(cidr.u.in.addr);
  826.         cidr.u.in.mask = ntohl(cidr.u.in.mask);
  827.     }

  828.     if (del) {
  829.         switch (cidr.family) {

  830. #if (NGX_HAVE_INET6)
  831.         case AF_INET6:
  832.             rc = ngx_radix128tree_delete(ctx->tree6,
  833.                                          cidr.u.in6.addr.s6_addr,
  834.                                          cidr.u.in6.mask.s6_addr);
  835.             break;
  836. #endif

  837.         default: /* AF_INET */
  838.             rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr,
  839.                                         cidr.u.in.mask);
  840.             break;
  841.         }

  842.         if (rc != NGX_OK) {
  843.             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  844.                                "no network \"%V\" to delete", net);
  845.         }

  846.         return NGX_CONF_OK;
  847.     }

  848.     return ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], net);
  849. }


  850. static char *
  851. ngx_http_geo_cidr_add(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  852.     ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net)
  853. {
  854.     ngx_int_t                   rc;
  855.     ngx_http_variable_value_t  *val, *old;

  856.     val = ngx_http_geo_value(cf, ctx, value);

  857.     if (val == NULL) {
  858.         return NGX_CONF_ERROR;
  859.     }

  860.     switch (cidr->family) {

  861. #if (NGX_HAVE_INET6)
  862.     case AF_INET6:
  863.         rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,
  864.                                      cidr->u.in6.mask.s6_addr,
  865.                                      (uintptr_t) val);

  866.         if (rc == NGX_OK) {
  867.             return NGX_CONF_OK;
  868.         }

  869.         if (rc == NGX_ERROR) {
  870.             return NGX_CONF_ERROR;
  871.         }

  872.         /* rc == NGX_BUSY */

  873.         old = (ngx_http_variable_value_t *)
  874.                    ngx_radix128tree_find(ctx->tree6,
  875.                                          cidr->u.in6.addr.s6_addr);

  876.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  877.               "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
  878.               net, val, old);

  879.         rc = ngx_radix128tree_delete(ctx->tree6,
  880.                                      cidr->u.in6.addr.s6_addr,
  881.                                      cidr->u.in6.mask.s6_addr);

  882.         if (rc == NGX_ERROR) {
  883.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
  884.             return NGX_CONF_ERROR;
  885.         }

  886.         rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,
  887.                                      cidr->u.in6.mask.s6_addr,
  888.                                      (uintptr_t) val);

  889.         break;
  890. #endif

  891.     default: /* AF_INET */
  892.         rc = ngx_radix32tree_insert(ctx->tree, cidr->u.in.addr,
  893.                                     cidr->u.in.mask, (uintptr_t) val);

  894.         if (rc == NGX_OK) {
  895.             return NGX_CONF_OK;
  896.         }

  897.         if (rc == NGX_ERROR) {
  898.             return NGX_CONF_ERROR;
  899.         }

  900.         /* rc == NGX_BUSY */

  901.         old = (ngx_http_variable_value_t *)
  902.                    ngx_radix32tree_find(ctx->tree, cidr->u.in.addr);

  903.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  904.               "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
  905.               net, val, old);

  906.         rc = ngx_radix32tree_delete(ctx->tree,
  907.                                     cidr->u.in.addr, cidr->u.in.mask);

  908.         if (rc == NGX_ERROR) {
  909.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
  910.             return NGX_CONF_ERROR;
  911.         }

  912.         rc = ngx_radix32tree_insert(ctx->tree, cidr->u.in.addr,
  913.                                     cidr->u.in.mask, (uintptr_t) val);

  914.         break;
  915.     }

  916.     if (rc == NGX_OK) {
  917.         return NGX_CONF_OK;
  918.     }

  919.     return NGX_CONF_ERROR;
  920. }


  921. static ngx_http_variable_value_t *
  922. ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  923.     ngx_str_t *value)
  924. {
  925.     uint32_t                             hash;
  926.     ngx_http_variable_value_t           *val;
  927.     ngx_http_geo_variable_value_node_t  *gvvn;

  928.     hash = ngx_crc32_long(value->data, value->len);

  929.     gvvn = (ngx_http_geo_variable_value_node_t *)
  930.                ngx_str_rbtree_lookup(&ctx->rbtree, value, hash);

  931.     if (gvvn) {
  932.         return gvvn->value;
  933.     }

  934.     val = ngx_pcalloc(ctx->pool, sizeof(ngx_http_variable_value_t));
  935.     if (val == NULL) {
  936.         return NULL;
  937.     }

  938.     val->len = value->len;
  939.     val->data = ngx_pstrdup(ctx->pool, value);
  940.     if (val->data == NULL) {
  941.         return NULL;
  942.     }

  943.     val->valid = 1;

  944.     gvvn = ngx_palloc(ctx->temp_pool,
  945.                       sizeof(ngx_http_geo_variable_value_node_t));
  946.     if (gvvn == NULL) {
  947.         return NULL;
  948.     }

  949.     gvvn->sn.node.key = hash;
  950.     gvvn->sn.str.len = val->len;
  951.     gvvn->sn.str.data = val->data;
  952.     gvvn->value = val;
  953.     gvvn->offset = 0;

  954.     ngx_rbtree_insert(&ctx->rbtree, &gvvn->sn.node);

  955.     ctx->data_size += ngx_align(sizeof(ngx_http_variable_value_t) + value->len,
  956.                                 sizeof(void *));

  957.     return val;
  958. }


  959. static char *
  960. ngx_http_geo_add_proxy(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  961.     ngx_cidr_t *cidr)
  962. {
  963.     ngx_cidr_t  *c;

  964.     if (ctx->proxies == NULL) {
  965.         ctx->proxies = ngx_array_create(ctx->pool, 4, sizeof(ngx_cidr_t));
  966.         if (ctx->proxies == NULL) {
  967.             return NGX_CONF_ERROR;
  968.         }
  969.     }

  970.     c = ngx_array_push(ctx->proxies);
  971.     if (c == NULL) {
  972.         return NGX_CONF_ERROR;
  973.     }

  974.     *c = *cidr;

  975.     return NGX_CONF_OK;
  976. }


  977. static ngx_int_t
  978. ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr)
  979. {
  980.     ngx_int_t  rc;

  981.     if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
  982.         cidr->family = AF_INET;
  983.         cidr->u.in.addr = 0xffffffff;
  984.         cidr->u.in.mask = 0xffffffff;

  985.         return NGX_OK;
  986.     }

  987.     rc = ngx_ptocidr(net, cidr);

  988.     if (rc == NGX_ERROR) {
  989.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net);
  990.         return NGX_ERROR;
  991.     }

  992.     if (rc == NGX_DONE) {
  993.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  994.                            "low address bits of %V are meaningless", net);
  995.     }

  996.     return NGX_OK;
  997. }


  998. static char *
  999. ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  1000.     ngx_str_t *name)
  1001. {
  1002.     char       *rv;
  1003.     ngx_str_t   file;

  1004.     file.len = name->len + 4;
  1005.     file.data = ngx_pnalloc(ctx->temp_pool, name->len + 5);
  1006.     if (file.data == NULL) {
  1007.         return NGX_CONF_ERROR;
  1008.     }

  1009.     ngx_sprintf(file.data, "%V.bin%Z", name);

  1010.     if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {
  1011.         return NGX_CONF_ERROR;
  1012.     }

  1013.     if (ctx->ranges) {
  1014.         ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);

  1015.         switch (ngx_http_geo_include_binary_base(cf, ctx, &file)) {
  1016.         case NGX_OK:
  1017.             return NGX_CONF_OK;
  1018.         case NGX_ERROR:
  1019.             return NGX_CONF_ERROR;
  1020.         default:
  1021.             break;
  1022.         }
  1023.     }

  1024.     file.len -= 4;
  1025.     file.data[file.len] = '\0';

  1026.     ctx->include_name = file;

  1027.     if (ctx->outside_entries) {
  1028.         ctx->allow_binary_include = 0;
  1029.     }

  1030.     ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);

  1031.     rv = ngx_conf_parse(cf, &file);

  1032.     ctx->includes++;
  1033.     ctx->outside_entries = 0;

  1034.     return rv;
  1035. }


  1036. static ngx_int_t
  1037. ngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  1038.     ngx_str_t *name)
  1039. {
  1040.     u_char                     *base, ch;
  1041.     time_t                      mtime;
  1042.     size_t                      size, len;
  1043.     ssize_t                     n;
  1044.     uint32_t                    crc32;
  1045.     ngx_err_t                   err;
  1046.     ngx_int_t                   rc;
  1047.     ngx_uint_t                  i;
  1048.     ngx_file_t                  file;
  1049.     ngx_file_info_t             fi;
  1050.     ngx_http_geo_range_t       *range, **ranges;
  1051.     ngx_http_geo_header_t      *header;
  1052.     ngx_http_variable_value_t  *vv;

  1053.     ngx_memzero(&file, sizeof(ngx_file_t));
  1054.     file.name = *name;
  1055.     file.log = cf->log;

  1056.     file.fd = ngx_open_file(name->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);

  1057.     if (file.fd == NGX_INVALID_FILE) {
  1058.         err = ngx_errno;
  1059.         if (err != NGX_ENOENT) {
  1060.             ngx_conf_log_error(NGX_LOG_CRIT, cf, err,
  1061.                                ngx_open_file_n " \"%s\" failed", name->data);
  1062.         }
  1063.         return NGX_DECLINED;
  1064.     }

  1065.     if (ctx->outside_entries) {
  1066.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1067.             "binary geo range base \"%s\" cannot be mixed with usual entries",
  1068.             name->data);
  1069.         rc = NGX_ERROR;
  1070.         goto done;
  1071.     }

  1072.     if (ctx->binary_include) {
  1073.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1074.             "second binary geo range base \"%s\" cannot be mixed with \"%s\"",
  1075.             name->data, ctx->include_name.data);
  1076.         rc = NGX_ERROR;
  1077.         goto done;
  1078.     }

  1079.     if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
  1080.         ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
  1081.                            ngx_fd_info_n " \"%s\" failed", name->data);
  1082.         goto failed;
  1083.     }

  1084.     size = (size_t) ngx_file_size(&fi);
  1085.     mtime = ngx_file_mtime(&fi);

  1086.     ch = name->data[name->len - 4];
  1087.     name->data[name->len - 4] = '\0';

  1088.     if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {
  1089.         ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
  1090.                            ngx_file_info_n " \"%s\" failed", name->data);
  1091.         goto failed;
  1092.     }

  1093.     name->data[name->len - 4] = ch;

  1094.     if (mtime < ngx_file_mtime(&fi)) {
  1095.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  1096.                            "stale binary geo range base \"%s\"", name->data);
  1097.         goto failed;
  1098.     }

  1099.     base = ngx_palloc(ctx->pool, size);
  1100.     if (base == NULL) {
  1101.         goto failed;
  1102.     }

  1103.     n = ngx_read_file(&file, base, size, 0);

  1104.     if (n == NGX_ERROR) {
  1105.         ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
  1106.                            ngx_read_file_n " \"%s\" failed", name->data);
  1107.         goto failed;
  1108.     }

  1109.     if ((size_t) n != size) {
  1110.         ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,
  1111.             ngx_read_file_n " \"%s\" returned only %z bytes instead of %z",
  1112.             name->data, n, size);
  1113.         goto failed;
  1114.     }

  1115.     header = (ngx_http_geo_header_t *) base;

  1116.     if (size < 16 || ngx_memcmp(&ngx_http_geo_header, header, 12) != 0) {
  1117.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  1118.              "incompatible binary geo range base \"%s\"", name->data);
  1119.         goto failed;
  1120.     }

  1121.     ngx_crc32_init(crc32);

  1122.     vv = (ngx_http_variable_value_t *) (base + sizeof(ngx_http_geo_header_t));

  1123.     while (vv->data) {
  1124.         len = ngx_align(sizeof(ngx_http_variable_value_t) + vv->len,
  1125.                         sizeof(void *));
  1126.         ngx_crc32_update(&crc32, (u_char *) vv, len);
  1127.         vv->data += (size_t) base;
  1128.         vv = (ngx_http_variable_value_t *) ((u_char *) vv + len);
  1129.     }
  1130.     ngx_crc32_update(&crc32, (u_char *) vv, sizeof(ngx_http_variable_value_t));
  1131.     vv++;

  1132.     ranges = (ngx_http_geo_range_t **) vv;

  1133.     for (i = 0; i < 0x10000; i++) {
  1134.         ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *));
  1135.         if (ranges[i]) {
  1136.             ranges[i] = (ngx_http_geo_range_t *)
  1137.                             ((u_char *) ranges[i] + (size_t) base);
  1138.         }
  1139.     }

  1140.     range = (ngx_http_geo_range_t *) &ranges[0x10000];

  1141.     while ((u_char *) range < base + size) {
  1142.         while (range->value) {
  1143.             ngx_crc32_update(&crc32, (u_char *) range,
  1144.                              sizeof(ngx_http_geo_range_t));
  1145.             range->value = (ngx_http_variable_value_t *)
  1146.                                ((u_char *) range->value + (size_t) base);
  1147.             range++;
  1148.         }
  1149.         ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *));
  1150.         range = (ngx_http_geo_range_t *) ((u_char *) range + sizeof(void *));
  1151.     }

  1152.     ngx_crc32_final(crc32);

  1153.     if (crc32 != header->crc32) {
  1154.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  1155.                   "CRC32 mismatch in binary geo range base \"%s\"", name->data);
  1156.         goto failed;
  1157.     }

  1158.     ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0,
  1159.                        "using binary geo range base \"%s\"", name->data);

  1160.     ctx->include_name = *name;
  1161.     ctx->binary_include = 1;
  1162.     ctx->high.low = ranges;
  1163.     rc = NGX_OK;

  1164.     goto done;

  1165. failed:

  1166.     rc = NGX_DECLINED;

  1167. done:

  1168.     if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
  1169.         ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
  1170.                       ngx_close_file_n " \"%s\" failed", name->data);
  1171.     }

  1172.     return rc;
  1173. }


  1174. static void
  1175. ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx)
  1176. {
  1177.     u_char                              *p;
  1178.     uint32_t                             hash;
  1179.     ngx_str_t                            s;
  1180.     ngx_uint_t                           i;
  1181.     ngx_file_mapping_t                   fm;
  1182.     ngx_http_geo_range_t                *r, *range, **ranges;
  1183.     ngx_http_geo_header_t               *header;
  1184.     ngx_http_geo_variable_value_node_t  *gvvn;

  1185.     fm.name = ngx_pnalloc(ctx->temp_pool, ctx->include_name.len + 5);
  1186.     if (fm.name == NULL) {
  1187.         return;
  1188.     }

  1189.     ngx_sprintf(fm.name, "%V.bin%Z", &ctx->include_name);

  1190.     fm.size = ctx->data_size;
  1191.     fm.log = ctx->pool->log;

  1192.     ngx_log_error(NGX_LOG_NOTICE, fm.log, 0,
  1193.                   "creating binary geo range base \"%s\"", fm.name);

  1194.     if (ngx_create_file_mapping(&fm) != NGX_OK) {
  1195.         return;
  1196.     }

  1197.     p = ngx_cpymem(fm.addr, &ngx_http_geo_header,
  1198.                    sizeof(ngx_http_geo_header_t));

  1199.     p = ngx_http_geo_copy_values(fm.addr, p, ctx->rbtree.root,
  1200.                                  ctx->rbtree.sentinel);

  1201.     p += sizeof(ngx_http_variable_value_t);

  1202.     ranges = (ngx_http_geo_range_t **) p;

  1203.     p += 0x10000 * sizeof(ngx_http_geo_range_t *);

  1204.     for (i = 0; i < 0x10000; i++) {
  1205.         r = ctx->high.low[i];
  1206.         if (r == NULL) {
  1207.             continue;
  1208.         }

  1209.         range = (ngx_http_geo_range_t *) p;
  1210.         ranges[i] = (ngx_http_geo_range_t *) (p - (u_char *) fm.addr);

  1211.         do {
  1212.             s.len = r->value->len;
  1213.             s.data = r->value->data;
  1214.             hash = ngx_crc32_long(s.data, s.len);
  1215.             gvvn = (ngx_http_geo_variable_value_node_t *)
  1216.                         ngx_str_rbtree_lookup(&ctx->rbtree, &s, hash);

  1217.             range->value = (ngx_http_variable_value_t *) gvvn->offset;
  1218.             range->start = r->start;
  1219.             range->end = r->end;
  1220.             range++;

  1221.         } while ((++r)->value);

  1222.         range->value = NULL;

  1223.         p = (u_char *) range + sizeof(void *);
  1224.     }

  1225.     header = fm.addr;
  1226.     header->crc32 = ngx_crc32_long((u_char *) fm.addr
  1227.                                        + sizeof(ngx_http_geo_header_t),
  1228.                                    fm.size - sizeof(ngx_http_geo_header_t));

  1229.     ngx_close_file_mapping(&fm);
  1230. }


  1231. static u_char *
  1232. ngx_http_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node,
  1233.     ngx_rbtree_node_t *sentinel)
  1234. {
  1235.     ngx_http_variable_value_t           *vv;
  1236.     ngx_http_geo_variable_value_node_t  *gvvn;

  1237.     if (node == sentinel) {
  1238.         return p;
  1239.     }

  1240.     gvvn = (ngx_http_geo_variable_value_node_t *) node;
  1241.     gvvn->offset = p - base;

  1242.     vv = (ngx_http_variable_value_t *) p;
  1243.     *vv = *gvvn->value;
  1244.     p += sizeof(ngx_http_variable_value_t);
  1245.     vv->data = (u_char *) (p - base);

  1246.     p = ngx_cpymem(p, gvvn->sn.str.data, gvvn->sn.str.len);

  1247.     p = ngx_align_ptr(p, sizeof(void *));

  1248.     p = ngx_http_geo_copy_values(base, p, node->left, sentinel);

  1249.     return ngx_http_geo_copy_values(base, p, node->right, sentinel);
  1250. }