src/http/modules/ngx_http_geo_module.c - nginx

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.     unsigned                         no_cacheable:1;
  51. } ngx_http_geo_conf_ctx_t;


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

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

  59.     ngx_int_t                        index;
  60. } ngx_http_geo_ctx_t;


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


  90. static ngx_command_t  ngx_http_geo_commands[] = {

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

  97.       ngx_null_command
  98. };


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

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

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

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


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


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


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


  133. /* geo range is AF_INET only */

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

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

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

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

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

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

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

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

  168.         break;
  169. #endif

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

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

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

  181.         break;
  182.     }

  183. done:

  184.     *v = *vv;

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

  187.     return NGX_OK;
  188. }


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

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

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

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

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

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

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

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

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

  218.             break;
  219. #endif

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

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

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

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

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

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

  249.     return NGX_OK;
  250. }


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

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

  259.     xfwd = r->headers_in.x_forwarded_for;

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

  264.     return NGX_OK;
  265. }


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

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

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

  277.         return NGX_OK;
  278.     }

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

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

  283.         return NGX_ERROR;
  284.     }

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

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

  290.     return NGX_ERROR;
  291. }


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

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

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

  313.     name = value[1];

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

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

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

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

  326.         name = value[2];

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

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

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

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

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

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

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

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

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

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

  363.     rv = ngx_conf_parse(cf, NULL);

  364.     *cf = save;

  365.     if (rv != NGX_CONF_OK) {
  366.         goto failed;
  367.     }

  368.     if (ctx.no_cacheable) {
  369.         var->flags |= NGX_HTTP_VAR_NOCACHEABLE;
  370.     }

  371.     geo->proxies = ctx.proxies;
  372.     geo->proxy_recursive = ctx.proxy_recursive;

  373.     if (ctx.ranges) {

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

  377.                 if (a == NULL) {
  378.                     continue;
  379.                 }

  380.                 if (a->nelts == 0) {
  381.                     ctx.high.low[i] = NULL;
  382.                     continue;
  383.                 }

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

  385.                 ctx.high.low[i] = ngx_palloc(cf->pool, len + sizeof(void *));
  386.                 if (ctx.high.low[i] == NULL) {
  387.                     goto failed;
  388.                 }

  389.                 ngx_memcpy(ctx.high.low[i], a->elts, len);
  390.                 ctx.high.low[i][a->nelts].value = NULL;
  391.                 ctx.data_size += len + sizeof(void *);
  392.             }

  393.             if (ctx.allow_binary_include
  394.                 && !ctx.outside_entries
  395.                 && ctx.entries > 100000
  396.                 && ctx.includes == 1)
  397.             {
  398.                 ngx_http_geo_create_binary_base(&ctx);
  399.             }
  400.         }

  401.         if (ctx.high.default_value == NULL) {
  402.             ctx.high.default_value = &ngx_http_variable_null_value;
  403.         }

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

  405.         var->get_handler = ngx_http_geo_range_variable;
  406.         var->data = (uintptr_t) geo;

  407.     } else {
  408.         if (ctx.tree == NULL) {
  409.             ctx.tree = ngx_radix_tree_create(cf->pool, -1);
  410.             if (ctx.tree == NULL) {
  411.                 goto failed;
  412.             }
  413.         }

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

  415. #if (NGX_HAVE_INET6)
  416.         if (ctx.tree6 == NULL) {
  417.             ctx.tree6 = ngx_radix_tree_create(cf->pool, -1);
  418.             if (ctx.tree6 == NULL) {
  419.                 goto failed;
  420.             }
  421.         }

  422.         geo->u.trees.tree6 = ctx.tree6;
  423. #endif

  424.         var->get_handler = ngx_http_geo_cidr_variable;
  425.         var->data = (uintptr_t) geo;

  426.         if (ngx_radix32tree_insert(ctx.tree, 0, 0,
  427.                                    (uintptr_t) &ngx_http_variable_null_value)
  428.             == NGX_ERROR)
  429.         {
  430.             goto failed;
  431.         }

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

  433. #if (NGX_HAVE_INET6)
  434.         if (ngx_radix128tree_insert(ctx.tree6, zero.s6_addr, zero.s6_addr,
  435.                                     (uintptr_t) &ngx_http_variable_null_value)
  436.             == NGX_ERROR)
  437.         {
  438.             goto failed;
  439.         }
  440. #endif
  441.     }

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

  444.     return NGX_CONF_OK;

  445. failed:

  446.     ngx_destroy_pool(ctx.temp_pool);
  447.     ngx_destroy_pool(pool);

  448.     return NGX_CONF_ERROR;
  449. }


  450. static char *
  451. ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
  452. {
  453.     char                     *rv;
  454.     ngx_str_t                *value;
  455.     ngx_cidr_t                cidr;
  456.     ngx_http_geo_conf_ctx_t  *ctx;

  457.     ctx = cf->ctx;

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

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

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

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

  472.             ctx->ranges = 1;

  473.             rv = NGX_CONF_OK;

  474.             goto done;
  475.         }

  476.         else if (ngx_strcmp(value[0].data, "proxy_recursive") == 0) {
  477.             ctx->proxy_recursive = 1;
  478.             rv = NGX_CONF_OK;
  479.             goto done;
  480.         }

  481.         else if (ngx_strcmp(value[0].data, "volatile") == 0) {
  482.             ctx->no_cacheable = 1;
  483.             rv = NGX_CONF_OK;
  484.             goto done;
  485.         }
  486.     }

  487.     if (cf->args->nelts != 2) {
  488.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  489.                            "invalid number of the geo parameters");
  490.         goto failed;
  491.     }

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

  493.         if (strpbrk((char *) value[1].data, "*?[") == NULL) {
  494.             rv = ngx_http_geo_include(cf, ctx, &value[1]);

  495.         } else {
  496.             rv = ngx_conf_include(cf, dummy, conf);
  497.         }

  498.         goto done;

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

  500.         if (ngx_http_geo_cidr_value(cf, &value[1], &cidr) != NGX_OK) {
  501.             goto failed;
  502.         }

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

  504.         goto done;
  505.     }

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

  508.     } else {
  509.         rv = ngx_http_geo_cidr(cf, ctx, value);
  510.     }

  511. done:

  512.     ngx_reset_pool(cf->pool);

  513.     return rv;

  514. failed:

  515.     ngx_reset_pool(cf->pool);

  516.     return NGX_CONF_ERROR;
  517. }


  518. static char *
  519. ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  520.     ngx_str_t *value)
  521. {
  522.     u_char      *p, *last;
  523.     in_addr_t    start, end;
  524.     ngx_str_t   *net;
  525.     ngx_uint_t   del;

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

  527.         if (ctx->high.default_value) {
  528.             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  529.                 "duplicate default geo range value: \"%V\", old value: \"%v\"",
  530.                 &value[1], ctx->high.default_value);
  531.         }

  532.         ctx->high.default_value = ngx_http_geo_value(cf, ctx, &value[1]);
  533.         if (ctx->high.default_value == NULL) {
  534.             return NGX_CONF_ERROR;
  535.         }

  536.         return NGX_CONF_OK;
  537.     }

  538.     if (ctx->binary_include) {
  539.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  540.             "binary geo range base \"%s\" cannot be mixed with usual entries",
  541.             ctx->include_name.data);
  542.         return NGX_CONF_ERROR;
  543.     }

  544.     if (ctx->high.low == NULL) {
  545.         ctx->high.low = ngx_pcalloc(ctx->pool,
  546.                                     0x10000 * sizeof(ngx_http_geo_range_t *));
  547.         if (ctx->high.low == NULL) {
  548.             return NGX_CONF_ERROR;
  549.         }
  550.     }

  551.     ctx->entries++;
  552.     ctx->outside_entries = 1;

  553.     if (ngx_strcmp(value[0].data, "delete") == 0) {
  554.         net = &value[1];
  555.         del = 1;

  556.     } else {
  557.         net = &value[0];
  558.         del = 0;
  559.     }

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

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

  562.     if (p == NULL) {
  563.         goto invalid;
  564.     }

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

  566.     if (start == INADDR_NONE) {
  567.         goto invalid;
  568.     }

  569.     start = ntohl(start);

  570.     p++;

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

  572.     if (end == INADDR_NONE) {
  573.         goto invalid;
  574.     }

  575.     end = ntohl(end);

  576.     if (start > end) {
  577.         goto invalid;
  578.     }

  579.     if (del) {
  580.         if (ngx_http_geo_delete_range(cf, ctx, start, end)) {
  581.             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  582.                                "no address range \"%V\" to delete", net);
  583.         }

  584.         return NGX_CONF_OK;
  585.     }

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

  587.     if (ctx->value == NULL) {
  588.         return NGX_CONF_ERROR;
  589.     }

  590.     ctx->net = net;

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

  592. invalid:

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

  594.     return NGX_CONF_ERROR;
  595. }


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

  597. static char *
  598. ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  599.     in_addr_t start, in_addr_t end)
  600. {
  601.     in_addr_t              n;
  602.     ngx_uint_t             h, i, s, e;
  603.     ngx_array_t           *a;
  604.     ngx_http_geo_range_t  *range;

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

  606.         h = n >> 16;

  607.         if (n == start) {
  608.             s = n & 0xffff;
  609.         } else {
  610.             s = 0;
  611.         }

  612.         if ((n | 0xffff) > end) {
  613.             e = end & 0xffff;

  614.         } else {
  615.             e = 0xffff;
  616.         }

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

  618.         if (a == NULL) {
  619.             a = ngx_array_create(ctx->temp_pool, 64,
  620.                                  sizeof(ngx_http_geo_range_t));
  621.             if (a == NULL) {
  622.                 return NGX_CONF_ERROR;
  623.             }

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

  626.         i = a->nelts;
  627.         range = a->elts;

  628.         while (i) {

  629.             i--;

  630.             if (e < (ngx_uint_t) range[i].start) {
  631.                 continue;
  632.             }

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

  634.                 /* add after the range */

  635.                 range = ngx_array_push(a);
  636.                 if (range == NULL) {
  637.                     return NGX_CONF_ERROR;
  638.                 }

  639.                 range = a->elts;

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

  642.                 range[i + 1].start = (u_short) s;
  643.                 range[i + 1].end = (u_short) e;
  644.                 range[i + 1].value = ctx->value;

  645.                 goto next;
  646.             }

  647.             if (s == (ngx_uint_t) range[i].start
  648.                 && e == (ngx_uint_t) range[i].end)
  649.             {
  650.                 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  651.                     "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
  652.                     ctx->net, ctx->value, range[i].value);

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

  654.                 goto next;
  655.             }

  656.             if (s > (ngx_uint_t) range[i].start
  657.                 && e < (ngx_uint_t) range[i].end)
  658.             {
  659.                 /* split the range and insert the new one */

  660.                 range = ngx_array_push(a);
  661.                 if (range == NULL) {
  662.                     return NGX_CONF_ERROR;
  663.                 }

  664.                 range = ngx_array_push(a);
  665.                 if (range == NULL) {
  666.                     return NGX_CONF_ERROR;
  667.                 }

  668.                 range = a->elts;

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

  671.                 range[i + 2].start = (u_short) (e + 1);
  672.                 range[i + 2].end = range[i].end;
  673.                 range[i + 2].value = range[i].value;

  674.                 range[i + 1].start = (u_short) s;
  675.                 range[i + 1].end = (u_short) e;
  676.                 range[i + 1].value = ctx->value;

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

  678.                 goto next;
  679.             }

  680.             if (s == (ngx_uint_t) range[i].start
  681.                 && e < (ngx_uint_t) range[i].end)
  682.             {
  683.                 /* shift the range start and insert the new range */

  684.                 range = ngx_array_push(a);
  685.                 if (range == NULL) {
  686.                     return NGX_CONF_ERROR;
  687.                 }

  688.                 range = a->elts;

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

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

  692.                 range[i].start = (u_short) s;
  693.                 range[i].end = (u_short) e;
  694.                 range[i].value = ctx->value;

  695.                 goto next;
  696.             }

  697.             if (s > (ngx_uint_t) range[i].start
  698.                 && e == (ngx_uint_t) range[i].end)
  699.             {
  700.                 /* shift the range end and insert the new range */

  701.                 range = ngx_array_push(a);
  702.                 if (range == NULL) {
  703.                     return NGX_CONF_ERROR;
  704.                 }

  705.                 range = a->elts;

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

  708.                 range[i + 1].start = (u_short) s;
  709.                 range[i + 1].end = (u_short) e;
  710.                 range[i + 1].value = ctx->value;

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

  712.                 goto next;
  713.             }

  714.             s = (ngx_uint_t) range[i].start;
  715.             e = (ngx_uint_t) range[i].end;

  716.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  717.                          "range \"%V\" overlaps \"%d.%d.%d.%d-%d.%d.%d.%d\"",
  718.                          ctx->net,
  719.                          h >> 8, h & 0xff, s >> 8, s & 0xff,
  720.                          h >> 8, h & 0xff, e >> 8, e & 0xff);

  721.             return NGX_CONF_ERROR;
  722.         }

  723.         /* add the first range */

  724.         range = ngx_array_push(a);
  725.         if (range == NULL) {
  726.             return NGX_CONF_ERROR;
  727.         }

  728.         range = a->elts;

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

  731.         range[0].start = (u_short) s;
  732.         range[0].end = (u_short) e;
  733.         range[0].value = ctx->value;

  734.     next:

  735.         if (h == 0xffff) {
  736.             break;
  737.         }
  738.     }

  739.     return NGX_CONF_OK;
  740. }


  741. static ngx_uint_t
  742. ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  743.     in_addr_t start, in_addr_t end)
  744. {
  745.     in_addr_t              n;
  746.     ngx_uint_t             h, i, s, e, warn;
  747.     ngx_array_t           *a;
  748.     ngx_http_geo_range_t  *range;

  749.     warn = 0;

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

  751.         h = n >> 16;

  752.         if (n == start) {
  753.             s = n & 0xffff;
  754.         } else {
  755.             s = 0;
  756.         }

  757.         if ((n | 0xffff) > end) {
  758.             e = end & 0xffff;

  759.         } else {
  760.             e = 0xffff;
  761.         }

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

  763.         if (a == NULL || a->nelts == 0) {
  764.             warn = 1;
  765.             goto next;
  766.         }

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

  769.             if (s == (ngx_uint_t) range[i].start
  770.                 && e == (ngx_uint_t) range[i].end)
  771.             {
  772.                 ngx_memmove(&range[i], &range[i + 1],
  773.                             (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));

  774.                 a->nelts--;

  775.                 break;
  776.             }

  777.             if (i == a->nelts - 1) {
  778.                 warn = 1;
  779.             }
  780.         }

  781.     next:

  782.         if (h == 0xffff) {
  783.             break;
  784.         }
  785.     }

  786.     return warn;
  787. }


  788. static char *
  789. ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  790.     ngx_str_t *value)
  791. {
  792.     char        *rv;
  793.     ngx_int_t    rc, del;
  794.     ngx_str_t   *net;
  795.     ngx_cidr_t   cidr;

  796.     if (ctx->tree == NULL) {
  797.         ctx->tree = ngx_radix_tree_create(ctx->pool, -1);
  798.         if (ctx->tree == NULL) {
  799.             return NGX_CONF_ERROR;
  800.         }
  801.     }

  802. #if (NGX_HAVE_INET6)
  803.     if (ctx->tree6 == NULL) {
  804.         ctx->tree6 = ngx_radix_tree_create(ctx->pool, -1);
  805.         if (ctx->tree6 == NULL) {
  806.             return NGX_CONF_ERROR;
  807.         }
  808.     }
  809. #endif

  810.     if (ngx_strcmp(value[0].data, "default") == 0) {
  811.         cidr.family = AF_INET;
  812.         cidr.u.in.addr = 0;
  813.         cidr.u.in.mask = 0;

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

  815.         if (rv != NGX_CONF_OK) {
  816.             return rv;
  817.         }

  818. #if (NGX_HAVE_INET6)
  819.         cidr.family = AF_INET6;
  820.         ngx_memzero(&cidr.u.in6, sizeof(ngx_in6_cidr_t));

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

  822.         if (rv != NGX_CONF_OK) {
  823.             return rv;
  824.         }
  825. #endif

  826.         return NGX_CONF_OK;
  827.     }

  828.     if (ngx_strcmp(value[0].data, "delete") == 0) {
  829.         net = &value[1];
  830.         del = 1;

  831.     } else {
  832.         net = &value[0];
  833.         del = 0;
  834.     }

  835.     if (ngx_http_geo_cidr_value(cf, net, &cidr) != NGX_OK) {
  836.         return NGX_CONF_ERROR;
  837.     }

  838.     if (cidr.family == AF_INET) {
  839.         cidr.u.in.addr = ntohl(cidr.u.in.addr);
  840.         cidr.u.in.mask = ntohl(cidr.u.in.mask);
  841.     }

  842.     if (del) {
  843.         switch (cidr.family) {

  844. #if (NGX_HAVE_INET6)
  845.         case AF_INET6:
  846.             rc = ngx_radix128tree_delete(ctx->tree6,
  847.                                          cidr.u.in6.addr.s6_addr,
  848.                                          cidr.u.in6.mask.s6_addr);
  849.             break;
  850. #endif

  851.         default: /* AF_INET */
  852.             rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr,
  853.                                         cidr.u.in.mask);
  854.             break;
  855.         }

  856.         if (rc != NGX_OK) {
  857.             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  858.                                "no network \"%V\" to delete", net);
  859.         }

  860.         return NGX_CONF_OK;
  861.     }

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


  864. static char *
  865. ngx_http_geo_cidr_add(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  866.     ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net)
  867. {
  868.     ngx_int_t                   rc;
  869.     ngx_http_variable_value_t  *val, *old;

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

  871.     if (val == NULL) {
  872.         return NGX_CONF_ERROR;
  873.     }

  874.     switch (cidr->family) {

  875. #if (NGX_HAVE_INET6)
  876.     case AF_INET6:
  877.         rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,
  878.                                      cidr->u.in6.mask.s6_addr,
  879.                                      (uintptr_t) val);

  880.         if (rc == NGX_OK) {
  881.             return NGX_CONF_OK;
  882.         }

  883.         if (rc == NGX_ERROR) {
  884.             return NGX_CONF_ERROR;
  885.         }

  886.         /* rc == NGX_BUSY */

  887.         old = (ngx_http_variable_value_t *)
  888.                    ngx_radix128tree_find(ctx->tree6,
  889.                                          cidr->u.in6.addr.s6_addr);

  890.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  891.               "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
  892.               net, val, old);

  893.         rc = ngx_radix128tree_delete(ctx->tree6,
  894.                                      cidr->u.in6.addr.s6_addr,
  895.                                      cidr->u.in6.mask.s6_addr);

  896.         if (rc == NGX_ERROR) {
  897.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
  898.             return NGX_CONF_ERROR;
  899.         }

  900.         rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,
  901.                                      cidr->u.in6.mask.s6_addr,
  902.                                      (uintptr_t) val);

  903.         break;
  904. #endif

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

  908.         if (rc == NGX_OK) {
  909.             return NGX_CONF_OK;
  910.         }

  911.         if (rc == NGX_ERROR) {
  912.             return NGX_CONF_ERROR;
  913.         }

  914.         /* rc == NGX_BUSY */

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

  917.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  918.               "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
  919.               net, val, old);

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

  922.         if (rc == NGX_ERROR) {
  923.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
  924.             return NGX_CONF_ERROR;
  925.         }

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

  928.         break;
  929.     }

  930.     if (rc == NGX_OK) {
  931.         return NGX_CONF_OK;
  932.     }

  933.     return NGX_CONF_ERROR;
  934. }


  935. static ngx_http_variable_value_t *
  936. ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  937.     ngx_str_t *value)
  938. {
  939.     uint32_t                             hash;
  940.     ngx_http_variable_value_t           *val;
  941.     ngx_http_geo_variable_value_node_t  *gvvn;

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

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

  945.     if (gvvn) {
  946.         return gvvn->value;
  947.     }

  948.     val = ngx_pcalloc(ctx->pool, sizeof(ngx_http_variable_value_t));
  949.     if (val == NULL) {
  950.         return NULL;
  951.     }

  952.     val->len = value->len;
  953.     val->data = ngx_pstrdup(ctx->pool, value);
  954.     if (val->data == NULL) {
  955.         return NULL;
  956.     }

  957.     val->valid = 1;

  958.     gvvn = ngx_palloc(ctx->temp_pool,
  959.                       sizeof(ngx_http_geo_variable_value_node_t));
  960.     if (gvvn == NULL) {
  961.         return NULL;
  962.     }

  963.     gvvn->sn.node.key = hash;
  964.     gvvn->sn.str.len = val->len;
  965.     gvvn->sn.str.data = val->data;
  966.     gvvn->value = val;
  967.     gvvn->offset = 0;

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

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

  971.     return val;
  972. }


  973. static char *
  974. ngx_http_geo_add_proxy(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  975.     ngx_cidr_t *cidr)
  976. {
  977.     ngx_cidr_t  *c;

  978.     if (ctx->proxies == NULL) {
  979.         ctx->proxies = ngx_array_create(ctx->pool, 4, sizeof(ngx_cidr_t));
  980.         if (ctx->proxies == NULL) {
  981.             return NGX_CONF_ERROR;
  982.         }
  983.     }

  984.     c = ngx_array_push(ctx->proxies);
  985.     if (c == NULL) {
  986.         return NGX_CONF_ERROR;
  987.     }

  988.     *c = *cidr;

  989.     return NGX_CONF_OK;
  990. }


  991. static ngx_int_t
  992. ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr)
  993. {
  994.     ngx_int_t  rc;

  995.     if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
  996.         cidr->family = AF_INET;
  997.         cidr->u.in.addr = 0xffffffff;
  998.         cidr->u.in.mask = 0xffffffff;

  999.         return NGX_OK;
  1000.     }

  1001.     rc = ngx_ptocidr(net, cidr);

  1002.     if (rc == NGX_ERROR) {
  1003.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net);
  1004.         return NGX_ERROR;
  1005.     }

  1006.     if (rc == NGX_DONE) {
  1007.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  1008.                            "low address bits of %V are meaningless", net);
  1009.     }

  1010.     return NGX_OK;
  1011. }


  1012. static char *
  1013. ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  1014.     ngx_str_t *name)
  1015. {
  1016.     char       *rv;
  1017.     ngx_str_t   file;

  1018.     file.len = name->len + 4;
  1019.     file.data = ngx_pnalloc(ctx->temp_pool, name->len + 5);
  1020.     if (file.data == NULL) {
  1021.         return NGX_CONF_ERROR;
  1022.     }

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

  1024.     if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {
  1025.         return NGX_CONF_ERROR;
  1026.     }

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

  1029.         switch (ngx_http_geo_include_binary_base(cf, ctx, &file)) {
  1030.         case NGX_OK:
  1031.             return NGX_CONF_OK;
  1032.         case NGX_ERROR:
  1033.             return NGX_CONF_ERROR;
  1034.         default:
  1035.             break;
  1036.         }
  1037.     }

  1038.     file.len -= 4;
  1039.     file.data[file.len] = '\0';

  1040.     ctx->include_name = file;

  1041.     if (ctx->outside_entries) {
  1042.         ctx->allow_binary_include = 0;
  1043.     }

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

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

  1046.     ctx->includes++;
  1047.     ctx->outside_entries = 0;

  1048.     return rv;
  1049. }


  1050. static ngx_int_t
  1051. ngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  1052.     ngx_str_t *name)
  1053. {
  1054.     u_char                     *base, ch;
  1055.     time_t                      mtime;
  1056.     size_t                      size, len;
  1057.     ssize_t                     n;
  1058.     uint32_t                    crc32;
  1059.     ngx_err_t                   err;
  1060.     ngx_int_t                   rc;
  1061.     ngx_uint_t                  i;
  1062.     ngx_file_t                  file;
  1063.     ngx_file_info_t             fi;
  1064.     ngx_http_geo_range_t       *range, **ranges;
  1065.     ngx_http_geo_header_t      *header;
  1066.     ngx_http_variable_value_t  *vv;

  1067.     ngx_memzero(&file, sizeof(ngx_file_t));
  1068.     file.name = *name;
  1069.     file.log = cf->log;

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

  1071.     if (file.fd == NGX_INVALID_FILE) {
  1072.         err = ngx_errno;
  1073.         if (err != NGX_ENOENT) {
  1074.             ngx_conf_log_error(NGX_LOG_CRIT, cf, err,
  1075.                                ngx_open_file_n " \"%s\" failed", name->data);
  1076.         }
  1077.         return NGX_DECLINED;
  1078.     }

  1079.     if (ctx->outside_entries) {
  1080.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1081.             "binary geo range base \"%s\" cannot be mixed with usual entries",
  1082.             name->data);
  1083.         rc = NGX_ERROR;
  1084.         goto done;
  1085.     }

  1086.     if (ctx->binary_include) {
  1087.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1088.             "second binary geo range base \"%s\" cannot be mixed with \"%s\"",
  1089.             name->data, ctx->include_name.data);
  1090.         rc = NGX_ERROR;
  1091.         goto done;
  1092.     }

  1093.     if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
  1094.         ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
  1095.                            ngx_fd_info_n " \"%s\" failed", name->data);
  1096.         goto failed;
  1097.     }

  1098.     size = (size_t) ngx_file_size(&fi);
  1099.     mtime = ngx_file_mtime(&fi);

  1100.     ch = name->data[name->len - 4];
  1101.     name->data[name->len - 4] = '\0';

  1102.     if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {
  1103.         ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
  1104.                            ngx_file_info_n " \"%s\" failed", name->data);
  1105.         goto failed;
  1106.     }

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

  1108.     if (mtime < ngx_file_mtime(&fi)) {
  1109.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  1110.                            "stale binary geo range base \"%s\"", name->data);
  1111.         goto failed;
  1112.     }

  1113.     base = ngx_palloc(ctx->pool, size);
  1114.     if (base == NULL) {
  1115.         goto failed;
  1116.     }

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

  1118.     if (n == NGX_ERROR) {
  1119.         ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
  1120.                            ngx_read_file_n " \"%s\" failed", name->data);
  1121.         goto failed;
  1122.     }

  1123.     if ((size_t) n != size) {
  1124.         ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,
  1125.             ngx_read_file_n " \"%s\" returned only %z bytes instead of %z",
  1126.             name->data, n, size);
  1127.         goto failed;
  1128.     }

  1129.     header = (ngx_http_geo_header_t *) base;

  1130.     if (size < 16 || ngx_memcmp(&ngx_http_geo_header, header, 12) != 0) {
  1131.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  1132.              "incompatible binary geo range base \"%s\"", name->data);
  1133.         goto failed;
  1134.     }

  1135.     ngx_crc32_init(crc32);

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

  1137.     while (vv->data) {
  1138.         len = ngx_align(sizeof(ngx_http_variable_value_t) + vv->len,
  1139.                         sizeof(void *));
  1140.         ngx_crc32_update(&crc32, (u_char *) vv, len);
  1141.         vv->data += (size_t) base;
  1142.         vv = (ngx_http_variable_value_t *) ((u_char *) vv + len);
  1143.     }
  1144.     ngx_crc32_update(&crc32, (u_char *) vv, sizeof(ngx_http_variable_value_t));
  1145.     vv++;

  1146.     ranges = (ngx_http_geo_range_t **) vv;

  1147.     for (i = 0; i < 0x10000; i++) {
  1148.         ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *));
  1149.         if (ranges[i]) {
  1150.             ranges[i] = (ngx_http_geo_range_t *)
  1151.                             ((u_char *) ranges[i] + (size_t) base);
  1152.         }
  1153.     }

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

  1155.     while ((u_char *) range < base + size) {
  1156.         while (range->value) {
  1157.             ngx_crc32_update(&crc32, (u_char *) range,
  1158.                              sizeof(ngx_http_geo_range_t));
  1159.             range->value = (ngx_http_variable_value_t *)
  1160.                                ((u_char *) range->value + (size_t) base);
  1161.             range++;
  1162.         }
  1163.         ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *));
  1164.         range = (ngx_http_geo_range_t *) ((u_char *) range + sizeof(void *));
  1165.     }

  1166.     ngx_crc32_final(crc32);

  1167.     if (crc32 != header->crc32) {
  1168.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  1169.                   "CRC32 mismatch in binary geo range base \"%s\"", name->data);
  1170.         goto failed;
  1171.     }

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

  1174.     ctx->include_name = *name;
  1175.     ctx->binary_include = 1;
  1176.     ctx->high.low = ranges;
  1177.     rc = NGX_OK;

  1178.     goto done;

  1179. failed:

  1180.     rc = NGX_DECLINED;

  1181. done:

  1182.     if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
  1183.         ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
  1184.                       ngx_close_file_n " \"%s\" failed", name->data);
  1185.     }

  1186.     return rc;
  1187. }


  1188. static void
  1189. ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx)
  1190. {
  1191.     u_char                              *p;
  1192.     uint32_t                             hash;
  1193.     ngx_str_t                            s;
  1194.     ngx_uint_t                           i;
  1195.     ngx_file_mapping_t                   fm;
  1196.     ngx_http_geo_range_t                *r, *range, **ranges;
  1197.     ngx_http_geo_header_t               *header;
  1198.     ngx_http_geo_variable_value_node_t  *gvvn;

  1199.     fm.name = ngx_pnalloc(ctx->temp_pool, ctx->include_name.len + 5);
  1200.     if (fm.name == NULL) {
  1201.         return;
  1202.     }

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

  1204.     fm.size = ctx->data_size;
  1205.     fm.log = ctx->pool->log;

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

  1208.     if (ngx_create_file_mapping(&fm) != NGX_OK) {
  1209.         return;
  1210.     }

  1211.     p = ngx_cpymem(fm.addr, &ngx_http_geo_header,
  1212.                    sizeof(ngx_http_geo_header_t));

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

  1215.     p += sizeof(ngx_http_variable_value_t);

  1216.     ranges = (ngx_http_geo_range_t **) p;

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

  1218.     for (i = 0; i < 0x10000; i++) {
  1219.         r = ctx->high.low[i];
  1220.         if (r == NULL) {
  1221.             continue;
  1222.         }

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

  1225.         do {
  1226.             s.len = r->value->len;
  1227.             s.data = r->value->data;
  1228.             hash = ngx_crc32_long(s.data, s.len);
  1229.             gvvn = (ngx_http_geo_variable_value_node_t *)
  1230.                         ngx_str_rbtree_lookup(&ctx->rbtree, &s, hash);

  1231.             range->value = (ngx_http_variable_value_t *) gvvn->offset;
  1232.             range->start = r->start;
  1233.             range->end = r->end;
  1234.             range++;

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

  1236.         range->value = NULL;

  1237.         p = (u_char *) range + sizeof(void *);
  1238.     }

  1239.     header = fm.addr;
  1240.     header->crc32 = ngx_crc32_long((u_char *) fm.addr
  1241.                                        + sizeof(ngx_http_geo_header_t),
  1242.                                    fm.size - sizeof(ngx_http_geo_header_t));

  1243.     ngx_close_file_mapping(&fm);
  1244. }


  1245. static u_char *
  1246. ngx_http_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node,
  1247.     ngx_rbtree_node_t *sentinel)
  1248. {
  1249.     ngx_http_variable_value_t           *vv;
  1250.     ngx_http_geo_variable_value_node_t  *gvvn;

  1251.     if (node == sentinel) {
  1252.         return p;
  1253.     }

  1254.     gvvn = (ngx_http_geo_variable_value_node_t *) node;
  1255.     gvvn->offset = p - base;

  1256.     vv = (ngx_http_variable_value_t *) p;
  1257.     *vv = *gvvn->value;
  1258.     p += sizeof(ngx_http_variable_value_t);
  1259.     vv->data = (u_char *) (p - base);

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

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

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

  1263.     return ngx_http_geo_copy_values(base, p, node->right, sentinel);
  1264. }