src/http/modules/ngx_http_geo_module.c - nginx-1.7.10

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 = 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.     default: /* AF_INET */
  170.         sin = (struct sockaddr_in *) addr.sockaddr;
  171.         inaddr = ntohl(sin->sin_addr.s_addr);

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

  174.         break;
  175.     }

  176. done:

  177.     *v = *vv;

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

  180.     return NGX_OK;
  181. }


  182. static ngx_int_t
  183. ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
  184.     uintptr_t data)
  185. {
  186.     ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;

  187.     in_addr_t              inaddr;
  188.     ngx_addr_t             addr;
  189.     ngx_uint_t             n;
  190.     struct sockaddr_in    *sin;
  191.     ngx_http_geo_range_t  *range;
  192. #if (NGX_HAVE_INET6)
  193.     u_char                *p;
  194.     struct in6_addr       *inaddr6;
  195. #endif

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

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

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

  199. #if (NGX_HAVE_INET6)
  200.         case AF_INET6:
  201.             inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;

  202.             if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
  203.                 p = inaddr6->s6_addr;

  204.                 inaddr = p[12] << 24;
  205.                 inaddr += p[13] << 16;
  206.                 inaddr += p[14] << 8;
  207.                 inaddr += p[15];

  208.             } else {
  209.                 inaddr = INADDR_NONE;
  210.             }

  211.             break;
  212. #endif

  213.         default: /* AF_INET */
  214.             sin = (struct sockaddr_in *) addr.sockaddr;
  215.             inaddr = ntohl(sin->sin_addr.s_addr);
  216.             break;
  217.         }

  218.     } else {
  219.         inaddr = INADDR_NONE;
  220.     }

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

  223.         if (range) {
  224.             n = inaddr & 0xffff;
  225.             do {
  226.                 if (n >= (ngx_uint_t) range->start
  227.                     && n <= (ngx_uint_t) range->end)
  228.                 {
  229.                     *v = *range->value;
  230.                     break;
  231.                 }
  232.             } while ((++range)->value);
  233.         }
  234.     }

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

  237.     return NGX_OK;
  238. }


  239. static ngx_int_t
  240. ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx,
  241.     ngx_addr_t *addr)
  242. {
  243.     ngx_array_t  *xfwd;

  244.     if (ngx_http_geo_real_addr(r, ctx, addr) != NGX_OK) {
  245.         return NGX_ERROR;
  246.     }

  247.     xfwd = &r->headers_in.x_forwarded_for;

  248.     if (xfwd->nelts > 0 && ctx->proxies != NULL) {
  249.         (void) ngx_http_get_forwarded_addr(r, addr, xfwd, NULL,
  250.                                            ctx->proxies, ctx->proxy_recursive);
  251.     }

  252.     return NGX_OK;
  253. }


  254. static ngx_int_t
  255. ngx_http_geo_real_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx,
  256.     ngx_addr_t *addr)
  257. {
  258.     ngx_http_variable_value_t  *v;

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

  262.         addr->sockaddr = r->connection->sockaddr;
  263.         addr->socklen = r->connection->socklen;
  264.         /* addr->name = r->connection->addr_text; */

  265.         return NGX_OK;
  266.     }

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

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

  271.         return NGX_ERROR;
  272.     }

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

  275.     if (ngx_parse_addr(r->pool, addr, v->data, v->len) == NGX_OK) {
  276.         return NGX_OK;
  277.     }

  278.     return NGX_ERROR;
  279. }


  280. static char *
  281. ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  282. {
  283.     char                     *rv;
  284.     size_t                    len;
  285.     ngx_str_t                *value, name;
  286.     ngx_uint_t                i;
  287.     ngx_conf_t                save;
  288.     ngx_pool_t               *pool;
  289.     ngx_array_t              *a;
  290.     ngx_http_variable_t      *var;
  291.     ngx_http_geo_ctx_t       *geo;
  292.     ngx_http_geo_conf_ctx_t   ctx;
  293. #if (NGX_HAVE_INET6)
  294.     static struct in6_addr    zero;
  295. #endif

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

  297.     geo = ngx_palloc(cf->pool, sizeof(ngx_http_geo_ctx_t));
  298.     if (geo == NULL) {
  299.         return NGX_CONF_ERROR;
  300.     }

  301.     name = value[1];

  302.     if (name.data[0] != '$') {
  303.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  304.                            "invalid variable name \"%V\"", &name);
  305.         return NGX_CONF_ERROR;
  306.     }

  307.     name.len--;
  308.     name.data++;

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

  310.         geo->index = ngx_http_get_variable_index(cf, &name);
  311.         if (geo->index == NGX_ERROR) {
  312.             return NGX_CONF_ERROR;
  313.         }

  314.         name = value[2];

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

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

  322.     } else {
  323.         geo->index = -1;
  324.     }

  325.     var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
  326.     if (var == NULL) {
  327.         return NGX_CONF_ERROR;
  328.     }

  329.     pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
  330.     if (pool == NULL) {
  331.         return NGX_CONF_ERROR;
  332.     }

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

  334.     ctx.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
  335.     if (ctx.temp_pool == NULL) {
  336.         return NGX_CONF_ERROR;
  337.     }

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

  339.     ctx.pool = cf->pool;
  340.     ctx.data_size = sizeof(ngx_http_geo_header_t)
  341.                   + sizeof(ngx_http_variable_value_t)
  342.                   + 0x10000 * sizeof(ngx_http_geo_range_t *);
  343.     ctx.allow_binary_include = 1;

  344.     save = *cf;
  345.     cf->pool = pool;
  346.     cf->ctx = &ctx;
  347.     cf->handler = ngx_http_geo;
  348.     cf->handler_conf = conf;

  349.     rv = ngx_conf_parse(cf, NULL);

  350.     *cf = save;

  351.     geo->proxies = ctx.proxies;
  352.     geo->proxy_recursive = ctx.proxy_recursive;

  353.     if (ctx.ranges) {

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

  357.                 if (a == NULL || a->nelts == 0) {
  358.                     continue;
  359.                 }

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

  361.                 ctx.high.low[i] = ngx_palloc(cf->pool, len + sizeof(void *));
  362.                 if (ctx.high.low[i] == NULL) {
  363.                     return NGX_CONF_ERROR;
  364.                 }

  365.                 ngx_memcpy(ctx.high.low[i], a->elts, len);
  366.                 ctx.high.low[i][a->nelts].value = NULL;
  367.                 ctx.data_size += len + sizeof(void *);
  368.             }

  369.             if (ctx.allow_binary_include
  370.                 && !ctx.outside_entries
  371.                 && ctx.entries > 100000
  372.                 && ctx.includes == 1)
  373.             {
  374.                 ngx_http_geo_create_binary_base(&ctx);
  375.             }
  376.         }

  377.         if (ctx.high.default_value == NULL) {
  378.             ctx.high.default_value = &ngx_http_variable_null_value;
  379.         }

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

  381.         var->get_handler = ngx_http_geo_range_variable;
  382.         var->data = (uintptr_t) geo;

  383.         ngx_destroy_pool(ctx.temp_pool);
  384.         ngx_destroy_pool(pool);

  385.     } else {
  386.         if (ctx.tree == NULL) {
  387.             ctx.tree = ngx_radix_tree_create(cf->pool, -1);
  388.             if (ctx.tree == NULL) {
  389.                 return NGX_CONF_ERROR;
  390.             }
  391.         }

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

  393. #if (NGX_HAVE_INET6)
  394.         if (ctx.tree6 == NULL) {
  395.             ctx.tree6 = ngx_radix_tree_create(cf->pool, -1);
  396.             if (ctx.tree6 == NULL) {
  397.                 return NGX_CONF_ERROR;
  398.             }
  399.         }

  400.         geo->u.trees.tree6 = ctx.tree6;
  401. #endif

  402.         var->get_handler = ngx_http_geo_cidr_variable;
  403.         var->data = (uintptr_t) geo;

  404.         ngx_destroy_pool(ctx.temp_pool);
  405.         ngx_destroy_pool(pool);

  406.         if (ngx_radix32tree_insert(ctx.tree, 0, 0,
  407.                                    (uintptr_t) &ngx_http_variable_null_value)
  408.             == NGX_ERROR)
  409.         {
  410.             return NGX_CONF_ERROR;
  411.         }

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

  413. #if (NGX_HAVE_INET6)
  414.         if (ngx_radix128tree_insert(ctx.tree6, zero.s6_addr, zero.s6_addr,
  415.                                     (uintptr_t) &ngx_http_variable_null_value)
  416.             == NGX_ERROR)
  417.         {
  418.             return NGX_CONF_ERROR;
  419.         }
  420. #endif
  421.     }

  422.     return rv;
  423. }


  424. static char *
  425. ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
  426. {
  427.     char                     *rv;
  428.     ngx_str_t                *value;
  429.     ngx_cidr_t                cidr;
  430.     ngx_http_geo_conf_ctx_t  *ctx;

  431.     ctx = cf->ctx;

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

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

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

  435.             if (ctx->tree
  436. #if (NGX_HAVE_INET6)
  437.                 || ctx->tree6
  438. #endif
  439.                )
  440.             {
  441.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  442.                                    "the \"ranges\" directive must be "
  443.                                    "the first directive inside \"geo\" block");
  444.                 goto failed;
  445.             }

  446.             ctx->ranges = 1;

  447.             rv = NGX_CONF_OK;

  448.             goto done;
  449.         }

  450.         else if (ngx_strcmp(value[0].data, "proxy_recursive") == 0) {
  451.             ctx->proxy_recursive = 1;
  452.             rv = NGX_CONF_OK;
  453.             goto done;
  454.         }
  455.     }

  456.     if (cf->args->nelts != 2) {
  457.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  458.                            "invalid number of the geo parameters");
  459.         goto failed;
  460.     }

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

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

  463.         goto done;

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

  465.         if (ngx_http_geo_cidr_value(cf, &value[1], &cidr) != NGX_OK) {
  466.             goto failed;
  467.         }

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

  469.         goto done;
  470.     }

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

  473.     } else {
  474.         rv = ngx_http_geo_cidr(cf, ctx, value);
  475.     }

  476. done:

  477.     ngx_reset_pool(cf->pool);

  478.     return rv;

  479. failed:

  480.     ngx_reset_pool(cf->pool);

  481.     return NGX_CONF_ERROR;
  482. }


  483. static char *
  484. ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  485.     ngx_str_t *value)
  486. {
  487.     u_char      *p, *last;
  488.     in_addr_t    start, end;
  489.     ngx_str_t   *net;
  490.     ngx_uint_t   del;

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

  492.         if (ctx->high.default_value) {
  493.             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  494.                 "duplicate default geo range value: \"%V\", old value: \"%v\"",
  495.                 &value[1], ctx->high.default_value);
  496.         }

  497.         ctx->high.default_value = ngx_http_geo_value(cf, ctx, &value[1]);
  498.         if (ctx->high.default_value == NULL) {
  499.             return NGX_CONF_ERROR;
  500.         }

  501.         return NGX_CONF_OK;
  502.     }

  503.     if (ctx->binary_include) {
  504.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  505.             "binary geo range base \"%s\" cannot be mixed with usual entries",
  506.             ctx->include_name.data);
  507.         return NGX_CONF_ERROR;
  508.     }

  509.     if (ctx->high.low == NULL) {
  510.         ctx->high.low = ngx_pcalloc(ctx->pool,
  511.                                     0x10000 * sizeof(ngx_http_geo_range_t *));
  512.         if (ctx->high.low == NULL) {
  513.             return NGX_CONF_ERROR;
  514.         }
  515.     }

  516.     ctx->entries++;
  517.     ctx->outside_entries = 1;

  518.     if (ngx_strcmp(value[0].data, "delete") == 0) {
  519.         net = &value[1];
  520.         del = 1;

  521.     } else {
  522.         net = &value[0];
  523.         del = 0;
  524.     }

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

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

  527.     if (p == NULL) {
  528.         goto invalid;
  529.     }

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

  531.     if (start == INADDR_NONE) {
  532.         goto invalid;
  533.     }

  534.     start = ntohl(start);

  535.     p++;

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

  537.     if (end == INADDR_NONE) {
  538.         goto invalid;
  539.     }

  540.     end = ntohl(end);

  541.     if (start > end) {
  542.         goto invalid;
  543.     }

  544.     if (del) {
  545.         if (ngx_http_geo_delete_range(cf, ctx, start, end)) {
  546.             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  547.                                "no address range \"%V\" to delete", net);
  548.         }

  549.         return NGX_CONF_OK;
  550.     }

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

  552.     if (ctx->value == NULL) {
  553.         return NGX_CONF_ERROR;
  554.     }

  555.     ctx->net = net;

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

  557. invalid:

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

  559.     return NGX_CONF_ERROR;
  560. }


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

  562. static char *
  563. ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  564.     in_addr_t start, in_addr_t end)
  565. {
  566.     in_addr_t              n;
  567.     ngx_uint_t             h, i, s, e;
  568.     ngx_array_t           *a;
  569.     ngx_http_geo_range_t  *range;

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

  571.         h = n >> 16;

  572.         if (n == start) {
  573.             s = n & 0xffff;
  574.         } else {
  575.             s = 0;
  576.         }

  577.         if ((n | 0xffff) > end) {
  578.             e = end & 0xffff;

  579.         } else {
  580.             e = 0xffff;
  581.         }

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

  583.         if (a == NULL) {
  584.             a = ngx_array_create(ctx->temp_pool, 64,
  585.                                  sizeof(ngx_http_geo_range_t));
  586.             if (a == NULL) {
  587.                 return NGX_CONF_ERROR;
  588.             }

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

  591.         i = a->nelts;
  592.         range = a->elts;

  593.         while (i) {

  594.             i--;

  595.             if (e < (ngx_uint_t) range[i].start) {
  596.                 continue;
  597.             }

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

  599.                 /* add after the range */

  600.                 range = ngx_array_push(a);
  601.                 if (range == NULL) {
  602.                     return NGX_CONF_ERROR;
  603.                 }

  604.                 range = a->elts;

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

  607.                 range[i + 1].start = (u_short) s;
  608.                 range[i + 1].end = (u_short) e;
  609.                 range[i + 1].value = ctx->value;

  610.                 goto next;
  611.             }

  612.             if (s == (ngx_uint_t) range[i].start
  613.                 && e == (ngx_uint_t) range[i].end)
  614.             {
  615.                 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  616.                     "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
  617.                     ctx->net, ctx->value, range[i].value);

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

  619.                 goto next;
  620.             }

  621.             if (s > (ngx_uint_t) range[i].start
  622.                 && e < (ngx_uint_t) range[i].end)
  623.             {
  624.                 /* split the range and insert the new one */

  625.                 range = ngx_array_push(a);
  626.                 if (range == NULL) {
  627.                     return NGX_CONF_ERROR;
  628.                 }

  629.                 range = ngx_array_push(a);
  630.                 if (range == NULL) {
  631.                     return NGX_CONF_ERROR;
  632.                 }

  633.                 range = a->elts;

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

  636.                 range[i + 2].start = (u_short) (e + 1);
  637.                 range[i + 2].end = range[i].end;
  638.                 range[i + 2].value = range[i].value;

  639.                 range[i + 1].start = (u_short) s;
  640.                 range[i + 1].end = (u_short) e;
  641.                 range[i + 1].value = ctx->value;

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

  643.                 goto next;
  644.             }

  645.             if (s == (ngx_uint_t) range[i].start
  646.                 && e < (ngx_uint_t) range[i].end)
  647.             {
  648.                 /* shift the range start and insert the new range */

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

  653.                 range = a->elts;

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

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

  657.                 range[i].start = (u_short) s;
  658.                 range[i].end = (u_short) e;
  659.                 range[i].value = ctx->value;

  660.                 goto next;
  661.             }

  662.             if (s > (ngx_uint_t) range[i].start
  663.                 && e == (ngx_uint_t) range[i].end)
  664.             {
  665.                 /* shift the range end and insert the new range */

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

  670.                 range = a->elts;

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

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

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

  677.                 goto next;
  678.             }

  679.             s = (ngx_uint_t) range[i].start;
  680.             e = (ngx_uint_t) range[i].end;

  681.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  682.                          "range \"%V\" overlaps \"%d.%d.%d.%d-%d.%d.%d.%d\"",
  683.                          ctx->net,
  684.                          h >> 8, h & 0xff, s >> 8, s & 0xff,
  685.                          h >> 8, h & 0xff, e >> 8, e & 0xff);

  686.             return NGX_CONF_ERROR;
  687.         }

  688.         /* add the first range */

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

  693.         range->start = (u_short) s;
  694.         range->end = (u_short) e;
  695.         range->value = ctx->value;

  696.     next:

  697.         continue;
  698.     }

  699.     return NGX_CONF_OK;
  700. }


  701. static ngx_uint_t
  702. ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  703.     in_addr_t start, in_addr_t end)
  704. {
  705.     in_addr_t              n;
  706.     ngx_uint_t             h, i, s, e, warn;
  707.     ngx_array_t           *a;
  708.     ngx_http_geo_range_t  *range;

  709.     warn = 0;

  710.     for (n = start; n <= end; n += 0x10000) {

  711.         h = n >> 16;

  712.         if (n == start) {
  713.             s = n & 0xffff;
  714.         } else {
  715.             s = 0;
  716.         }

  717.         if ((n | 0xffff) > end) {
  718.             e = end & 0xffff;

  719.         } else {
  720.             e = 0xffff;
  721.         }

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

  723.         if (a == NULL) {
  724.             warn = 1;
  725.             continue;
  726.         }

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

  729.             if (s == (ngx_uint_t) range[i].start
  730.                 && e == (ngx_uint_t) range[i].end)
  731.             {
  732.                 ngx_memmove(&range[i], &range[i + 1],
  733.                            (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));

  734.                 a->nelts--;

  735.                 break;
  736.             }

  737.             if (s != (ngx_uint_t) range[i].start
  738.                 && e != (ngx_uint_t) range[i].end)
  739.             {
  740.                 continue;
  741.             }

  742.             warn = 1;
  743.         }
  744.     }

  745.     return warn;
  746. }


  747. static char *
  748. ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  749.     ngx_str_t *value)
  750. {
  751.     char        *rv;
  752.     ngx_int_t    rc, del;
  753.     ngx_str_t   *net;
  754.     ngx_cidr_t   cidr;

  755.     if (ctx->tree == NULL) {
  756.         ctx->tree = ngx_radix_tree_create(ctx->pool, -1);
  757.         if (ctx->tree == NULL) {
  758.             return NGX_CONF_ERROR;
  759.         }
  760.     }

  761. #if (NGX_HAVE_INET6)
  762.     if (ctx->tree6 == NULL) {
  763.         ctx->tree6 = ngx_radix_tree_create(ctx->pool, -1);
  764.         if (ctx->tree6 == NULL) {
  765.             return NGX_CONF_ERROR;
  766.         }
  767.     }
  768. #endif

  769.     if (ngx_strcmp(value[0].data, "default") == 0) {
  770.         cidr.family = AF_INET;
  771.         cidr.u.in.addr = 0;
  772.         cidr.u.in.mask = 0;

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

  774.         if (rv != NGX_CONF_OK) {
  775.             return rv;
  776.         }

  777. #if (NGX_HAVE_INET6)
  778.         cidr.family = AF_INET6;
  779.         ngx_memzero(&cidr.u.in6, sizeof(ngx_in6_cidr_t));

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

  781.         if (rv != NGX_CONF_OK) {
  782.             return rv;
  783.         }
  784. #endif

  785.         return NGX_CONF_OK;
  786.     }

  787.     if (ngx_strcmp(value[0].data, "delete") == 0) {
  788.         net = &value[1];
  789.         del = 1;

  790.     } else {
  791.         net = &value[0];
  792.         del = 0;
  793.     }

  794.     if (ngx_http_geo_cidr_value(cf, net, &cidr) != NGX_OK) {
  795.         return NGX_CONF_ERROR;
  796.     }

  797.     if (cidr.family == AF_INET) {
  798.         cidr.u.in.addr = ntohl(cidr.u.in.addr);
  799.         cidr.u.in.mask = ntohl(cidr.u.in.mask);
  800.     }

  801.     if (del) {
  802.         switch (cidr.family) {

  803. #if (NGX_HAVE_INET6)
  804.         case AF_INET6:
  805.             rc = ngx_radix128tree_delete(ctx->tree6,
  806.                                          cidr.u.in6.addr.s6_addr,
  807.                                          cidr.u.in6.mask.s6_addr);
  808.             break;
  809. #endif

  810.         default: /* AF_INET */
  811.             rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr,
  812.                                         cidr.u.in.mask);
  813.             break;
  814.         }

  815.         if (rc != NGX_OK) {
  816.             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  817.                                "no network \"%V\" to delete", net);
  818.         }

  819.         return NGX_CONF_OK;
  820.     }

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


  823. static char *
  824. ngx_http_geo_cidr_add(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  825.     ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net)
  826. {
  827.     ngx_int_t                   rc;
  828.     ngx_http_variable_value_t  *val, *old;

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

  830.     if (val == NULL) {
  831.         return NGX_CONF_ERROR;
  832.     }

  833.     switch (cidr->family) {

  834. #if (NGX_HAVE_INET6)
  835.     case AF_INET6:
  836.         rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,
  837.                                      cidr->u.in6.mask.s6_addr,
  838.                                      (uintptr_t) val);

  839.         if (rc == NGX_OK) {
  840.             return NGX_CONF_OK;
  841.         }

  842.         if (rc == NGX_ERROR) {
  843.             return NGX_CONF_ERROR;
  844.         }

  845.         /* rc == NGX_BUSY */

  846.         old = (ngx_http_variable_value_t *)
  847.                    ngx_radix128tree_find(ctx->tree6,
  848.                                          cidr->u.in6.addr.s6_addr);

  849.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  850.               "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
  851.               net, val, old);

  852.         rc = ngx_radix128tree_delete(ctx->tree6,
  853.                                      cidr->u.in6.addr.s6_addr,
  854.                                      cidr->u.in6.mask.s6_addr);

  855.         if (rc == NGX_ERROR) {
  856.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
  857.             return NGX_CONF_ERROR;
  858.         }

  859.         rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,
  860.                                      cidr->u.in6.mask.s6_addr,
  861.                                      (uintptr_t) val);

  862.         break;
  863. #endif

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

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

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

  873.         /* rc == NGX_BUSY */

  874.         old = (ngx_http_variable_value_t *)
  875.                    ngx_radix32tree_find(ctx->tree, cidr->u.in.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_radix32tree_delete(ctx->tree,
  880.                                     cidr->u.in.addr, cidr->u.in.mask);

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

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

  887.         break;
  888.     }

  889.     if (rc == NGX_OK) {
  890.         return NGX_CONF_OK;
  891.     }

  892.     return NGX_CONF_ERROR;
  893. }


  894. static ngx_http_variable_value_t *
  895. ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  896.     ngx_str_t *value)
  897. {
  898.     uint32_t                             hash;
  899.     ngx_http_variable_value_t           *val;
  900.     ngx_http_geo_variable_value_node_t  *gvvn;

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

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

  904.     if (gvvn) {
  905.         return gvvn->value;
  906.     }

  907.     val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));
  908.     if (val == NULL) {
  909.         return NULL;
  910.     }

  911.     val->len = value->len;
  912.     val->data = ngx_pstrdup(ctx->pool, value);
  913.     if (val->data == NULL) {
  914.         return NULL;
  915.     }

  916.     val->valid = 1;
  917.     val->no_cacheable = 0;
  918.     val->not_found = 0;

  919.     gvvn = ngx_palloc(ctx->temp_pool,
  920.                       sizeof(ngx_http_geo_variable_value_node_t));
  921.     if (gvvn == NULL) {
  922.         return NULL;
  923.     }

  924.     gvvn->sn.node.key = hash;
  925.     gvvn->sn.str.len = val->len;
  926.     gvvn->sn.str.data = val->data;
  927.     gvvn->value = val;
  928.     gvvn->offset = 0;

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

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

  932.     return val;
  933. }


  934. static char *
  935. ngx_http_geo_add_proxy(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  936.     ngx_cidr_t *cidr)
  937. {
  938.     ngx_cidr_t  *c;

  939.     if (ctx->proxies == NULL) {
  940.         ctx->proxies = ngx_array_create(ctx->pool, 4, sizeof(ngx_cidr_t));
  941.         if (ctx->proxies == NULL) {
  942.             return NGX_CONF_ERROR;
  943.         }
  944.     }

  945.     c = ngx_array_push(ctx->proxies);
  946.     if (c == NULL) {
  947.         return NGX_CONF_ERROR;
  948.     }

  949.     *c = *cidr;

  950.     return NGX_CONF_OK;
  951. }


  952. static ngx_int_t
  953. ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr)
  954. {
  955.     ngx_int_t  rc;

  956.     if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
  957.         cidr->family = AF_INET;
  958.         cidr->u.in.addr = 0xffffffff;
  959.         cidr->u.in.mask = 0xffffffff;

  960.         return NGX_OK;
  961.     }

  962.     rc = ngx_ptocidr(net, cidr);

  963.     if (rc == NGX_ERROR) {
  964.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net);
  965.         return NGX_ERROR;
  966.     }

  967.     if (rc == NGX_DONE) {
  968.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  969.                            "low address bits of %V are meaningless", net);
  970.     }

  971.     return NGX_OK;
  972. }


  973. static char *
  974. ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  975.     ngx_str_t *name)
  976. {
  977.     char       *rv;
  978.     ngx_str_t   file;

  979.     file.len = name->len + 4;
  980.     file.data = ngx_pnalloc(ctx->temp_pool, name->len + 5);
  981.     if (file.data == NULL) {
  982.         return NGX_CONF_ERROR;
  983.     }

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

  985.     if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {
  986.         return NGX_CONF_ERROR;
  987.     }

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

  990.         switch (ngx_http_geo_include_binary_base(cf, ctx, &file)) {
  991.         case NGX_OK:
  992.             return NGX_CONF_OK;
  993.         case NGX_ERROR:
  994.             return NGX_CONF_ERROR;
  995.         default:
  996.             break;
  997.         }
  998.     }

  999.     file.len -= 4;
  1000.     file.data[file.len] = '\0';

  1001.     ctx->include_name = file;

  1002.     if (ctx->outside_entries) {
  1003.         ctx->allow_binary_include = 0;
  1004.     }

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

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

  1007.     ctx->includes++;
  1008.     ctx->outside_entries = 0;

  1009.     return rv;
  1010. }


  1011. static ngx_int_t
  1012. ngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
  1013.     ngx_str_t *name)
  1014. {
  1015.     u_char                     *base, ch;
  1016.     time_t                      mtime;
  1017.     size_t                      size, len;
  1018.     ssize_t                     n;
  1019.     uint32_t                    crc32;
  1020.     ngx_err_t                   err;
  1021.     ngx_int_t                   rc;
  1022.     ngx_uint_t                  i;
  1023.     ngx_file_t                  file;
  1024.     ngx_file_info_t             fi;
  1025.     ngx_http_geo_range_t       *range, **ranges;
  1026.     ngx_http_geo_header_t      *header;
  1027.     ngx_http_variable_value_t  *vv;

  1028.     ngx_memzero(&file, sizeof(ngx_file_t));
  1029.     file.name = *name;
  1030.     file.log = cf->log;

  1031.     file.fd = ngx_open_file(name->data, NGX_FILE_RDONLY, 0, 0);
  1032.     if (file.fd == NGX_INVALID_FILE) {
  1033.         err = ngx_errno;
  1034.         if (err != NGX_ENOENT) {
  1035.             ngx_conf_log_error(NGX_LOG_CRIT, cf, err,
  1036.                                ngx_open_file_n " \"%s\" failed", name->data);
  1037.         }
  1038.         return NGX_DECLINED;
  1039.     }

  1040.     if (ctx->outside_entries) {
  1041.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1042.             "binary geo range base \"%s\" cannot be mixed with usual entries",
  1043.             name->data);
  1044.         rc = NGX_ERROR;
  1045.         goto done;
  1046.     }

  1047.     if (ctx->binary_include) {
  1048.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1049.             "second binary geo range base \"%s\" cannot be mixed with \"%s\"",
  1050.             name->data, ctx->include_name.data);
  1051.         rc = NGX_ERROR;
  1052.         goto done;
  1053.     }

  1054.     if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
  1055.         ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
  1056.                            ngx_fd_info_n " \"%s\" failed", name->data);
  1057.         goto failed;
  1058.     }

  1059.     size = (size_t) ngx_file_size(&fi);
  1060.     mtime = ngx_file_mtime(&fi);

  1061.     ch = name->data[name->len - 4];
  1062.     name->data[name->len - 4] = '\0';

  1063.     if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {
  1064.         ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
  1065.                            ngx_file_info_n " \"%s\" failed", name->data);
  1066.         goto failed;
  1067.     }

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

  1069.     if (mtime < ngx_file_mtime(&fi)) {
  1070.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  1071.                            "stale binary geo range base \"%s\"", name->data);
  1072.         goto failed;
  1073.     }

  1074.     base = ngx_palloc(ctx->pool, size);
  1075.     if (base == NULL) {
  1076.         goto failed;
  1077.     }

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

  1079.     if (n == NGX_ERROR) {
  1080.         ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
  1081.                            ngx_read_file_n " \"%s\" failed", name->data);
  1082.         goto failed;
  1083.     }

  1084.     if ((size_t) n != size) {
  1085.         ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,
  1086.             ngx_read_file_n " \"%s\" returned only %z bytes instead of %z",
  1087.             name->data, n, size);
  1088.         goto failed;
  1089.     }

  1090.     header = (ngx_http_geo_header_t *) base;

  1091.     if (size < 16 || ngx_memcmp(&ngx_http_geo_header, header, 12) != 0) {
  1092.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  1093.              "incompatible binary geo range base \"%s\"", name->data);
  1094.         goto failed;
  1095.     }

  1096.     ngx_crc32_init(crc32);

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

  1098.     while (vv->data) {
  1099.         len = ngx_align(sizeof(ngx_http_variable_value_t) + vv->len,
  1100.                         sizeof(void *));
  1101.         ngx_crc32_update(&crc32, (u_char *) vv, len);
  1102.         vv->data += (size_t) base;
  1103.         vv = (ngx_http_variable_value_t *) ((u_char *) vv + len);
  1104.     }
  1105.     ngx_crc32_update(&crc32, (u_char *) vv, sizeof(ngx_http_variable_value_t));
  1106.     vv++;

  1107.     ranges = (ngx_http_geo_range_t **) vv;

  1108.     for (i = 0; i < 0x10000; i++) {
  1109.         ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *));
  1110.         if (ranges[i]) {
  1111.             ranges[i] = (ngx_http_geo_range_t *)
  1112.                             ((u_char *) ranges[i] + (size_t) base);
  1113.         }
  1114.     }

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

  1116.     while ((u_char *) range < base + size) {
  1117.         while (range->value) {
  1118.             ngx_crc32_update(&crc32, (u_char *) range,
  1119.                              sizeof(ngx_http_geo_range_t));
  1120.             range->value = (ngx_http_variable_value_t *)
  1121.                                ((u_char *) range->value + (size_t) base);
  1122.             range++;
  1123.         }
  1124.         ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *));
  1125.         range = (ngx_http_geo_range_t *) ((u_char *) range + sizeof(void *));
  1126.     }

  1127.     ngx_crc32_final(crc32);

  1128.     if (crc32 != header->crc32) {
  1129.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  1130.                   "CRC32 mismatch in binary geo range base \"%s\"", name->data);
  1131.         goto failed;
  1132.     }

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

  1135.     ctx->include_name = *name;
  1136.     ctx->binary_include = 1;
  1137.     ctx->high.low = ranges;
  1138.     rc = NGX_OK;

  1139.     goto done;

  1140. failed:

  1141.     rc = NGX_DECLINED;

  1142. done:

  1143.     if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
  1144.         ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
  1145.                       ngx_close_file_n " \"%s\" failed", name->data);
  1146.     }

  1147.     return rc;
  1148. }


  1149. static void
  1150. ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx)
  1151. {
  1152.     u_char                              *p;
  1153.     uint32_t                             hash;
  1154.     ngx_str_t                            s;
  1155.     ngx_uint_t                           i;
  1156.     ngx_file_mapping_t                   fm;
  1157.     ngx_http_geo_range_t                *r, *range, **ranges;
  1158.     ngx_http_geo_header_t               *header;
  1159.     ngx_http_geo_variable_value_node_t  *gvvn;

  1160.     fm.name = ngx_pnalloc(ctx->temp_pool, ctx->include_name.len + 5);
  1161.     if (fm.name == NULL) {
  1162.         return;
  1163.     }

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

  1165.     fm.size = ctx->data_size;
  1166.     fm.log = ctx->pool->log;

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

  1169.     if (ngx_create_file_mapping(&fm) != NGX_OK) {
  1170.         return;
  1171.     }

  1172.     p = ngx_cpymem(fm.addr, &ngx_http_geo_header,
  1173.                    sizeof(ngx_http_geo_header_t));

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

  1176.     p += sizeof(ngx_http_variable_value_t);

  1177.     ranges = (ngx_http_geo_range_t **) p;

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

  1179.     for (i = 0; i < 0x10000; i++) {
  1180.         r = ctx->high.low[i];
  1181.         if (r == NULL) {
  1182.             continue;
  1183.         }

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

  1186.         do {
  1187.             s.len = r->value->len;
  1188.             s.data = r->value->data;
  1189.             hash = ngx_crc32_long(s.data, s.len);
  1190.             gvvn = (ngx_http_geo_variable_value_node_t *)
  1191.                         ngx_str_rbtree_lookup(&ctx->rbtree, &s, hash);

  1192.             range->value = (ngx_http_variable_value_t *) gvvn->offset;
  1193.             range->start = r->start;
  1194.             range->end = r->end;
  1195.             range++;

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

  1197.         range->value = NULL;

  1198.         p = (u_char *) range + sizeof(void *);
  1199.     }

  1200.     header = fm.addr;
  1201.     header->crc32 = ngx_crc32_long((u_char *) fm.addr
  1202.                                        + sizeof(ngx_http_geo_header_t),
  1203.                                    fm.size - sizeof(ngx_http_geo_header_t));

  1204.     ngx_close_file_mapping(&fm);
  1205. }


  1206. static u_char *
  1207. ngx_http_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node,
  1208.     ngx_rbtree_node_t *sentinel)
  1209. {
  1210.     ngx_http_variable_value_t           *vv;
  1211.     ngx_http_geo_variable_value_node_t  *gvvn;

  1212.     if (node == sentinel) {
  1213.         return p;
  1214.     }

  1215.     gvvn = (ngx_http_geo_variable_value_node_t *) node;
  1216.     gvvn->offset = p - base;

  1217.     vv = (ngx_http_variable_value_t *) p;
  1218.     *vv = *gvvn->value;
  1219.     p += sizeof(ngx_http_variable_value_t);
  1220.     vv->data = (u_char *) (p - base);

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

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

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

  1224.     return ngx_http_geo_copy_values(base, p, node->right, sentinel);
  1225. }