Mercurial > hg > nginx
comparison src/http/modules/ngx_http_geo_module.c @ 2340:22d2b6609853
*) descrease geo configuration memory usage
*) geo delete
*) geo ranges
author | Igor Sysoev <igor@sysoev.ru> |
---|---|
date | Tue, 25 Nov 2008 15:59:06 +0000 |
parents | 6e30d64f919b |
children | ac0cf19eb582 |
comparison
equal
deleted
inserted
replaced
2339:2142237f66da | 2340:22d2b6609853 |
---|---|
8 #include <ngx_core.h> | 8 #include <ngx_core.h> |
9 #include <ngx_http.h> | 9 #include <ngx_http.h> |
10 | 10 |
11 | 11 |
12 typedef struct { | 12 typedef struct { |
13 ngx_radix_tree_t *tree; | 13 u_short start; |
14 ngx_rbtree_t rbtree; | 14 u_short end; |
15 ngx_rbtree_node_t sentinel; | 15 ngx_http_variable_value_t *value; |
16 ngx_pool_t *pool; | 16 } ngx_http_geo_range_t; |
17 | |
18 | |
19 typedef struct { | |
20 ngx_http_geo_range_t *ranges; | |
21 ngx_uint_t n; | |
22 } ngx_http_geo_low_ranges_t; | |
23 | |
24 | |
25 typedef struct { | |
26 ngx_http_geo_low_ranges_t low[0x10000]; | |
27 ngx_http_variable_value_t *default_value; | |
28 } ngx_http_geo_high_ranges_t; | |
29 | |
30 | |
31 typedef struct { | |
32 ngx_http_variable_value_t *value; | |
33 ngx_str_t *net; | |
34 ngx_http_geo_high_ranges_t *high; | |
35 ngx_radix_tree_t *tree; | |
36 ngx_rbtree_t rbtree; | |
37 ngx_rbtree_node_t sentinel; | |
38 ngx_pool_t *pool; | |
39 ngx_pool_t *temp_pool; | |
17 } ngx_http_geo_conf_ctx_t; | 40 } ngx_http_geo_conf_ctx_t; |
18 | 41 |
19 | 42 |
20 static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); | 43 static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); |
21 static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); | 44 static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); |
45 static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, | |
46 ngx_str_t *value); | |
47 static char *ngx_http_geo_add_range(ngx_conf_t *cf, | |
48 ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end); | |
49 static ngx_uint_t ngx_http_geo_delete_range(ngx_conf_t *cf, | |
50 ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end); | |
51 static char *ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, | |
52 ngx_str_t *value); | |
53 static ngx_http_variable_value_t *ngx_http_geo_value(ngx_conf_t *cf, | |
54 ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value); | |
22 | 55 |
23 | 56 |
24 static ngx_command_t ngx_http_geo_commands[] = { | 57 static ngx_command_t ngx_http_geo_commands[] = { |
25 | 58 |
26 { ngx_string("geo"), | 59 { ngx_string("geo"), |
66 | 99 |
67 | 100 |
68 /* AF_INET only */ | 101 /* AF_INET only */ |
69 | 102 |
70 static ngx_int_t | 103 static ngx_int_t |
71 ngx_http_geo_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, | 104 ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, |
72 uintptr_t data) | 105 uintptr_t data) |
73 { | 106 { |
74 ngx_radix_tree_t *tree = (ngx_radix_tree_t *) data; | 107 ngx_radix_tree_t *tree = (ngx_radix_tree_t *) data; |
75 | 108 |
76 struct sockaddr_in *sin; | 109 struct sockaddr_in *sin; |
77 ngx_http_variable_value_t *vv; | 110 ngx_http_variable_value_t *vv; |
78 | 111 |
79 sin = (struct sockaddr_in *) r->connection->sockaddr; | |
80 | |
81 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | 112 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
82 "http geo started"); | 113 "http geo started"); |
83 | 114 |
115 sin = (struct sockaddr_in *) r->connection->sockaddr; | |
116 | |
84 vv = (ngx_http_variable_value_t *) | 117 vv = (ngx_http_variable_value_t *) |
85 ngx_radix32tree_find(tree, ntohl(sin->sin_addr.s_addr)); | 118 ngx_radix32tree_find(tree, ntohl(sin->sin_addr.s_addr)); |
86 | 119 |
87 *v = *vv; | 120 *v = *vv; |
88 | 121 |
91 | 124 |
92 return NGX_OK; | 125 return NGX_OK; |
93 } | 126 } |
94 | 127 |
95 | 128 |
129 static ngx_int_t | |
130 ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, | |
131 uintptr_t data) | |
132 { | |
133 ngx_http_geo_high_ranges_t *high = (ngx_http_geo_high_ranges_t *) data; | |
134 | |
135 in_addr_t addr; | |
136 ngx_uint_t i, n; | |
137 struct sockaddr_in *sin; | |
138 ngx_http_geo_range_t *range; | |
139 | |
140 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
141 "http geo started"); | |
142 | |
143 sin = (struct sockaddr_in *) r->connection->sockaddr; | |
144 | |
145 *v = *high->default_value; | |
146 | |
147 addr = ntohl(sin->sin_addr.s_addr); | |
148 | |
149 range = high->low[addr >> 16].ranges; | |
150 | |
151 n = addr & 0xffff; | |
152 | |
153 for (i = 0; i < high->low[addr >> 16].n; i++) { | |
154 if (n >= (ngx_uint_t) range[i].start | |
155 && n <= (ngx_uint_t) range[i].end) | |
156 { | |
157 *v = *range[i].value; | |
158 } | |
159 } | |
160 | |
161 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
162 "http geo: %V %v", &r->connection->addr_text, v); | |
163 | |
164 return NGX_OK; | |
165 } | |
166 | |
167 | |
96 static char * | 168 static char * |
97 ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | 169 ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) |
98 { | 170 { |
99 char *rv; | 171 char *rv; |
172 size_t len; | |
100 ngx_str_t *value, name; | 173 ngx_str_t *value, name; |
174 ngx_uint_t i; | |
101 ngx_conf_t save; | 175 ngx_conf_t save; |
102 ngx_pool_t *pool; | 176 ngx_pool_t *pool; |
103 ngx_radix_tree_t *tree; | 177 ngx_array_t *a; |
104 ngx_http_variable_t *var; | 178 ngx_http_variable_t *var; |
105 ngx_http_geo_conf_ctx_t ctx; | 179 ngx_http_geo_conf_ctx_t ctx; |
106 | 180 |
107 value = cf->args->elts; | 181 value = cf->args->elts; |
108 | 182 |
120 var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE); | 194 var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE); |
121 if (var == NULL) { | 195 if (var == NULL) { |
122 return NGX_CONF_ERROR; | 196 return NGX_CONF_ERROR; |
123 } | 197 } |
124 | 198 |
125 tree = ngx_radix_tree_create(cf->pool, -1); | |
126 | |
127 if (tree == NULL) { | |
128 return NGX_CONF_ERROR; | |
129 } | |
130 | |
131 var->get_handler = ngx_http_geo_variable; | |
132 var->data = (uintptr_t) tree; | |
133 | |
134 pool = ngx_create_pool(16384, cf->log); | 199 pool = ngx_create_pool(16384, cf->log); |
135 if (pool == NULL) { | 200 if (pool == NULL) { |
136 return NGX_CONF_ERROR; | 201 return NGX_CONF_ERROR; |
137 } | 202 } |
138 | 203 |
204 ctx.temp_pool = ngx_create_pool(16384, cf->log); | |
205 if (ctx.temp_pool == NULL) { | |
206 return NGX_CONF_ERROR; | |
207 } | |
208 | |
139 ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, | 209 ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, |
140 ngx_http_variable_value_rbtree_insert); | 210 ngx_http_variable_value_rbtree_insert); |
141 | 211 |
142 ctx.tree = tree; | 212 ctx.high = NULL; |
213 ctx.tree = NULL; | |
143 ctx.pool = cf->pool; | 214 ctx.pool = cf->pool; |
144 | 215 |
145 save = *cf; | 216 save = *cf; |
146 cf->pool = pool; | 217 cf->pool = pool; |
147 cf->ctx = &ctx; | 218 cf->ctx = &ctx; |
150 | 221 |
151 rv = ngx_conf_parse(cf, NULL); | 222 rv = ngx_conf_parse(cf, NULL); |
152 | 223 |
153 *cf = save; | 224 *cf = save; |
154 | 225 |
155 ngx_destroy_pool(pool); | 226 if (ctx.high) { |
156 | 227 |
157 if (ngx_radix32tree_find(tree, 0) != NGX_RADIX_NO_VALUE) { | 228 for (i = 0; i < 0x10000; i++) { |
158 return rv; | 229 a = (ngx_array_t *) ctx.high->low[i].ranges; |
159 } | 230 |
160 | 231 if (a == NULL || a->nelts == 0) { |
161 if (ngx_radix32tree_insert(tree, 0, 0, | 232 continue; |
162 (uintptr_t) &ngx_http_variable_null_value) | 233 } |
163 == NGX_ERROR) | 234 |
164 { | 235 ctx.high->low[i].n = a->nelts; |
165 return NGX_CONF_ERROR; | 236 |
237 len = a->nelts * sizeof(ngx_http_geo_range_t); | |
238 | |
239 ctx.high->low[i].ranges = ngx_palloc(cf->pool, len); | |
240 if (ctx.high->low[i].ranges == NULL ){ | |
241 return NGX_CONF_ERROR; | |
242 } | |
243 | |
244 ngx_memcpy(ctx.high->low[i].ranges, a->elts, len); | |
245 } | |
246 | |
247 var->get_handler = ngx_http_geo_range_variable; | |
248 var->data = (uintptr_t) ctx.high; | |
249 | |
250 ngx_destroy_pool(ctx.temp_pool); | |
251 ngx_destroy_pool(pool); | |
252 | |
253 if (ctx.high->default_value == NULL) { | |
254 ctx.high->default_value = &ngx_http_variable_null_value; | |
255 } | |
256 | |
257 } else { | |
258 var->get_handler = ngx_http_geo_cidr_variable; | |
259 var->data = (uintptr_t) ctx.tree; | |
260 | |
261 ngx_destroy_pool(ctx.temp_pool); | |
262 ngx_destroy_pool(pool); | |
263 | |
264 if (ngx_radix32tree_find(ctx.tree, 0) != NGX_RADIX_NO_VALUE) { | |
265 return rv; | |
266 } | |
267 | |
268 if (ngx_radix32tree_insert(ctx.tree, 0, 0, | |
269 (uintptr_t) &ngx_http_variable_null_value) | |
270 == NGX_ERROR) | |
271 { | |
272 return NGX_CONF_ERROR; | |
273 } | |
166 } | 274 } |
167 | 275 |
168 return rv; | 276 return rv; |
169 } | 277 } |
170 | 278 |
171 | |
172 /* AF_INET only */ | |
173 | 279 |
174 static char * | 280 static char * |
175 ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) | 281 ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) |
176 { | 282 { |
177 uint32_t hash; | 283 char *rv; |
178 ngx_int_t rc; | 284 ngx_str_t *value, file; |
179 ngx_str_t *value, file; | 285 ngx_http_geo_conf_ctx_t *ctx; |
180 ngx_uint_t i; | |
181 ngx_inet_cidr_t cidrin; | |
182 ngx_http_geo_conf_ctx_t *ctx; | |
183 ngx_http_variable_value_t *val, *old; | |
184 ngx_http_variable_value_node_t *vvn; | |
185 | 286 |
186 ctx = cf->ctx; | 287 ctx = cf->ctx; |
288 | |
289 value = cf->args->elts; | |
290 | |
291 if (cf->args->nelts == 1) { | |
292 | |
293 if (ngx_strcmp(value[0].data, "ranges") == 0) { | |
294 | |
295 if (ctx->tree) { | |
296 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
297 "the \"ranges\" directive must be " | |
298 "the first directive inside \"geo\" block"); | |
299 goto failed; | |
300 } | |
301 | |
302 ctx->high = ngx_pcalloc(ctx->pool, | |
303 sizeof(ngx_http_geo_high_ranges_t)); | |
304 if (ctx->high == NULL) { | |
305 goto failed; | |
306 } | |
307 | |
308 rv = NGX_CONF_OK; | |
309 | |
310 goto done; | |
311 } | |
312 } | |
187 | 313 |
188 if (cf->args->nelts != 2) { | 314 if (cf->args->nelts != 2) { |
189 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | 315 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, |
190 "invalid number of the geo parameters"); | 316 "invalid number of the geo parameters"); |
317 goto failed; | |
318 } | |
319 | |
320 if (ngx_strcmp(value[0].data, "include") == 0) { | |
321 | |
322 file.len = value[1].len++; | |
323 | |
324 file.data = ngx_pstrdup(ctx->temp_pool, &value[1]); | |
325 if (file.data == NULL) { | |
326 goto failed; | |
327 } | |
328 | |
329 if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK){ | |
330 goto failed; | |
331 } | |
332 | |
333 ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); | |
334 | |
335 rv = ngx_conf_parse(cf, &file); | |
336 | |
337 goto done; | |
338 } | |
339 | |
340 if (ctx->high) { | |
341 rv = ngx_http_geo_range(cf, ctx, value); | |
342 | |
343 } else { | |
344 rv = ngx_http_geo_cidr(cf, ctx, value); | |
345 } | |
346 | |
347 done: | |
348 | |
349 ngx_reset_pool(cf->pool); | |
350 | |
351 return rv; | |
352 | |
353 failed: | |
354 | |
355 ngx_reset_pool(cf->pool); | |
356 | |
357 return NGX_CONF_ERROR; | |
358 } | |
359 | |
360 | |
361 static char * | |
362 ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, | |
363 ngx_str_t *value) | |
364 { | |
365 u_char *p, *last; | |
366 in_addr_t start, end; | |
367 ngx_str_t *net; | |
368 ngx_uint_t del; | |
369 ngx_http_variable_value_t *old; | |
370 | |
371 if (ngx_strcmp(value[0].data, "default") == 0) { | |
372 | |
373 old = ctx->high->default_value; | |
374 | |
375 ctx->high->default_value = ngx_http_geo_value(cf, ctx, &value[1]); | |
376 if (ctx->high->default_value == NULL) { | |
377 return NGX_CONF_ERROR; | |
378 } | |
379 | |
380 if (old) { | |
381 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | |
382 "duplicate range \"%V\", value: \"%v\", old value: \"%v\"", | |
383 &value[0], ctx->high->default_value, old); | |
384 } | |
385 | |
386 return NGX_CONF_OK; | |
387 } | |
388 | |
389 if (ngx_strcmp(value[0].data, "delete") == 0) { | |
390 net = &value[1]; | |
391 del = 1; | |
392 | |
393 } else { | |
394 net = &value[0]; | |
395 del = 0; | |
396 } | |
397 | |
398 last = net->data + net->len; | |
399 | |
400 p = ngx_strlchr(net->data, last, '-'); | |
401 | |
402 if (p == NULL) { | |
403 goto invalid; | |
404 } | |
405 | |
406 start = ngx_inet_addr(net->data, p - net->data); | |
407 | |
408 if (start == INADDR_NONE) { | |
409 goto invalid; | |
410 } | |
411 | |
412 start = ntohl(start); | |
413 | |
414 p++; | |
415 | |
416 end = ngx_inet_addr(p, last - p); | |
417 | |
418 if (end == INADDR_NONE) { | |
419 goto invalid; | |
420 } | |
421 | |
422 end = ntohl(end); | |
423 | |
424 if (start > end) { | |
425 goto invalid; | |
426 } | |
427 | |
428 if (del) { | |
429 if (ngx_http_geo_delete_range(cf, ctx, start, end)) { | |
430 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | |
431 "no address range \"%V\" to delete", net); | |
432 } | |
433 | |
434 return NGX_CONF_OK; | |
435 } | |
436 | |
437 ctx->value = ngx_http_geo_value(cf, ctx, &value[1]); | |
438 | |
439 if (ctx->value == NULL) { | |
191 return NGX_CONF_ERROR; | 440 return NGX_CONF_ERROR; |
192 } | 441 } |
193 | 442 |
194 value = cf->args->elts; | 443 ctx->net = net; |
195 | 444 |
196 if (ngx_strcmp(value[0].data, "include") == 0) { | 445 return ngx_http_geo_add_range(cf, ctx, start, end); |
197 file = value[1]; | 446 |
198 | 447 invalid: |
199 if (ngx_conf_full_name(cf->cycle, &file, 1) == NGX_ERROR){ | 448 |
449 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid range \"%V\"", net); | |
450 | |
451 return NGX_CONF_ERROR; | |
452 } | |
453 | |
454 | |
455 /* the add procedure is optimized to add a growing up sequence */ | |
456 | |
457 static char * | |
458 ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, | |
459 in_addr_t start, in_addr_t end) | |
460 { | |
461 in_addr_t n; | |
462 ngx_uint_t h, i, s, e; | |
463 ngx_array_t *a; | |
464 ngx_http_geo_range_t *range; | |
465 | |
466 for (n = start; n < end; n += 0x10000) { | |
467 | |
468 h = n >> 16; | |
469 s = n & 0xffff; | |
470 | |
471 if ((n | 0xffff) > end) { | |
472 e = end & 0xffff; | |
473 | |
474 } else { | |
475 e = 0xffff; | |
476 } | |
477 | |
478 a = (ngx_array_t *) ctx->high->low[h].ranges; | |
479 | |
480 if (a == NULL) { | |
481 a = ngx_array_create(ctx->temp_pool, 64, | |
482 sizeof(ngx_http_geo_range_t)); | |
483 if (a == NULL) { | |
484 return NGX_CONF_ERROR; | |
485 } | |
486 | |
487 ctx->high->low[h].ranges = (ngx_http_geo_range_t *) a; | |
488 } | |
489 | |
490 i = a->nelts; | |
491 range = a->elts; | |
492 | |
493 while (i) { | |
494 | |
495 i--; | |
496 | |
497 if (e < (ngx_uint_t) range[i].start) { | |
498 continue; | |
499 } | |
500 | |
501 if (s > (ngx_uint_t) range[i].end) { | |
502 | |
503 /* add after the range */ | |
504 | |
505 range = ngx_array_push(a); | |
506 if (range == NULL) { | |
507 return NGX_CONF_ERROR; | |
508 } | |
509 | |
510 range = a->elts; | |
511 | |
512 ngx_memcpy(&range[i + 2], &range[i + 1], | |
513 (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t)); | |
514 | |
515 range = &range[i + 1]; | |
516 | |
517 goto next; | |
518 } | |
519 | |
520 if (s == (ngx_uint_t) range[i].start | |
521 && e == (ngx_uint_t) range[i].end) | |
522 { | |
523 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | |
524 "duplicate range \"%V\", value: \"%v\", old value: \"%v\"", | |
525 ctx->net, ctx->value, range[i].value); | |
526 continue; | |
527 } | |
528 | |
529 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
530 "overlapped range \"%V\"", ctx->net); | |
531 | |
200 return NGX_CONF_ERROR; | 532 return NGX_CONF_ERROR; |
201 } | 533 } |
202 | 534 |
203 ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); | 535 /* add the first range */ |
204 | 536 |
205 return ngx_conf_parse(cf, &file); | 537 range = ngx_array_push(a); |
538 if (range == NULL) { | |
539 return NGX_CONF_ERROR; | |
540 } | |
541 | |
542 next: | |
543 | |
544 range->start = (u_short) s; | |
545 range->end = (u_short) e; | |
546 range->value = ctx->value; | |
547 } | |
548 | |
549 return NGX_CONF_OK; | |
550 } | |
551 | |
552 | |
553 static ngx_uint_t | |
554 ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, | |
555 in_addr_t start, in_addr_t end) | |
556 { | |
557 in_addr_t n; | |
558 ngx_uint_t h, i, s, e, warn; | |
559 ngx_array_t *a; | |
560 ngx_http_geo_range_t *range; | |
561 | |
562 warn = 0; | |
563 | |
564 for (n = start; n < end; n += 0x10000) { | |
565 | |
566 h = n >> 16; | |
567 s = n & 0xffff; | |
568 | |
569 if ((n | 0xffff) > end) { | |
570 e = end & 0xffff; | |
571 | |
572 } else { | |
573 e = 0xffff; | |
574 } | |
575 | |
576 a = (ngx_array_t *) ctx->high->low[h].ranges; | |
577 | |
578 if (a == NULL) { | |
579 warn = 1; | |
580 continue; | |
581 } | |
582 | |
583 range = a->elts; | |
584 for (i = 0; i < a->nelts; i++) { | |
585 | |
586 if (s == (ngx_uint_t) range[i].start | |
587 && e == (ngx_uint_t) range[i].end) | |
588 { | |
589 ngx_memcpy(&range[i], &range[i + 1], | |
590 (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t)); | |
591 break; | |
592 } | |
593 | |
594 if (s != (ngx_uint_t) range[i].start | |
595 && e != (ngx_uint_t) range[i].end) | |
596 { | |
597 continue; | |
598 } | |
599 | |
600 warn = 1; | |
601 } | |
602 } | |
603 | |
604 return warn; | |
605 } | |
606 | |
607 | |
608 static char * | |
609 ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, | |
610 ngx_str_t *value) | |
611 { | |
612 ngx_int_t rc, del; | |
613 ngx_str_t *net; | |
614 ngx_uint_t i; | |
615 ngx_inet_cidr_t cidrin; | |
616 ngx_http_variable_value_t *val, *old; | |
617 | |
618 if (ctx->tree == NULL) { | |
619 ctx->tree = ngx_radix_tree_create(ctx->pool, -1); | |
620 if (ctx->tree == NULL) { | |
621 return NGX_CONF_ERROR; | |
622 } | |
206 } | 623 } |
207 | 624 |
208 if (ngx_strcmp(value[0].data, "default") == 0) { | 625 if (ngx_strcmp(value[0].data, "default") == 0) { |
209 cidrin.addr = 0; | 626 cidrin.addr = 0; |
210 cidrin.mask = 0; | 627 cidrin.mask = 0; |
628 net = &value[0]; | |
211 | 629 |
212 } else { | 630 } else { |
213 rc = ngx_ptocidr(&value[0], &cidrin); | 631 if (ngx_strcmp(value[0].data, "delete") == 0) { |
632 net = &value[1]; | |
633 del = 1; | |
634 | |
635 } else { | |
636 net = &value[0]; | |
637 del = 0; | |
638 } | |
639 | |
640 rc = ngx_ptocidr(net, &cidrin); | |
214 | 641 |
215 if (rc == NGX_ERROR) { | 642 if (rc == NGX_ERROR) { |
216 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | 643 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, |
217 "invalid parameter \"%V\"", &value[0]); | 644 "invalid network \"%V\"", net); |
218 return NGX_CONF_ERROR; | 645 return NGX_CONF_ERROR; |
219 } | 646 } |
220 | 647 |
221 if (rc == NGX_DONE) { | 648 if (rc == NGX_DONE) { |
222 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | 649 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, |
223 "low address bits of %V are meaningless", | 650 "low address bits of %V are meaningless", net); |
224 &value[0]); | |
225 } | 651 } |
226 | 652 |
227 cidrin.addr = ntohl(cidrin.addr); | 653 cidrin.addr = ntohl(cidrin.addr); |
228 cidrin.mask = ntohl(cidrin.mask); | 654 cidrin.mask = ntohl(cidrin.mask); |
229 } | 655 |
230 | 656 if (del) { |
231 hash = ngx_crc32_long(value[1].data, value[1].len); | 657 if (ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask) |
232 | 658 != NGX_OK) |
233 val = ngx_http_variable_value_lookup(&ctx->rbtree, &value[1], hash); | 659 { |
660 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | |
661 "no network \"%V\" to delete", net); | |
662 return NGX_CONF_OK; | |
663 } | |
664 } | |
665 } | |
666 | |
667 val = ngx_http_geo_value(cf, ctx, &value[1]); | |
234 | 668 |
235 if (val == NULL) { | 669 if (val == NULL) { |
236 val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t)); | 670 return NGX_CONF_ERROR; |
237 if (val == NULL) { | |
238 return NGX_CONF_ERROR; | |
239 } | |
240 | |
241 val->len = value[1].len; | |
242 val->data = ngx_pstrdup(ctx->pool, &value[1]); | |
243 if (val->data == NULL) { | |
244 return NGX_CONF_ERROR; | |
245 } | |
246 | |
247 val->valid = 1; | |
248 val->no_cacheable = 0; | |
249 val->not_found = 0; | |
250 | |
251 vvn = ngx_palloc(cf->pool, sizeof(ngx_http_variable_value_node_t)); | |
252 if (vvn == NULL) { | |
253 return NGX_CONF_ERROR; | |
254 } | |
255 | |
256 vvn->node.key = hash; | |
257 vvn->len = val->len; | |
258 vvn->value = val; | |
259 | |
260 ngx_rbtree_insert(&ctx->rbtree, &vvn->node); | |
261 } | 671 } |
262 | 672 |
263 for (i = 2; i; i--) { | 673 for (i = 2; i; i--) { |
264 rc = ngx_radix32tree_insert(ctx->tree, cidrin.addr, cidrin.mask, | 674 rc = ngx_radix32tree_insert(ctx->tree, cidrin.addr, cidrin.mask, |
265 (uintptr_t) val); | 675 (uintptr_t) val); |
272 } | 682 } |
273 | 683 |
274 /* rc == NGX_BUSY */ | 684 /* rc == NGX_BUSY */ |
275 | 685 |
276 old = (ngx_http_variable_value_t *) | 686 old = (ngx_http_variable_value_t *) |
277 ngx_radix32tree_find(ctx->tree, cidrin.addr & cidrin.mask); | 687 ngx_radix32tree_find(ctx->tree, cidrin.addr & cidrin.mask); |
278 | 688 |
279 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | 689 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, |
280 "duplicate parameter \"%V\", value: \"%v\", old value: \"%v\"", | 690 "duplicate network \"%V\", value: \"%v\", old value: \"%v\"", |
281 &value[0], val, old); | 691 net, val, old); |
282 | 692 |
283 rc = ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask); | 693 rc = ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask); |
284 | 694 |
285 if (rc == NGX_ERROR) { | 695 if (rc == NGX_ERROR) { |
696 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree"); | |
286 return NGX_CONF_ERROR; | 697 return NGX_CONF_ERROR; |
287 } | 698 } |
288 } | 699 } |
289 | 700 |
290 return NGX_CONF_ERROR; | 701 return NGX_CONF_ERROR; |
291 } | 702 } |
703 | |
704 | |
705 static ngx_http_variable_value_t * | |
706 ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, | |
707 ngx_str_t *value) | |
708 { | |
709 uint32_t hash; | |
710 ngx_http_variable_value_t *val; | |
711 ngx_http_variable_value_node_t *vvn; | |
712 | |
713 hash = ngx_crc32_long(value->data, value->len); | |
714 | |
715 val = ngx_http_variable_value_lookup(&ctx->rbtree, value, hash); | |
716 | |
717 if (val) { | |
718 return val; | |
719 } | |
720 | |
721 val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t)); | |
722 if (val == NULL) { | |
723 return NULL; | |
724 } | |
725 | |
726 val->len = value->len; | |
727 val->data = ngx_pstrdup(ctx->pool, value); | |
728 if (val->data == NULL) { | |
729 return NULL; | |
730 } | |
731 | |
732 val->valid = 1; | |
733 val->no_cacheable = 0; | |
734 val->not_found = 0; | |
735 | |
736 vvn = ngx_palloc(ctx->temp_pool, sizeof(ngx_http_variable_value_node_t)); | |
737 if (vvn == NULL) { | |
738 return NULL; | |
739 } | |
740 | |
741 vvn->node.key = hash; | |
742 vvn->len = val->len; | |
743 vvn->value = val; | |
744 | |
745 ngx_rbtree_insert(&ctx->rbtree, &vvn->node); | |
746 | |
747 return val; | |
748 } |