3 * Copyright (C) Igor Sysoev
7 #include <ngx_config.h>
13 ngx_http_variable_value_t
*value
;
16 } ngx_http_geo_range_t
;
20 ngx_http_geo_range_t
**low
;
21 ngx_http_variable_value_t
*default_value
;
22 } ngx_http_geo_high_ranges_t
;
27 ngx_http_variable_value_t
*value
;
29 } ngx_http_geo_variable_value_node_t
;
33 ngx_http_variable_value_t
*value
;
35 ngx_http_geo_high_ranges_t high
;
36 ngx_radix_tree_t
*tree
;
38 ngx_rbtree_node_t sentinel
;
41 ngx_pool_t
*temp_pool
;
45 ngx_str_t include_name
;
50 unsigned outside_entries
:1;
51 unsigned allow_binary_include
:1;
52 unsigned binary_include
:1;
53 } ngx_http_geo_conf_ctx_t
;
58 ngx_radix_tree_t
*tree
;
59 ngx_http_geo_high_ranges_t high
;
68 static in_addr_t
ngx_http_geo_addr(ngx_http_request_t
*r
,
69 ngx_http_geo_ctx_t
*ctx
);
70 static in_addr_t
ngx_http_geo_real_addr(ngx_http_request_t
*r
,
71 ngx_http_geo_ctx_t
*ctx
);
72 static char *ngx_http_geo_block(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
);
73 static char *ngx_http_geo(ngx_conf_t
*cf
, ngx_command_t
*dummy
, void *conf
);
74 static char *ngx_http_geo_range(ngx_conf_t
*cf
, ngx_http_geo_conf_ctx_t
*ctx
,
76 static char *ngx_http_geo_add_range(ngx_conf_t
*cf
,
77 ngx_http_geo_conf_ctx_t
*ctx
, in_addr_t start
, in_addr_t end
);
78 static ngx_uint_t
ngx_http_geo_delete_range(ngx_conf_t
*cf
,
79 ngx_http_geo_conf_ctx_t
*ctx
, in_addr_t start
, in_addr_t end
);
80 static char *ngx_http_geo_cidr(ngx_conf_t
*cf
, ngx_http_geo_conf_ctx_t
*ctx
,
82 static ngx_http_variable_value_t
*ngx_http_geo_value(ngx_conf_t
*cf
,
83 ngx_http_geo_conf_ctx_t
*ctx
, ngx_str_t
*value
);
84 static char *ngx_http_geo_add_proxy(ngx_conf_t
*cf
,
85 ngx_http_geo_conf_ctx_t
*ctx
, ngx_cidr_t
*cidr
);
86 static ngx_int_t
ngx_http_geo_cidr_value(ngx_conf_t
*cf
, ngx_str_t
*net
,
88 static char *ngx_http_geo_include(ngx_conf_t
*cf
, ngx_http_geo_conf_ctx_t
*ctx
,
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
);
97 static ngx_command_t ngx_http_geo_commands
[] = {
100 NGX_HTTP_MAIN_CONF
|NGX_CONF_BLOCK
|NGX_CONF_TAKE12
,
102 NGX_HTTP_MAIN_CONF_OFFSET
,
110 static ngx_http_module_t ngx_http_geo_module_ctx
= {
111 NULL
, /* preconfiguration */
112 NULL
, /* postconfiguration */
114 NULL
, /* create main configuration */
115 NULL
, /* init main configuration */
117 NULL
, /* create server configuration */
118 NULL
, /* merge server configuration */
120 NULL
, /* create location configuration */
121 NULL
/* merge location configuration */
125 ngx_module_t ngx_http_geo_module
= {
127 &ngx_http_geo_module_ctx
, /* module context */
128 ngx_http_geo_commands
, /* module directives */
129 NGX_HTTP_MODULE
, /* module type */
130 NULL
, /* init master */
131 NULL
, /* init module */
132 NULL
, /* init process */
133 NULL
, /* init thread */
134 NULL
, /* exit thread */
135 NULL
, /* exit process */
136 NULL
, /* exit master */
137 NGX_MODULE_V1_PADDING
147 } ngx_http_geo_header_t
;
150 static ngx_http_geo_header_t ngx_http_geo_header
= {
151 { 'G', 'E', 'O', 'R', 'N', 'G' }, 0, sizeof(void *), 0x12345678, 0
158 ngx_http_geo_cidr_variable(ngx_http_request_t
*r
, ngx_http_variable_value_t
*v
,
161 ngx_http_geo_ctx_t
*ctx
= (ngx_http_geo_ctx_t
*) data
;
163 ngx_http_variable_value_t
*vv
;
165 vv
= (ngx_http_variable_value_t
*)
166 ngx_radix32tree_find(ctx
->u
.tree
, ngx_http_geo_addr(r
, ctx
));
170 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
178 ngx_http_geo_range_variable(ngx_http_request_t
*r
, ngx_http_variable_value_t
*v
,
181 ngx_http_geo_ctx_t
*ctx
= (ngx_http_geo_ctx_t
*) data
;
185 ngx_http_geo_range_t
*range
;
187 *v
= *ctx
->u
.high
.default_value
;
189 addr
= ngx_http_geo_addr(r
, ctx
);
191 range
= ctx
->u
.high
.low
[addr
>> 16];
196 if (n
>= (ngx_uint_t
) range
->start
&& n
<= (ngx_uint_t
) range
->end
)
201 } while ((++range
)->value
);
204 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
212 ngx_http_geo_addr(ngx_http_request_t
*r
, ngx_http_geo_ctx_t
*ctx
)
218 ngx_in_cidr_t
*proxies
;
219 ngx_table_elt_t
*xfwd
;
221 addr
= ngx_http_geo_real_addr(r
, ctx
);
223 xfwd
= r
->headers_in
.x_forwarded_for
;
225 if (xfwd
== NULL
|| ctx
->proxies
== NULL
) {
229 proxies
= ctx
->proxies
->elts
;
230 n
= ctx
->proxies
->nelts
;
232 for (i
= 0; i
< n
; i
++) {
233 if ((addr
& proxies
[i
].mask
) == proxies
[i
].addr
) {
235 len
= xfwd
->value
.len
;
236 ip
= xfwd
->value
.data
;
238 for (p
= ip
+ len
- 1; p
> ip
; p
--) {
239 if (*p
== ' ' || *p
== ',') {
247 return ntohl(ngx_inet_addr(ip
, len
));
256 ngx_http_geo_real_addr(ngx_http_request_t
*r
, ngx_http_geo_ctx_t
*ctx
)
258 struct sockaddr_in
*sin
;
259 ngx_http_variable_value_t
*v
;
263 struct sockaddr_in6
*sin6
;
266 if (ctx
->index
== -1) {
267 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
268 "http geo started: %V", &r
->connection
->addr_text
);
270 switch (r
->connection
->sockaddr
->sa_family
) {
273 sin
= (struct sockaddr_in
*) r
->connection
->sockaddr
;
274 return ntohl(sin
->sin_addr
.s_addr
);
279 sin6
= (struct sockaddr_in6
*) r
->connection
->sockaddr
;
281 if (IN6_IS_ADDR_V4MAPPED(&sin6
->sin6_addr
)) {
282 p
= sin6
->sin6_addr
.s6_addr
;
297 v
= ngx_http_get_flushed_variable(r
, ctx
->index
);
299 if (v
== NULL
|| v
->not_found
) {
300 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
301 "http geo not found");
306 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
307 "http geo started: %v", v
);
309 return ntohl(ngx_inet_addr(v
->data
, v
->len
));
314 ngx_http_geo_block(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
)
319 ngx_str_t
*value
, name
;
324 ngx_http_variable_t
*var
;
325 ngx_http_geo_ctx_t
*geo
;
326 ngx_http_geo_conf_ctx_t ctx
;
328 value
= cf
->args
->elts
;
330 geo
= ngx_palloc(cf
->pool
, sizeof(ngx_http_geo_ctx_t
));
332 return NGX_CONF_ERROR
;
339 if (cf
->args
->nelts
== 3) {
341 geo
->index
= ngx_http_get_variable_index(cf
, &name
);
342 if (geo
->index
== NGX_ERROR
) {
343 return NGX_CONF_ERROR
;
354 var
= ngx_http_add_variable(cf
, &name
, NGX_HTTP_VAR_CHANGEABLE
);
356 return NGX_CONF_ERROR
;
359 pool
= ngx_create_pool(16384, cf
->log
);
361 return NGX_CONF_ERROR
;
364 ngx_memzero(&ctx
, sizeof(ngx_http_geo_conf_ctx_t
));
366 ctx
.temp_pool
= ngx_create_pool(16384, cf
->log
);
367 if (ctx
.temp_pool
== NULL
) {
368 return NGX_CONF_ERROR
;
371 ngx_rbtree_init(&ctx
.rbtree
, &ctx
.sentinel
, ngx_str_rbtree_insert_value
);
374 ctx
.data_size
= sizeof(ngx_http_geo_header_t
)
375 + sizeof(ngx_http_variable_value_t
)
376 + 0x10000 * sizeof(ngx_http_geo_range_t
*);
377 ctx
.allow_binary_include
= 1;
382 cf
->handler
= ngx_http_geo
;
383 cf
->handler_conf
= conf
;
385 rv
= ngx_conf_parse(cf
, NULL
);
389 geo
->proxies
= ctx
.proxies
;
393 if (!ctx
.binary_include
) {
394 for (i
= 0; i
< 0x10000; i
++) {
395 a
= (ngx_array_t
*) ctx
.high
.low
[i
];
397 if (a
== NULL
|| a
->nelts
== 0) {
401 len
= a
->nelts
* sizeof(ngx_http_geo_range_t
);
403 ctx
.high
.low
[i
] = ngx_palloc(cf
->pool
, len
+ sizeof(void *));
404 if (ctx
.high
.low
[i
] == NULL
) {
405 return NGX_CONF_ERROR
;
408 p
= (void **) ngx_cpymem(ctx
.high
.low
[i
], a
->elts
, len
);
410 ctx
.data_size
+= len
+ sizeof(void *);
413 if (ctx
.allow_binary_include
414 && !ctx
.outside_entries
415 && ctx
.entries
> 100000
416 && ctx
.includes
== 1)
418 ngx_http_geo_create_binary_base(&ctx
);
422 geo
->u
.high
= ctx
.high
;
424 var
->get_handler
= ngx_http_geo_range_variable
;
425 var
->data
= (uintptr_t) geo
;
427 if (ctx
.high
.default_value
== NULL
) {
428 ctx
.high
.default_value
= &ngx_http_variable_null_value
;
431 ngx_destroy_pool(ctx
.temp_pool
);
432 ngx_destroy_pool(pool
);
435 if (ctx
.tree
== NULL
) {
436 ctx
.tree
= ngx_radix_tree_create(cf
->pool
, -1);
437 if (ctx
.tree
== NULL
) {
438 return NGX_CONF_ERROR
;
442 geo
->u
.tree
= ctx
.tree
;
444 var
->get_handler
= ngx_http_geo_cidr_variable
;
445 var
->data
= (uintptr_t) geo
;
447 ngx_destroy_pool(ctx
.temp_pool
);
448 ngx_destroy_pool(pool
);
450 if (ngx_radix32tree_find(ctx
.tree
, 0) != NGX_RADIX_NO_VALUE
) {
454 if (ngx_radix32tree_insert(ctx
.tree
, 0, 0,
455 (uintptr_t) &ngx_http_variable_null_value
)
458 return NGX_CONF_ERROR
;
467 ngx_http_geo(ngx_conf_t
*cf
, ngx_command_t
*dummy
, void *conf
)
472 ngx_http_geo_conf_ctx_t
*ctx
;
476 value
= cf
->args
->elts
;
478 if (cf
->args
->nelts
== 1) {
480 if (ngx_strcmp(value
[0].data
, "ranges") == 0) {
483 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
484 "the \"ranges\" directive must be "
485 "the first directive inside \"geo\" block");
497 if (cf
->args
->nelts
!= 2) {
498 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
499 "invalid number of the geo parameters");
503 if (ngx_strcmp(value
[0].data
, "include") == 0) {
505 rv
= ngx_http_geo_include(cf
, ctx
, &value
[1]);
509 } else if (ngx_strcmp(value
[0].data
, "proxy") == 0) {
511 if (ngx_http_geo_cidr_value(cf
, &value
[1], &cidr
) != NGX_OK
) {
515 rv
= ngx_http_geo_add_proxy(cf
, ctx
, &cidr
);
521 rv
= ngx_http_geo_range(cf
, ctx
, value
);
524 rv
= ngx_http_geo_cidr(cf
, ctx
, value
);
529 ngx_reset_pool(cf
->pool
);
535 ngx_reset_pool(cf
->pool
);
537 return NGX_CONF_ERROR
;
542 ngx_http_geo_range(ngx_conf_t
*cf
, ngx_http_geo_conf_ctx_t
*ctx
,
546 in_addr_t start
, end
;
550 if (ngx_strcmp(value
[0].data
, "default") == 0) {
552 if (ctx
->high
.default_value
) {
553 ngx_conf_log_error(NGX_LOG_WARN
, cf
, 0,
554 "duplicate default geo range value: \"%V\", old value: \"%v\"",
555 &value
[1], ctx
->high
.default_value
);
558 ctx
->high
.default_value
= ngx_http_geo_value(cf
, ctx
, &value
[1]);
559 if (ctx
->high
.default_value
== NULL
) {
560 return NGX_CONF_ERROR
;
566 if (ctx
->binary_include
) {
567 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
568 "binary geo range base \"%s\" may not be mixed with usual entries",
569 ctx
->include_name
.data
);
570 return NGX_CONF_ERROR
;
573 if (ctx
->high
.low
== NULL
) {
574 ctx
->high
.low
= ngx_pcalloc(ctx
->pool
,
575 0x10000 * sizeof(ngx_http_geo_range_t
*));
576 if (ctx
->high
.low
== NULL
) {
577 return NGX_CONF_ERROR
;
582 ctx
->outside_entries
= 1;
584 if (ngx_strcmp(value
[0].data
, "delete") == 0) {
593 last
= net
->data
+ net
->len
;
595 p
= ngx_strlchr(net
->data
, last
, '-');
601 start
= ngx_inet_addr(net
->data
, p
- net
->data
);
603 if (start
== INADDR_NONE
) {
607 start
= ntohl(start
);
611 end
= ngx_inet_addr(p
, last
- p
);
613 if (end
== INADDR_NONE
) {
624 if (ngx_http_geo_delete_range(cf
, ctx
, start
, end
)) {
625 ngx_conf_log_error(NGX_LOG_WARN
, cf
, 0,
626 "no address range \"%V\" to delete", net
);
632 ctx
->value
= ngx_http_geo_value(cf
, ctx
, &value
[1]);
634 if (ctx
->value
== NULL
) {
635 return NGX_CONF_ERROR
;
640 return ngx_http_geo_add_range(cf
, ctx
, start
, end
);
644 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0, "invalid range \"%V\"", net
);
646 return NGX_CONF_ERROR
;
650 /* the add procedure is optimized to add a growing up sequence */
653 ngx_http_geo_add_range(ngx_conf_t
*cf
, ngx_http_geo_conf_ctx_t
*ctx
,
654 in_addr_t start
, in_addr_t end
)
657 ngx_uint_t h
, i
, s
, e
;
659 ngx_http_geo_range_t
*range
;
661 for (n
= start
; n
<= end
; n
= (n
+ 0x10000) & 0xffff0000) {
671 if ((n
| 0xffff) > end
) {
678 a
= (ngx_array_t
*) ctx
->high
.low
[h
];
681 a
= ngx_array_create(ctx
->temp_pool
, 64,
682 sizeof(ngx_http_geo_range_t
));
684 return NGX_CONF_ERROR
;
687 ctx
->high
.low
[h
] = (ngx_http_geo_range_t
*) a
;
697 if (e
< (ngx_uint_t
) range
[i
].start
) {
701 if (s
> (ngx_uint_t
) range
[i
].end
) {
703 /* add after the range */
705 range
= ngx_array_push(a
);
707 return NGX_CONF_ERROR
;
712 ngx_memmove(&range
[i
+ 2], &range
[i
+ 1],
713 (a
->nelts
- 2 - i
) * sizeof(ngx_http_geo_range_t
));
715 range
[i
+ 1].start
= (u_short
) s
;
716 range
[i
+ 1].end
= (u_short
) e
;
717 range
[i
+ 1].value
= ctx
->value
;
722 if (s
== (ngx_uint_t
) range
[i
].start
723 && e
== (ngx_uint_t
) range
[i
].end
)
725 ngx_conf_log_error(NGX_LOG_WARN
, cf
, 0,
726 "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
727 ctx
->net
, ctx
->value
, range
[i
].value
);
729 range
[i
].value
= ctx
->value
;
734 if (s
> (ngx_uint_t
) range
[i
].start
735 && e
< (ngx_uint_t
) range
[i
].end
)
737 /* split the range and insert the new one */
739 range
= ngx_array_push(a
);
741 return NGX_CONF_ERROR
;
744 range
= ngx_array_push(a
);
746 return NGX_CONF_ERROR
;
751 ngx_memmove(&range
[i
+ 3], &range
[i
+ 1],
752 (a
->nelts
- 3 - i
) * sizeof(ngx_http_geo_range_t
));
754 range
[i
+ 2].start
= (u_short
) (e
+ 1);
755 range
[i
+ 2].end
= range
[i
].end
;
756 range
[i
+ 2].value
= range
[i
].value
;
758 range
[i
+ 1].start
= (u_short
) s
;
759 range
[i
+ 1].end
= (u_short
) e
;
760 range
[i
+ 1].value
= ctx
->value
;
762 range
[i
].end
= (u_short
) (s
- 1);
767 if (s
== (ngx_uint_t
) range
[i
].start
768 && e
< (ngx_uint_t
) range
[i
].end
)
770 /* shift the range start and insert the new range */
772 range
= ngx_array_push(a
);
774 return NGX_CONF_ERROR
;
779 ngx_memmove(&range
[i
+ 1], &range
[i
],
780 (a
->nelts
- 1 - i
) * sizeof(ngx_http_geo_range_t
));
782 range
[i
+ 1].start
= (u_short
) (e
+ 1);
784 range
[i
].start
= (u_short
) s
;
785 range
[i
].end
= (u_short
) e
;
786 range
[i
].value
= ctx
->value
;
791 if (s
> (ngx_uint_t
) range
[i
].start
792 && e
== (ngx_uint_t
) range
[i
].end
)
794 /* shift the range end and insert the new range */
796 range
= ngx_array_push(a
);
798 return NGX_CONF_ERROR
;
803 ngx_memmove(&range
[i
+ 2], &range
[i
+ 1],
804 (a
->nelts
- 2 - i
) * sizeof(ngx_http_geo_range_t
));
806 range
[i
+ 1].start
= (u_short
) s
;
807 range
[i
+ 1].end
= (u_short
) e
;
808 range
[i
+ 1].value
= ctx
->value
;
810 range
[i
].end
= (u_short
) (s
- 1);
815 s
= (ngx_uint_t
) range
[i
].start
;
816 e
= (ngx_uint_t
) range
[i
].end
;
818 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
819 "range \"%V\" overlaps \"%d.%d.%d.%d-%d.%d.%d.%d\"",
821 h
>> 8, h
& 0xff, s
>> 8, s
& 0xff,
822 h
>> 8, h
& 0xff, e
>> 8, e
& 0xff);
824 return NGX_CONF_ERROR
;
827 /* add the first range */
829 range
= ngx_array_push(a
);
831 return NGX_CONF_ERROR
;
834 range
->start
= (u_short
) s
;
835 range
->end
= (u_short
) e
;
836 range
->value
= ctx
->value
;
848 ngx_http_geo_delete_range(ngx_conf_t
*cf
, ngx_http_geo_conf_ctx_t
*ctx
,
849 in_addr_t start
, in_addr_t end
)
852 ngx_uint_t h
, i
, s
, e
, warn
;
854 ngx_http_geo_range_t
*range
;
858 for (n
= start
; n
<= end
; n
+= 0x10000) {
868 if ((n
| 0xffff) > end
) {
875 a
= (ngx_array_t
*) ctx
->high
.low
[h
];
883 for (i
= 0; i
< a
->nelts
; i
++) {
885 if (s
== (ngx_uint_t
) range
[i
].start
886 && e
== (ngx_uint_t
) range
[i
].end
)
888 ngx_memmove(&range
[i
], &range
[i
+ 1],
889 (a
->nelts
- 1 - i
) * sizeof(ngx_http_geo_range_t
));
896 if (s
!= (ngx_uint_t
) range
[i
].start
897 && e
!= (ngx_uint_t
) range
[i
].end
)
911 ngx_http_geo_cidr(ngx_conf_t
*cf
, ngx_http_geo_conf_ctx_t
*ctx
,
918 ngx_http_variable_value_t
*val
, *old
;
920 if (ctx
->tree
== NULL
) {
921 ctx
->tree
= ngx_radix_tree_create(ctx
->pool
, -1);
922 if (ctx
->tree
== NULL
) {
923 return NGX_CONF_ERROR
;
927 if (ngx_strcmp(value
[0].data
, "default") == 0) {
933 if (ngx_strcmp(value
[0].data
, "delete") == 0) {
942 if (ngx_http_geo_cidr_value(cf
, net
, &cidr
) != NGX_OK
) {
943 return NGX_CONF_ERROR
;
947 if (ngx_radix32tree_delete(ctx
->tree
, cidr
.u
.in
.addr
,
951 ngx_conf_log_error(NGX_LOG_WARN
, cf
, 0,
952 "no network \"%V\" to delete", net
);
959 val
= ngx_http_geo_value(cf
, ctx
, &value
[1]);
962 return NGX_CONF_ERROR
;
965 for (i
= 2; i
; i
--) {
966 rc
= ngx_radix32tree_insert(ctx
->tree
, cidr
.u
.in
.addr
, cidr
.u
.in
.mask
,
972 if (rc
== NGX_ERROR
) {
973 return NGX_CONF_ERROR
;
978 old
= (ngx_http_variable_value_t
*)
979 ngx_radix32tree_find(ctx
->tree
, cidr
.u
.in
.addr
& cidr
.u
.in
.mask
);
981 ngx_conf_log_error(NGX_LOG_WARN
, cf
, 0,
982 "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
985 rc
= ngx_radix32tree_delete(ctx
->tree
, cidr
.u
.in
.addr
, cidr
.u
.in
.mask
);
987 if (rc
== NGX_ERROR
) {
988 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0, "invalid radix tree");
989 return NGX_CONF_ERROR
;
993 return NGX_CONF_ERROR
;
997 static ngx_http_variable_value_t
*
998 ngx_http_geo_value(ngx_conf_t
*cf
, ngx_http_geo_conf_ctx_t
*ctx
,
1002 ngx_http_variable_value_t
*val
;
1003 ngx_http_geo_variable_value_node_t
*gvvn
;
1005 hash
= ngx_crc32_long(value
->data
, value
->len
);
1007 gvvn
= (ngx_http_geo_variable_value_node_t
*)
1008 ngx_str_rbtree_lookup(&ctx
->rbtree
, value
, hash
);
1014 val
= ngx_palloc(ctx
->pool
, sizeof(ngx_http_variable_value_t
));
1019 val
->len
= value
->len
;
1020 val
->data
= ngx_pstrdup(ctx
->pool
, value
);
1021 if (val
->data
== NULL
) {
1026 val
->no_cacheable
= 0;
1029 gvvn
= ngx_palloc(ctx
->temp_pool
,
1030 sizeof(ngx_http_geo_variable_value_node_t
));
1035 gvvn
->sn
.node
.key
= hash
;
1036 gvvn
->sn
.str
.len
= val
->len
;
1037 gvvn
->sn
.str
.data
= val
->data
;
1041 ngx_rbtree_insert(&ctx
->rbtree
, &gvvn
->sn
.node
);
1043 ctx
->data_size
+= ngx_align(sizeof(ngx_http_variable_value_t
) + value
->len
,
1051 ngx_http_geo_add_proxy(ngx_conf_t
*cf
, ngx_http_geo_conf_ctx_t
*ctx
,
1056 if (ctx
->proxies
== NULL
) {
1057 ctx
->proxies
= ngx_array_create(ctx
->pool
, 4, sizeof(ngx_in_cidr_t
));
1058 if (ctx
->proxies
== NULL
) {
1059 return NGX_CONF_ERROR
;
1063 c
= ngx_array_push(ctx
->proxies
);
1065 return NGX_CONF_ERROR
;
1068 c
->addr
= cidr
->u
.in
.addr
;
1069 c
->mask
= cidr
->u
.in
.mask
;
1076 ngx_http_geo_cidr_value(ngx_conf_t
*cf
, ngx_str_t
*net
, ngx_cidr_t
*cidr
)
1080 if (ngx_strcmp(net
->data
, "255.255.255.255") == 0) {
1081 cidr
->u
.in
.addr
= 0xffffffff;
1082 cidr
->u
.in
.mask
= 0xffffffff;
1087 rc
= ngx_ptocidr(net
, cidr
);
1089 if (rc
== NGX_ERROR
) {
1090 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0, "invalid network \"%V\"", net
);
1094 if (cidr
->family
!= AF_INET
) {
1095 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0, "\"geo\" supports IPv4 only");
1099 if (rc
== NGX_DONE
) {
1100 ngx_conf_log_error(NGX_LOG_WARN
, cf
, 0,
1101 "low address bits of %V are meaningless", net
);
1104 cidr
->u
.in
.addr
= ntohl(cidr
->u
.in
.addr
);
1105 cidr
->u
.in
.mask
= ntohl(cidr
->u
.in
.mask
);
1112 ngx_http_geo_include(ngx_conf_t
*cf
, ngx_http_geo_conf_ctx_t
*ctx
,
1118 file
.len
= name
->len
+ 4;
1119 file
.data
= ngx_pnalloc(ctx
->temp_pool
, name
->len
+ 5);
1120 if (file
.data
== NULL
) {
1121 return NGX_CONF_ERROR
;
1124 ngx_sprintf(file
.data
, "%V.bin%Z", name
);
1126 if (ngx_conf_full_name(cf
->cycle
, &file
, 1) != NGX_OK
) {
1127 return NGX_CONF_ERROR
;
1131 ngx_log_debug1(NGX_LOG_DEBUG_CORE
, cf
->log
, 0, "include %s", file
.data
);
1133 switch (ngx_http_geo_include_binary_base(cf
, ctx
, &file
)) {
1137 return NGX_CONF_ERROR
;
1144 file
.data
[file
.len
] = '\0';
1146 ctx
->include_name
= file
;
1148 if (ctx
->outside_entries
) {
1149 ctx
->allow_binary_include
= 0;
1152 ngx_log_debug1(NGX_LOG_DEBUG_CORE
, cf
->log
, 0, "include %s", file
.data
);
1154 rv
= ngx_conf_parse(cf
, &file
);
1157 ctx
->outside_entries
= 0;
1164 ngx_http_geo_include_binary_base(ngx_conf_t
*cf
, ngx_http_geo_conf_ctx_t
*ctx
,
1177 ngx_http_geo_range_t
*range
, **ranges
;
1178 ngx_http_geo_header_t
*header
;
1179 ngx_http_variable_value_t
*vv
;
1181 ngx_memzero(&file
, sizeof(ngx_file_t
));
1185 file
.fd
= ngx_open_file(name
->data
, NGX_FILE_RDONLY
, 0, 0);
1186 if (file
.fd
== NGX_INVALID_FILE
) {
1188 if (err
!= NGX_ENOENT
) {
1189 ngx_conf_log_error(NGX_LOG_CRIT
, cf
, err
,
1190 ngx_open_file_n
" \"%s\" failed", name
->data
);
1192 return NGX_DECLINED
;
1195 if (ctx
->outside_entries
) {
1196 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
1197 "binary geo range base \"%s\" may not be mixed with usual entries",
1203 if (ctx
->binary_include
) {
1204 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
1205 "second binary geo range base \"%s\" may not be mixed with \"%s\"",
1206 name
->data
, ctx
->include_name
.data
);
1211 if (ngx_fd_info(file
.fd
, &fi
) == NGX_FILE_ERROR
) {
1212 ngx_conf_log_error(NGX_LOG_CRIT
, cf
, ngx_errno
,
1213 ngx_fd_info_n
" \"%s\" failed", name
->data
);
1217 size
= (size_t) ngx_file_size(&fi
);
1218 mtime
= ngx_file_mtime(&fi
);
1220 ch
= name
->data
[name
->len
- 4];
1221 name
->data
[name
->len
- 4] = '\0';
1223 if (ngx_file_info(name
->data
, &fi
) == NGX_FILE_ERROR
) {
1224 ngx_conf_log_error(NGX_LOG_CRIT
, cf
, ngx_errno
,
1225 ngx_file_info_n
" \"%s\" failed", name
->data
);
1229 name
->data
[name
->len
- 4] = ch
;
1231 if (mtime
< ngx_file_mtime(&fi
)) {
1232 ngx_conf_log_error(NGX_LOG_WARN
, cf
, 0,
1233 "stale binary geo range base \"%s\"", name
->data
);
1237 base
= ngx_palloc(ctx
->pool
, size
);
1242 n
= ngx_read_file(&file
, base
, size
, 0);
1244 if (n
== NGX_ERROR
) {
1245 ngx_conf_log_error(NGX_LOG_CRIT
, cf
, ngx_errno
,
1246 ngx_read_file_n
" \"%s\" failed", name
->data
);
1250 if ((size_t) n
!= size
) {
1251 ngx_conf_log_error(NGX_LOG_CRIT
, cf
, 0,
1252 ngx_read_file_n
" \"%s\" returned only %z bytes instead of %z",
1253 name
->data
, n
, size
);
1257 header
= (ngx_http_geo_header_t
*) base
;
1259 if (size
< 16 || ngx_memcmp(&ngx_http_geo_header
, header
, 12) != 0) {
1260 ngx_conf_log_error(NGX_LOG_WARN
, cf
, 0,
1261 "incompatible binary geo range base \"%s\"", name
->data
);
1265 ngx_crc32_init(crc32
);
1267 vv
= (ngx_http_variable_value_t
*) (base
+ sizeof(ngx_http_geo_header_t
));
1270 len
= ngx_align(sizeof(ngx_http_variable_value_t
) + vv
->len
,
1272 ngx_crc32_update(&crc32
, (u_char
*) vv
, len
);
1273 vv
->data
+= (size_t) base
;
1274 vv
= (ngx_http_variable_value_t
*) ((u_char
*) vv
+ len
);
1276 ngx_crc32_update(&crc32
, (u_char
*) vv
, sizeof(ngx_http_variable_value_t
));
1279 ranges
= (ngx_http_geo_range_t
**) vv
;
1281 for (i
= 0; i
< 0x10000; i
++) {
1282 ngx_crc32_update(&crc32
, (u_char
*) &ranges
[i
], sizeof(void *));
1284 ranges
[i
] = (ngx_http_geo_range_t
*)
1285 ((u_char
*) ranges
[i
] + (size_t) base
);
1289 range
= (ngx_http_geo_range_t
*) &ranges
[0x10000];
1291 while ((u_char
*) range
< base
+ size
) {
1292 while (range
->value
) {
1293 ngx_crc32_update(&crc32
, (u_char
*) range
,
1294 sizeof(ngx_http_geo_range_t
));
1295 range
->value
= (ngx_http_variable_value_t
*)
1296 ((u_char
*) range
->value
+ (size_t) base
);
1299 ngx_crc32_update(&crc32
, (u_char
*) range
, sizeof(void *));
1300 range
= (ngx_http_geo_range_t
*) ((u_char
*) range
+ sizeof(void *));
1303 ngx_crc32_final(crc32
);
1305 if (crc32
!= header
->crc32
) {
1306 ngx_conf_log_error(NGX_LOG_WARN
, cf
, 0,
1307 "CRC32 mismatch in binary geo range base \"%s\"", name
->data
);
1311 ngx_conf_log_error(NGX_LOG_NOTICE
, cf
, 0,
1312 "using binary geo range base \"%s\"", name
->data
);
1314 ctx
->include_name
= *name
;
1315 ctx
->binary_include
= 1;
1316 ctx
->high
.low
= ranges
;
1327 if (ngx_close_file(file
.fd
) == NGX_FILE_ERROR
) {
1328 ngx_log_error(NGX_LOG_ALERT
, cf
->log
, ngx_errno
,
1329 ngx_close_file_n
" \"%s\" failed", name
->data
);
1337 ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t
*ctx
)
1343 ngx_file_mapping_t fm
;
1344 ngx_http_geo_range_t
*r
, *range
, **ranges
;
1345 ngx_http_geo_header_t
*header
;
1346 ngx_http_geo_variable_value_node_t
*gvvn
;
1348 fm
.name
= ngx_pnalloc(ctx
->temp_pool
, ctx
->include_name
.len
+ 5);
1349 if (fm
.name
== NULL
) {
1353 ngx_sprintf(fm
.name
, "%V.bin%Z", &ctx
->include_name
);
1355 fm
.size
= ctx
->data_size
;
1356 fm
.log
= ctx
->pool
->log
;
1358 ngx_log_error(NGX_LOG_NOTICE
, fm
.log
, 0,
1359 "creating binary geo range base \"%s\"", fm
.name
);
1361 if (ngx_create_file_mapping(&fm
) != NGX_OK
) {
1365 p
= ngx_cpymem(fm
.addr
, &ngx_http_geo_header
,
1366 sizeof(ngx_http_geo_header_t
));
1368 p
= ngx_http_geo_copy_values(fm
.addr
, p
, ctx
->rbtree
.root
,
1369 ctx
->rbtree
.sentinel
);
1371 p
+= sizeof(ngx_http_variable_value_t
);
1373 ranges
= (ngx_http_geo_range_t
**) p
;
1375 p
+= 0x10000 * sizeof(ngx_http_geo_range_t
*);
1377 for (i
= 0; i
< 0x10000; i
++) {
1378 r
= ctx
->high
.low
[i
];
1383 range
= (ngx_http_geo_range_t
*) p
;
1384 ranges
[i
] = (ngx_http_geo_range_t
*) (p
- (u_char
*) fm
.addr
);
1387 s
.len
= r
->value
->len
;
1388 s
.data
= r
->value
->data
;
1389 hash
= ngx_crc32_long(s
.data
, s
.len
);
1390 gvvn
= (ngx_http_geo_variable_value_node_t
*)
1391 ngx_str_rbtree_lookup(&ctx
->rbtree
, &s
, hash
);
1393 range
->value
= (ngx_http_variable_value_t
*) gvvn
->offset
;
1394 range
->start
= r
->start
;
1395 range
->end
= r
->end
;
1398 } while ((++r
)->value
);
1400 range
->value
= NULL
;
1402 p
= (u_char
*) range
+ sizeof(void *);
1406 header
->crc32
= ngx_crc32_long((u_char
*) fm
.addr
1407 + sizeof(ngx_http_geo_header_t
),
1408 fm
.size
- sizeof(ngx_http_geo_header_t
));
1410 ngx_close_file_mapping(&fm
);
1415 ngx_http_geo_copy_values(u_char
*base
, u_char
*p
, ngx_rbtree_node_t
*node
,
1416 ngx_rbtree_node_t
*sentinel
)
1418 ngx_http_variable_value_t
*vv
;
1419 ngx_http_geo_variable_value_node_t
*gvvn
;
1421 if (node
== sentinel
) {
1425 gvvn
= (ngx_http_geo_variable_value_node_t
*) node
;
1426 gvvn
->offset
= p
- base
;
1428 vv
= (ngx_http_variable_value_t
*) p
;
1430 p
+= sizeof(ngx_http_variable_value_t
);
1431 vv
->data
= (u_char
*) (p
- base
);
1433 p
= ngx_cpymem(p
, gvvn
->sn
.str
.data
, gvvn
->sn
.str
.len
);
1435 p
= ngx_align_ptr(p
, sizeof(void *));
1437 p
= ngx_http_geo_copy_values(base
, p
, node
->left
, sentinel
);
1439 return ngx_http_geo_copy_values(base
, p
, node
->right
, sentinel
);