src/stream/ngx_stream_geo_module.c - nginx source code

Global variables defined

Data types defined

Functions defined

Source code


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


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


  8. typedef struct {
  9.     ngx_stream_variable_value_t       *value;
  10.     u_short                            start;
  11.     u_short                            end;
  12. } ngx_stream_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_stream_geo_trees_t;


  19. typedef struct {
  20.     ngx_stream_geo_range_t           **low;
  21.     ngx_stream_variable_value_t       *default_value;
  22. } ngx_stream_geo_high_ranges_t;


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


  28. typedef struct {
  29.     ngx_stream_variable_value_t       *value;
  30.     ngx_str_t                         *net;
  31.     ngx_stream_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_pool_t                        *pool;
  39.     ngx_pool_t                        *temp_pool;

  40.     size_t                             data_size;

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

  44.     unsigned                           ranges:1;
  45.     unsigned                           outside_entries:1;
  46.     unsigned                           allow_binary_include:1;
  47.     unsigned                           binary_include:1;
  48. } ngx_stream_geo_conf_ctx_t;


  49. typedef struct {
  50.     union {
  51.         ngx_stream_geo_trees_t         trees;
  52.         ngx_stream_geo_high_ranges_t   high;
  53.     } u;

  54.     ngx_int_t                          index;
  55. } ngx_stream_geo_ctx_t;


  56. static ngx_int_t ngx_stream_geo_addr(ngx_stream_session_t *s,
  57.     ngx_stream_geo_ctx_t *ctx, ngx_addr_t *addr);

  58. static char *ngx_stream_geo_block(ngx_conf_t *cf, ngx_command_t *cmd,
  59.     void *conf);
  60. static char *ngx_stream_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
  61. static char *ngx_stream_geo_range(ngx_conf_t *cf,
  62.     ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *value);
  63. static char *ngx_stream_geo_add_range(ngx_conf_t *cf,
  64.     ngx_stream_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
  65. static ngx_uint_t ngx_stream_geo_delete_range(ngx_conf_t *cf,
  66.     ngx_stream_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
  67. static char *ngx_stream_geo_cidr(ngx_conf_t *cf,
  68.     ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *value);
  69. static char *ngx_stream_geo_cidr_add(ngx_conf_t *cf,
  70.     ngx_stream_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr, ngx_str_t *value,
  71.     ngx_str_t *net);
  72. static ngx_stream_variable_value_t *ngx_stream_geo_value(ngx_conf_t *cf,
  73.     ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *value);
  74. static ngx_int_t ngx_stream_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net,
  75.     ngx_cidr_t *cidr);
  76. static char *ngx_stream_geo_include(ngx_conf_t *cf,
  77.     ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *name);
  78. static ngx_int_t ngx_stream_geo_include_binary_base(ngx_conf_t *cf,
  79.     ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *name);
  80. static void ngx_stream_geo_create_binary_base(ngx_stream_geo_conf_ctx_t *ctx);
  81. static u_char *ngx_stream_geo_copy_values(u_char *base, u_char *p,
  82.     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);


  83. static ngx_command_t  ngx_stream_geo_commands[] = {

  84.     { ngx_string("geo"),
  85.       NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,
  86.       ngx_stream_geo_block,
  87.       0,
  88.       0,
  89.       NULL },

  90.       ngx_null_command
  91. };


  92. static ngx_stream_module_t  ngx_stream_geo_module_ctx = {
  93.     NULL,                                  /* preconfiguration */
  94.     NULL,                                  /* postconfiguration */

  95.     NULL,                                  /* create main configuration */
  96.     NULL,                                  /* init main configuration */

  97.     NULL,                                  /* create server configuration */
  98.     NULL                                   /* merge server configuration */
  99. };


  100. ngx_module_t  ngx_stream_geo_module = {
  101.     NGX_MODULE_V1,
  102.     &ngx_stream_geo_module_ctx,            /* module context */
  103.     ngx_stream_geo_commands,               /* module directives */
  104.     NGX_STREAM_MODULE,                     /* module type */
  105.     NULL,                                  /* init master */
  106.     NULL,                                  /* init module */
  107.     NULL,                                  /* init process */
  108.     NULL,                                  /* init thread */
  109.     NULL,                                  /* exit thread */
  110.     NULL,                                  /* exit process */
  111.     NULL,                                  /* exit master */
  112.     NGX_MODULE_V1_PADDING
  113. };


  114. typedef struct {
  115.     u_char    GEORNG[6];
  116.     u_char    version;
  117.     u_char    ptr_size;
  118.     uint32_t  endianness;
  119.     uint32_t  crc32;
  120. } ngx_stream_geo_header_t;


  121. static ngx_stream_geo_header_t  ngx_stream_geo_header = {
  122.     { 'G', 'E', 'O', 'R', 'N', 'G' }, 0, sizeof(void *), 0x12345678, 0
  123. };


  124. /* geo range is AF_INET only */

  125. static ngx_int_t
  126. ngx_stream_geo_cidr_variable(ngx_stream_session_t *s,
  127.     ngx_stream_variable_value_t *v, uintptr_t data)
  128. {
  129.     ngx_stream_geo_ctx_t *ctx = (ngx_stream_geo_ctx_t *) data;

  130.     in_addr_t                     inaddr;
  131.     ngx_addr_t                    addr;
  132.     struct sockaddr_in           *sin;
  133.     ngx_stream_variable_value_t  *vv;
  134. #if (NGX_HAVE_INET6)
  135.     u_char                       *p;
  136.     struct in6_addr              *inaddr6;
  137. #endif

  138.     if (ngx_stream_geo_addr(s, ctx, &addr) != NGX_OK) {
  139.         vv = (ngx_stream_variable_value_t *)
  140.                   ngx_radix32tree_find(ctx->u.trees.tree, INADDR_NONE);
  141.         goto done;
  142.     }

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

  144. #if (NGX_HAVE_INET6)
  145.     case AF_INET6:
  146.         inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;
  147.         p = inaddr6->s6_addr;

  148.         if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
  149.             inaddr = (in_addr_t) p[12] << 24;
  150.             inaddr += p[13] << 16;
  151.             inaddr += p[14] << 8;
  152.             inaddr += p[15];

  153.             vv = (ngx_stream_variable_value_t *)
  154.                       ngx_radix32tree_find(ctx->u.trees.tree, inaddr);

  155.         } else {
  156.             vv = (ngx_stream_variable_value_t *)
  157.                       ngx_radix128tree_find(ctx->u.trees.tree6, p);
  158.         }

  159.         break;
  160. #endif

  161. #if (NGX_HAVE_UNIX_DOMAIN)
  162.     case AF_UNIX:
  163.         vv = (ngx_stream_variable_value_t *)
  164.                   ngx_radix32tree_find(ctx->u.trees.tree, INADDR_NONE);
  165.         break;
  166. #endif

  167.     default: /* AF_INET */
  168.         sin = (struct sockaddr_in *) addr.sockaddr;
  169.         inaddr = ntohl(sin->sin_addr.s_addr);

  170.         vv = (ngx_stream_variable_value_t *)
  171.                   ngx_radix32tree_find(ctx->u.trees.tree, inaddr);

  172.         break;
  173.     }

  174. done:

  175.     *v = *vv;

  176.     ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
  177.                    "stream geo: %v", v);

  178.     return NGX_OK;
  179. }


  180. static ngx_int_t
  181. ngx_stream_geo_range_variable(ngx_stream_session_t *s,
  182.     ngx_stream_variable_value_t *v, uintptr_t data)
  183. {
  184.     ngx_stream_geo_ctx_t *ctx = (ngx_stream_geo_ctx_t *) data;

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

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

  195.     if (ngx_stream_geo_addr(s, ctx, &addr) == NGX_OK) {

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

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

  200.             if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
  201.                 p = inaddr6->s6_addr;

  202.                 inaddr = (in_addr_t) p[12] << 24;
  203.                 inaddr += p[13] << 16;
  204.                 inaddr += p[14] << 8;
  205.                 inaddr += p[15];

  206.             } else {
  207.                 inaddr = INADDR_NONE;
  208.             }

  209.             break;
  210. #endif

  211. #if (NGX_HAVE_UNIX_DOMAIN)
  212.         case AF_UNIX:
  213.             inaddr = INADDR_NONE;
  214.             break;
  215. #endif

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

  221.     } else {
  222.         inaddr = INADDR_NONE;
  223.     }

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

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

  238.     ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
  239.                    "stream geo: %v", v);

  240.     return NGX_OK;
  241. }


  242. static ngx_int_t
  243. ngx_stream_geo_addr(ngx_stream_session_t *s, ngx_stream_geo_ctx_t *ctx,
  244.     ngx_addr_t *addr)
  245. {
  246.     ngx_stream_variable_value_t  *v;

  247.     if (ctx->index == -1) {
  248.         ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
  249.                        "stream geo started: %V", &s->connection->addr_text);

  250.         addr->sockaddr = s->connection->sockaddr;
  251.         addr->socklen = s->connection->socklen;
  252.         /* addr->name = s->connection->addr_text; */

  253.         return NGX_OK;
  254.     }

  255.     v = ngx_stream_get_flushed_variable(s, ctx->index);

  256.     if (v == NULL || v->not_found) {
  257.         ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
  258.                        "stream geo not found");

  259.         return NGX_ERROR;
  260.     }

  261.     ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
  262.                    "stream geo started: %v", v);

  263.     if (ngx_parse_addr(s->connection->pool, addr, v->data, v->len) == NGX_OK) {
  264.         return NGX_OK;
  265.     }

  266.     return NGX_ERROR;
  267. }


  268. static char *
  269. ngx_stream_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  270. {
  271.     char                       *rv;
  272.     size_t                      len;
  273.     ngx_str_t                  *value, name;
  274.     ngx_uint_t                  i;
  275.     ngx_conf_t                  save;
  276.     ngx_pool_t                 *pool;
  277.     ngx_array_t                *a;
  278.     ngx_stream_variable_t      *var;
  279.     ngx_stream_geo_ctx_t       *geo;
  280.     ngx_stream_geo_conf_ctx_t   ctx;
  281. #if (NGX_HAVE_INET6)
  282.     static struct in6_addr      zero;
  283. #endif

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

  285.     geo = ngx_palloc(cf->pool, sizeof(ngx_stream_geo_ctx_t));
  286.     if (geo == NULL) {
  287.         return NGX_CONF_ERROR;
  288.     }

  289.     name = value[1];

  290.     if (name.data[0] != '$') {
  291.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  292.                            "invalid variable name \"%V\"", &name);
  293.         return NGX_CONF_ERROR;
  294.     }

  295.     name.len--;
  296.     name.data++;

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

  298.         geo->index = ngx_stream_get_variable_index(cf, &name);
  299.         if (geo->index == NGX_ERROR) {
  300.             return NGX_CONF_ERROR;
  301.         }

  302.         name = value[2];

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

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

  310.     } else {
  311.         geo->index = -1;
  312.     }

  313.     var = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE);
  314.     if (var == NULL) {
  315.         return NGX_CONF_ERROR;
  316.     }

  317.     pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
  318.     if (pool == NULL) {
  319.         return NGX_CONF_ERROR;
  320.     }

  321.     ngx_memzero(&ctx, sizeof(ngx_stream_geo_conf_ctx_t));

  322.     ctx.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
  323.     if (ctx.temp_pool == NULL) {
  324.         ngx_destroy_pool(pool);
  325.         return NGX_CONF_ERROR;
  326.     }

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

  328.     ctx.pool = cf->pool;
  329.     ctx.data_size = sizeof(ngx_stream_geo_header_t)
  330.                   + sizeof(ngx_stream_variable_value_t)
  331.                   + 0x10000 * sizeof(ngx_stream_geo_range_t *);
  332.     ctx.allow_binary_include = 1;

  333.     save = *cf;
  334.     cf->pool = pool;
  335.     cf->ctx = &ctx;
  336.     cf->handler = ngx_stream_geo;
  337.     cf->handler_conf = conf;

  338.     rv = ngx_conf_parse(cf, NULL);

  339.     *cf = save;

  340.     if (rv != NGX_CONF_OK) {
  341.         goto failed;
  342.     }

  343.     if (ctx.ranges) {

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

  347.                 if (a == NULL) {
  348.                     continue;
  349.                 }

  350.                 if (a->nelts == 0) {
  351.                     ctx.high.low[i] = NULL;
  352.                     continue;
  353.                 }

  354.                 len = a->nelts * sizeof(ngx_stream_geo_range_t);

  355.                 ctx.high.low[i] = ngx_palloc(cf->pool, len + sizeof(void *));
  356.                 if (ctx.high.low[i] == NULL) {
  357.                     goto failed;
  358.                 }

  359.                 ngx_memcpy(ctx.high.low[i], a->elts, len);
  360.                 ctx.high.low[i][a->nelts].value = NULL;
  361.                 ctx.data_size += len + sizeof(void *);
  362.             }

  363.             if (ctx.allow_binary_include
  364.                 && !ctx.outside_entries
  365.                 && ctx.entries > 100000
  366.                 && ctx.includes == 1)
  367.             {
  368.                 ngx_stream_geo_create_binary_base(&ctx);
  369.             }
  370.         }

  371.         if (ctx.high.default_value == NULL) {
  372.             ctx.high.default_value = &ngx_stream_variable_null_value;
  373.         }

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

  375.         var->get_handler = ngx_stream_geo_range_variable;
  376.         var->data = (uintptr_t) geo;

  377.     } else {
  378.         if (ctx.tree == NULL) {
  379.             ctx.tree = ngx_radix_tree_create(cf->pool, -1);
  380.             if (ctx.tree == NULL) {
  381.                 goto failed;
  382.             }
  383.         }

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

  385. #if (NGX_HAVE_INET6)
  386.         if (ctx.tree6 == NULL) {
  387.             ctx.tree6 = ngx_radix_tree_create(cf->pool, -1);
  388.             if (ctx.tree6 == NULL) {
  389.                 goto failed;
  390.             }
  391.         }

  392.         geo->u.trees.tree6 = ctx.tree6;
  393. #endif

  394.         var->get_handler = ngx_stream_geo_cidr_variable;
  395.         var->data = (uintptr_t) geo;

  396.         if (ngx_radix32tree_insert(ctx.tree, 0, 0,
  397.                                    (uintptr_t) &ngx_stream_variable_null_value)
  398.             == NGX_ERROR)
  399.         {
  400.             goto failed;
  401.         }

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

  403. #if (NGX_HAVE_INET6)
  404.         if (ngx_radix128tree_insert(ctx.tree6, zero.s6_addr, zero.s6_addr,
  405.                                     (uintptr_t) &ngx_stream_variable_null_value)
  406.             == NGX_ERROR)
  407.         {
  408.             goto failed;
  409.         }
  410. #endif
  411.     }

  412.     ngx_destroy_pool(ctx.temp_pool);
  413.     ngx_destroy_pool(pool);

  414.     return NGX_CONF_OK;

  415. failed:

  416.     ngx_destroy_pool(ctx.temp_pool);
  417.     ngx_destroy_pool(pool);

  418.     return NGX_CONF_ERROR;
  419. }


  420. static char *
  421. ngx_stream_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
  422. {
  423.     char                       *rv;
  424.     ngx_str_t                  *value;
  425.     ngx_stream_geo_conf_ctx_t  *ctx;

  426.     ctx = cf->ctx;

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

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

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

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

  441.             ctx->ranges = 1;

  442.             rv = NGX_CONF_OK;

  443.             goto done;
  444.         }
  445.     }

  446.     if (cf->args->nelts != 2) {
  447.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  448.                            "invalid number of the geo parameters");
  449.         goto failed;
  450.     }

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

  452.         rv = ngx_stream_geo_include(cf, ctx, &value[1]);

  453.         goto done;
  454.     }

  455.     if (ctx->ranges) {
  456.         rv = ngx_stream_geo_range(cf, ctx, value);

  457.     } else {
  458.         rv = ngx_stream_geo_cidr(cf, ctx, value);
  459.     }

  460. done:

  461.     ngx_reset_pool(cf->pool);

  462.     return rv;

  463. failed:

  464.     ngx_reset_pool(cf->pool);

  465.     return NGX_CONF_ERROR;
  466. }


  467. static char *
  468. ngx_stream_geo_range(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx,
  469.     ngx_str_t *value)
  470. {
  471.     u_char      *p, *last;
  472.     in_addr_t    start, end;
  473.     ngx_str_t   *net;
  474.     ngx_uint_t   del;

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

  476.         if (ctx->high.default_value) {
  477.             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  478.                 "duplicate default geo range value: \"%V\", old value: \"%v\"",
  479.                 &value[1], ctx->high.default_value);
  480.         }

  481.         ctx->high.default_value = ngx_stream_geo_value(cf, ctx, &value[1]);
  482.         if (ctx->high.default_value == NULL) {
  483.             return NGX_CONF_ERROR;
  484.         }

  485.         return NGX_CONF_OK;
  486.     }

  487.     if (ctx->binary_include) {
  488.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  489.             "binary geo range base \"%s\" cannot be mixed with usual entries",
  490.             ctx->include_name.data);
  491.         return NGX_CONF_ERROR;
  492.     }

  493.     if (ctx->high.low == NULL) {
  494.         ctx->high.low = ngx_pcalloc(ctx->pool,
  495.                                     0x10000 * sizeof(ngx_stream_geo_range_t *));
  496.         if (ctx->high.low == NULL) {
  497.             return NGX_CONF_ERROR;
  498.         }
  499.     }

  500.     ctx->entries++;
  501.     ctx->outside_entries = 1;

  502.     if (ngx_strcmp(value[0].data, "delete") == 0) {
  503.         net = &value[1];
  504.         del = 1;

  505.     } else {
  506.         net = &value[0];
  507.         del = 0;
  508.     }

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

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

  511.     if (p == NULL) {
  512.         goto invalid;
  513.     }

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

  515.     if (start == INADDR_NONE) {
  516.         goto invalid;
  517.     }

  518.     start = ntohl(start);

  519.     p++;

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

  521.     if (end == INADDR_NONE) {
  522.         goto invalid;
  523.     }

  524.     end = ntohl(end);

  525.     if (start > end) {
  526.         goto invalid;
  527.     }

  528.     if (del) {
  529.         if (ngx_stream_geo_delete_range(cf, ctx, start, end)) {
  530.             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  531.                                "no address range \"%V\" to delete", net);
  532.         }

  533.         return NGX_CONF_OK;
  534.     }

  535.     ctx->value = ngx_stream_geo_value(cf, ctx, &value[1]);

  536.     if (ctx->value == NULL) {
  537.         return NGX_CONF_ERROR;
  538.     }

  539.     ctx->net = net;

  540.     return ngx_stream_geo_add_range(cf, ctx, start, end);

  541. invalid:

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

  543.     return NGX_CONF_ERROR;
  544. }


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

  546. static char *
  547. ngx_stream_geo_add_range(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx,
  548.     in_addr_t start, in_addr_t end)
  549. {
  550.     in_addr_t                n;
  551.     ngx_uint_t               h, i, s, e;
  552.     ngx_array_t             *a;
  553.     ngx_stream_geo_range_t  *range;

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

  555.         h = n >> 16;

  556.         if (n == start) {
  557.             s = n & 0xffff;
  558.         } else {
  559.             s = 0;
  560.         }

  561.         if ((n | 0xffff) > end) {
  562.             e = end & 0xffff;

  563.         } else {
  564.             e = 0xffff;
  565.         }

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

  567.         if (a == NULL) {
  568.             a = ngx_array_create(ctx->temp_pool, 64,
  569.                                  sizeof(ngx_stream_geo_range_t));
  570.             if (a == NULL) {
  571.                 return NGX_CONF_ERROR;
  572.             }

  573.             ctx->high.low[h] = (ngx_stream_geo_range_t *) a;
  574.         }

  575.         i = a->nelts;
  576.         range = a->elts;

  577.         while (i) {

  578.             i--;

  579.             if (e < (ngx_uint_t) range[i].start) {
  580.                 continue;
  581.             }

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

  583.                 /* add after the range */

  584.                 range = ngx_array_push(a);
  585.                 if (range == NULL) {
  586.                     return NGX_CONF_ERROR;
  587.                 }

  588.                 range = a->elts;

  589.                 ngx_memmove(&range[i + 2], &range[i + 1],
  590.                            (a->nelts - 2 - i) * sizeof(ngx_stream_geo_range_t));

  591.                 range[i + 1].start = (u_short) s;
  592.                 range[i + 1].end = (u_short) e;
  593.                 range[i + 1].value = ctx->value;

  594.                 goto next;
  595.             }

  596.             if (s == (ngx_uint_t) range[i].start
  597.                 && e == (ngx_uint_t) range[i].end)
  598.             {
  599.                 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  600.                     "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
  601.                     ctx->net, ctx->value, range[i].value);

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

  603.                 goto next;
  604.             }

  605.             if (s > (ngx_uint_t) range[i].start
  606.                 && e < (ngx_uint_t) range[i].end)
  607.             {
  608.                 /* split the range and insert the new one */

  609.                 range = ngx_array_push(a);
  610.                 if (range == NULL) {
  611.                     return NGX_CONF_ERROR;
  612.                 }

  613.                 range = ngx_array_push(a);
  614.                 if (range == NULL) {
  615.                     return NGX_CONF_ERROR;
  616.                 }

  617.                 range = a->elts;

  618.                 ngx_memmove(&range[i + 3], &range[i + 1],
  619.                            (a->nelts - 3 - i) * sizeof(ngx_stream_geo_range_t));

  620.                 range[i + 2].start = (u_short) (e + 1);
  621.                 range[i + 2].end = range[i].end;
  622.                 range[i + 2].value = range[i].value;

  623.                 range[i + 1].start = (u_short) s;
  624.                 range[i + 1].end = (u_short) e;
  625.                 range[i + 1].value = ctx->value;

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

  627.                 goto next;
  628.             }

  629.             if (s == (ngx_uint_t) range[i].start
  630.                 && e < (ngx_uint_t) range[i].end)
  631.             {
  632.                 /* shift the range start and insert the new range */

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

  637.                 range = a->elts;

  638.                 ngx_memmove(&range[i + 1], &range[i],
  639.                            (a->nelts - 1 - i) * sizeof(ngx_stream_geo_range_t));

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

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

  644.                 goto next;
  645.             }

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

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

  654.                 range = a->elts;

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

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

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

  661.                 goto next;
  662.             }

  663.             s = (ngx_uint_t) range[i].start;
  664.             e = (ngx_uint_t) range[i].end;

  665.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  666.                          "range \"%V\" overlaps \"%d.%d.%d.%d-%d.%d.%d.%d\"",
  667.                          ctx->net,
  668.                          h >> 8, h & 0xff, s >> 8, s & 0xff,
  669.                          h >> 8, h & 0xff, e >> 8, e & 0xff);

  670.             return NGX_CONF_ERROR;
  671.         }

  672.         /* add the first range */

  673.         range = ngx_array_push(a);
  674.         if (range == NULL) {
  675.             return NGX_CONF_ERROR;
  676.         }

  677.         range = a->elts;

  678.         ngx_memmove(&range[1], &range[0],
  679.                     (a->nelts - 1) * sizeof(ngx_stream_geo_range_t));

  680.         range[0].start = (u_short) s;
  681.         range[0].end = (u_short) e;
  682.         range[0].value = ctx->value;

  683.     next:

  684.         if (h == 0xffff) {
  685.             break;
  686.         }
  687.     }

  688.     return NGX_CONF_OK;
  689. }


  690. static ngx_uint_t
  691. ngx_stream_geo_delete_range(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx,
  692.     in_addr_t start, in_addr_t end)
  693. {
  694.     in_addr_t                n;
  695.     ngx_uint_t               h, i, s, e, warn;
  696.     ngx_array_t             *a;
  697.     ngx_stream_geo_range_t  *range;

  698.     warn = 0;

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

  700.         h = n >> 16;

  701.         if (n == start) {
  702.             s = n & 0xffff;
  703.         } else {
  704.             s = 0;
  705.         }

  706.         if ((n | 0xffff) > end) {
  707.             e = end & 0xffff;

  708.         } else {
  709.             e = 0xffff;
  710.         }

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

  712.         if (a == NULL || a->nelts == 0) {
  713.             warn = 1;
  714.             goto next;
  715.         }

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

  718.             if (s == (ngx_uint_t) range[i].start
  719.                 && e == (ngx_uint_t) range[i].end)
  720.             {
  721.                 ngx_memmove(&range[i], &range[i + 1],
  722.                            (a->nelts - 1 - i) * sizeof(ngx_stream_geo_range_t));

  723.                 a->nelts--;

  724.                 break;
  725.             }

  726.             if (i == a->nelts - 1) {
  727.                 warn = 1;
  728.             }
  729.         }

  730.     next:

  731.         if (h == 0xffff) {
  732.             break;
  733.         }
  734.     }

  735.     return warn;
  736. }


  737. static char *
  738. ngx_stream_geo_cidr(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx,
  739.     ngx_str_t *value)
  740. {
  741.     char        *rv;
  742.     ngx_int_t    rc, del;
  743.     ngx_str_t   *net;
  744.     ngx_cidr_t   cidr;

  745.     if (ctx->tree == NULL) {
  746.         ctx->tree = ngx_radix_tree_create(ctx->pool, -1);
  747.         if (ctx->tree == NULL) {
  748.             return NGX_CONF_ERROR;
  749.         }
  750.     }

  751. #if (NGX_HAVE_INET6)
  752.     if (ctx->tree6 == NULL) {
  753.         ctx->tree6 = ngx_radix_tree_create(ctx->pool, -1);
  754.         if (ctx->tree6 == NULL) {
  755.             return NGX_CONF_ERROR;
  756.         }
  757.     }
  758. #endif

  759.     if (ngx_strcmp(value[0].data, "default") == 0) {
  760.         cidr.family = AF_INET;
  761.         cidr.u.in.addr = 0;
  762.         cidr.u.in.mask = 0;

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

  764.         if (rv != NGX_CONF_OK) {
  765.             return rv;
  766.         }

  767. #if (NGX_HAVE_INET6)
  768.         cidr.family = AF_INET6;
  769.         ngx_memzero(&cidr.u.in6, sizeof(ngx_in6_cidr_t));

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

  771.         if (rv != NGX_CONF_OK) {
  772.             return rv;
  773.         }
  774. #endif

  775.         return NGX_CONF_OK;
  776.     }

  777.     if (ngx_strcmp(value[0].data, "delete") == 0) {
  778.         net = &value[1];
  779.         del = 1;

  780.     } else {
  781.         net = &value[0];
  782.         del = 0;
  783.     }

  784.     if (ngx_stream_geo_cidr_value(cf, net, &cidr) != NGX_OK) {
  785.         return NGX_CONF_ERROR;
  786.     }

  787.     if (cidr.family == AF_INET) {
  788.         cidr.u.in.addr = ntohl(cidr.u.in.addr);
  789.         cidr.u.in.mask = ntohl(cidr.u.in.mask);
  790.     }

  791.     if (del) {
  792.         switch (cidr.family) {

  793. #if (NGX_HAVE_INET6)
  794.         case AF_INET6:
  795.             rc = ngx_radix128tree_delete(ctx->tree6,
  796.                                          cidr.u.in6.addr.s6_addr,
  797.                                          cidr.u.in6.mask.s6_addr);
  798.             break;
  799. #endif

  800.         default: /* AF_INET */
  801.             rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr,
  802.                                         cidr.u.in.mask);
  803.             break;
  804.         }

  805.         if (rc != NGX_OK) {
  806.             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  807.                                "no network \"%V\" to delete", net);
  808.         }

  809.         return NGX_CONF_OK;
  810.     }

  811.     return ngx_stream_geo_cidr_add(cf, ctx, &cidr, &value[1], net);
  812. }


  813. static char *
  814. ngx_stream_geo_cidr_add(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx,
  815.     ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net)
  816. {
  817.     ngx_int_t                     rc;
  818.     ngx_stream_variable_value_t  *val, *old;

  819.     val = ngx_stream_geo_value(cf, ctx, value);

  820.     if (val == NULL) {
  821.         return NGX_CONF_ERROR;
  822.     }

  823.     switch (cidr->family) {

  824. #if (NGX_HAVE_INET6)
  825.     case AF_INET6:
  826.         rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,
  827.                                      cidr->u.in6.mask.s6_addr,
  828.                                      (uintptr_t) val);

  829.         if (rc == NGX_OK) {
  830.             return NGX_CONF_OK;
  831.         }

  832.         if (rc == NGX_ERROR) {
  833.             return NGX_CONF_ERROR;
  834.         }

  835.         /* rc == NGX_BUSY */

  836.         old = (ngx_stream_variable_value_t *)
  837.                    ngx_radix128tree_find(ctx->tree6,
  838.                                          cidr->u.in6.addr.s6_addr);

  839.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  840.               "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
  841.               net, val, old);

  842.         rc = ngx_radix128tree_delete(ctx->tree6,
  843.                                      cidr->u.in6.addr.s6_addr,
  844.                                      cidr->u.in6.mask.s6_addr);

  845.         if (rc == NGX_ERROR) {
  846.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
  847.             return NGX_CONF_ERROR;
  848.         }

  849.         rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,
  850.                                      cidr->u.in6.mask.s6_addr,
  851.                                      (uintptr_t) val);

  852.         break;
  853. #endif

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

  857.         if (rc == NGX_OK) {
  858.             return NGX_CONF_OK;
  859.         }

  860.         if (rc == NGX_ERROR) {
  861.             return NGX_CONF_ERROR;
  862.         }

  863.         /* rc == NGX_BUSY */

  864.         old = (ngx_stream_variable_value_t *)
  865.                    ngx_radix32tree_find(ctx->tree, cidr->u.in.addr);

  866.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  867.               "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
  868.               net, val, old);

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

  871.         if (rc == NGX_ERROR) {
  872.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
  873.             return NGX_CONF_ERROR;
  874.         }

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

  877.         break;
  878.     }

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

  882.     return NGX_CONF_ERROR;
  883. }


  884. static ngx_stream_variable_value_t *
  885. ngx_stream_geo_value(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx,
  886.     ngx_str_t *value)
  887. {
  888.     uint32_t                               hash;
  889.     ngx_stream_variable_value_t           *val;
  890.     ngx_stream_geo_variable_value_node_t  *gvvn;

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

  892.     gvvn = (ngx_stream_geo_variable_value_node_t *)
  893.                ngx_str_rbtree_lookup(&ctx->rbtree, value, hash);

  894.     if (gvvn) {
  895.         return gvvn->value;
  896.     }

  897.     val = ngx_pcalloc(ctx->pool, sizeof(ngx_stream_variable_value_t));
  898.     if (val == NULL) {
  899.         return NULL;
  900.     }

  901.     val->len = value->len;
  902.     val->data = ngx_pstrdup(ctx->pool, value);
  903.     if (val->data == NULL) {
  904.         return NULL;
  905.     }

  906.     val->valid = 1;

  907.     gvvn = ngx_palloc(ctx->temp_pool,
  908.                       sizeof(ngx_stream_geo_variable_value_node_t));
  909.     if (gvvn == NULL) {
  910.         return NULL;
  911.     }

  912.     gvvn->sn.node.key = hash;
  913.     gvvn->sn.str.len = val->len;
  914.     gvvn->sn.str.data = val->data;
  915.     gvvn->value = val;
  916.     gvvn->offset = 0;

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

  918.     ctx->data_size += ngx_align(sizeof(ngx_stream_variable_value_t)
  919.                                 + value->len, sizeof(void *));

  920.     return val;
  921. }


  922. static ngx_int_t
  923. ngx_stream_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr)
  924. {
  925.     ngx_int_t  rc;

  926.     if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
  927.         cidr->family = AF_INET;
  928.         cidr->u.in.addr = 0xffffffff;
  929.         cidr->u.in.mask = 0xffffffff;

  930.         return NGX_OK;
  931.     }

  932.     rc = ngx_ptocidr(net, cidr);

  933.     if (rc == NGX_ERROR) {
  934.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net);
  935.         return NGX_ERROR;
  936.     }

  937.     if (rc == NGX_DONE) {
  938.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  939.                            "low address bits of %V are meaningless", net);
  940.     }

  941.     return NGX_OK;
  942. }


  943. static char *
  944. ngx_stream_geo_include(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx,
  945.     ngx_str_t *name)
  946. {
  947.     char       *rv;
  948.     ngx_str_t   file;

  949.     file.len = name->len + 4;
  950.     file.data = ngx_pnalloc(ctx->temp_pool, name->len + 5);
  951.     if (file.data == NULL) {
  952.         return NGX_CONF_ERROR;
  953.     }

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

  955.     if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {
  956.         return NGX_CONF_ERROR;
  957.     }

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

  960.         switch (ngx_stream_geo_include_binary_base(cf, ctx, &file)) {
  961.         case NGX_OK:
  962.             return NGX_CONF_OK;
  963.         case NGX_ERROR:
  964.             return NGX_CONF_ERROR;
  965.         default:
  966.             break;
  967.         }
  968.     }

  969.     file.len -= 4;
  970.     file.data[file.len] = '\0';

  971.     ctx->include_name = file;

  972.     if (ctx->outside_entries) {
  973.         ctx->allow_binary_include = 0;
  974.     }

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

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

  977.     ctx->includes++;
  978.     ctx->outside_entries = 0;

  979.     return rv;
  980. }


  981. static ngx_int_t
  982. ngx_stream_geo_include_binary_base(ngx_conf_t *cf,
  983.     ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *name)
  984. {
  985.     u_char                       *base, ch;
  986.     time_t                        mtime;
  987.     size_t                        size, len;
  988.     ssize_t                       n;
  989.     uint32_t                      crc32;
  990.     ngx_err_t                     err;
  991.     ngx_int_t                     rc;
  992.     ngx_uint_t                    i;
  993.     ngx_file_t                    file;
  994.     ngx_file_info_t               fi;
  995.     ngx_stream_geo_range_t       *range, **ranges;
  996.     ngx_stream_geo_header_t      *header;
  997.     ngx_stream_variable_value_t  *vv;

  998.     ngx_memzero(&file, sizeof(ngx_file_t));
  999.     file.name = *name;
  1000.     file.log = cf->log;

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

  1002.     if (file.fd == NGX_INVALID_FILE) {
  1003.         err = ngx_errno;
  1004.         if (err != NGX_ENOENT) {
  1005.             ngx_conf_log_error(NGX_LOG_CRIT, cf, err,
  1006.                                ngx_open_file_n " \"%s\" failed", name->data);
  1007.         }
  1008.         return NGX_DECLINED;
  1009.     }

  1010.     if (ctx->outside_entries) {
  1011.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1012.             "binary geo range base \"%s\" cannot be mixed with usual entries",
  1013.             name->data);
  1014.         rc = NGX_ERROR;
  1015.         goto done;
  1016.     }

  1017.     if (ctx->binary_include) {
  1018.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1019.             "second binary geo range base \"%s\" cannot be mixed with \"%s\"",
  1020.             name->data, ctx->include_name.data);
  1021.         rc = NGX_ERROR;
  1022.         goto done;
  1023.     }

  1024.     if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
  1025.         ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
  1026.                            ngx_fd_info_n " \"%s\" failed", name->data);
  1027.         goto failed;
  1028.     }

  1029.     size = (size_t) ngx_file_size(&fi);
  1030.     mtime = ngx_file_mtime(&fi);

  1031.     ch = name->data[name->len - 4];
  1032.     name->data[name->len - 4] = '\0';

  1033.     if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {
  1034.         ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
  1035.                            ngx_file_info_n " \"%s\" failed", name->data);
  1036.         goto failed;
  1037.     }

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

  1039.     if (mtime < ngx_file_mtime(&fi)) {
  1040.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  1041.                            "stale binary geo range base \"%s\"", name->data);
  1042.         goto failed;
  1043.     }

  1044.     base = ngx_palloc(ctx->pool, size);
  1045.     if (base == NULL) {
  1046.         goto failed;
  1047.     }

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

  1049.     if (n == NGX_ERROR) {
  1050.         ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
  1051.                            ngx_read_file_n " \"%s\" failed", name->data);
  1052.         goto failed;
  1053.     }

  1054.     if ((size_t) n != size) {
  1055.         ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,
  1056.             ngx_read_file_n " \"%s\" returned only %z bytes instead of %z",
  1057.             name->data, n, size);
  1058.         goto failed;
  1059.     }

  1060.     header = (ngx_stream_geo_header_t *) base;

  1061.     if (size < 16 || ngx_memcmp(&ngx_stream_geo_header, header, 12) != 0) {
  1062.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  1063.              "incompatible binary geo range base \"%s\"", name->data);
  1064.         goto failed;
  1065.     }

  1066.     ngx_crc32_init(crc32);

  1067.     vv = (ngx_stream_variable_value_t *)
  1068.             (base + sizeof(ngx_stream_geo_header_t));

  1069.     while (vv->data) {
  1070.         len = ngx_align(sizeof(ngx_stream_variable_value_t) + vv->len,
  1071.                         sizeof(void *));
  1072.         ngx_crc32_update(&crc32, (u_char *) vv, len);
  1073.         vv->data += (size_t) base;
  1074.         vv = (ngx_stream_variable_value_t *) ((u_char *) vv + len);
  1075.     }
  1076.     ngx_crc32_update(&crc32, (u_char *) vv,
  1077.                      sizeof(ngx_stream_variable_value_t));
  1078.     vv++;

  1079.     ranges = (ngx_stream_geo_range_t **) vv;

  1080.     for (i = 0; i < 0x10000; i++) {
  1081.         ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *));
  1082.         if (ranges[i]) {
  1083.             ranges[i] = (ngx_stream_geo_range_t *)
  1084.                             ((u_char *) ranges[i] + (size_t) base);
  1085.         }
  1086.     }

  1087.     range = (ngx_stream_geo_range_t *) &ranges[0x10000];

  1088.     while ((u_char *) range < base + size) {
  1089.         while (range->value) {
  1090.             ngx_crc32_update(&crc32, (u_char *) range,
  1091.                              sizeof(ngx_stream_geo_range_t));
  1092.             range->value = (ngx_stream_variable_value_t *)
  1093.                                ((u_char *) range->value + (size_t) base);
  1094.             range++;
  1095.         }
  1096.         ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *));
  1097.         range = (ngx_stream_geo_range_t *) ((u_char *) range + sizeof(void *));
  1098.     }

  1099.     ngx_crc32_final(crc32);

  1100.     if (crc32 != header->crc32) {
  1101.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  1102.                   "CRC32 mismatch in binary geo range base \"%s\"", name->data);
  1103.         goto failed;
  1104.     }

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

  1107.     ctx->include_name = *name;
  1108.     ctx->binary_include = 1;
  1109.     ctx->high.low = ranges;
  1110.     rc = NGX_OK;

  1111.     goto done;

  1112. failed:

  1113.     rc = NGX_DECLINED;

  1114. done:

  1115.     if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
  1116.         ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
  1117.                       ngx_close_file_n " \"%s\" failed", name->data);
  1118.     }

  1119.     return rc;
  1120. }


  1121. static void
  1122. ngx_stream_geo_create_binary_base(ngx_stream_geo_conf_ctx_t *ctx)
  1123. {
  1124.     u_char                                *p;
  1125.     uint32_t                               hash;
  1126.     ngx_str_t                              s;
  1127.     ngx_uint_t                             i;
  1128.     ngx_file_mapping_t                     fm;
  1129.     ngx_stream_geo_range_t                *r, *range, **ranges;
  1130.     ngx_stream_geo_header_t               *header;
  1131.     ngx_stream_geo_variable_value_node_t  *gvvn;

  1132.     fm.name = ngx_pnalloc(ctx->temp_pool, ctx->include_name.len + 5);
  1133.     if (fm.name == NULL) {
  1134.         return;
  1135.     }

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

  1137.     fm.size = ctx->data_size;
  1138.     fm.log = ctx->pool->log;

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

  1141.     if (ngx_create_file_mapping(&fm) != NGX_OK) {
  1142.         return;
  1143.     }

  1144.     p = ngx_cpymem(fm.addr, &ngx_stream_geo_header,
  1145.                    sizeof(ngx_stream_geo_header_t));

  1146.     p = ngx_stream_geo_copy_values(fm.addr, p, ctx->rbtree.root,
  1147.                                    ctx->rbtree.sentinel);

  1148.     p += sizeof(ngx_stream_variable_value_t);

  1149.     ranges = (ngx_stream_geo_range_t **) p;

  1150.     p += 0x10000 * sizeof(ngx_stream_geo_range_t *);

  1151.     for (i = 0; i < 0x10000; i++) {
  1152.         r = ctx->high.low[i];
  1153.         if (r == NULL) {
  1154.             continue;
  1155.         }

  1156.         range = (ngx_stream_geo_range_t *) p;
  1157.         ranges[i] = (ngx_stream_geo_range_t *) (p - (u_char *) fm.addr);

  1158.         do {
  1159.             s.len = r->value->len;
  1160.             s.data = r->value->data;
  1161.             hash = ngx_crc32_long(s.data, s.len);
  1162.             gvvn = (ngx_stream_geo_variable_value_node_t *)
  1163.                         ngx_str_rbtree_lookup(&ctx->rbtree, &s, hash);

  1164.             range->value = (ngx_stream_variable_value_t *) gvvn->offset;
  1165.             range->start = r->start;
  1166.             range->end = r->end;
  1167.             range++;

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

  1169.         range->value = NULL;

  1170.         p = (u_char *) range + sizeof(void *);
  1171.     }

  1172.     header = fm.addr;
  1173.     header->crc32 = ngx_crc32_long((u_char *) fm.addr
  1174.                                        + sizeof(ngx_stream_geo_header_t),
  1175.                                    fm.size - sizeof(ngx_stream_geo_header_t));

  1176.     ngx_close_file_mapping(&fm);
  1177. }


  1178. static u_char *
  1179. ngx_stream_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node,
  1180.     ngx_rbtree_node_t *sentinel)
  1181. {
  1182.     ngx_stream_variable_value_t           *vv;
  1183.     ngx_stream_geo_variable_value_node_t  *gvvn;

  1184.     if (node == sentinel) {
  1185.         return p;
  1186.     }

  1187.     gvvn = (ngx_stream_geo_variable_value_node_t *) node;
  1188.     gvvn->offset = p - base;

  1189.     vv = (ngx_stream_variable_value_t *) p;
  1190.     *vv = *gvvn->value;
  1191.     p += sizeof(ngx_stream_variable_value_t);
  1192.     vv->data = (u_char *) (p - base);

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

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

  1195.     p = ngx_stream_geo_copy_values(base, p, node->left, sentinel);

  1196.     return ngx_stream_geo_copy_values(base, p, node->right, sentinel);
  1197. }