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