src/stream/ngx_stream_geo_module.c - nginx

Global variables defined

Data types defined

Functions defined

Source code


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


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_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.     unsigned                           no_cacheable:1;
  49. } ngx_stream_geo_conf_ctx_t;


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

  55.     ngx_int_t                          index;
  56. } ngx_stream_geo_ctx_t;


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

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


  84. static ngx_command_t  ngx_stream_geo_commands[] = {

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

  91.       ngx_null_command
  92. };


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

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

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


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


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


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


  125. /* geo range is AF_INET only */

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

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

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

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

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

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

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

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

  160.         break;
  161. #endif

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

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

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

  173.         break;
  174.     }

  175. done:

  176.     *v = *vv;

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

  179.     return NGX_OK;
  180. }


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

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

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

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

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

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

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

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

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

  210.             break;
  211. #endif

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

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

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

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

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

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

  241.     return NGX_OK;
  242. }


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

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

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

  254.         return NGX_OK;
  255.     }

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

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

  260.         return NGX_ERROR;
  261.     }

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

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

  267.     return NGX_ERROR;
  268. }


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

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

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

  290.     name = value[1];

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

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

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

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

  303.         name = value[2];

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

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

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

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

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

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

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

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

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

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

  340.     rv = ngx_conf_parse(cf, NULL);

  341.     *cf = save;

  342.     if (rv != NGX_CONF_OK) {
  343.         goto failed;
  344.     }

  345.     if (ctx.no_cacheable) {
  346.         var->flags |= NGX_STREAM_VAR_NOCACHEABLE;
  347.     }

  348.     if (ctx.ranges) {

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

  352.                 if (a == NULL) {
  353.                     continue;
  354.                 }

  355.                 if (a->nelts == 0) {
  356.                     ctx.high.low[i] = NULL;
  357.                     continue;
  358.                 }

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

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

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

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

  376.         if (ctx.high.default_value == NULL) {
  377.             ctx.high.default_value = &ngx_stream_variable_null_value;
  378.         }

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

  380.         var->get_handler = ngx_stream_geo_range_variable;
  381.         var->data = (uintptr_t) geo;

  382.     } else {
  383.         if (ctx.tree == NULL) {
  384.             ctx.tree = ngx_radix_tree_create(cf->pool, -1);
  385.             if (ctx.tree == NULL) {
  386.                 goto failed;
  387.             }
  388.         }

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

  390. #if (NGX_HAVE_INET6)
  391.         if (ctx.tree6 == NULL) {
  392.             ctx.tree6 = ngx_radix_tree_create(cf->pool, -1);
  393.             if (ctx.tree6 == NULL) {
  394.                 goto failed;
  395.             }
  396.         }

  397.         geo->u.trees.tree6 = ctx.tree6;
  398. #endif

  399.         var->get_handler = ngx_stream_geo_cidr_variable;
  400.         var->data = (uintptr_t) geo;

  401.         if (ngx_radix32tree_insert(ctx.tree, 0, 0,
  402.                                    (uintptr_t) &ngx_stream_variable_null_value)
  403.             == NGX_ERROR)
  404.         {
  405.             goto failed;
  406.         }

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

  408. #if (NGX_HAVE_INET6)
  409.         if (ngx_radix128tree_insert(ctx.tree6, zero.s6_addr, zero.s6_addr,
  410.                                     (uintptr_t) &ngx_stream_variable_null_value)
  411.             == NGX_ERROR)
  412.         {
  413.             goto failed;
  414.         }
  415. #endif
  416.     }

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

  419.     return NGX_CONF_OK;

  420. failed:

  421.     ngx_destroy_pool(ctx.temp_pool);
  422.     ngx_destroy_pool(pool);

  423.     return NGX_CONF_ERROR;
  424. }


  425. static char *
  426. ngx_stream_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
  427. {
  428.     char                       *rv;
  429.     ngx_str_t                  *value;
  430.     ngx_stream_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, "volatile") == 0) {
  451.             ctx->no_cacheable = 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.         if (strpbrk((char *) value[1].data, "*?[") == NULL) {
  463.             rv = ngx_stream_geo_include(cf, ctx, &value[1]);

  464.         } else {
  465.             rv = ngx_conf_include(cf, dummy, conf);
  466.         }

  467.         goto done;
  468.     }

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

  471.     } else {
  472.         rv = ngx_stream_geo_cidr(cf, ctx, value);
  473.     }

  474. done:

  475.     ngx_reset_pool(cf->pool);

  476.     return rv;

  477. failed:

  478.     ngx_reset_pool(cf->pool);

  479.     return NGX_CONF_ERROR;
  480. }


  481. static char *
  482. ngx_stream_geo_range(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx,
  483.     ngx_str_t *value)
  484. {
  485.     u_char      *p, *last;
  486.     in_addr_t    start, end;
  487.     ngx_str_t   *net;
  488.     ngx_uint_t   del;

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

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

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

  499.         return NGX_CONF_OK;
  500.     }

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

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

  514.     ctx->entries++;
  515.     ctx->outside_entries = 1;

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

  519.     } else {
  520.         net = &value[0];
  521.         del = 0;
  522.     }

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

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

  525.     if (p == NULL) {
  526.         goto invalid;
  527.     }

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

  529.     if (start == INADDR_NONE) {
  530.         goto invalid;
  531.     }

  532.     start = ntohl(start);

  533.     p++;

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

  535.     if (end == INADDR_NONE) {
  536.         goto invalid;
  537.     }

  538.     end = ntohl(end);

  539.     if (start > end) {
  540.         goto invalid;
  541.     }

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

  547.         return NGX_CONF_OK;
  548.     }

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

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

  553.     ctx->net = net;

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

  555. invalid:

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

  557.     return NGX_CONF_ERROR;
  558. }


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

  560. static char *
  561. ngx_stream_geo_add_range(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx,
  562.     in_addr_t start, in_addr_t end)
  563. {
  564.     in_addr_t                n;
  565.     ngx_uint_t               h, i, s, e;
  566.     ngx_array_t             *a;
  567.     ngx_stream_geo_range_t  *range;

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

  569.         h = n >> 16;

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

  575.         if ((n | 0xffff) > end) {
  576.             e = end & 0xffff;

  577.         } else {
  578.             e = 0xffff;
  579.         }

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

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

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

  589.         i = a->nelts;
  590.         range = a->elts;

  591.         while (i) {

  592.             i--;

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

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

  597.                 /* add after the range */

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

  602.                 range = a->elts;

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

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

  608.                 goto next;
  609.             }

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

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

  617.                 goto next;
  618.             }

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

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

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

  631.                 range = a->elts;

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

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

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

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

  641.                 goto next;
  642.             }

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

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

  651.                 range = a->elts;

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

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

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

  658.                 goto next;
  659.             }

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

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

  668.                 range = a->elts;

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

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

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

  675.                 goto next;
  676.             }

  677.             s = (ngx_uint_t) range[i].start;
  678.             e = (ngx_uint_t) range[i].end;

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

  684.             return NGX_CONF_ERROR;
  685.         }

  686.         /* add the first range */

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

  691.         range = a->elts;

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

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

  697.     next:

  698.         if (h == 0xffff) {
  699.             break;
  700.         }
  701.     }

  702.     return NGX_CONF_OK;
  703. }


  704. static ngx_uint_t
  705. ngx_stream_geo_delete_range(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx,
  706.     in_addr_t start, in_addr_t end)
  707. {
  708.     in_addr_t                n;
  709.     ngx_uint_t               h, i, s, e, warn;
  710.     ngx_array_t             *a;
  711.     ngx_stream_geo_range_t  *range;

  712.     warn = 0;

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

  714.         h = n >> 16;

  715.         if (n == start) {
  716.             s = n & 0xffff;
  717.         } else {
  718.             s = 0;
  719.         }

  720.         if ((n | 0xffff) > end) {
  721.             e = end & 0xffff;

  722.         } else {
  723.             e = 0xffff;
  724.         }

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

  726.         if (a == NULL || a->nelts == 0) {
  727.             warn = 1;
  728.             goto next;
  729.         }

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

  732.             if (s == (ngx_uint_t) range[i].start
  733.                 && e == (ngx_uint_t) range[i].end)
  734.             {
  735.                 ngx_memmove(&range[i], &range[i + 1],
  736.                            (a->nelts - 1 - i) * sizeof(ngx_stream_geo_range_t));

  737.                 a->nelts--;

  738.                 break;
  739.             }

  740.             if (i == a->nelts - 1) {
  741.                 warn = 1;
  742.             }
  743.         }

  744.     next:

  745.         if (h == 0xffff) {
  746.             break;
  747.         }
  748.     }

  749.     return warn;
  750. }


  751. static char *
  752. ngx_stream_geo_cidr(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx,
  753.     ngx_str_t *value)
  754. {
  755.     char        *rv;
  756.     ngx_int_t    rc, del;
  757.     ngx_str_t   *net;
  758.     ngx_cidr_t   cidr;

  759.     if (ctx->tree == NULL) {
  760.         ctx->tree = ngx_radix_tree_create(ctx->pool, -1);
  761.         if (ctx->tree == NULL) {
  762.             return NGX_CONF_ERROR;
  763.         }
  764.     }

  765. #if (NGX_HAVE_INET6)
  766.     if (ctx->tree6 == NULL) {
  767.         ctx->tree6 = ngx_radix_tree_create(ctx->pool, -1);
  768.         if (ctx->tree6 == NULL) {
  769.             return NGX_CONF_ERROR;
  770.         }
  771.     }
  772. #endif

  773.     if (ngx_strcmp(value[0].data, "default") == 0) {
  774.         cidr.family = AF_INET;
  775.         cidr.u.in.addr = 0;
  776.         cidr.u.in.mask = 0;

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

  778.         if (rv != NGX_CONF_OK) {
  779.             return rv;
  780.         }

  781. #if (NGX_HAVE_INET6)
  782.         cidr.family = AF_INET6;
  783.         ngx_memzero(&cidr.u.in6, sizeof(ngx_in6_cidr_t));

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

  785.         if (rv != NGX_CONF_OK) {
  786.             return rv;
  787.         }
  788. #endif

  789.         return NGX_CONF_OK;
  790.     }

  791.     if (ngx_strcmp(value[0].data, "delete") == 0) {
  792.         net = &value[1];
  793.         del = 1;

  794.     } else {
  795.         net = &value[0];
  796.         del = 0;
  797.     }

  798.     if (ngx_stream_geo_cidr_value(cf, net, &cidr) != NGX_OK) {
  799.         return NGX_CONF_ERROR;
  800.     }

  801.     if (cidr.family == AF_INET) {
  802.         cidr.u.in.addr = ntohl(cidr.u.in.addr);
  803.         cidr.u.in.mask = ntohl(cidr.u.in.mask);
  804.     }

  805.     if (del) {
  806.         switch (cidr.family) {

  807. #if (NGX_HAVE_INET6)
  808.         case AF_INET6:
  809.             rc = ngx_radix128tree_delete(ctx->tree6,
  810.                                          cidr.u.in6.addr.s6_addr,
  811.                                          cidr.u.in6.mask.s6_addr);
  812.             break;
  813. #endif

  814.         default: /* AF_INET */
  815.             rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr,
  816.                                         cidr.u.in.mask);
  817.             break;
  818.         }

  819.         if (rc != NGX_OK) {
  820.             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  821.                                "no network \"%V\" to delete", net);
  822.         }

  823.         return NGX_CONF_OK;
  824.     }

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


  827. static char *
  828. ngx_stream_geo_cidr_add(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx,
  829.     ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net)
  830. {
  831.     ngx_int_t                     rc;
  832.     ngx_stream_variable_value_t  *val, *old;

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

  834.     if (val == NULL) {
  835.         return NGX_CONF_ERROR;
  836.     }

  837.     switch (cidr->family) {

  838. #if (NGX_HAVE_INET6)
  839.     case AF_INET6:
  840.         rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,
  841.                                      cidr->u.in6.mask.s6_addr,
  842.                                      (uintptr_t) val);

  843.         if (rc == NGX_OK) {
  844.             return NGX_CONF_OK;
  845.         }

  846.         if (rc == NGX_ERROR) {
  847.             return NGX_CONF_ERROR;
  848.         }

  849.         /* rc == NGX_BUSY */

  850.         old = (ngx_stream_variable_value_t *)
  851.                    ngx_radix128tree_find(ctx->tree6,
  852.                                          cidr->u.in6.addr.s6_addr);

  853.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  854.               "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
  855.               net, val, old);

  856.         rc = ngx_radix128tree_delete(ctx->tree6,
  857.                                      cidr->u.in6.addr.s6_addr,
  858.                                      cidr->u.in6.mask.s6_addr);

  859.         if (rc == NGX_ERROR) {
  860.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
  861.             return NGX_CONF_ERROR;
  862.         }

  863.         rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,
  864.                                      cidr->u.in6.mask.s6_addr,
  865.                                      (uintptr_t) val);

  866.         break;
  867. #endif

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

  871.         if (rc == NGX_OK) {
  872.             return NGX_CONF_OK;
  873.         }

  874.         if (rc == NGX_ERROR) {
  875.             return NGX_CONF_ERROR;
  876.         }

  877.         /* rc == NGX_BUSY */

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

  880.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  881.               "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
  882.               net, val, old);

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

  885.         if (rc == NGX_ERROR) {
  886.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
  887.             return NGX_CONF_ERROR;
  888.         }

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

  891.         break;
  892.     }

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

  896.     return NGX_CONF_ERROR;
  897. }


  898. static ngx_stream_variable_value_t *
  899. ngx_stream_geo_value(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx,
  900.     ngx_str_t *value)
  901. {
  902.     uint32_t                               hash;
  903.     ngx_stream_variable_value_t           *val;
  904.     ngx_stream_geo_variable_value_node_t  *gvvn;

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

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

  908.     if (gvvn) {
  909.         return gvvn->value;
  910.     }

  911.     val = ngx_pcalloc(ctx->pool, sizeof(ngx_stream_variable_value_t));
  912.     if (val == NULL) {
  913.         return NULL;
  914.     }

  915.     val->len = value->len;
  916.     val->data = ngx_pstrdup(ctx->pool, value);
  917.     if (val->data == NULL) {
  918.         return NULL;
  919.     }

  920.     val->valid = 1;

  921.     gvvn = ngx_palloc(ctx->temp_pool,
  922.                       sizeof(ngx_stream_geo_variable_value_node_t));
  923.     if (gvvn == NULL) {
  924.         return NULL;
  925.     }

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

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

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

  934.     return val;
  935. }


  936. static ngx_int_t
  937. ngx_stream_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr)
  938. {
  939.     ngx_int_t  rc;

  940.     if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
  941.         cidr->family = AF_INET;
  942.         cidr->u.in.addr = 0xffffffff;
  943.         cidr->u.in.mask = 0xffffffff;

  944.         return NGX_OK;
  945.     }

  946.     rc = ngx_ptocidr(net, cidr);

  947.     if (rc == NGX_ERROR) {
  948.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net);
  949.         return NGX_ERROR;
  950.     }

  951.     if (rc == NGX_DONE) {
  952.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  953.                            "low address bits of %V are meaningless", net);
  954.     }

  955.     return NGX_OK;
  956. }


  957. static char *
  958. ngx_stream_geo_include(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx,
  959.     ngx_str_t *name)
  960. {
  961.     char       *rv;
  962.     ngx_str_t   file;

  963.     file.len = name->len + 4;
  964.     file.data = ngx_pnalloc(ctx->temp_pool, name->len + 5);
  965.     if (file.data == NULL) {
  966.         return NGX_CONF_ERROR;
  967.     }

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

  969.     if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {
  970.         return NGX_CONF_ERROR;
  971.     }

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

  974.         switch (ngx_stream_geo_include_binary_base(cf, ctx, &file)) {
  975.         case NGX_OK:
  976.             return NGX_CONF_OK;
  977.         case NGX_ERROR:
  978.             return NGX_CONF_ERROR;
  979.         default:
  980.             break;
  981.         }
  982.     }

  983.     file.len -= 4;
  984.     file.data[file.len] = '\0';

  985.     ctx->include_name = file;

  986.     if (ctx->outside_entries) {
  987.         ctx->allow_binary_include = 0;
  988.     }

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

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

  991.     ctx->includes++;
  992.     ctx->outside_entries = 0;

  993.     return rv;
  994. }


  995. static ngx_int_t
  996. ngx_stream_geo_include_binary_base(ngx_conf_t *cf,
  997.     ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *name)
  998. {
  999.     u_char                       *base, ch;
  1000.     time_t                        mtime;
  1001.     size_t                        size, len;
  1002.     ssize_t                       n;
  1003.     uint32_t                      crc32;
  1004.     ngx_err_t                     err;
  1005.     ngx_int_t                     rc;
  1006.     ngx_uint_t                    i;
  1007.     ngx_file_t                    file;
  1008.     ngx_file_info_t               fi;
  1009.     ngx_stream_geo_range_t       *range, **ranges;
  1010.     ngx_stream_geo_header_t      *header;
  1011.     ngx_stream_variable_value_t  *vv;

  1012.     ngx_memzero(&file, sizeof(ngx_file_t));
  1013.     file.name = *name;
  1014.     file.log = cf->log;

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

  1016.     if (file.fd == NGX_INVALID_FILE) {
  1017.         err = ngx_errno;
  1018.         if (err != NGX_ENOENT) {
  1019.             ngx_conf_log_error(NGX_LOG_CRIT, cf, err,
  1020.                                ngx_open_file_n " \"%s\" failed", name->data);
  1021.         }
  1022.         return NGX_DECLINED;
  1023.     }

  1024.     if (ctx->outside_entries) {
  1025.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1026.             "binary geo range base \"%s\" cannot be mixed with usual entries",
  1027.             name->data);
  1028.         rc = NGX_ERROR;
  1029.         goto done;
  1030.     }

  1031.     if (ctx->binary_include) {
  1032.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1033.             "second binary geo range base \"%s\" cannot be mixed with \"%s\"",
  1034.             name->data, ctx->include_name.data);
  1035.         rc = NGX_ERROR;
  1036.         goto done;
  1037.     }

  1038.     if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
  1039.         ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
  1040.                            ngx_fd_info_n " \"%s\" failed", name->data);
  1041.         goto failed;
  1042.     }

  1043.     size = (size_t) ngx_file_size(&fi);
  1044.     mtime = ngx_file_mtime(&fi);

  1045.     ch = name->data[name->len - 4];
  1046.     name->data[name->len - 4] = '\0';

  1047.     if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {
  1048.         ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
  1049.                            ngx_file_info_n " \"%s\" failed", name->data);
  1050.         goto failed;
  1051.     }

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

  1053.     if (mtime < ngx_file_mtime(&fi)) {
  1054.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  1055.                            "stale binary geo range base \"%s\"", name->data);
  1056.         goto failed;
  1057.     }

  1058.     base = ngx_palloc(ctx->pool, size);
  1059.     if (base == NULL) {
  1060.         goto failed;
  1061.     }

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

  1063.     if (n == NGX_ERROR) {
  1064.         ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
  1065.                            ngx_read_file_n " \"%s\" failed", name->data);
  1066.         goto failed;
  1067.     }

  1068.     if ((size_t) n != size) {
  1069.         ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,
  1070.             ngx_read_file_n " \"%s\" returned only %z bytes instead of %z",
  1071.             name->data, n, size);
  1072.         goto failed;
  1073.     }

  1074.     header = (ngx_stream_geo_header_t *) base;

  1075.     if (size < 16 || ngx_memcmp(&ngx_stream_geo_header, header, 12) != 0) {
  1076.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  1077.              "incompatible binary geo range base \"%s\"", name->data);
  1078.         goto failed;
  1079.     }

  1080.     ngx_crc32_init(crc32);

  1081.     vv = (ngx_stream_variable_value_t *)
  1082.             (base + sizeof(ngx_stream_geo_header_t));

  1083.     while (vv->data) {
  1084.         len = ngx_align(sizeof(ngx_stream_variable_value_t) + vv->len,
  1085.                         sizeof(void *));
  1086.         ngx_crc32_update(&crc32, (u_char *) vv, len);
  1087.         vv->data += (size_t) base;
  1088.         vv = (ngx_stream_variable_value_t *) ((u_char *) vv + len);
  1089.     }
  1090.     ngx_crc32_update(&crc32, (u_char *) vv,
  1091.                      sizeof(ngx_stream_variable_value_t));
  1092.     vv++;

  1093.     ranges = (ngx_stream_geo_range_t **) vv;

  1094.     for (i = 0; i < 0x10000; i++) {
  1095.         ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *));
  1096.         if (ranges[i]) {
  1097.             ranges[i] = (ngx_stream_geo_range_t *)
  1098.                             ((u_char *) ranges[i] + (size_t) base);
  1099.         }
  1100.     }

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

  1102.     while ((u_char *) range < base + size) {
  1103.         while (range->value) {
  1104.             ngx_crc32_update(&crc32, (u_char *) range,
  1105.                              sizeof(ngx_stream_geo_range_t));
  1106.             range->value = (ngx_stream_variable_value_t *)
  1107.                                ((u_char *) range->value + (size_t) base);
  1108.             range++;
  1109.         }
  1110.         ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *));
  1111.         range = (ngx_stream_geo_range_t *) ((u_char *) range + sizeof(void *));
  1112.     }

  1113.     ngx_crc32_final(crc32);

  1114.     if (crc32 != header->crc32) {
  1115.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  1116.                   "CRC32 mismatch in binary geo range base \"%s\"", name->data);
  1117.         goto failed;
  1118.     }

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

  1121.     ctx->include_name = *name;
  1122.     ctx->binary_include = 1;
  1123.     ctx->high.low = ranges;
  1124.     rc = NGX_OK;

  1125.     goto done;

  1126. failed:

  1127.     rc = NGX_DECLINED;

  1128. done:

  1129.     if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
  1130.         ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
  1131.                       ngx_close_file_n " \"%s\" failed", name->data);
  1132.     }

  1133.     return rc;
  1134. }


  1135. static void
  1136. ngx_stream_geo_create_binary_base(ngx_stream_geo_conf_ctx_t *ctx)
  1137. {
  1138.     u_char                                *p;
  1139.     uint32_t                               hash;
  1140.     ngx_str_t                              s;
  1141.     ngx_uint_t                             i;
  1142.     ngx_file_mapping_t                     fm;
  1143.     ngx_stream_geo_range_t                *r, *range, **ranges;
  1144.     ngx_stream_geo_header_t               *header;
  1145.     ngx_stream_geo_variable_value_node_t  *gvvn;

  1146.     fm.name = ngx_pnalloc(ctx->temp_pool, ctx->include_name.len + 5);
  1147.     if (fm.name == NULL) {
  1148.         return;
  1149.     }

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

  1151.     fm.size = ctx->data_size;
  1152.     fm.log = ctx->pool->log;

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

  1155.     if (ngx_create_file_mapping(&fm) != NGX_OK) {
  1156.         return;
  1157.     }

  1158.     p = ngx_cpymem(fm.addr, &ngx_stream_geo_header,
  1159.                    sizeof(ngx_stream_geo_header_t));

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

  1162.     p += sizeof(ngx_stream_variable_value_t);

  1163.     ranges = (ngx_stream_geo_range_t **) p;

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

  1165.     for (i = 0; i < 0x10000; i++) {
  1166.         r = ctx->high.low[i];
  1167.         if (r == NULL) {
  1168.             continue;
  1169.         }

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

  1172.         do {
  1173.             s.len = r->value->len;
  1174.             s.data = r->value->data;
  1175.             hash = ngx_crc32_long(s.data, s.len);
  1176.             gvvn = (ngx_stream_geo_variable_value_node_t *)
  1177.                         ngx_str_rbtree_lookup(&ctx->rbtree, &s, hash);

  1178.             range->value = (ngx_stream_variable_value_t *) gvvn->offset;
  1179.             range->start = r->start;
  1180.             range->end = r->end;
  1181.             range++;

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

  1183.         range->value = NULL;

  1184.         p = (u_char *) range + sizeof(void *);
  1185.     }

  1186.     header = fm.addr;
  1187.     header->crc32 = ngx_crc32_long((u_char *) fm.addr
  1188.                                        + sizeof(ngx_stream_geo_header_t),
  1189.                                    fm.size - sizeof(ngx_stream_geo_header_t));

  1190.     ngx_close_file_mapping(&fm);
  1191. }


  1192. static u_char *
  1193. ngx_stream_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node,
  1194.     ngx_rbtree_node_t *sentinel)
  1195. {
  1196.     ngx_stream_variable_value_t           *vv;
  1197.     ngx_stream_geo_variable_value_node_t  *gvvn;

  1198.     if (node == sentinel) {
  1199.         return p;
  1200.     }

  1201.     gvvn = (ngx_stream_geo_variable_value_node_t *) node;
  1202.     gvvn->offset = p - base;

  1203.     vv = (ngx_stream_variable_value_t *) p;
  1204.     *vv = *gvvn->value;
  1205.     p += sizeof(ngx_stream_variable_value_t);
  1206.     vv->data = (u_char *) (p - base);

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

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

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

  1210.     return ngx_stream_geo_copy_values(base, p, node->right, sentinel);
  1211. }