Annotation of embedaddon/nginx/src/http/modules/ngx_http_geo_module.c, revision 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>