Annotation of embedaddon/nginx/src/http/modules/ngx_http_geo_module.c, revision 1.1.1.1

1.1       misho       1: 
                      2: /*
                      3:  * Copyright (C) Igor Sysoev
                      4:  * Copyright (C) Nginx, Inc.
                      5:  */
                      6: 
                      7: 
                      8: #include <ngx_config.h>
                      9: #include <ngx_core.h>
                     10: #include <ngx_http.h>
                     11: 
                     12: 
                     13: typedef struct {
                     14:     ngx_http_variable_value_t       *value;
                     15:     u_short                          start;
                     16:     u_short                          end;
                     17: } ngx_http_geo_range_t;
                     18: 
                     19: 
                     20: typedef struct {
                     21:     ngx_radix_tree_t                *tree;
                     22: #if (NGX_HAVE_INET6)
                     23:     ngx_radix_tree_t                *tree6;
                     24: #endif
                     25: } ngx_http_geo_trees_t;
                     26: 
                     27: 
                     28: typedef struct {
                     29:     ngx_http_geo_range_t           **low;
                     30:     ngx_http_variable_value_t       *default_value;
                     31: } ngx_http_geo_high_ranges_t;
                     32: 
                     33: 
                     34: typedef struct {
                     35:     ngx_str_node_t                   sn;
                     36:     ngx_http_variable_value_t       *value;
                     37:     size_t                           offset;
                     38: } ngx_http_geo_variable_value_node_t;
                     39: 
                     40: 
                     41: typedef struct {
                     42:     ngx_http_variable_value_t       *value;
                     43:     ngx_str_t                       *net;
                     44:     ngx_http_geo_high_ranges_t       high;
                     45:     ngx_radix_tree_t                *tree;
                     46: #if (NGX_HAVE_INET6)
                     47:     ngx_radix_tree_t                *tree6;
                     48: #endif
                     49:     ngx_rbtree_t                     rbtree;
                     50:     ngx_rbtree_node_t                sentinel;
                     51:     ngx_array_t                     *proxies;
                     52:     ngx_pool_t                      *pool;
                     53:     ngx_pool_t                      *temp_pool;
                     54: 
                     55:     size_t                           data_size;
                     56: 
                     57:     ngx_str_t                        include_name;
                     58:     ngx_uint_t                       includes;
                     59:     ngx_uint_t                       entries;
                     60: 
                     61:     unsigned                         ranges:1;
                     62:     unsigned                         outside_entries:1;
                     63:     unsigned                         allow_binary_include:1;
                     64:     unsigned                         binary_include:1;
                     65:     unsigned                         proxy_recursive:1;
                     66: } ngx_http_geo_conf_ctx_t;
                     67: 
                     68: 
                     69: typedef struct {
                     70:     union {
                     71:         ngx_http_geo_trees_t         trees;
                     72:         ngx_http_geo_high_ranges_t   high;
                     73:     } u;
                     74: 
                     75:     ngx_array_t                     *proxies;
                     76:     unsigned                         proxy_recursive:1;
                     77: 
                     78:     ngx_int_t                        index;
                     79: } ngx_http_geo_ctx_t;
                     80: 
                     81: 
                     82: static ngx_int_t ngx_http_geo_addr(ngx_http_request_t *r,
                     83:     ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr);
                     84: static ngx_int_t ngx_http_geo_real_addr(ngx_http_request_t *r,
                     85:     ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr);
                     86: static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
                     87: static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
                     88: static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
                     89:     ngx_str_t *value);
                     90: static char *ngx_http_geo_add_range(ngx_conf_t *cf,
                     91:     ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
                     92: static ngx_uint_t ngx_http_geo_delete_range(ngx_conf_t *cf,
                     93:     ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
                     94: static char *ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
                     95:     ngx_str_t *value);
                     96: static char *ngx_http_geo_cidr_add(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
                     97:     ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net);
                     98: static ngx_http_variable_value_t *ngx_http_geo_value(ngx_conf_t *cf,
                     99:     ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value);
                    100: static char *ngx_http_geo_add_proxy(ngx_conf_t *cf,
                    101:     ngx_http_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr);
                    102: static ngx_int_t ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net,
                    103:     ngx_cidr_t *cidr);
                    104: static char *ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
                    105:     ngx_str_t *name);
                    106: static ngx_int_t ngx_http_geo_include_binary_base(ngx_conf_t *cf,
                    107:     ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *name);
                    108: static void ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx);
                    109: static u_char *ngx_http_geo_copy_values(u_char *base, u_char *p,
                    110:     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
                    111: 
                    112: 
                    113: static ngx_command_t  ngx_http_geo_commands[] = {
                    114: 
                    115:     { ngx_string("geo"),
                    116:       NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,
                    117:       ngx_http_geo_block,
                    118:       NGX_HTTP_MAIN_CONF_OFFSET,
                    119:       0,
                    120:       NULL },
                    121: 
                    122:       ngx_null_command
                    123: };
                    124: 
                    125: 
                    126: static ngx_http_module_t  ngx_http_geo_module_ctx = {
                    127:     NULL,                                  /* preconfiguration */
                    128:     NULL,                                  /* postconfiguration */
                    129: 
                    130:     NULL,                                  /* create main configuration */
                    131:     NULL,                                  /* init main configuration */
                    132: 
                    133:     NULL,                                  /* create server configuration */
                    134:     NULL,                                  /* merge server configuration */
                    135: 
                    136:     NULL,                                  /* create location configuration */
                    137:     NULL                                   /* merge location configuration */
                    138: };
                    139: 
                    140: 
                    141: ngx_module_t  ngx_http_geo_module = {
                    142:     NGX_MODULE_V1,
                    143:     &ngx_http_geo_module_ctx,              /* module context */
                    144:     ngx_http_geo_commands,                 /* module directives */
                    145:     NGX_HTTP_MODULE,                       /* module type */
                    146:     NULL,                                  /* init master */
                    147:     NULL,                                  /* init module */
                    148:     NULL,                                  /* init process */
                    149:     NULL,                                  /* init thread */
                    150:     NULL,                                  /* exit thread */
                    151:     NULL,                                  /* exit process */
                    152:     NULL,                                  /* exit master */
                    153:     NGX_MODULE_V1_PADDING
                    154: };
                    155: 
                    156: 
                    157: typedef struct {
                    158:     u_char    GEORNG[6];
                    159:     u_char    version;
                    160:     u_char    ptr_size;
                    161:     uint32_t  endianness;
                    162:     uint32_t  crc32;
                    163: } ngx_http_geo_header_t;
                    164: 
                    165: 
                    166: static ngx_http_geo_header_t  ngx_http_geo_header = {
                    167:     { 'G', 'E', 'O', 'R', 'N', 'G' }, 0, sizeof(void *), 0x12345678, 0
                    168: };
                    169: 
                    170: 
                    171: /* geo range is AF_INET only */
                    172: 
                    173: static ngx_int_t
                    174: ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
                    175:     uintptr_t data)
                    176: {
                    177:     ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
                    178: 
                    179:     in_addr_t                   inaddr;
                    180:     ngx_addr_t                  addr;
                    181:     struct sockaddr_in         *sin;
                    182:     ngx_http_variable_value_t  *vv;
                    183: #if (NGX_HAVE_INET6)
                    184:     u_char                     *p;
                    185:     struct in6_addr            *inaddr6;
                    186: #endif
                    187: 
                    188:     if (ngx_http_geo_addr(r, ctx, &addr) != NGX_OK) {
                    189:         vv = (ngx_http_variable_value_t *)
                    190:                   ngx_radix32tree_find(ctx->u.trees.tree, INADDR_NONE);
                    191:         goto done;
                    192:     }
                    193: 
                    194:     switch (addr.sockaddr->sa_family) {
                    195: 
                    196: #if (NGX_HAVE_INET6)
                    197:     case AF_INET6:
                    198:         inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;
                    199:         p = inaddr6->s6_addr;
                    200: 
                    201:         if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
                    202:             inaddr = p[12] << 24;
                    203:             inaddr += p[13] << 16;
                    204:             inaddr += p[14] << 8;
                    205:             inaddr += p[15];
                    206: 
                    207:             vv = (ngx_http_variable_value_t *)
                    208:                       ngx_radix32tree_find(ctx->u.trees.tree, inaddr);
                    209: 
                    210:         } else {
                    211:             vv = (ngx_http_variable_value_t *)
                    212:                       ngx_radix128tree_find(ctx->u.trees.tree6, p);
                    213:         }
                    214: 
                    215:         break;
                    216: #endif
                    217: 
                    218:     default: /* AF_INET */
                    219:         sin = (struct sockaddr_in *) addr.sockaddr;
                    220:         inaddr = ntohl(sin->sin_addr.s_addr);
                    221: 
                    222:         vv = (ngx_http_variable_value_t *)
                    223:                   ngx_radix32tree_find(ctx->u.trees.tree, inaddr);
                    224: 
                    225:         break;
                    226:     }
                    227: 
                    228: done:
                    229: 
                    230:     *v = *vv;
                    231: 
                    232:     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    233:                    "http geo: %v", v);
                    234: 
                    235:     return NGX_OK;
                    236: }
                    237: 
                    238: 
                    239: static ngx_int_t
                    240: ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
                    241:     uintptr_t data)
                    242: {
                    243:     ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
                    244: 
                    245:     in_addr_t              inaddr;
                    246:     ngx_addr_t             addr;
                    247:     ngx_uint_t             n;
                    248:     struct sockaddr_in    *sin;
                    249:     ngx_http_geo_range_t  *range;
                    250: #if (NGX_HAVE_INET6)
                    251:     u_char                *p;
                    252:     struct in6_addr       *inaddr6;
                    253: #endif
                    254: 
                    255:     *v = *ctx->u.high.default_value;
                    256: 
                    257:     if (ngx_http_geo_addr(r, ctx, &addr) == NGX_OK) {
                    258: 
                    259:         switch (addr.sockaddr->sa_family) {
                    260: 
                    261: #if (NGX_HAVE_INET6)
                    262:         case AF_INET6:
                    263:             inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;
                    264: 
                    265:             if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
                    266:                 p = inaddr6->s6_addr;
                    267: 
                    268:                 inaddr = p[12] << 24;
                    269:                 inaddr += p[13] << 16;
                    270:                 inaddr += p[14] << 8;
                    271:                 inaddr += p[15];
                    272: 
                    273:             } else {
                    274:                 inaddr = INADDR_NONE;
                    275:             }
                    276: 
                    277:             break;
                    278: #endif
                    279: 
                    280:         default: /* AF_INET */
                    281:             sin = (struct sockaddr_in *) addr.sockaddr;
                    282:             inaddr = ntohl(sin->sin_addr.s_addr);
                    283:             break;
                    284:         }
                    285: 
                    286:     } else {
                    287:         inaddr = INADDR_NONE;
                    288:     }
                    289: 
                    290:     if (ctx->u.high.low) {
                    291:         range = ctx->u.high.low[inaddr >> 16];
                    292: 
                    293:         if (range) {
                    294:             n = inaddr & 0xffff;
                    295:             do {
                    296:                 if (n >= (ngx_uint_t) range->start
                    297:                     && n <= (ngx_uint_t) range->end)
                    298:                 {
                    299:                     *v = *range->value;
                    300:                     break;
                    301:                 }
                    302:             } while ((++range)->value);
                    303:         }
                    304:     }
                    305: 
                    306:     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    307:                    "http geo: %v", v);
                    308: 
                    309:     return NGX_OK;
                    310: }
                    311: 
                    312: 
                    313: static ngx_int_t
                    314: ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx,
                    315:     ngx_addr_t *addr)
                    316: {
                    317:     ngx_array_t  *xfwd;
                    318: 
                    319:     if (ngx_http_geo_real_addr(r, ctx, addr) != NGX_OK) {
                    320:         return NGX_ERROR;
                    321:     }
                    322: 
                    323:     xfwd = &r->headers_in.x_forwarded_for;
                    324: 
                    325:     if (xfwd->nelts > 0 && ctx->proxies != NULL) {
                    326:         (void) ngx_http_get_forwarded_addr(r, addr, xfwd, NULL,
                    327:                                            ctx->proxies, ctx->proxy_recursive);
                    328:     }
                    329: 
                    330:     return NGX_OK;
                    331: }
                    332: 
                    333: 
                    334: static ngx_int_t
                    335: ngx_http_geo_real_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx,
                    336:     ngx_addr_t *addr)
                    337: {
                    338:     ngx_http_variable_value_t  *v;
                    339: 
                    340:     if (ctx->index == -1) {
                    341:         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    342:                        "http geo started: %V", &r->connection->addr_text);
                    343: 
                    344:         addr->sockaddr = r->connection->sockaddr;
                    345:         addr->socklen = r->connection->socklen;
                    346:         /* addr->name = r->connection->addr_text; */
                    347: 
                    348:         return NGX_OK;
                    349:     }
                    350: 
                    351:     v = ngx_http_get_flushed_variable(r, ctx->index);
                    352: 
                    353:     if (v == NULL || v->not_found) {
                    354:         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    355:                        "http geo not found");
                    356: 
                    357:         return NGX_ERROR;
                    358:     }
                    359: 
                    360:     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    361:                    "http geo started: %v", v);
                    362: 
                    363:     if (ngx_parse_addr(r->pool, addr, v->data, v->len) == NGX_OK) {
                    364:         return NGX_OK;
                    365:     }
                    366: 
                    367:     return NGX_ERROR;
                    368: }
                    369: 
                    370: 
                    371: static char *
                    372: ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
                    373: {
                    374:     char                     *rv;
                    375:     size_t                    len;
                    376:     ngx_str_t                *value, name;
                    377:     ngx_uint_t                i;
                    378:     ngx_conf_t                save;
                    379:     ngx_pool_t               *pool;
                    380:     ngx_array_t              *a;
                    381:     ngx_http_variable_t      *var;
                    382:     ngx_http_geo_ctx_t       *geo;
                    383:     ngx_http_geo_conf_ctx_t   ctx;
                    384: #if (NGX_HAVE_INET6)
                    385:     static struct in6_addr    zero;
                    386: #endif
                    387: 
                    388:     value = cf->args->elts;
                    389: 
                    390:     geo = ngx_palloc(cf->pool, sizeof(ngx_http_geo_ctx_t));
                    391:     if (geo == NULL) {
                    392:         return NGX_CONF_ERROR;
                    393:     }
                    394: 
                    395:     name = value[1];
                    396: 
                    397:     if (name.data[0] != '$') {
                    398:         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                    399:                            "invalid variable name \"%V\"", &name);
                    400:         return NGX_CONF_ERROR;
                    401:     }
                    402: 
                    403:     name.len--;
                    404:     name.data++;
                    405: 
                    406:     if (cf->args->nelts == 3) {
                    407: 
                    408:         geo->index = ngx_http_get_variable_index(cf, &name);
                    409:         if (geo->index == NGX_ERROR) {
                    410:             return NGX_CONF_ERROR;
                    411:         }
                    412: 
                    413:         name = value[2];
                    414: 
                    415:         if (name.data[0] != '$') {
                    416:             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                    417:                                "invalid variable name \"%V\"", &name);
                    418:             return NGX_CONF_ERROR;
                    419:         }
                    420: 
                    421:         name.len--;
                    422:         name.data++;
                    423: 
                    424:     } else {
                    425:         geo->index = -1;
                    426:     }
                    427: 
                    428:     var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
                    429:     if (var == NULL) {
                    430:         return NGX_CONF_ERROR;
                    431:     }
                    432: 
                    433:     pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
                    434:     if (pool == NULL) {
                    435:         return NGX_CONF_ERROR;
                    436:     }
                    437: 
                    438:     ngx_memzero(&ctx, sizeof(ngx_http_geo_conf_ctx_t));
                    439: 
                    440:     ctx.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
                    441:     if (ctx.temp_pool == NULL) {
                    442:         return NGX_CONF_ERROR;
                    443:     }
                    444: 
                    445:     ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, ngx_str_rbtree_insert_value);
                    446: 
                    447:     ctx.pool = cf->pool;
                    448:     ctx.data_size = sizeof(ngx_http_geo_header_t)
                    449:                   + sizeof(ngx_http_variable_value_t)
                    450:                   + 0x10000 * sizeof(ngx_http_geo_range_t *);
                    451:     ctx.allow_binary_include = 1;
                    452: 
                    453:     save = *cf;
                    454:     cf->pool = pool;
                    455:     cf->ctx = &ctx;
                    456:     cf->handler = ngx_http_geo;
                    457:     cf->handler_conf = conf;
                    458: 
                    459:     rv = ngx_conf_parse(cf, NULL);
                    460: 
                    461:     *cf = save;
                    462: 
                    463:     geo->proxies = ctx.proxies;
                    464:     geo->proxy_recursive = ctx.proxy_recursive;
                    465: 
                    466:     if (ctx.ranges) {
                    467: 
                    468:         if (ctx.high.low && !ctx.binary_include) {
                    469:             for (i = 0; i < 0x10000; i++) {
                    470:                 a = (ngx_array_t *) ctx.high.low[i];
                    471: 
                    472:                 if (a == NULL || a->nelts == 0) {
                    473:                     continue;
                    474:                 }
                    475: 
                    476:                 len = a->nelts * sizeof(ngx_http_geo_range_t);
                    477: 
                    478:                 ctx.high.low[i] = ngx_palloc(cf->pool, len + sizeof(void *));
                    479:                 if (ctx.high.low[i] == NULL) {
                    480:                     return NGX_CONF_ERROR;
                    481:                 }
                    482: 
                    483:                 ngx_memcpy(ctx.high.low[i], a->elts, len);
                    484:                 ctx.high.low[i][a->nelts].value = NULL;
                    485:                 ctx.data_size += len + sizeof(void *);
                    486:             }
                    487: 
                    488:             if (ctx.allow_binary_include
                    489:                 && !ctx.outside_entries
                    490:                 && ctx.entries > 100000
                    491:                 && ctx.includes == 1)
                    492:             {
                    493:                 ngx_http_geo_create_binary_base(&ctx);
                    494:             }
                    495:         }
                    496: 
                    497:         if (ctx.high.default_value == NULL) {
                    498:             ctx.high.default_value = &ngx_http_variable_null_value;
                    499:         }
                    500: 
                    501:         geo->u.high = ctx.high;
                    502: 
                    503:         var->get_handler = ngx_http_geo_range_variable;
                    504:         var->data = (uintptr_t) geo;
                    505: 
                    506:         ngx_destroy_pool(ctx.temp_pool);
                    507:         ngx_destroy_pool(pool);
                    508: 
                    509:     } else {
                    510:         if (ctx.tree == NULL) {
                    511:             ctx.tree = ngx_radix_tree_create(cf->pool, -1);
                    512:             if (ctx.tree == NULL) {
                    513:                 return NGX_CONF_ERROR;
                    514:             }
                    515:         }
                    516: 
                    517:         geo->u.trees.tree = ctx.tree;
                    518: 
                    519: #if (NGX_HAVE_INET6)
                    520:         if (ctx.tree6 == NULL) {
                    521:             ctx.tree6 = ngx_radix_tree_create(cf->pool, -1);
                    522:             if (ctx.tree6 == NULL) {
                    523:                 return NGX_CONF_ERROR;
                    524:             }
                    525:         }
                    526: 
                    527:         geo->u.trees.tree6 = ctx.tree6;
                    528: #endif
                    529: 
                    530:         var->get_handler = ngx_http_geo_cidr_variable;
                    531:         var->data = (uintptr_t) geo;
                    532: 
                    533:         ngx_destroy_pool(ctx.temp_pool);
                    534:         ngx_destroy_pool(pool);
                    535: 
                    536:         if (ngx_radix32tree_insert(ctx.tree, 0, 0,
                    537:                                    (uintptr_t) &ngx_http_variable_null_value)
                    538:             == NGX_ERROR)
                    539:         {
                    540:             return NGX_CONF_ERROR;
                    541:         }
                    542: 
                    543:         /* NGX_BUSY is okay (default was set explicitly) */
                    544: 
                    545: #if (NGX_HAVE_INET6)
                    546:         if (ngx_radix128tree_insert(ctx.tree6, zero.s6_addr, zero.s6_addr,
                    547:                                     (uintptr_t) &ngx_http_variable_null_value)
                    548:             == NGX_ERROR)
                    549:         {
                    550:             return NGX_CONF_ERROR;
                    551:         }
                    552: #endif
                    553:     }
                    554: 
                    555:     return rv;
                    556: }
                    557: 
                    558: 
                    559: static char *
                    560: ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
                    561: {
                    562:     char                     *rv;
                    563:     ngx_str_t                *value;
                    564:     ngx_cidr_t                cidr;
                    565:     ngx_http_geo_conf_ctx_t  *ctx;
                    566: 
                    567:     ctx = cf->ctx;
                    568: 
                    569:     value = cf->args->elts;
                    570: 
                    571:     if (cf->args->nelts == 1) {
                    572: 
                    573:         if (ngx_strcmp(value[0].data, "ranges") == 0) {
                    574: 
                    575:             if (ctx->tree
                    576: #if (NGX_HAVE_INET6)
                    577:                 || ctx->tree6
                    578: #endif
                    579:                )
                    580:             {
                    581:                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                    582:                                    "the \"ranges\" directive must be "
                    583:                                    "the first directive inside \"geo\" block");
                    584:                 goto failed;
                    585:             }
                    586: 
                    587:             ctx->ranges = 1;
                    588: 
                    589:             rv = NGX_CONF_OK;
                    590: 
                    591:             goto done;
                    592:         }
                    593: 
                    594:         else if (ngx_strcmp(value[0].data, "proxy_recursive") == 0) {
                    595:             ctx->proxy_recursive = 1;
                    596:             rv = NGX_CONF_OK;
                    597:             goto done;
                    598:         }
                    599:     }
                    600: 
                    601:     if (cf->args->nelts != 2) {
                    602:         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                    603:                            "invalid number of the geo parameters");
                    604:         goto failed;
                    605:     }
                    606: 
                    607:     if (ngx_strcmp(value[0].data, "include") == 0) {
                    608: 
                    609:         rv = ngx_http_geo_include(cf, ctx, &value[1]);
                    610: 
                    611:         goto done;
                    612: 
                    613:     } else if (ngx_strcmp(value[0].data, "proxy") == 0) {
                    614: 
                    615:         if (ngx_http_geo_cidr_value(cf, &value[1], &cidr) != NGX_OK) {
                    616:             goto failed;
                    617:         }
                    618: 
                    619:         rv = ngx_http_geo_add_proxy(cf, ctx, &cidr);
                    620: 
                    621:         goto done;
                    622:     }
                    623: 
                    624:     if (ctx->ranges) {
                    625:         rv = ngx_http_geo_range(cf, ctx, value);
                    626: 
                    627:     } else {
                    628:         rv = ngx_http_geo_cidr(cf, ctx, value);
                    629:     }
                    630: 
                    631: done:
                    632: 
                    633:     ngx_reset_pool(cf->pool);
                    634: 
                    635:     return rv;
                    636: 
                    637: failed:
                    638: 
                    639:     ngx_reset_pool(cf->pool);
                    640: 
                    641:     return NGX_CONF_ERROR;
                    642: }
                    643: 
                    644: 
                    645: static char *
                    646: ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
                    647:     ngx_str_t *value)
                    648: {
                    649:     u_char      *p, *last;
                    650:     in_addr_t    start, end;
                    651:     ngx_str_t   *net;
                    652:     ngx_uint_t   del;
                    653: 
                    654:     if (ngx_strcmp(value[0].data, "default") == 0) {
                    655: 
                    656:         if (ctx->high.default_value) {
                    657:             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                    658:                 "duplicate default geo range value: \"%V\", old value: \"%v\"",
                    659:                 &value[1], ctx->high.default_value);
                    660:         }
                    661: 
                    662:         ctx->high.default_value = ngx_http_geo_value(cf, ctx, &value[1]);
                    663:         if (ctx->high.default_value == NULL) {
                    664:             return NGX_CONF_ERROR;
                    665:         }
                    666: 
                    667:         return NGX_CONF_OK;
                    668:     }
                    669: 
                    670:     if (ctx->binary_include) {
                    671:         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                    672:             "binary geo range base \"%s\" cannot be mixed with usual entries",
                    673:             ctx->include_name.data);
                    674:         return NGX_CONF_ERROR;
                    675:     }
                    676: 
                    677:     if (ctx->high.low == NULL) {
                    678:         ctx->high.low = ngx_pcalloc(ctx->pool,
                    679:                                     0x10000 * sizeof(ngx_http_geo_range_t *));
                    680:         if (ctx->high.low == NULL) {
                    681:             return NGX_CONF_ERROR;
                    682:         }
                    683:     }
                    684: 
                    685:     ctx->entries++;
                    686:     ctx->outside_entries = 1;
                    687: 
                    688:     if (ngx_strcmp(value[0].data, "delete") == 0) {
                    689:         net = &value[1];
                    690:         del = 1;
                    691: 
                    692:     } else {
                    693:         net = &value[0];
                    694:         del = 0;
                    695:     }
                    696: 
                    697:     last = net->data + net->len;
                    698: 
                    699:     p = ngx_strlchr(net->data, last, '-');
                    700: 
                    701:     if (p == NULL) {
                    702:         goto invalid;
                    703:     }
                    704: 
                    705:     start = ngx_inet_addr(net->data, p - net->data);
                    706: 
                    707:     if (start == INADDR_NONE) {
                    708:         goto invalid;
                    709:     }
                    710: 
                    711:     start = ntohl(start);
                    712: 
                    713:     p++;
                    714: 
                    715:     end = ngx_inet_addr(p, last - p);
                    716: 
                    717:     if (end == INADDR_NONE) {
                    718:         goto invalid;
                    719:     }
                    720: 
                    721:     end = ntohl(end);
                    722: 
                    723:     if (start > end) {
                    724:         goto invalid;
                    725:     }
                    726: 
                    727:     if (del) {
                    728:         if (ngx_http_geo_delete_range(cf, ctx, start, end)) {
                    729:             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                    730:                                "no address range \"%V\" to delete", net);
                    731:         }
                    732: 
                    733:         return NGX_CONF_OK;
                    734:     }
                    735: 
                    736:     ctx->value = ngx_http_geo_value(cf, ctx, &value[1]);
                    737: 
                    738:     if (ctx->value == NULL) {
                    739:         return NGX_CONF_ERROR;
                    740:     }
                    741: 
                    742:     ctx->net = net;
                    743: 
                    744:     return ngx_http_geo_add_range(cf, ctx, start, end);
                    745: 
                    746: invalid:
                    747: 
                    748:     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid range \"%V\"", net);
                    749: 
                    750:     return NGX_CONF_ERROR;
                    751: }
                    752: 
                    753: 
                    754: /* the add procedure is optimized to add a growing up sequence */
                    755: 
                    756: static char *
                    757: ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
                    758:     in_addr_t start, in_addr_t end)
                    759: {
                    760:     in_addr_t              n;
                    761:     ngx_uint_t             h, i, s, e;
                    762:     ngx_array_t           *a;
                    763:     ngx_http_geo_range_t  *range;
                    764: 
                    765:     for (n = start; n <= end; n = (n + 0x10000) & 0xffff0000) {
                    766: 
                    767:         h = n >> 16;
                    768: 
                    769:         if (n == start) {
                    770:             s = n & 0xffff;
                    771:         } else {
                    772:             s = 0;
                    773:         }
                    774: 
                    775:         if ((n | 0xffff) > end) {
                    776:             e = end & 0xffff;
                    777: 
                    778:         } else {
                    779:             e = 0xffff;
                    780:         }
                    781: 
                    782:         a = (ngx_array_t *) ctx->high.low[h];
                    783: 
                    784:         if (a == NULL) {
                    785:             a = ngx_array_create(ctx->temp_pool, 64,
                    786:                                  sizeof(ngx_http_geo_range_t));
                    787:             if (a == NULL) {
                    788:                 return NGX_CONF_ERROR;
                    789:             }
                    790: 
                    791:             ctx->high.low[h] = (ngx_http_geo_range_t *) a;
                    792:         }
                    793: 
                    794:         i = a->nelts;
                    795:         range = a->elts;
                    796: 
                    797:         while (i) {
                    798: 
                    799:             i--;
                    800: 
                    801:             if (e < (ngx_uint_t) range[i].start) {
                    802:                 continue;
                    803:             }
                    804: 
                    805:             if (s > (ngx_uint_t) range[i].end) {
                    806: 
                    807:                 /* add after the range */
                    808: 
                    809:                 range = ngx_array_push(a);
                    810:                 if (range == NULL) {
                    811:                     return NGX_CONF_ERROR;
                    812:                 }
                    813: 
                    814:                 range = a->elts;
                    815: 
                    816:                 ngx_memmove(&range[i + 2], &range[i + 1],
                    817:                            (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));
                    818: 
                    819:                 range[i + 1].start = (u_short) s;
                    820:                 range[i + 1].end = (u_short) e;
                    821:                 range[i + 1].value = ctx->value;
                    822: 
                    823:                 goto next;
                    824:             }
                    825: 
                    826:             if (s == (ngx_uint_t) range[i].start
                    827:                 && e == (ngx_uint_t) range[i].end)
                    828:             {
                    829:                 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                    830:                     "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
                    831:                     ctx->net, ctx->value, range[i].value);
                    832: 
                    833:                 range[i].value = ctx->value;
                    834: 
                    835:                 goto next;
                    836:             }
                    837: 
                    838:             if (s > (ngx_uint_t) range[i].start
                    839:                 && e < (ngx_uint_t) range[i].end)
                    840:             {
                    841:                 /* split the range and insert the new one */
                    842: 
                    843:                 range = ngx_array_push(a);
                    844:                 if (range == NULL) {
                    845:                     return NGX_CONF_ERROR;
                    846:                 }
                    847: 
                    848:                 range = ngx_array_push(a);
                    849:                 if (range == NULL) {
                    850:                     return NGX_CONF_ERROR;
                    851:                 }
                    852: 
                    853:                 range = a->elts;
                    854: 
                    855:                 ngx_memmove(&range[i + 3], &range[i + 1],
                    856:                            (a->nelts - 3 - i) * sizeof(ngx_http_geo_range_t));
                    857: 
                    858:                 range[i + 2].start = (u_short) (e + 1);
                    859:                 range[i + 2].end = range[i].end;
                    860:                 range[i + 2].value = range[i].value;
                    861: 
                    862:                 range[i + 1].start = (u_short) s;
                    863:                 range[i + 1].end = (u_short) e;
                    864:                 range[i + 1].value = ctx->value;
                    865: 
                    866:                 range[i].end = (u_short) (s - 1);
                    867: 
                    868:                 goto next;
                    869:             }
                    870: 
                    871:             if (s == (ngx_uint_t) range[i].start
                    872:                 && e < (ngx_uint_t) range[i].end)
                    873:             {
                    874:                 /* shift the range start and insert the new range */
                    875: 
                    876:                 range = ngx_array_push(a);
                    877:                 if (range == NULL) {
                    878:                     return NGX_CONF_ERROR;
                    879:                 }
                    880: 
                    881:                 range = a->elts;
                    882: 
                    883:                 ngx_memmove(&range[i + 1], &range[i],
                    884:                            (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));
                    885: 
                    886:                 range[i + 1].start = (u_short) (e + 1);
                    887: 
                    888:                 range[i].start = (u_short) s;
                    889:                 range[i].end = (u_short) e;
                    890:                 range[i].value = ctx->value;
                    891: 
                    892:                 goto next;
                    893:             }
                    894: 
                    895:             if (s > (ngx_uint_t) range[i].start
                    896:                 && e == (ngx_uint_t) range[i].end)
                    897:             {
                    898:                 /* shift the range end and insert the new range */
                    899: 
                    900:                 range = ngx_array_push(a);
                    901:                 if (range == NULL) {
                    902:                     return NGX_CONF_ERROR;
                    903:                 }
                    904: 
                    905:                 range = a->elts;
                    906: 
                    907:                 ngx_memmove(&range[i + 2], &range[i + 1],
                    908:                            (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));
                    909: 
                    910:                 range[i + 1].start = (u_short) s;
                    911:                 range[i + 1].end = (u_short) e;
                    912:                 range[i + 1].value = ctx->value;
                    913: 
                    914:                 range[i].end = (u_short) (s - 1);
                    915: 
                    916:                 goto next;
                    917:             }
                    918: 
                    919:             s = (ngx_uint_t) range[i].start;
                    920:             e = (ngx_uint_t) range[i].end;
                    921: 
                    922:             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                    923:                          "range \"%V\" overlaps \"%d.%d.%d.%d-%d.%d.%d.%d\"",
                    924:                          ctx->net,
                    925:                          h >> 8, h & 0xff, s >> 8, s & 0xff,
                    926:                          h >> 8, h & 0xff, e >> 8, e & 0xff);
                    927: 
                    928:             return NGX_CONF_ERROR;
                    929:         }
                    930: 
                    931:         /* add the first range */
                    932: 
                    933:         range = ngx_array_push(a);
                    934:         if (range == NULL) {
                    935:             return NGX_CONF_ERROR;
                    936:         }
                    937: 
                    938:         range->start = (u_short) s;
                    939:         range->end = (u_short) e;
                    940:         range->value = ctx->value;
                    941: 
                    942:     next:
                    943: 
                    944:         continue;
                    945:     }
                    946: 
                    947:     return NGX_CONF_OK;
                    948: }
                    949: 
                    950: 
                    951: static ngx_uint_t
                    952: ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
                    953:     in_addr_t start, in_addr_t end)
                    954: {
                    955:     in_addr_t              n;
                    956:     ngx_uint_t             h, i, s, e, warn;
                    957:     ngx_array_t           *a;
                    958:     ngx_http_geo_range_t  *range;
                    959: 
                    960:     warn = 0;
                    961: 
                    962:     for (n = start; n <= end; n += 0x10000) {
                    963: 
                    964:         h = n >> 16;
                    965: 
                    966:         if (n == start) {
                    967:             s = n & 0xffff;
                    968:         } else {
                    969:             s = 0;
                    970:         }
                    971: 
                    972:         if ((n | 0xffff) > end) {
                    973:             e = end & 0xffff;
                    974: 
                    975:         } else {
                    976:             e = 0xffff;
                    977:         }
                    978: 
                    979:         a = (ngx_array_t *) ctx->high.low[h];
                    980: 
                    981:         if (a == NULL) {
                    982:             warn = 1;
                    983:             continue;
                    984:         }
                    985: 
                    986:         range = a->elts;
                    987:         for (i = 0; i < a->nelts; i++) {
                    988: 
                    989:             if (s == (ngx_uint_t) range[i].start
                    990:                 && e == (ngx_uint_t) range[i].end)
                    991:             {
                    992:                 ngx_memmove(&range[i], &range[i + 1],
                    993:                            (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));
                    994: 
                    995:                 a->nelts--;
                    996: 
                    997:                 break;
                    998:             }
                    999: 
                   1000:             if (s != (ngx_uint_t) range[i].start
                   1001:                 && e != (ngx_uint_t) range[i].end)
                   1002:             {
                   1003:                 continue;
                   1004:             }
                   1005: 
                   1006:             warn = 1;
                   1007:         }
                   1008:     }
                   1009: 
                   1010:     return warn;
                   1011: }
                   1012: 
                   1013: 
                   1014: static char *
                   1015: ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
                   1016:     ngx_str_t *value)
                   1017: {
                   1018:     char        *rv;
                   1019:     ngx_int_t    rc, del;
                   1020:     ngx_str_t   *net;
                   1021:     ngx_cidr_t   cidr;
                   1022: 
                   1023:     if (ctx->tree == NULL) {
                   1024:         ctx->tree = ngx_radix_tree_create(ctx->pool, -1);
                   1025:         if (ctx->tree == NULL) {
                   1026:             return NGX_CONF_ERROR;
                   1027:         }
                   1028:     }
                   1029: 
                   1030: #if (NGX_HAVE_INET6)
                   1031:     if (ctx->tree6 == NULL) {
                   1032:         ctx->tree6 = ngx_radix_tree_create(ctx->pool, -1);
                   1033:         if (ctx->tree6 == NULL) {
                   1034:             return NGX_CONF_ERROR;
                   1035:         }
                   1036:     }
                   1037: #endif
                   1038: 
                   1039:     if (ngx_strcmp(value[0].data, "default") == 0) {
                   1040:         cidr.family = AF_INET;
                   1041:         cidr.u.in.addr = 0;
                   1042:         cidr.u.in.mask = 0;
                   1043: 
                   1044:         rv = ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]);
                   1045: 
                   1046:         if (rv != NGX_CONF_OK) {
                   1047:             return rv;
                   1048:         }
                   1049: 
                   1050: #if (NGX_HAVE_INET6)
                   1051:         cidr.family = AF_INET6;
                   1052:         ngx_memzero(&cidr.u.in6, sizeof(ngx_in6_cidr_t));
                   1053: 
                   1054:         rv = ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]);
                   1055: 
                   1056:         if (rv != NGX_CONF_OK) {
                   1057:             return rv;
                   1058:         }
                   1059: #endif
                   1060: 
                   1061:         return NGX_CONF_OK;
                   1062:     }
                   1063: 
                   1064:     if (ngx_strcmp(value[0].data, "delete") == 0) {
                   1065:         net = &value[1];
                   1066:         del = 1;
                   1067: 
                   1068:     } else {
                   1069:         net = &value[0];
                   1070:         del = 0;
                   1071:     }
                   1072: 
                   1073:     if (ngx_http_geo_cidr_value(cf, net, &cidr) != NGX_OK) {
                   1074:         return NGX_CONF_ERROR;
                   1075:     }
                   1076: 
                   1077:     if (cidr.family == AF_INET) {
                   1078:         cidr.u.in.addr = ntohl(cidr.u.in.addr);
                   1079:         cidr.u.in.mask = ntohl(cidr.u.in.mask);
                   1080:     }
                   1081: 
                   1082:     if (del) {
                   1083:         switch (cidr.family) {
                   1084: 
                   1085: #if (NGX_HAVE_INET6)
                   1086:         case AF_INET6:
                   1087:             rc = ngx_radix128tree_delete(ctx->tree6,
                   1088:                                          cidr.u.in6.addr.s6_addr,
                   1089:                                          cidr.u.in6.mask.s6_addr);
                   1090:             break;
                   1091: #endif
                   1092: 
                   1093:         default: /* AF_INET */
                   1094:             rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr,
                   1095:                                         cidr.u.in.mask);
                   1096:             break;
                   1097:         }
                   1098: 
                   1099:         if (rc != NGX_OK) {
                   1100:             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                   1101:                                "no network \"%V\" to delete", net);
                   1102:         }
                   1103: 
                   1104:         return NGX_CONF_OK;
                   1105:     }
                   1106: 
                   1107:     return ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], net);
                   1108: }
                   1109: 
                   1110: 
                   1111: static char *
                   1112: ngx_http_geo_cidr_add(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
                   1113:     ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net)
                   1114: {
                   1115:     ngx_int_t                   rc;
                   1116:     ngx_http_variable_value_t  *val, *old;
                   1117: 
                   1118:     val = ngx_http_geo_value(cf, ctx, value);
                   1119: 
                   1120:     if (val == NULL) {
                   1121:         return NGX_CONF_ERROR;
                   1122:     }
                   1123: 
                   1124:     switch (cidr->family) {
                   1125: 
                   1126: #if (NGX_HAVE_INET6)
                   1127:     case AF_INET6:
                   1128:         rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,
                   1129:                                      cidr->u.in6.mask.s6_addr,
                   1130:                                      (uintptr_t) val);
                   1131: 
                   1132:         if (rc == NGX_OK) {
                   1133:             return NGX_CONF_OK;
                   1134:         }
                   1135: 
                   1136:         if (rc == NGX_ERROR) {
                   1137:             return NGX_CONF_ERROR;
                   1138:         }
                   1139: 
                   1140:         /* rc == NGX_BUSY */
                   1141: 
                   1142:         old = (ngx_http_variable_value_t *)
                   1143:                    ngx_radix128tree_find(ctx->tree6,
                   1144:                                          cidr->u.in6.addr.s6_addr);
                   1145: 
                   1146:         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                   1147:               "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
                   1148:               net, val, old);
                   1149: 
                   1150:         rc = ngx_radix128tree_delete(ctx->tree6,
                   1151:                                      cidr->u.in6.addr.s6_addr,
                   1152:                                      cidr->u.in6.mask.s6_addr);
                   1153: 
                   1154:         if (rc == NGX_ERROR) {
                   1155:             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
                   1156:             return NGX_CONF_ERROR;
                   1157:         }
                   1158: 
                   1159:         rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,
                   1160:                                      cidr->u.in6.mask.s6_addr,
                   1161:                                      (uintptr_t) val);
                   1162: 
                   1163:         break;
                   1164: #endif
                   1165: 
                   1166:     default: /* AF_INET */
                   1167:         rc = ngx_radix32tree_insert(ctx->tree, cidr->u.in.addr,
                   1168:                                     cidr->u.in.mask, (uintptr_t) val);
                   1169: 
                   1170:         if (rc == NGX_OK) {
                   1171:             return NGX_CONF_OK;
                   1172:         }
                   1173: 
                   1174:         if (rc == NGX_ERROR) {
                   1175:             return NGX_CONF_ERROR;
                   1176:         }
                   1177: 
                   1178:         /* rc == NGX_BUSY */
                   1179: 
                   1180:         old = (ngx_http_variable_value_t *)
                   1181:                    ngx_radix32tree_find(ctx->tree, cidr->u.in.addr);
                   1182: 
                   1183:         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                   1184:               "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
                   1185:               net, val, old);
                   1186: 
                   1187:         rc = ngx_radix32tree_delete(ctx->tree,
                   1188:                                     cidr->u.in.addr, cidr->u.in.mask);
                   1189: 
                   1190:         if (rc == NGX_ERROR) {
                   1191:             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
                   1192:             return NGX_CONF_ERROR;
                   1193:         }
                   1194: 
                   1195:         rc = ngx_radix32tree_insert(ctx->tree, cidr->u.in.addr,
                   1196:                                     cidr->u.in.mask, (uintptr_t) val);
                   1197: 
                   1198:         break;
                   1199:     }
                   1200: 
                   1201:     if (rc == NGX_OK) {
                   1202:         return NGX_CONF_OK;
                   1203:     }
                   1204: 
                   1205:     return NGX_CONF_ERROR;
                   1206: }
                   1207: 
                   1208: 
                   1209: static ngx_http_variable_value_t *
                   1210: ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
                   1211:     ngx_str_t *value)
                   1212: {
                   1213:     uint32_t                             hash;
                   1214:     ngx_http_variable_value_t           *val;
                   1215:     ngx_http_geo_variable_value_node_t  *gvvn;
                   1216: 
                   1217:     hash = ngx_crc32_long(value->data, value->len);
                   1218: 
                   1219:     gvvn = (ngx_http_geo_variable_value_node_t *)
                   1220:                ngx_str_rbtree_lookup(&ctx->rbtree, value, hash);
                   1221: 
                   1222:     if (gvvn) {
                   1223:         return gvvn->value;
                   1224:     }
                   1225: 
                   1226:     val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));
                   1227:     if (val == NULL) {
                   1228:         return NULL;
                   1229:     }
                   1230: 
                   1231:     val->len = value->len;
                   1232:     val->data = ngx_pstrdup(ctx->pool, value);
                   1233:     if (val->data == NULL) {
                   1234:         return NULL;
                   1235:     }
                   1236: 
                   1237:     val->valid = 1;
                   1238:     val->no_cacheable = 0;
                   1239:     val->not_found = 0;
                   1240: 
                   1241:     gvvn = ngx_palloc(ctx->temp_pool,
                   1242:                       sizeof(ngx_http_geo_variable_value_node_t));
                   1243:     if (gvvn == NULL) {
                   1244:         return NULL;
                   1245:     }
                   1246: 
                   1247:     gvvn->sn.node.key = hash;
                   1248:     gvvn->sn.str.len = val->len;
                   1249:     gvvn->sn.str.data = val->data;
                   1250:     gvvn->value = val;
                   1251:     gvvn->offset = 0;
                   1252: 
                   1253:     ngx_rbtree_insert(&ctx->rbtree, &gvvn->sn.node);
                   1254: 
                   1255:     ctx->data_size += ngx_align(sizeof(ngx_http_variable_value_t) + value->len,
                   1256:                                 sizeof(void *));
                   1257: 
                   1258:     return val;
                   1259: }
                   1260: 
                   1261: 
                   1262: static char *
                   1263: ngx_http_geo_add_proxy(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
                   1264:     ngx_cidr_t *cidr)
                   1265: {
                   1266:     ngx_cidr_t  *c;
                   1267: 
                   1268:     if (ctx->proxies == NULL) {
                   1269:         ctx->proxies = ngx_array_create(ctx->pool, 4, sizeof(ngx_cidr_t));
                   1270:         if (ctx->proxies == NULL) {
                   1271:             return NGX_CONF_ERROR;
                   1272:         }
                   1273:     }
                   1274: 
                   1275:     c = ngx_array_push(ctx->proxies);
                   1276:     if (c == NULL) {
                   1277:         return NGX_CONF_ERROR;
                   1278:     }
                   1279: 
                   1280:     *c = *cidr;
                   1281: 
                   1282:     return NGX_CONF_OK;
                   1283: }
                   1284: 
                   1285: 
                   1286: static ngx_int_t
                   1287: ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr)
                   1288: {
                   1289:     ngx_int_t  rc;
                   1290: 
                   1291:     if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
                   1292:         cidr->family = AF_INET;
                   1293:         cidr->u.in.addr = 0xffffffff;
                   1294:         cidr->u.in.mask = 0xffffffff;
                   1295: 
                   1296:         return NGX_OK;
                   1297:     }
                   1298: 
                   1299:     rc = ngx_ptocidr(net, cidr);
                   1300: 
                   1301:     if (rc == NGX_ERROR) {
                   1302:         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net);
                   1303:         return NGX_ERROR;
                   1304:     }
                   1305: 
                   1306:     if (rc == NGX_DONE) {
                   1307:         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                   1308:                            "low address bits of %V are meaningless", net);
                   1309:     }
                   1310: 
                   1311:     return NGX_OK;
                   1312: }
                   1313: 
                   1314: 
                   1315: static char *
                   1316: ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
                   1317:     ngx_str_t *name)
                   1318: {
                   1319:     char       *rv;
                   1320:     ngx_str_t   file;
                   1321: 
                   1322:     file.len = name->len + 4;
                   1323:     file.data = ngx_pnalloc(ctx->temp_pool, name->len + 5);
                   1324:     if (file.data == NULL) {
                   1325:         return NGX_CONF_ERROR;
                   1326:     }
                   1327: 
                   1328:     ngx_sprintf(file.data, "%V.bin%Z", name);
                   1329: 
                   1330:     if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {
                   1331:         return NGX_CONF_ERROR;
                   1332:     }
                   1333: 
                   1334:     if (ctx->ranges) {
                   1335:         ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
                   1336: 
                   1337:         switch (ngx_http_geo_include_binary_base(cf, ctx, &file)) {
                   1338:         case NGX_OK:
                   1339:             return NGX_CONF_OK;
                   1340:         case NGX_ERROR:
                   1341:             return NGX_CONF_ERROR;
                   1342:         default:
                   1343:             break;
                   1344:         }
                   1345:     }
                   1346: 
                   1347:     file.len -= 4;
                   1348:     file.data[file.len] = '\0';
                   1349: 
                   1350:     ctx->include_name = file;
                   1351: 
                   1352:     if (ctx->outside_entries) {
                   1353:         ctx->allow_binary_include = 0;
                   1354:     }
                   1355: 
                   1356:     ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
                   1357: 
                   1358:     rv = ngx_conf_parse(cf, &file);
                   1359: 
                   1360:     ctx->includes++;
                   1361:     ctx->outside_entries = 0;
                   1362: 
                   1363:     return rv;
                   1364: }
                   1365: 
                   1366: 
                   1367: static ngx_int_t
                   1368: ngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
                   1369:     ngx_str_t *name)
                   1370: {
                   1371:     u_char                     *base, ch;
                   1372:     time_t                      mtime;
                   1373:     size_t                      size, len;
                   1374:     ssize_t                     n;
                   1375:     uint32_t                    crc32;
                   1376:     ngx_err_t                   err;
                   1377:     ngx_int_t                   rc;
                   1378:     ngx_uint_t                  i;
                   1379:     ngx_file_t                  file;
                   1380:     ngx_file_info_t             fi;
                   1381:     ngx_http_geo_range_t       *range, **ranges;
                   1382:     ngx_http_geo_header_t      *header;
                   1383:     ngx_http_variable_value_t  *vv;
                   1384: 
                   1385:     ngx_memzero(&file, sizeof(ngx_file_t));
                   1386:     file.name = *name;
                   1387:     file.log = cf->log;
                   1388: 
                   1389:     file.fd = ngx_open_file(name->data, NGX_FILE_RDONLY, 0, 0);
                   1390:     if (file.fd == NGX_INVALID_FILE) {
                   1391:         err = ngx_errno;
                   1392:         if (err != NGX_ENOENT) {
                   1393:             ngx_conf_log_error(NGX_LOG_CRIT, cf, err,
                   1394:                                ngx_open_file_n " \"%s\" failed", name->data);
                   1395:         }
                   1396:         return NGX_DECLINED;
                   1397:     }
                   1398: 
                   1399:     if (ctx->outside_entries) {
                   1400:         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                   1401:             "binary geo range base \"%s\" cannot be mixed with usual entries",
                   1402:             name->data);
                   1403:         rc = NGX_ERROR;
                   1404:         goto done;
                   1405:     }
                   1406: 
                   1407:     if (ctx->binary_include) {
                   1408:         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                   1409:             "second binary geo range base \"%s\" cannot be mixed with \"%s\"",
                   1410:             name->data, ctx->include_name.data);
                   1411:         rc = NGX_ERROR;
                   1412:         goto done;
                   1413:     }
                   1414: 
                   1415:     if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
                   1416:         ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
                   1417:                            ngx_fd_info_n " \"%s\" failed", name->data);
                   1418:         goto failed;
                   1419:     }
                   1420: 
                   1421:     size = (size_t) ngx_file_size(&fi);
                   1422:     mtime = ngx_file_mtime(&fi);
                   1423: 
                   1424:     ch = name->data[name->len - 4];
                   1425:     name->data[name->len - 4] = '\0';
                   1426: 
                   1427:     if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {
                   1428:         ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
                   1429:                            ngx_file_info_n " \"%s\" failed", name->data);
                   1430:         goto failed;
                   1431:     }
                   1432: 
                   1433:     name->data[name->len - 4] = ch;
                   1434: 
                   1435:     if (mtime < ngx_file_mtime(&fi)) {
                   1436:         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                   1437:                            "stale binary geo range base \"%s\"", name->data);
                   1438:         goto failed;
                   1439:     }
                   1440: 
                   1441:     base = ngx_palloc(ctx->pool, size);
                   1442:     if (base == NULL) {
                   1443:         goto failed;
                   1444:     }
                   1445: 
                   1446:     n = ngx_read_file(&file, base, size, 0);
                   1447: 
                   1448:     if (n == NGX_ERROR) {
                   1449:         ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
                   1450:                            ngx_read_file_n " \"%s\" failed", name->data);
                   1451:         goto failed;
                   1452:     }
                   1453: 
                   1454:     if ((size_t) n != size) {
                   1455:         ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,
                   1456:             ngx_read_file_n " \"%s\" returned only %z bytes instead of %z",
                   1457:             name->data, n, size);
                   1458:         goto failed;
                   1459:     }
                   1460: 
                   1461:     header = (ngx_http_geo_header_t *) base;
                   1462: 
                   1463:     if (size < 16 || ngx_memcmp(&ngx_http_geo_header, header, 12) != 0) {
                   1464:         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                   1465:              "incompatible binary geo range base \"%s\"", name->data);
                   1466:         goto failed;
                   1467:     }
                   1468: 
                   1469:     ngx_crc32_init(crc32);
                   1470: 
                   1471:     vv = (ngx_http_variable_value_t *) (base + sizeof(ngx_http_geo_header_t));
                   1472: 
                   1473:     while(vv->data) {
                   1474:         len = ngx_align(sizeof(ngx_http_variable_value_t) + vv->len,
                   1475:                         sizeof(void *));
                   1476:         ngx_crc32_update(&crc32, (u_char *) vv, len);
                   1477:         vv->data += (size_t) base;
                   1478:         vv = (ngx_http_variable_value_t *) ((u_char *) vv + len);
                   1479:     }
                   1480:     ngx_crc32_update(&crc32, (u_char *) vv, sizeof(ngx_http_variable_value_t));
                   1481:     vv++;
                   1482: 
                   1483:     ranges = (ngx_http_geo_range_t **) vv;
                   1484: 
                   1485:     for (i = 0; i < 0x10000; i++) {
                   1486:         ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *));
                   1487:         if (ranges[i]) {
                   1488:             ranges[i] = (ngx_http_geo_range_t *)
                   1489:                             ((u_char *) ranges[i] + (size_t) base);
                   1490:         }
                   1491:     }
                   1492: 
                   1493:     range = (ngx_http_geo_range_t *) &ranges[0x10000];
                   1494: 
                   1495:     while ((u_char *) range < base + size) {
                   1496:         while (range->value) {
                   1497:             ngx_crc32_update(&crc32, (u_char *) range,
                   1498:                              sizeof(ngx_http_geo_range_t));
                   1499:             range->value = (ngx_http_variable_value_t *)
                   1500:                                ((u_char *) range->value + (size_t) base);
                   1501:             range++;
                   1502:         }
                   1503:         ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *));
                   1504:         range = (ngx_http_geo_range_t *) ((u_char *) range + sizeof(void *));
                   1505:     }
                   1506: 
                   1507:     ngx_crc32_final(crc32);
                   1508: 
                   1509:     if (crc32 != header->crc32) {
                   1510:         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                   1511:                   "CRC32 mismatch in binary geo range base \"%s\"", name->data);
                   1512:         goto failed;
                   1513:     }
                   1514: 
                   1515:     ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0,
                   1516:                        "using binary geo range base \"%s\"", name->data);
                   1517: 
                   1518:     ctx->include_name = *name;
                   1519:     ctx->binary_include = 1;
                   1520:     ctx->high.low = ranges;
                   1521:     rc = NGX_OK;
                   1522: 
                   1523:     goto done;
                   1524: 
                   1525: failed:
                   1526: 
                   1527:     rc = NGX_DECLINED;
                   1528: 
                   1529: done:
                   1530: 
                   1531:     if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
                   1532:         ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
                   1533:                       ngx_close_file_n " \"%s\" failed", name->data);
                   1534:     }
                   1535: 
                   1536:     return rc;
                   1537: }
                   1538: 
                   1539: 
                   1540: static void
                   1541: ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx)
                   1542: {
                   1543:     u_char                              *p;
                   1544:     uint32_t                             hash;
                   1545:     ngx_str_t                            s;
                   1546:     ngx_uint_t                           i;
                   1547:     ngx_file_mapping_t                   fm;
                   1548:     ngx_http_geo_range_t                *r, *range, **ranges;
                   1549:     ngx_http_geo_header_t               *header;
                   1550:     ngx_http_geo_variable_value_node_t  *gvvn;
                   1551: 
                   1552:     fm.name = ngx_pnalloc(ctx->temp_pool, ctx->include_name.len + 5);
                   1553:     if (fm.name == NULL) {
                   1554:         return;
                   1555:     }
                   1556: 
                   1557:     ngx_sprintf(fm.name, "%V.bin%Z", &ctx->include_name);
                   1558: 
                   1559:     fm.size = ctx->data_size;
                   1560:     fm.log = ctx->pool->log;
                   1561: 
                   1562:     ngx_log_error(NGX_LOG_NOTICE, fm.log, 0,
                   1563:                   "creating binary geo range base \"%s\"", fm.name);
                   1564: 
                   1565:     if (ngx_create_file_mapping(&fm) != NGX_OK) {
                   1566:         return;
                   1567:     }
                   1568: 
                   1569:     p = ngx_cpymem(fm.addr, &ngx_http_geo_header,
                   1570:                    sizeof(ngx_http_geo_header_t));
                   1571: 
                   1572:     p = ngx_http_geo_copy_values(fm.addr, p, ctx->rbtree.root,
                   1573:                                  ctx->rbtree.sentinel);
                   1574: 
                   1575:     p += sizeof(ngx_http_variable_value_t);
                   1576: 
                   1577:     ranges = (ngx_http_geo_range_t **) p;
                   1578: 
                   1579:     p += 0x10000 * sizeof(ngx_http_geo_range_t *);
                   1580: 
                   1581:     for (i = 0; i < 0x10000; i++) {
                   1582:         r = ctx->high.low[i];
                   1583:         if (r == NULL) {
                   1584:             continue;
                   1585:         }
                   1586: 
                   1587:         range = (ngx_http_geo_range_t *) p;
                   1588:         ranges[i] = (ngx_http_geo_range_t *) (p - (u_char *) fm.addr);
                   1589: 
                   1590:         do {
                   1591:             s.len = r->value->len;
                   1592:             s.data = r->value->data;
                   1593:             hash = ngx_crc32_long(s.data, s.len);
                   1594:             gvvn = (ngx_http_geo_variable_value_node_t *)
                   1595:                         ngx_str_rbtree_lookup(&ctx->rbtree, &s, hash);
                   1596: 
                   1597:             range->value = (ngx_http_variable_value_t *) gvvn->offset;
                   1598:             range->start = r->start;
                   1599:             range->end = r->end;
                   1600:             range++;
                   1601: 
                   1602:         } while ((++r)->value);
                   1603: 
                   1604:         range->value = NULL;
                   1605: 
                   1606:         p = (u_char *) range + sizeof(void *);
                   1607:     }
                   1608: 
                   1609:     header = fm.addr;
                   1610:     header->crc32 = ngx_crc32_long((u_char *) fm.addr
                   1611:                                        + sizeof(ngx_http_geo_header_t),
                   1612:                                    fm.size - sizeof(ngx_http_geo_header_t));
                   1613: 
                   1614:     ngx_close_file_mapping(&fm);
                   1615: }
                   1616: 
                   1617: 
                   1618: static u_char *
                   1619: ngx_http_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node,
                   1620:     ngx_rbtree_node_t *sentinel)
                   1621: {
                   1622:     ngx_http_variable_value_t           *vv;
                   1623:     ngx_http_geo_variable_value_node_t  *gvvn;
                   1624: 
                   1625:     if (node == sentinel) {
                   1626:         return p;
                   1627:     }
                   1628: 
                   1629:     gvvn = (ngx_http_geo_variable_value_node_t *) node;
                   1630:     gvvn->offset = p - base;
                   1631: 
                   1632:     vv = (ngx_http_variable_value_t *) p;
                   1633:     *vv = *gvvn->value;
                   1634:     p += sizeof(ngx_http_variable_value_t);
                   1635:     vv->data = (u_char *) (p - base);
                   1636: 
                   1637:     p = ngx_cpymem(p, gvvn->sn.str.data, gvvn->sn.str.len);
                   1638: 
                   1639:     p = ngx_align_ptr(p, sizeof(void *));
                   1640: 
                   1641:     p = ngx_http_geo_copy_values(base, p, node->left, sentinel);
                   1642: 
                   1643:     return ngx_http_geo_copy_values(base, p, node->right, sentinel);
                   1644: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>