Mercurial > hg > nginx-ranges
comparison src/http/modules/ngx_http_geo_module.c @ 635:e67b227c8dbb default tip
Merge with current.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Mon, 25 Apr 2011 04:07:55 +0400 |
parents | b4dcae568a2a |
children |
comparison
equal
deleted
inserted
replaced
578:f3a9e57d2e17 | 635:e67b227c8dbb |
---|---|
8 #include <ngx_core.h> | 8 #include <ngx_core.h> |
9 #include <ngx_http.h> | 9 #include <ngx_http.h> |
10 | 10 |
11 | 11 |
12 typedef struct { | 12 typedef struct { |
13 ngx_http_variable_value_t *value; | |
13 u_short start; | 14 u_short start; |
14 u_short end; | 15 u_short end; |
15 ngx_http_variable_value_t *value; | |
16 } ngx_http_geo_range_t; | 16 } ngx_http_geo_range_t; |
17 | 17 |
18 | 18 |
19 typedef struct { | 19 typedef struct { |
20 ngx_http_geo_range_t *ranges; | 20 ngx_http_geo_range_t **low; |
21 ngx_uint_t n; | |
22 } ngx_http_geo_low_ranges_t; | |
23 | |
24 | |
25 typedef struct { | |
26 ngx_http_geo_low_ranges_t low[0x10000]; | |
27 ngx_http_variable_value_t *default_value; | 21 ngx_http_variable_value_t *default_value; |
28 } ngx_http_geo_high_ranges_t; | 22 } ngx_http_geo_high_ranges_t; |
23 | |
24 | |
25 typedef struct { | |
26 ngx_str_node_t sn; | |
27 ngx_http_variable_value_t *value; | |
28 size_t offset; | |
29 } ngx_http_geo_variable_value_node_t; | |
29 | 30 |
30 | 31 |
31 typedef struct { | 32 typedef struct { |
32 ngx_http_variable_value_t *value; | 33 ngx_http_variable_value_t *value; |
33 ngx_str_t *net; | 34 ngx_str_t *net; |
34 ngx_http_geo_high_ranges_t *high; | 35 ngx_http_geo_high_ranges_t high; |
35 ngx_radix_tree_t *tree; | 36 ngx_radix_tree_t *tree; |
36 ngx_rbtree_t rbtree; | 37 ngx_rbtree_t rbtree; |
37 ngx_rbtree_node_t sentinel; | 38 ngx_rbtree_node_t sentinel; |
38 ngx_array_t *proxies; | 39 ngx_array_t *proxies; |
39 ngx_pool_t *pool; | 40 ngx_pool_t *pool; |
40 ngx_pool_t *temp_pool; | 41 ngx_pool_t *temp_pool; |
42 | |
43 size_t data_size; | |
44 | |
45 ngx_str_t include_name; | |
46 ngx_uint_t includes; | |
47 ngx_uint_t entries; | |
48 | |
49 unsigned ranges:1; | |
50 unsigned outside_entries:1; | |
51 unsigned allow_binary_include:1; | |
52 unsigned binary_include:1; | |
41 } ngx_http_geo_conf_ctx_t; | 53 } ngx_http_geo_conf_ctx_t; |
42 | 54 |
43 | 55 |
44 typedef struct { | 56 typedef struct { |
45 union { | 57 union { |
46 ngx_radix_tree_t *tree; | 58 ngx_radix_tree_t *tree; |
47 ngx_http_geo_high_ranges_t *high; | 59 ngx_http_geo_high_ranges_t high; |
48 } u; | 60 } u; |
49 | 61 |
50 ngx_array_t *proxies; | 62 ngx_array_t *proxies; |
51 | 63 |
52 ngx_int_t index; | 64 ngx_int_t index; |
71 ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value); | 83 ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value); |
72 static char *ngx_http_geo_add_proxy(ngx_conf_t *cf, | 84 static char *ngx_http_geo_add_proxy(ngx_conf_t *cf, |
73 ngx_http_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr); | 85 ngx_http_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr); |
74 static ngx_int_t ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, | 86 static ngx_int_t ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, |
75 ngx_cidr_t *cidr); | 87 ngx_cidr_t *cidr); |
88 static char *ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, | |
89 ngx_str_t *name); | |
90 static ngx_int_t ngx_http_geo_include_binary_base(ngx_conf_t *cf, | |
91 ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *name); | |
92 static void ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx); | |
93 static u_char *ngx_http_geo_copy_values(u_char *base, u_char *p, | |
94 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); | |
76 | 95 |
77 | 96 |
78 static ngx_command_t ngx_http_geo_commands[] = { | 97 static ngx_command_t ngx_http_geo_commands[] = { |
79 | 98 |
80 { ngx_string("geo"), | 99 { ngx_string("geo"), |
117 NULL, /* exit master */ | 136 NULL, /* exit master */ |
118 NGX_MODULE_V1_PADDING | 137 NGX_MODULE_V1_PADDING |
119 }; | 138 }; |
120 | 139 |
121 | 140 |
141 typedef struct { | |
142 u_char GEORNG[6]; | |
143 u_char version; | |
144 u_char ptr_size; | |
145 uint32_t endianess; | |
146 uint32_t crc32; | |
147 } ngx_http_geo_header_t; | |
148 | |
149 | |
150 static ngx_http_geo_header_t ngx_http_geo_header = { | |
151 { 'G', 'E', 'O', 'R', 'N', 'G' }, 0, sizeof(void *), 0x12345678, 0 | |
152 }; | |
153 | |
154 | |
122 /* AF_INET only */ | 155 /* AF_INET only */ |
123 | 156 |
124 static ngx_int_t | 157 static ngx_int_t |
125 ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, | 158 ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, |
126 uintptr_t data) | 159 uintptr_t data) |
146 uintptr_t data) | 179 uintptr_t data) |
147 { | 180 { |
148 ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data; | 181 ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data; |
149 | 182 |
150 in_addr_t addr; | 183 in_addr_t addr; |
151 ngx_uint_t i, n; | 184 ngx_uint_t n; |
152 ngx_http_geo_range_t *range; | 185 ngx_http_geo_range_t *range; |
153 | 186 |
154 *v = *ctx->u.high->default_value; | 187 *v = *ctx->u.high.default_value; |
155 | 188 |
156 addr = ngx_http_geo_addr(r, ctx); | 189 addr = ngx_http_geo_addr(r, ctx); |
157 | 190 |
158 range = ctx->u.high->low[addr >> 16].ranges; | 191 range = ctx->u.high.low[addr >> 16]; |
159 | 192 |
160 n = addr & 0xffff; | 193 if (range) { |
161 | 194 n = addr & 0xffff; |
162 for (i = 0; i < ctx->u.high->low[addr >> 16].n; i++) { | 195 do { |
163 if (n >= (ngx_uint_t) range[i].start | 196 if (n >= (ngx_uint_t) range->start && n <= (ngx_uint_t) range->end) |
164 && n <= (ngx_uint_t) range[i].end) | 197 { |
165 { | 198 *v = *range->value; |
166 *v = *range[i].value; | 199 break; |
167 } | 200 } |
201 } while ((++range)->value); | |
168 } | 202 } |
169 | 203 |
170 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | 204 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
171 "http geo: %v", v); | 205 "http geo: %v", v); |
172 | 206 |
254 | 288 |
255 static char * | 289 static char * |
256 ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | 290 ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) |
257 { | 291 { |
258 char *rv; | 292 char *rv; |
293 void **p; | |
259 size_t len; | 294 size_t len; |
260 ngx_str_t *value, name; | 295 ngx_str_t *value, name; |
261 ngx_uint_t i; | 296 ngx_uint_t i; |
262 ngx_conf_t save; | 297 ngx_conf_t save; |
263 ngx_pool_t *pool; | 298 ngx_pool_t *pool; |
300 pool = ngx_create_pool(16384, cf->log); | 335 pool = ngx_create_pool(16384, cf->log); |
301 if (pool == NULL) { | 336 if (pool == NULL) { |
302 return NGX_CONF_ERROR; | 337 return NGX_CONF_ERROR; |
303 } | 338 } |
304 | 339 |
340 ngx_memzero(&ctx, sizeof(ngx_http_geo_conf_ctx_t)); | |
341 | |
305 ctx.temp_pool = ngx_create_pool(16384, cf->log); | 342 ctx.temp_pool = ngx_create_pool(16384, cf->log); |
306 if (ctx.temp_pool == NULL) { | 343 if (ctx.temp_pool == NULL) { |
307 return NGX_CONF_ERROR; | 344 return NGX_CONF_ERROR; |
308 } | 345 } |
309 | 346 |
310 ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, | 347 ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, ngx_str_rbtree_insert_value); |
311 ngx_http_variable_value_rbtree_insert); | 348 |
312 | |
313 ctx.high = NULL; | |
314 ctx.tree = NULL; | |
315 ctx.proxies = NULL; | |
316 ctx.pool = cf->pool; | 349 ctx.pool = cf->pool; |
350 ctx.data_size = sizeof(ngx_http_geo_header_t) | |
351 + sizeof(ngx_http_variable_value_t) | |
352 + 0x10000 * sizeof(ngx_http_geo_range_t *); | |
353 ctx.allow_binary_include = 1; | |
317 | 354 |
318 save = *cf; | 355 save = *cf; |
319 cf->pool = pool; | 356 cf->pool = pool; |
320 cf->ctx = &ctx; | 357 cf->ctx = &ctx; |
321 cf->handler = ngx_http_geo; | 358 cf->handler = ngx_http_geo; |
325 | 362 |
326 *cf = save; | 363 *cf = save; |
327 | 364 |
328 geo->proxies = ctx.proxies; | 365 geo->proxies = ctx.proxies; |
329 | 366 |
330 if (ctx.high) { | 367 if (ctx.high.low) { |
331 | 368 |
332 for (i = 0; i < 0x10000; i++) { | 369 if (!ctx.binary_include) { |
333 a = (ngx_array_t *) ctx.high->low[i].ranges; | 370 for (i = 0; i < 0x10000; i++) { |
334 | 371 a = (ngx_array_t *) ctx.high.low[i]; |
335 if (a == NULL || a->nelts == 0) { | 372 |
336 continue; | 373 if (a == NULL || a->nelts == 0) { |
337 } | 374 continue; |
338 | 375 } |
339 ctx.high->low[i].n = a->nelts; | 376 |
340 | 377 len = a->nelts * sizeof(ngx_http_geo_range_t); |
341 len = a->nelts * sizeof(ngx_http_geo_range_t); | 378 |
342 | 379 ctx.high.low[i] = ngx_palloc(cf->pool, len + sizeof(void *)); |
343 ctx.high->low[i].ranges = ngx_palloc(cf->pool, len); | 380 if (ctx.high.low[i] == NULL) { |
344 if (ctx.high->low[i].ranges == NULL ){ | 381 return NGX_CONF_ERROR; |
345 return NGX_CONF_ERROR; | 382 } |
346 } | 383 |
347 | 384 p = (void **) ngx_cpymem(ctx.high.low[i], a->elts, len); |
348 ngx_memcpy(ctx.high->low[i].ranges, a->elts, len); | 385 *p = NULL; |
386 ctx.data_size += len + sizeof(void *); | |
387 } | |
388 | |
389 if (ctx.allow_binary_include | |
390 && !ctx.outside_entries | |
391 && ctx.entries > 100000 | |
392 && ctx.includes == 1) | |
393 { | |
394 ngx_http_geo_create_binary_base(&ctx); | |
395 } | |
349 } | 396 } |
350 | 397 |
351 geo->u.high = ctx.high; | 398 geo->u.high = ctx.high; |
352 | 399 |
353 var->get_handler = ngx_http_geo_range_variable; | 400 var->get_handler = ngx_http_geo_range_variable; |
354 var->data = (uintptr_t) geo; | 401 var->data = (uintptr_t) geo; |
355 | 402 |
403 if (ctx.high.default_value == NULL) { | |
404 ctx.high.default_value = &ngx_http_variable_null_value; | |
405 } | |
406 | |
356 ngx_destroy_pool(ctx.temp_pool); | 407 ngx_destroy_pool(ctx.temp_pool); |
357 ngx_destroy_pool(pool); | 408 ngx_destroy_pool(pool); |
358 | |
359 if (ctx.high->default_value == NULL) { | |
360 ctx.high->default_value = &ngx_http_variable_null_value; | |
361 } | |
362 | 409 |
363 } else { | 410 } else { |
364 if (ctx.tree == NULL) { | 411 if (ctx.tree == NULL) { |
365 ctx.tree = ngx_radix_tree_create(cf->pool, -1); | 412 ctx.tree = ngx_radix_tree_create(cf->pool, -1); |
366 if (ctx.tree == NULL) { | 413 if (ctx.tree == NULL) { |
394 | 441 |
395 static char * | 442 static char * |
396 ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) | 443 ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) |
397 { | 444 { |
398 char *rv; | 445 char *rv; |
399 ngx_str_t *value, file; | 446 ngx_str_t *value; |
400 ngx_cidr_t cidr; | 447 ngx_cidr_t cidr; |
401 ngx_http_geo_conf_ctx_t *ctx; | 448 ngx_http_geo_conf_ctx_t *ctx; |
402 | 449 |
403 ctx = cf->ctx; | 450 ctx = cf->ctx; |
404 | 451 |
413 "the \"ranges\" directive must be " | 460 "the \"ranges\" directive must be " |
414 "the first directive inside \"geo\" block"); | 461 "the first directive inside \"geo\" block"); |
415 goto failed; | 462 goto failed; |
416 } | 463 } |
417 | 464 |
418 ctx->high = ngx_pcalloc(ctx->pool, | 465 ctx->ranges = 1; |
419 sizeof(ngx_http_geo_high_ranges_t)); | |
420 if (ctx->high == NULL) { | |
421 goto failed; | |
422 } | |
423 | 466 |
424 rv = NGX_CONF_OK; | 467 rv = NGX_CONF_OK; |
425 | 468 |
426 goto done; | 469 goto done; |
427 } | 470 } |
433 goto failed; | 476 goto failed; |
434 } | 477 } |
435 | 478 |
436 if (ngx_strcmp(value[0].data, "include") == 0) { | 479 if (ngx_strcmp(value[0].data, "include") == 0) { |
437 | 480 |
438 file.len = value[1].len++; | 481 rv = ngx_http_geo_include(cf, ctx, &value[1]); |
439 | |
440 file.data = ngx_pstrdup(ctx->temp_pool, &value[1]); | |
441 if (file.data == NULL) { | |
442 goto failed; | |
443 } | |
444 | |
445 if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK){ | |
446 goto failed; | |
447 } | |
448 | |
449 ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); | |
450 | |
451 rv = ngx_conf_parse(cf, &file); | |
452 | 482 |
453 goto done; | 483 goto done; |
454 | 484 |
455 } else if (ngx_strcmp(value[0].data, "proxy") == 0) { | 485 } else if (ngx_strcmp(value[0].data, "proxy") == 0) { |
456 | 486 |
461 rv = ngx_http_geo_add_proxy(cf, ctx, &cidr); | 491 rv = ngx_http_geo_add_proxy(cf, ctx, &cidr); |
462 | 492 |
463 goto done; | 493 goto done; |
464 } | 494 } |
465 | 495 |
466 if (ctx->high) { | 496 if (ctx->ranges) { |
467 rv = ngx_http_geo_range(cf, ctx, value); | 497 rv = ngx_http_geo_range(cf, ctx, value); |
468 | 498 |
469 } else { | 499 } else { |
470 rv = ngx_http_geo_cidr(cf, ctx, value); | 500 rv = ngx_http_geo_cidr(cf, ctx, value); |
471 } | 501 } |
486 | 516 |
487 static char * | 517 static char * |
488 ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, | 518 ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, |
489 ngx_str_t *value) | 519 ngx_str_t *value) |
490 { | 520 { |
491 u_char *p, *last; | 521 u_char *p, *last; |
492 in_addr_t start, end; | 522 in_addr_t start, end; |
493 ngx_str_t *net; | 523 ngx_str_t *net; |
494 ngx_uint_t del; | 524 ngx_uint_t del; |
495 ngx_http_variable_value_t *old; | |
496 | 525 |
497 if (ngx_strcmp(value[0].data, "default") == 0) { | 526 if (ngx_strcmp(value[0].data, "default") == 0) { |
498 | 527 |
499 old = ctx->high->default_value; | 528 if (ctx->high.default_value) { |
500 | 529 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, |
501 ctx->high->default_value = ngx_http_geo_value(cf, ctx, &value[1]); | 530 "duplicate default geo range value: \"%V\", old value: \"%v\"", |
502 if (ctx->high->default_value == NULL) { | 531 &value[1], ctx->high.default_value); |
532 } | |
533 | |
534 ctx->high.default_value = ngx_http_geo_value(cf, ctx, &value[1]); | |
535 if (ctx->high.default_value == NULL) { | |
503 return NGX_CONF_ERROR; | 536 return NGX_CONF_ERROR; |
504 } | 537 } |
505 | 538 |
506 if (old) { | |
507 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | |
508 "duplicate range \"%V\", value: \"%v\", old value: \"%v\"", | |
509 &value[0], ctx->high->default_value, old); | |
510 } | |
511 | |
512 return NGX_CONF_OK; | 539 return NGX_CONF_OK; |
513 } | 540 } |
541 | |
542 if (ctx->binary_include) { | |
543 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
544 "binary geo range base \"%s\" may not be mixed with usual entries", | |
545 ctx->include_name.data); | |
546 return NGX_CONF_ERROR; | |
547 } | |
548 | |
549 if (ctx->high.low == NULL) { | |
550 ctx->high.low = ngx_pcalloc(ctx->pool, | |
551 0x10000 * sizeof(ngx_http_geo_range_t *)); | |
552 if (ctx->high.low == NULL) { | |
553 return NGX_CONF_ERROR; | |
554 } | |
555 } | |
556 | |
557 ctx->entries++; | |
558 ctx->outside_entries = 1; | |
514 | 559 |
515 if (ngx_strcmp(value[0].data, "delete") == 0) { | 560 if (ngx_strcmp(value[0].data, "delete") == 0) { |
516 net = &value[1]; | 561 net = &value[1]; |
517 del = 1; | 562 del = 1; |
518 | 563 |
604 | 649 |
605 } else { | 650 } else { |
606 e = 0xffff; | 651 e = 0xffff; |
607 } | 652 } |
608 | 653 |
609 a = (ngx_array_t *) ctx->high->low[h].ranges; | 654 a = (ngx_array_t *) ctx->high.low[h]; |
610 | 655 |
611 if (a == NULL) { | 656 if (a == NULL) { |
612 a = ngx_array_create(ctx->temp_pool, 64, | 657 a = ngx_array_create(ctx->temp_pool, 64, |
613 sizeof(ngx_http_geo_range_t)); | 658 sizeof(ngx_http_geo_range_t)); |
614 if (a == NULL) { | 659 if (a == NULL) { |
615 return NGX_CONF_ERROR; | 660 return NGX_CONF_ERROR; |
616 } | 661 } |
617 | 662 |
618 ctx->high->low[h].ranges = (ngx_http_geo_range_t *) a; | 663 ctx->high.low[h] = (ngx_http_geo_range_t *) a; |
619 } | 664 } |
620 | 665 |
621 i = a->nelts; | 666 i = a->nelts; |
622 range = a->elts; | 667 range = a->elts; |
623 | 668 |
638 return NGX_CONF_ERROR; | 683 return NGX_CONF_ERROR; |
639 } | 684 } |
640 | 685 |
641 range = a->elts; | 686 range = a->elts; |
642 | 687 |
643 ngx_memcpy(&range[i + 2], &range[i + 1], | 688 ngx_memmove(&range[i + 2], &range[i + 1], |
644 (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t)); | 689 (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t)); |
645 | 690 |
646 range[i + 1].start = (u_short) s; | 691 range[i + 1].start = (u_short) s; |
647 range[i + 1].end = (u_short) e; | 692 range[i + 1].end = (u_short) e; |
648 range[i + 1].value = ctx->value; | 693 range[i + 1].value = ctx->value; |
677 return NGX_CONF_ERROR; | 722 return NGX_CONF_ERROR; |
678 } | 723 } |
679 | 724 |
680 range = a->elts; | 725 range = a->elts; |
681 | 726 |
682 ngx_memcpy(&range[i + 3], &range[i + 1], | 727 ngx_memmove(&range[i + 3], &range[i + 1], |
683 (a->nelts - 3 - i) * sizeof(ngx_http_geo_range_t)); | 728 (a->nelts - 3 - i) * sizeof(ngx_http_geo_range_t)); |
684 | 729 |
685 range[i + 2].start = (u_short) (e + 1); | 730 range[i + 2].start = (u_short) (e + 1); |
686 range[i + 2].end = range[i].end; | 731 range[i + 2].end = range[i].end; |
687 range[i + 2].value = range[i].value; | 732 range[i + 2].value = range[i].value; |
705 return NGX_CONF_ERROR; | 750 return NGX_CONF_ERROR; |
706 } | 751 } |
707 | 752 |
708 range = a->elts; | 753 range = a->elts; |
709 | 754 |
710 ngx_memcpy(&range[i + 1], &range[i], | 755 ngx_memmove(&range[i + 1], &range[i], |
711 (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t)); | 756 (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t)); |
712 | 757 |
713 range[i + 1].start = (u_short) (e + 1); | 758 range[i + 1].start = (u_short) (e + 1); |
714 | 759 |
715 range[i].start = (u_short) s; | 760 range[i].start = (u_short) s; |
729 return NGX_CONF_ERROR; | 774 return NGX_CONF_ERROR; |
730 } | 775 } |
731 | 776 |
732 range = a->elts; | 777 range = a->elts; |
733 | 778 |
734 ngx_memcpy(&range[i + 2], &range[i + 1], | 779 ngx_memmove(&range[i + 2], &range[i + 1], |
735 (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t)); | 780 (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t)); |
736 | 781 |
737 range[i + 1].start = (u_short) s; | 782 range[i + 1].start = (u_short) s; |
738 range[i + 1].end = (u_short) e; | 783 range[i + 1].end = (u_short) e; |
739 range[i + 1].value = ctx->value; | 784 range[i + 1].value = ctx->value; |
801 | 846 |
802 } else { | 847 } else { |
803 e = 0xffff; | 848 e = 0xffff; |
804 } | 849 } |
805 | 850 |
806 a = (ngx_array_t *) ctx->high->low[h].ranges; | 851 a = (ngx_array_t *) ctx->high.low[h]; |
807 | 852 |
808 if (a == NULL) { | 853 if (a == NULL) { |
809 warn = 1; | 854 warn = 1; |
810 continue; | 855 continue; |
811 } | 856 } |
814 for (i = 0; i < a->nelts; i++) { | 859 for (i = 0; i < a->nelts; i++) { |
815 | 860 |
816 if (s == (ngx_uint_t) range[i].start | 861 if (s == (ngx_uint_t) range[i].start |
817 && e == (ngx_uint_t) range[i].end) | 862 && e == (ngx_uint_t) range[i].end) |
818 { | 863 { |
819 ngx_memcpy(&range[i], &range[i + 1], | 864 ngx_memmove(&range[i], &range[i + 1], |
820 (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t)); | 865 (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t)); |
821 | 866 |
822 a->nelts--; | 867 a->nelts--; |
823 | 868 |
824 break; | 869 break; |
927 | 972 |
928 static ngx_http_variable_value_t * | 973 static ngx_http_variable_value_t * |
929 ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, | 974 ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, |
930 ngx_str_t *value) | 975 ngx_str_t *value) |
931 { | 976 { |
932 uint32_t hash; | 977 uint32_t hash; |
933 ngx_http_variable_value_t *val; | 978 ngx_http_variable_value_t *val; |
934 ngx_http_variable_value_node_t *vvn; | 979 ngx_http_geo_variable_value_node_t *gvvn; |
935 | 980 |
936 hash = ngx_crc32_long(value->data, value->len); | 981 hash = ngx_crc32_long(value->data, value->len); |
937 | 982 |
938 val = ngx_http_variable_value_lookup(&ctx->rbtree, value, hash); | 983 gvvn = (ngx_http_geo_variable_value_node_t *) |
939 | 984 ngx_str_rbtree_lookup(&ctx->rbtree, value, hash); |
940 if (val) { | 985 |
941 return val; | 986 if (gvvn) { |
987 return gvvn->value; | |
942 } | 988 } |
943 | 989 |
944 val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t)); | 990 val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t)); |
945 if (val == NULL) { | 991 if (val == NULL) { |
946 return NULL; | 992 return NULL; |
954 | 1000 |
955 val->valid = 1; | 1001 val->valid = 1; |
956 val->no_cacheable = 0; | 1002 val->no_cacheable = 0; |
957 val->not_found = 0; | 1003 val->not_found = 0; |
958 | 1004 |
959 vvn = ngx_palloc(ctx->temp_pool, sizeof(ngx_http_variable_value_node_t)); | 1005 gvvn = ngx_palloc(ctx->temp_pool, |
960 if (vvn == NULL) { | 1006 sizeof(ngx_http_geo_variable_value_node_t)); |
1007 if (gvvn == NULL) { | |
961 return NULL; | 1008 return NULL; |
962 } | 1009 } |
963 | 1010 |
964 vvn->node.key = hash; | 1011 gvvn->sn.node.key = hash; |
965 vvn->len = val->len; | 1012 gvvn->sn.str.len = val->len; |
966 vvn->value = val; | 1013 gvvn->sn.str.data = val->data; |
967 | 1014 gvvn->value = val; |
968 ngx_rbtree_insert(&ctx->rbtree, &vvn->node); | 1015 gvvn->offset = 0; |
1016 | |
1017 ngx_rbtree_insert(&ctx->rbtree, &gvvn->sn.node); | |
1018 | |
1019 ctx->data_size += ngx_align(sizeof(ngx_http_variable_value_t) + value->len, | |
1020 sizeof(void *)); | |
969 | 1021 |
970 return val; | 1022 return val; |
971 } | 1023 } |
972 | 1024 |
973 | 1025 |
1028 cidr->u.in.addr = ntohl(cidr->u.in.addr); | 1080 cidr->u.in.addr = ntohl(cidr->u.in.addr); |
1029 cidr->u.in.mask = ntohl(cidr->u.in.mask); | 1081 cidr->u.in.mask = ntohl(cidr->u.in.mask); |
1030 | 1082 |
1031 return NGX_OK; | 1083 return NGX_OK; |
1032 } | 1084 } |
1085 | |
1086 | |
1087 static char * | |
1088 ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, | |
1089 ngx_str_t *name) | |
1090 { | |
1091 char *rv; | |
1092 ngx_str_t file; | |
1093 | |
1094 file.len = name->len + 4; | |
1095 file.data = ngx_pnalloc(ctx->temp_pool, name->len + 5); | |
1096 if (file.data == NULL) { | |
1097 return NGX_CONF_ERROR; | |
1098 } | |
1099 | |
1100 ngx_sprintf(file.data, "%V.bin%Z", name); | |
1101 | |
1102 if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) { | |
1103 return NGX_CONF_ERROR; | |
1104 } | |
1105 | |
1106 if (ctx->ranges) { | |
1107 ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); | |
1108 | |
1109 switch (ngx_http_geo_include_binary_base(cf, ctx, &file)) { | |
1110 case NGX_OK: | |
1111 return NGX_CONF_OK; | |
1112 case NGX_ERROR: | |
1113 return NGX_CONF_ERROR; | |
1114 default: | |
1115 break; | |
1116 } | |
1117 } | |
1118 | |
1119 file.len -= 4; | |
1120 file.data[file.len] = '\0'; | |
1121 | |
1122 ctx->include_name = file; | |
1123 | |
1124 if (ctx->outside_entries) { | |
1125 ctx->allow_binary_include = 0; | |
1126 } | |
1127 | |
1128 ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); | |
1129 | |
1130 rv = ngx_conf_parse(cf, &file); | |
1131 | |
1132 ctx->includes++; | |
1133 ctx->outside_entries = 0; | |
1134 | |
1135 return rv; | |
1136 } | |
1137 | |
1138 | |
1139 static ngx_int_t | |
1140 ngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, | |
1141 ngx_str_t *name) | |
1142 { | |
1143 u_char *base, ch; | |
1144 time_t mtime; | |
1145 size_t size, len; | |
1146 ssize_t n; | |
1147 uint32_t crc32; | |
1148 ngx_err_t err; | |
1149 ngx_int_t rc; | |
1150 ngx_uint_t i; | |
1151 ngx_file_t file; | |
1152 ngx_file_info_t fi; | |
1153 ngx_http_geo_range_t *range, **ranges; | |
1154 ngx_http_geo_header_t *header; | |
1155 ngx_http_variable_value_t *vv; | |
1156 | |
1157 ngx_memzero(&file, sizeof(ngx_file_t)); | |
1158 file.name = *name; | |
1159 file.log = cf->log; | |
1160 | |
1161 file.fd = ngx_open_file(name->data, NGX_FILE_RDONLY, 0, 0); | |
1162 if (file.fd == NGX_INVALID_FILE) { | |
1163 err = ngx_errno; | |
1164 if (err != NGX_ENOENT) { | |
1165 ngx_conf_log_error(NGX_LOG_CRIT, cf, err, | |
1166 ngx_open_file_n " \"%s\" failed", name->data); | |
1167 } | |
1168 return NGX_DECLINED; | |
1169 } | |
1170 | |
1171 if (ctx->outside_entries) { | |
1172 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
1173 "binary geo range base \"%s\" may not be mixed with usual entries", | |
1174 name->data); | |
1175 rc = NGX_ERROR; | |
1176 goto done; | |
1177 } | |
1178 | |
1179 if (ctx->binary_include) { | |
1180 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
1181 "second binary geo range base \"%s\" may not be mixed with \"%s\"", | |
1182 name->data, ctx->include_name.data); | |
1183 rc = NGX_ERROR; | |
1184 goto done; | |
1185 } | |
1186 | |
1187 if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) { | |
1188 ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, | |
1189 ngx_fd_info_n " \"%s\" failed", name->data); | |
1190 goto failed; | |
1191 } | |
1192 | |
1193 size = (size_t) ngx_file_size(&fi); | |
1194 mtime = ngx_file_mtime(&fi); | |
1195 | |
1196 ch = name->data[name->len - 4]; | |
1197 name->data[name->len - 4] = '\0'; | |
1198 | |
1199 if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) { | |
1200 ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, | |
1201 ngx_file_info_n " \"%s\" failed", name->data); | |
1202 goto failed; | |
1203 } | |
1204 | |
1205 name->data[name->len - 4] = ch; | |
1206 | |
1207 if (mtime < ngx_file_mtime(&fi)) { | |
1208 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | |
1209 "stale binary geo range base \"%s\"", name->data); | |
1210 goto failed; | |
1211 } | |
1212 | |
1213 base = ngx_palloc(ctx->pool, size); | |
1214 if (base == NULL) { | |
1215 goto failed; | |
1216 } | |
1217 | |
1218 n = ngx_read_file(&file, base, size, 0); | |
1219 | |
1220 if (n == NGX_ERROR) { | |
1221 ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, | |
1222 ngx_read_file_n " \"%s\" failed", name->data); | |
1223 goto failed; | |
1224 } | |
1225 | |
1226 if ((size_t) n != size) { | |
1227 ngx_conf_log_error(NGX_LOG_CRIT, cf, 0, | |
1228 ngx_read_file_n " \"%s\" returned only %z bytes instead of %z", | |
1229 name->data, n, size); | |
1230 goto failed; | |
1231 } | |
1232 | |
1233 header = (ngx_http_geo_header_t *) base; | |
1234 | |
1235 if (size < 16 || ngx_memcmp(&ngx_http_geo_header, header, 12) != 0) { | |
1236 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | |
1237 "incompatible binary geo range base \"%s\"", name->data); | |
1238 goto failed; | |
1239 } | |
1240 | |
1241 ngx_crc32_init(crc32); | |
1242 | |
1243 vv = (ngx_http_variable_value_t *) (base + sizeof(ngx_http_geo_header_t)); | |
1244 | |
1245 while(vv->data) { | |
1246 len = ngx_align(sizeof(ngx_http_variable_value_t) + vv->len, | |
1247 sizeof(void *)); | |
1248 ngx_crc32_update(&crc32, (u_char *) vv, len); | |
1249 vv->data += (size_t) base; | |
1250 vv = (ngx_http_variable_value_t *) ((u_char *) vv + len); | |
1251 } | |
1252 ngx_crc32_update(&crc32, (u_char *) vv, sizeof(ngx_http_variable_value_t)); | |
1253 vv++; | |
1254 | |
1255 ranges = (ngx_http_geo_range_t **) vv; | |
1256 | |
1257 for (i = 0; i < 0x10000; i++) { | |
1258 ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *)); | |
1259 if (ranges[i]) { | |
1260 ranges[i] = (ngx_http_geo_range_t *) | |
1261 ((u_char *) ranges[i] + (size_t) base); | |
1262 } | |
1263 } | |
1264 | |
1265 range = (ngx_http_geo_range_t *) &ranges[0x10000]; | |
1266 | |
1267 while ((u_char *) range < base + size) { | |
1268 while (range->value) { | |
1269 ngx_crc32_update(&crc32, (u_char *) range, | |
1270 sizeof(ngx_http_geo_range_t)); | |
1271 range->value = (ngx_http_variable_value_t *) | |
1272 ((u_char *) range->value + (size_t) base); | |
1273 range++; | |
1274 } | |
1275 ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *)); | |
1276 range = (ngx_http_geo_range_t *) ((u_char *) range + sizeof(void *)); | |
1277 } | |
1278 | |
1279 ngx_crc32_final(crc32); | |
1280 | |
1281 if (crc32 != header->crc32) { | |
1282 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | |
1283 "CRC32 mismatch in binary geo range base \"%s\"", name->data); | |
1284 goto failed; | |
1285 } | |
1286 | |
1287 ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0, | |
1288 "using binary geo range base \"%s\"", name->data); | |
1289 | |
1290 ctx->include_name = *name; | |
1291 ctx->binary_include = 1; | |
1292 ctx->high.low = ranges; | |
1293 rc = NGX_OK; | |
1294 | |
1295 goto done; | |
1296 | |
1297 failed: | |
1298 | |
1299 rc = NGX_DECLINED; | |
1300 | |
1301 done: | |
1302 | |
1303 if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { | |
1304 ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, | |
1305 ngx_close_file_n " \"%s\" failed", name->data); | |
1306 } | |
1307 | |
1308 return rc; | |
1309 } | |
1310 | |
1311 | |
1312 static void | |
1313 ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx) | |
1314 { | |
1315 u_char *p; | |
1316 uint32_t hash; | |
1317 ngx_str_t s; | |
1318 ngx_uint_t i; | |
1319 ngx_file_mapping_t fm; | |
1320 ngx_http_geo_range_t *r, *range, **ranges; | |
1321 ngx_http_geo_header_t *header; | |
1322 ngx_http_geo_variable_value_node_t *gvvn; | |
1323 | |
1324 fm.name = ngx_pnalloc(ctx->temp_pool, ctx->include_name.len + 5); | |
1325 if (fm.name == NULL) { | |
1326 return; | |
1327 } | |
1328 | |
1329 ngx_sprintf(fm.name, "%V.bin%Z", &ctx->include_name); | |
1330 | |
1331 fm.size = ctx->data_size; | |
1332 fm.log = ctx->pool->log; | |
1333 | |
1334 ngx_log_error(NGX_LOG_NOTICE, fm.log, 0, | |
1335 "creating binary geo range base \"%s\"", fm.name); | |
1336 | |
1337 if (ngx_create_file_mapping(&fm) != NGX_OK) { | |
1338 return; | |
1339 } | |
1340 | |
1341 p = ngx_cpymem(fm.addr, &ngx_http_geo_header, | |
1342 sizeof(ngx_http_geo_header_t)); | |
1343 | |
1344 p = ngx_http_geo_copy_values(fm.addr, p, ctx->rbtree.root, | |
1345 ctx->rbtree.sentinel); | |
1346 | |
1347 p += sizeof(ngx_http_variable_value_t); | |
1348 | |
1349 ranges = (ngx_http_geo_range_t **) p; | |
1350 | |
1351 p += 0x10000 * sizeof(ngx_http_geo_range_t *); | |
1352 | |
1353 for (i = 0; i < 0x10000; i++) { | |
1354 r = ctx->high.low[i]; | |
1355 if (r == NULL) { | |
1356 continue; | |
1357 } | |
1358 | |
1359 range = (ngx_http_geo_range_t *) p; | |
1360 ranges[i] = (ngx_http_geo_range_t *) (p - (u_char *) fm.addr); | |
1361 | |
1362 do { | |
1363 s.len = r->value->len; | |
1364 s.data = r->value->data; | |
1365 hash = ngx_crc32_long(s.data, s.len); | |
1366 gvvn = (ngx_http_geo_variable_value_node_t *) | |
1367 ngx_str_rbtree_lookup(&ctx->rbtree, &s, hash); | |
1368 | |
1369 range->value = (ngx_http_variable_value_t *) gvvn->offset; | |
1370 range->start = r->start; | |
1371 range->end = r->end; | |
1372 range++; | |
1373 | |
1374 } while ((++r)->value); | |
1375 | |
1376 range->value = NULL; | |
1377 | |
1378 p = (u_char *) range + sizeof(void *); | |
1379 } | |
1380 | |
1381 header = fm.addr; | |
1382 header->crc32 = ngx_crc32_long((u_char *) fm.addr | |
1383 + sizeof(ngx_http_geo_header_t), | |
1384 fm.size - sizeof(ngx_http_geo_header_t)); | |
1385 | |
1386 ngx_close_file_mapping(&fm); | |
1387 } | |
1388 | |
1389 | |
1390 static u_char * | |
1391 ngx_http_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node, | |
1392 ngx_rbtree_node_t *sentinel) | |
1393 { | |
1394 ngx_http_variable_value_t *vv; | |
1395 ngx_http_geo_variable_value_node_t *gvvn; | |
1396 | |
1397 if (node == sentinel) { | |
1398 return p; | |
1399 } | |
1400 | |
1401 gvvn = (ngx_http_geo_variable_value_node_t *) node; | |
1402 gvvn->offset = p - base; | |
1403 | |
1404 vv = (ngx_http_variable_value_t *) p; | |
1405 *vv = *gvvn->value; | |
1406 p += sizeof(ngx_http_variable_value_t); | |
1407 vv->data = (u_char *) (p - base); | |
1408 | |
1409 p = ngx_cpymem(p, gvvn->sn.str.data, gvvn->sn.str.len); | |
1410 | |
1411 p = ngx_align_ptr(p, sizeof(void *)); | |
1412 | |
1413 p = ngx_http_geo_copy_values(base, p, node->left, sentinel); | |
1414 | |
1415 return ngx_http_geo_copy_values(base, p, node->right, sentinel); | |
1416 } |