3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
8 #include <ngx_config.h>
14 ngx_http_variable_value_t
*value
;
17 } ngx_http_geo_range_t
;
21 ngx_radix_tree_t
*tree
;
23 ngx_radix_tree_t
*tree6
;
25 } ngx_http_geo_trees_t
;
29 ngx_http_geo_range_t
**low
;
30 ngx_http_variable_value_t
*default_value
;
31 } ngx_http_geo_high_ranges_t
;
36 ngx_http_variable_value_t
*value
;
38 } ngx_http_geo_variable_value_node_t
;
42 ngx_http_variable_value_t
*value
;
44 ngx_http_geo_high_ranges_t high
;
45 ngx_radix_tree_t
*tree
;
47 ngx_radix_tree_t
*tree6
;
50 ngx_rbtree_node_t sentinel
;
53 ngx_pool_t
*temp_pool
;
57 ngx_str_t include_name
;
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
;
71 ngx_http_geo_trees_t trees
;
72 ngx_http_geo_high_ranges_t high
;
76 unsigned proxy_recursive
:1;
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
,
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
,
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
,
104 static char *ngx_http_geo_include(ngx_conf_t
*cf
, ngx_http_geo_conf_ctx_t
*ctx
,
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
);
113 static ngx_command_t ngx_http_geo_commands
[] = {
116 NGX_HTTP_MAIN_CONF
|NGX_CONF_BLOCK
|NGX_CONF_TAKE12
,
118 NGX_HTTP_MAIN_CONF_OFFSET
,
126 static ngx_http_module_t ngx_http_geo_module_ctx
= {
127 NULL
, /* preconfiguration */
128 NULL
, /* postconfiguration */
130 NULL
, /* create main configuration */
131 NULL
, /* init main configuration */
133 NULL
, /* create server configuration */
134 NULL
, /* merge server configuration */
136 NULL
, /* create location configuration */
137 NULL
/* merge location configuration */
141 ngx_module_t ngx_http_geo_module
= {
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
163 } ngx_http_geo_header_t
;
166 static ngx_http_geo_header_t ngx_http_geo_header
= {
167 { 'G', 'E', 'O', 'R', 'N', 'G' }, 0, sizeof(void *), 0x12345678, 0
171 /* geo range is AF_INET only */
174 ngx_http_geo_cidr_variable(ngx_http_request_t
*r
, ngx_http_variable_value_t
*v
,
177 ngx_http_geo_ctx_t
*ctx
= (ngx_http_geo_ctx_t
*) data
;
181 struct sockaddr_in
*sin
;
182 ngx_http_variable_value_t
*vv
;
185 struct in6_addr
*inaddr6
;
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
);
194 switch (addr
.sockaddr
->sa_family
) {
198 inaddr6
= &((struct sockaddr_in6
*) addr
.sockaddr
)->sin6_addr
;
199 p
= inaddr6
->s6_addr
;
201 if (IN6_IS_ADDR_V4MAPPED(inaddr6
)) {
202 inaddr
= p
[12] << 24;
203 inaddr
+= p
[13] << 16;
204 inaddr
+= p
[14] << 8;
207 vv
= (ngx_http_variable_value_t
*)
208 ngx_radix32tree_find(ctx
->u
.trees
.tree
, inaddr
);
211 vv
= (ngx_http_variable_value_t
*)
212 ngx_radix128tree_find(ctx
->u
.trees
.tree6
, p
);
218 default: /* AF_INET */
219 sin
= (struct sockaddr_in
*) addr
.sockaddr
;
220 inaddr
= ntohl(sin
->sin_addr
.s_addr
);
222 vv
= (ngx_http_variable_value_t
*)
223 ngx_radix32tree_find(ctx
->u
.trees
.tree
, inaddr
);
232 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
240 ngx_http_geo_range_variable(ngx_http_request_t
*r
, ngx_http_variable_value_t
*v
,
243 ngx_http_geo_ctx_t
*ctx
= (ngx_http_geo_ctx_t
*) data
;
248 struct sockaddr_in
*sin
;
249 ngx_http_geo_range_t
*range
;
252 struct in6_addr
*inaddr6
;
255 *v
= *ctx
->u
.high
.default_value
;
257 if (ngx_http_geo_addr(r
, ctx
, &addr
) == NGX_OK
) {
259 switch (addr
.sockaddr
->sa_family
) {
263 inaddr6
= &((struct sockaddr_in6
*) addr
.sockaddr
)->sin6_addr
;
265 if (IN6_IS_ADDR_V4MAPPED(inaddr6
)) {
266 p
= inaddr6
->s6_addr
;
268 inaddr
= p
[12] << 24;
269 inaddr
+= p
[13] << 16;
270 inaddr
+= p
[14] << 8;
274 inaddr
= INADDR_NONE
;
280 default: /* AF_INET */
281 sin
= (struct sockaddr_in
*) addr
.sockaddr
;
282 inaddr
= ntohl(sin
->sin_addr
.s_addr
);
287 inaddr
= INADDR_NONE
;
290 if (ctx
->u
.high
.low
) {
291 range
= ctx
->u
.high
.low
[inaddr
>> 16];
296 if (n
>= (ngx_uint_t
) range
->start
297 && n
<= (ngx_uint_t
) range
->end
)
302 } while ((++range
)->value
);
306 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
314 ngx_http_geo_addr(ngx_http_request_t
*r
, ngx_http_geo_ctx_t
*ctx
,
319 if (ngx_http_geo_real_addr(r
, ctx
, addr
) != NGX_OK
) {
323 xfwd
= &r
->headers_in
.x_forwarded_for
;
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
);
335 ngx_http_geo_real_addr(ngx_http_request_t
*r
, ngx_http_geo_ctx_t
*ctx
,
338 ngx_http_variable_value_t
*v
;
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
);
344 addr
->sockaddr
= r
->connection
->sockaddr
;
345 addr
->socklen
= r
->connection
->socklen
;
346 /* addr->name = r->connection->addr_text; */
351 v
= ngx_http_get_flushed_variable(r
, ctx
->index
);
353 if (v
== NULL
|| v
->not_found
) {
354 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
355 "http geo not found");
360 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
361 "http geo started: %v", v
);
363 if (ngx_parse_addr(r
->pool
, addr
, v
->data
, v
->len
) == NGX_OK
) {
372 ngx_http_geo_block(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
)
376 ngx_str_t
*value
, name
;
381 ngx_http_variable_t
*var
;
382 ngx_http_geo_ctx_t
*geo
;
383 ngx_http_geo_conf_ctx_t ctx
;
385 static struct in6_addr zero
;
388 value
= cf
->args
->elts
;
390 geo
= ngx_palloc(cf
->pool
, sizeof(ngx_http_geo_ctx_t
));
392 return NGX_CONF_ERROR
;
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
;
406 if (cf
->args
->nelts
== 3) {
408 geo
->index
= ngx_http_get_variable_index(cf
, &name
);
409 if (geo
->index
== NGX_ERROR
) {
410 return NGX_CONF_ERROR
;
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
;
428 var
= ngx_http_add_variable(cf
, &name
, NGX_HTTP_VAR_CHANGEABLE
);
430 return NGX_CONF_ERROR
;
433 pool
= ngx_create_pool(NGX_DEFAULT_POOL_SIZE
, cf
->log
);
435 return NGX_CONF_ERROR
;
438 ngx_memzero(&ctx
, sizeof(ngx_http_geo_conf_ctx_t
));
440 ctx
.temp_pool
= ngx_create_pool(NGX_DEFAULT_POOL_SIZE
, cf
->log
);
441 if (ctx
.temp_pool
== NULL
) {
442 return NGX_CONF_ERROR
;
445 ngx_rbtree_init(&ctx
.rbtree
, &ctx
.sentinel
, ngx_str_rbtree_insert_value
);
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;
456 cf
->handler
= ngx_http_geo
;
457 cf
->handler_conf
= conf
;
459 rv
= ngx_conf_parse(cf
, NULL
);
463 geo
->proxies
= ctx
.proxies
;
464 geo
->proxy_recursive
= ctx
.proxy_recursive
;
468 if (ctx
.high
.low
&& !ctx
.binary_include
) {
469 for (i
= 0; i
< 0x10000; i
++) {
470 a
= (ngx_array_t
*) ctx
.high
.low
[i
];
472 if (a
== NULL
|| a
->nelts
== 0) {
476 len
= a
->nelts
* sizeof(ngx_http_geo_range_t
);
478 ctx
.high
.low
[i
] = ngx_palloc(cf
->pool
, len
+ sizeof(void *));
479 if (ctx
.high
.low
[i
] == NULL
) {
480 return NGX_CONF_ERROR
;
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 *);
488 if (ctx
.allow_binary_include
489 && !ctx
.outside_entries
490 && ctx
.entries
> 100000
491 && ctx
.includes
== 1)
493 ngx_http_geo_create_binary_base(&ctx
);
497 if (ctx
.high
.default_value
== NULL
) {
498 ctx
.high
.default_value
= &ngx_http_variable_null_value
;
501 geo
->u
.high
= ctx
.high
;
503 var
->get_handler
= ngx_http_geo_range_variable
;
504 var
->data
= (uintptr_t) geo
;
506 ngx_destroy_pool(ctx
.temp_pool
);
507 ngx_destroy_pool(pool
);
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
;
517 geo
->u
.trees
.tree
= ctx
.tree
;
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
;
527 geo
->u
.trees
.tree6
= ctx
.tree6
;
530 var
->get_handler
= ngx_http_geo_cidr_variable
;
531 var
->data
= (uintptr_t) geo
;
533 ngx_destroy_pool(ctx
.temp_pool
);
534 ngx_destroy_pool(pool
);
536 if (ngx_radix32tree_insert(ctx
.tree
, 0, 0,
537 (uintptr_t) &ngx_http_variable_null_value
)
540 return NGX_CONF_ERROR
;
543 /* NGX_BUSY is okay (default was set explicitly) */
546 if (ngx_radix128tree_insert(ctx
.tree6
, zero
.s6_addr
, zero
.s6_addr
,
547 (uintptr_t) &ngx_http_variable_null_value
)
550 return NGX_CONF_ERROR
;
560 ngx_http_geo(ngx_conf_t
*cf
, ngx_command_t
*dummy
, void *conf
)
565 ngx_http_geo_conf_ctx_t
*ctx
;
569 value
= cf
->args
->elts
;
571 if (cf
->args
->nelts
== 1) {
573 if (ngx_strcmp(value
[0].data
, "ranges") == 0) {
581 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
582 "the \"ranges\" directive must be "
583 "the first directive inside \"geo\" block");
594 else if (ngx_strcmp(value
[0].data
, "proxy_recursive") == 0) {
595 ctx
->proxy_recursive
= 1;
601 if (cf
->args
->nelts
!= 2) {
602 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
603 "invalid number of the geo parameters");
607 if (ngx_strcmp(value
[0].data
, "include") == 0) {
609 rv
= ngx_http_geo_include(cf
, ctx
, &value
[1]);
613 } else if (ngx_strcmp(value
[0].data
, "proxy") == 0) {
615 if (ngx_http_geo_cidr_value(cf
, &value
[1], &cidr
) != NGX_OK
) {
619 rv
= ngx_http_geo_add_proxy(cf
, ctx
, &cidr
);
625 rv
= ngx_http_geo_range(cf
, ctx
, value
);
628 rv
= ngx_http_geo_cidr(cf
, ctx
, value
);
633 ngx_reset_pool(cf
->pool
);
639 ngx_reset_pool(cf
->pool
);
641 return NGX_CONF_ERROR
;
646 ngx_http_geo_range(ngx_conf_t
*cf
, ngx_http_geo_conf_ctx_t
*ctx
,
650 in_addr_t start
, end
;
654 if (ngx_strcmp(value
[0].data
, "default") == 0) {
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
);
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
;
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
;
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
;
686 ctx
->outside_entries
= 1;
688 if (ngx_strcmp(value
[0].data
, "delete") == 0) {
697 last
= net
->data
+ net
->len
;
699 p
= ngx_strlchr(net
->data
, last
, '-');
705 start
= ngx_inet_addr(net
->data
, p
- net
->data
);
707 if (start
== INADDR_NONE
) {
711 start
= ntohl(start
);
715 end
= ngx_inet_addr(p
, last
- p
);
717 if (end
== INADDR_NONE
) {
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
);
736 ctx
->value
= ngx_http_geo_value(cf
, ctx
, &value
[1]);
738 if (ctx
->value
== NULL
) {
739 return NGX_CONF_ERROR
;
744 return ngx_http_geo_add_range(cf
, ctx
, start
, end
);
748 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0, "invalid range \"%V\"", net
);
750 return NGX_CONF_ERROR
;
754 /* the add procedure is optimized to add a growing up sequence */
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
)
761 ngx_uint_t h
, i
, s
, e
;
763 ngx_http_geo_range_t
*range
;
765 for (n
= start
; n
<= end
; n
= (n
+ 0x10000) & 0xffff0000) {
775 if ((n
| 0xffff) > end
) {
782 a
= (ngx_array_t
*) ctx
->high
.low
[h
];
785 a
= ngx_array_create(ctx
->temp_pool
, 64,
786 sizeof(ngx_http_geo_range_t
));
788 return NGX_CONF_ERROR
;
791 ctx
->high
.low
[h
] = (ngx_http_geo_range_t
*) a
;
801 if (e
< (ngx_uint_t
) range
[i
].start
) {
805 if (s
> (ngx_uint_t
) range
[i
].end
) {
807 /* add after the range */
809 range
= ngx_array_push(a
);
811 return NGX_CONF_ERROR
;
816 ngx_memmove(&range
[i
+ 2], &range
[i
+ 1],
817 (a
->nelts
- 2 - i
) * sizeof(ngx_http_geo_range_t
));
819 range
[i
+ 1].start
= (u_short
) s
;
820 range
[i
+ 1].end
= (u_short
) e
;
821 range
[i
+ 1].value
= ctx
->value
;
826 if (s
== (ngx_uint_t
) range
[i
].start
827 && e
== (ngx_uint_t
) range
[i
].end
)
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
);
833 range
[i
].value
= ctx
->value
;
838 if (s
> (ngx_uint_t
) range
[i
].start
839 && e
< (ngx_uint_t
) range
[i
].end
)
841 /* split the range and insert the new one */
843 range
= ngx_array_push(a
);
845 return NGX_CONF_ERROR
;
848 range
= ngx_array_push(a
);
850 return NGX_CONF_ERROR
;
855 ngx_memmove(&range
[i
+ 3], &range
[i
+ 1],
856 (a
->nelts
- 3 - i
) * sizeof(ngx_http_geo_range_t
));
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
;
862 range
[i
+ 1].start
= (u_short
) s
;
863 range
[i
+ 1].end
= (u_short
) e
;
864 range
[i
+ 1].value
= ctx
->value
;
866 range
[i
].end
= (u_short
) (s
- 1);
871 if (s
== (ngx_uint_t
) range
[i
].start
872 && e
< (ngx_uint_t
) range
[i
].end
)
874 /* shift the range start and insert the new range */
876 range
= ngx_array_push(a
);
878 return NGX_CONF_ERROR
;
883 ngx_memmove(&range
[i
+ 1], &range
[i
],
884 (a
->nelts
- 1 - i
) * sizeof(ngx_http_geo_range_t
));
886 range
[i
+ 1].start
= (u_short
) (e
+ 1);
888 range
[i
].start
= (u_short
) s
;
889 range
[i
].end
= (u_short
) e
;
890 range
[i
].value
= ctx
->value
;
895 if (s
> (ngx_uint_t
) range
[i
].start
896 && e
== (ngx_uint_t
) range
[i
].end
)
898 /* shift the range end and insert the new range */
900 range
= ngx_array_push(a
);
902 return NGX_CONF_ERROR
;
907 ngx_memmove(&range
[i
+ 2], &range
[i
+ 1],
908 (a
->nelts
- 2 - i
) * sizeof(ngx_http_geo_range_t
));
910 range
[i
+ 1].start
= (u_short
) s
;
911 range
[i
+ 1].end
= (u_short
) e
;
912 range
[i
+ 1].value
= ctx
->value
;
914 range
[i
].end
= (u_short
) (s
- 1);
919 s
= (ngx_uint_t
) range
[i
].start
;
920 e
= (ngx_uint_t
) range
[i
].end
;
922 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
923 "range \"%V\" overlaps \"%d.%d.%d.%d-%d.%d.%d.%d\"",
925 h
>> 8, h
& 0xff, s
>> 8, s
& 0xff,
926 h
>> 8, h
& 0xff, e
>> 8, e
& 0xff);
928 return NGX_CONF_ERROR
;
931 /* add the first range */
933 range
= ngx_array_push(a
);
935 return NGX_CONF_ERROR
;
938 range
->start
= (u_short
) s
;
939 range
->end
= (u_short
) e
;
940 range
->value
= ctx
->value
;
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
)
956 ngx_uint_t h
, i
, s
, e
, warn
;
958 ngx_http_geo_range_t
*range
;
962 for (n
= start
; n
<= end
; n
+= 0x10000) {
972 if ((n
| 0xffff) > end
) {
979 a
= (ngx_array_t
*) ctx
->high
.low
[h
];
987 for (i
= 0; i
< a
->nelts
; i
++) {
989 if (s
== (ngx_uint_t
) range
[i
].start
990 && e
== (ngx_uint_t
) range
[i
].end
)
992 ngx_memmove(&range
[i
], &range
[i
+ 1],
993 (a
->nelts
- 1 - i
) * sizeof(ngx_http_geo_range_t
));
1000 if (s
!= (ngx_uint_t
) range
[i
].start
1001 && e
!= (ngx_uint_t
) range
[i
].end
)
1015 ngx_http_geo_cidr(ngx_conf_t
*cf
, ngx_http_geo_conf_ctx_t
*ctx
,
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
;
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
;
1039 if (ngx_strcmp(value
[0].data
, "default") == 0) {
1040 cidr
.family
= AF_INET
;
1044 rv
= ngx_http_geo_cidr_add(cf
, ctx
, &cidr
, &value
[1], &value
[0]);
1046 if (rv
!= NGX_CONF_OK
) {
1050 #if (NGX_HAVE_INET6)
1051 cidr
.family
= AF_INET6
;
1052 ngx_memzero(&cidr
.u
.in6
, sizeof(ngx_in6_cidr_t
));
1054 rv
= ngx_http_geo_cidr_add(cf
, ctx
, &cidr
, &value
[1], &value
[0]);
1056 if (rv
!= NGX_CONF_OK
) {
1064 if (ngx_strcmp(value
[0].data
, "delete") == 0) {
1073 if (ngx_http_geo_cidr_value(cf
, net
, &cidr
) != NGX_OK
) {
1074 return NGX_CONF_ERROR
;
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
);
1083 switch (cidr
.family
) {
1085 #if (NGX_HAVE_INET6)
1087 rc
= ngx_radix128tree_delete(ctx
->tree6
,
1088 cidr
.u
.in6
.addr
.s6_addr
,
1089 cidr
.u
.in6
.mask
.s6_addr
);
1093 default: /* AF_INET */
1094 rc
= ngx_radix32tree_delete(ctx
->tree
, cidr
.u
.in
.addr
,
1100 ngx_conf_log_error(NGX_LOG_WARN
, cf
, 0,
1101 "no network \"%V\" to delete", net
);
1107 return ngx_http_geo_cidr_add(cf
, ctx
, &cidr
, &value
[1], net
);
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
)
1116 ngx_http_variable_value_t
*val
, *old
;
1118 val
= ngx_http_geo_value(cf
, ctx
, value
);
1121 return NGX_CONF_ERROR
;
1124 switch (cidr
->family
) {
1126 #if (NGX_HAVE_INET6)
1128 rc
= ngx_radix128tree_insert(ctx
->tree6
, cidr
->u
.in6
.addr
.s6_addr
,
1129 cidr
->u
.in6
.mask
.s6_addr
,
1136 if (rc
== NGX_ERROR
) {
1137 return NGX_CONF_ERROR
;
1140 /* rc == NGX_BUSY */
1142 old
= (ngx_http_variable_value_t
*)
1143 ngx_radix128tree_find(ctx
->tree6
,
1144 cidr
->u
.in6
.addr
.s6_addr
);
1146 ngx_conf_log_error(NGX_LOG_WARN
, cf
, 0,
1147 "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
1150 rc
= ngx_radix128tree_delete(ctx
->tree6
,
1151 cidr
->u
.in6
.addr
.s6_addr
,
1152 cidr
->u
.in6
.mask
.s6_addr
);
1154 if (rc
== NGX_ERROR
) {
1155 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0, "invalid radix tree");
1156 return NGX_CONF_ERROR
;
1159 rc
= ngx_radix128tree_insert(ctx
->tree6
, cidr
->u
.in6
.addr
.s6_addr
,
1160 cidr
->u
.in6
.mask
.s6_addr
,
1166 default: /* AF_INET */
1167 rc
= ngx_radix32tree_insert(ctx
->tree
, cidr
->u
.in
.addr
,
1168 cidr
->u
.in
.mask
, (uintptr_t) val
);
1174 if (rc
== NGX_ERROR
) {
1175 return NGX_CONF_ERROR
;
1178 /* rc == NGX_BUSY */
1180 old
= (ngx_http_variable_value_t
*)
1181 ngx_radix32tree_find(ctx
->tree
, cidr
->u
.in
.addr
);
1183 ngx_conf_log_error(NGX_LOG_WARN
, cf
, 0,
1184 "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
1187 rc
= ngx_radix32tree_delete(ctx
->tree
,
1188 cidr
->u
.in
.addr
, cidr
->u
.in
.mask
);
1190 if (rc
== NGX_ERROR
) {
1191 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0, "invalid radix tree");
1192 return NGX_CONF_ERROR
;
1195 rc
= ngx_radix32tree_insert(ctx
->tree
, cidr
->u
.in
.addr
,
1196 cidr
->u
.in
.mask
, (uintptr_t) val
);
1205 return NGX_CONF_ERROR
;
1209 static ngx_http_variable_value_t
*
1210 ngx_http_geo_value(ngx_conf_t
*cf
, ngx_http_geo_conf_ctx_t
*ctx
,
1214 ngx_http_variable_value_t
*val
;
1215 ngx_http_geo_variable_value_node_t
*gvvn
;
1217 hash
= ngx_crc32_long(value
->data
, value
->len
);
1219 gvvn
= (ngx_http_geo_variable_value_node_t
*)
1220 ngx_str_rbtree_lookup(&ctx
->rbtree
, value
, hash
);
1226 val
= ngx_palloc(ctx
->pool
, sizeof(ngx_http_variable_value_t
));
1231 val
->len
= value
->len
;
1232 val
->data
= ngx_pstrdup(ctx
->pool
, value
);
1233 if (val
->data
== NULL
) {
1238 val
->no_cacheable
= 0;
1241 gvvn
= ngx_palloc(ctx
->temp_pool
,
1242 sizeof(ngx_http_geo_variable_value_node_t
));
1247 gvvn
->sn
.node
.key
= hash
;
1248 gvvn
->sn
.str
.len
= val
->len
;
1249 gvvn
->sn
.str
.data
= val
->data
;
1253 ngx_rbtree_insert(&ctx
->rbtree
, &gvvn
->sn
.node
);
1255 ctx
->data_size
+= ngx_align(sizeof(ngx_http_variable_value_t
) + value
->len
,
1263 ngx_http_geo_add_proxy(ngx_conf_t
*cf
, ngx_http_geo_conf_ctx_t
*ctx
,
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
;
1275 c
= ngx_array_push(ctx
->proxies
);
1277 return NGX_CONF_ERROR
;
1287 ngx_http_geo_cidr_value(ngx_conf_t
*cf
, ngx_str_t
*net
, ngx_cidr_t
*cidr
)
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;
1299 rc
= ngx_ptocidr(net
, cidr
);
1301 if (rc
== NGX_ERROR
) {
1302 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0, "invalid network \"%V\"", net
);
1306 if (rc
== NGX_DONE
) {
1307 ngx_conf_log_error(NGX_LOG_WARN
, cf
, 0,
1308 "low address bits of %V are meaningless", net
);
1316 ngx_http_geo_include(ngx_conf_t
*cf
, ngx_http_geo_conf_ctx_t
*ctx
,
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
;
1328 ngx_sprintf(file
.data
, "%V.bin%Z", name
);
1330 if (ngx_conf_full_name(cf
->cycle
, &file
, 1) != NGX_OK
) {
1331 return NGX_CONF_ERROR
;
1335 ngx_log_debug1(NGX_LOG_DEBUG_CORE
, cf
->log
, 0, "include %s", file
.data
);
1337 switch (ngx_http_geo_include_binary_base(cf
, ctx
, &file
)) {
1341 return NGX_CONF_ERROR
;
1348 file
.data
[file
.len
] = '\0';
1350 ctx
->include_name
= file
;
1352 if (ctx
->outside_entries
) {
1353 ctx
->allow_binary_include
= 0;
1356 ngx_log_debug1(NGX_LOG_DEBUG_CORE
, cf
->log
, 0, "include %s", file
.data
);
1358 rv
= ngx_conf_parse(cf
, &file
);
1361 ctx
->outside_entries
= 0;
1368 ngx_http_geo_include_binary_base(ngx_conf_t
*cf
, ngx_http_geo_conf_ctx_t
*ctx
,
1381 ngx_http_geo_range_t
*range
, **ranges
;
1382 ngx_http_geo_header_t
*header
;
1383 ngx_http_variable_value_t
*vv
;
1385 ngx_memzero(&file
, sizeof(ngx_file_t
));
1389 file
.fd
= ngx_open_file(name
->data
, NGX_FILE_RDONLY
, 0, 0);
1390 if (file
.fd
== NGX_INVALID_FILE
) {
1392 if (err
!= NGX_ENOENT
) {
1393 ngx_conf_log_error(NGX_LOG_CRIT
, cf
, err
,
1394 ngx_open_file_n
" \"%s\" failed", name
->data
);
1396 return NGX_DECLINED
;
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",
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
);
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
);
1421 size
= (size_t) ngx_file_size(&fi
);
1422 mtime
= ngx_file_mtime(&fi
);
1424 ch
= name
->data
[name
->len
- 4];
1425 name
->data
[name
->len
- 4] = '\0';
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
);
1433 name
->data
[name
->len
- 4] = ch
;
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
);
1441 base
= ngx_palloc(ctx
->pool
, size
);
1446 n
= ngx_read_file(&file
, base
, size
, 0);
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
);
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
);
1461 header
= (ngx_http_geo_header_t
*) base
;
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
);
1469 ngx_crc32_init(crc32
);
1471 vv
= (ngx_http_variable_value_t
*) (base
+ sizeof(ngx_http_geo_header_t
));
1474 len
= ngx_align(sizeof(ngx_http_variable_value_t
) + vv
->len
,
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
);
1480 ngx_crc32_update(&crc32
, (u_char
*) vv
, sizeof(ngx_http_variable_value_t
));
1483 ranges
= (ngx_http_geo_range_t
**) vv
;
1485 for (i
= 0; i
< 0x10000; i
++) {
1486 ngx_crc32_update(&crc32
, (u_char
*) &ranges
[i
], sizeof(void *));
1488 ranges
[i
] = (ngx_http_geo_range_t
*)
1489 ((u_char
*) ranges
[i
] + (size_t) base
);
1493 range
= (ngx_http_geo_range_t
*) &ranges
[0x10000];
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
);
1503 ngx_crc32_update(&crc32
, (u_char
*) range
, sizeof(void *));
1504 range
= (ngx_http_geo_range_t
*) ((u_char
*) range
+ sizeof(void *));
1507 ngx_crc32_final(crc32
);
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
);
1515 ngx_conf_log_error(NGX_LOG_NOTICE
, cf
, 0,
1516 "using binary geo range base \"%s\"", name
->data
);
1518 ctx
->include_name
= *name
;
1519 ctx
->binary_include
= 1;
1520 ctx
->high
.low
= ranges
;
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
);
1541 ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t
*ctx
)
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
;
1552 fm
.name
= ngx_pnalloc(ctx
->temp_pool
, ctx
->include_name
.len
+ 5);
1553 if (fm
.name
== NULL
) {
1557 ngx_sprintf(fm
.name
, "%V.bin%Z", &ctx
->include_name
);
1559 fm
.size
= ctx
->data_size
;
1560 fm
.log
= ctx
->pool
->log
;
1562 ngx_log_error(NGX_LOG_NOTICE
, fm
.log
, 0,
1563 "creating binary geo range base \"%s\"", fm
.name
);
1565 if (ngx_create_file_mapping(&fm
) != NGX_OK
) {
1569 p
= ngx_cpymem(fm
.addr
, &ngx_http_geo_header
,
1570 sizeof(ngx_http_geo_header_t
));
1572 p
= ngx_http_geo_copy_values(fm
.addr
, p
, ctx
->rbtree
.root
,
1573 ctx
->rbtree
.sentinel
);
1575 p
+= sizeof(ngx_http_variable_value_t
);
1577 ranges
= (ngx_http_geo_range_t
**) p
;
1579 p
+= 0x10000 * sizeof(ngx_http_geo_range_t
*);
1581 for (i
= 0; i
< 0x10000; i
++) {
1582 r
= ctx
->high
.low
[i
];
1587 range
= (ngx_http_geo_range_t
*) p
;
1588 ranges
[i
] = (ngx_http_geo_range_t
*) (p
- (u_char
*) fm
.addr
);
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
);
1597 range
->value
= (ngx_http_variable_value_t
*) gvvn
->offset
;
1598 range
->start
= r
->start
;
1599 range
->end
= r
->end
;
1602 } while ((++r
)->value
);
1604 range
->value
= NULL
;
1606 p
= (u_char
*) range
+ sizeof(void *);
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
));
1614 ngx_close_file_mapping(&fm
);
1619 ngx_http_geo_copy_values(u_char
*base
, u_char
*p
, ngx_rbtree_node_t
*node
,
1620 ngx_rbtree_node_t
*sentinel
)
1622 ngx_http_variable_value_t
*vv
;
1623 ngx_http_geo_variable_value_node_t
*gvvn
;
1625 if (node
== sentinel
) {
1629 gvvn
= (ngx_http_geo_variable_value_node_t
*) node
;
1630 gvvn
->offset
= p
- base
;
1632 vv
= (ngx_http_variable_value_t
*) p
;
1634 p
+= sizeof(ngx_http_variable_value_t
);
1635 vv
->data
= (u_char
*) (p
- base
);
1637 p
= ngx_cpymem(p
, gvvn
->sn
.str
.data
, gvvn
->sn
.str
.len
);
1639 p
= ngx_align_ptr(p
, sizeof(void *));
1641 p
= ngx_http_geo_copy_values(base
, p
, node
->left
, sentinel
);
1643 return ngx_http_geo_copy_values(base
, p
, node
->right
, sentinel
);