1
2 /*
3 * Copyright (C) Igor Sysoev
4 */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_http.h>
10
11
12 typedef struct {
13 u_short start;
14 u_short end;
15 ngx_http_variable_value_t *value;
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_array_t *proxies;
39 ngx_pool_t *pool;
40 ngx_pool_t *temp_pool;
41 } ngx_http_geo_conf_ctx_t;
42
43
44 typedef struct {
45 union {
46 ngx_radix_tree_t *tree;
47 ngx_http_geo_high_ranges_t *high;
48 } u;
49
50 ngx_array_t *proxies;
51
52 ngx_int_t index;
53 } ngx_http_geo_ctx_t;
54
55
56 static in_addr_t ngx_http_geo_addr(ngx_http_request_t *r,
57 ngx_http_geo_ctx_t *ctx);
58 static in_addr_t ngx_http_geo_real_addr(ngx_http_request_t *r,
59 ngx_http_geo_ctx_t *ctx);
60 static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
61 static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
62 static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
63 ngx_str_t *value);
64 static char *ngx_http_geo_add_range(ngx_conf_t *cf,
65 ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
66 static ngx_uint_t ngx_http_geo_delete_range(ngx_conf_t *cf,
67 ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
68 static char *ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
69 ngx_str_t *value);
70 static ngx_http_variable_value_t *ngx_http_geo_value(ngx_conf_t *cf,
71 ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value);
72 static char *ngx_http_geo_add_proxy(ngx_conf_t *cf,
73 ngx_http_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr);
74 static ngx_int_t ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net,
75 ngx_cidr_t *cidr);
76
77
78 static ngx_command_t ngx_http_geo_commands[] = {
79
80 { ngx_string("geo"),
81 NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,
82 ngx_http_geo_block,
83 NGX_HTTP_MAIN_CONF_OFFSET,
84 0,
85 NULL },
86
87 ngx_null_command
88 };
89
90
91 static ngx_http_module_t ngx_http_geo_module_ctx = {
92 NULL, /* preconfiguration */
93 NULL, /* postconfiguration */
94
95 NULL, /* create main configuration */
96 NULL, /* init main configuration */
97
98 NULL, /* create server configuration */
99 NULL, /* merge server configuration */
100
101 NULL, /* create location configuration */
102 NULL /* merge location configuration */
103 };
104
105
106 ngx_module_t ngx_http_geo_module = {
107 NGX_MODULE_V1,
108 &ngx_http_geo_module_ctx, /* module context */
109 ngx_http_geo_commands, /* module directives */
110 NGX_HTTP_MODULE, /* module type */
111 NULL, /* init master */
112 NULL, /* init module */
113 NULL, /* init process */
114 NULL, /* init thread */
115 NULL, /* exit thread */
116 NULL, /* exit process */
117 NULL, /* exit master */
118 NGX_MODULE_V1_PADDING
119 };
120
121
122 /* AF_INET only */
123
124 static ngx_int_t
125 ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
126 uintptr_t data)
127 {
128 ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
129
130 ngx_http_variable_value_t *vv;
131
132 vv = (ngx_http_variable_value_t *)
133 ngx_radix32tree_find(ctx->u.tree, ngx_http_geo_addr(r, ctx));
134
135 *v = *vv;
136
137 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
138 "http geo: %v", v);
139
140 return NGX_OK;
141 }
142
143
144 static ngx_int_t
145 ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
146 uintptr_t data)
147 {
148 ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
149
150 in_addr_t addr;
151 ngx_uint_t i, n;
152 ngx_http_geo_range_t *range;
153
154 *v = *ctx->u.high->default_value;
155
156 addr = ngx_http_geo_addr(r, ctx);
157
158 range = ctx->u.high->low[addr >> 16].ranges;
159
160 n = addr & 0xffff;
161
162 for (i = 0; i < ctx->u.high->low[addr >> 16].n; i++) {
163 if (n >= (ngx_uint_t) range[i].start
164 && n <= (ngx_uint_t) range[i].end)
165 {
166 *v = *range[i].value;
167 }
168 }
169
170 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
171 "http geo: %v", v);
172
173 return NGX_OK;
174 }
175
176
177 static in_addr_t
178 ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx)
179 {
180 u_char *p, *ip;
181 size_t len;
182 in_addr_t addr;
183 ngx_uint_t i, n;
184 ngx_in_cidr_t *proxies;
185 ngx_table_elt_t *xfwd;
186
187 addr = ngx_http_geo_real_addr(r, ctx);
188
189 xfwd = r->headers_in.x_forwarded_for;
190
191 if (xfwd == NULL || ctx->proxies == NULL) {
192 return addr;
193 }
194
195 proxies = ctx->proxies->elts;
196 n = ctx->proxies->nelts;
197
198 for (i = 0; i < n; i++) {
199 if ((addr & proxies[i].mask) == proxies[i].addr) {
200
201 len = xfwd->value.len;
202 ip = xfwd->value.data;
203
204 for (p = ip + len - 1; p > ip; p--) {
205 if (*p == ' ' || *p == ',') {
206 p++;
207 len -= p - ip;
208 ip = p;
209 break;
210 }
211 }
212
213 return ntohl(ngx_inet_addr(ip, len));
214 }
215 }
216
217 return addr;
218 }
219
220
221 static in_addr_t
222 ngx_http_geo_real_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx)
223 {
224 struct sockaddr_in *sin;
225 ngx_http_variable_value_t *v;
226
227 if (ctx->index == -1) {
228 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
229 "http geo started: %V", &r->connection->addr_text);
230
231 if (r->connection->sockaddr->sa_family != AF_INET) {
232 return 0;
233 }
234
235 sin = (struct sockaddr_in *) r->connection->sockaddr;
236 return ntohl(sin->sin_addr.s_addr);
237 }
238
239 v = ngx_http_get_flushed_variable(r, ctx->index);
240
241 if (v == NULL || v->not_found) {
242 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
243 "http geo not found");
244
245 return 0;
246 }
247
248 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
249 "http geo started: %v", v);
250
251 return ntohl(ngx_inet_addr(v->data, v->len));
252 }
253
254
255 static char *
256 ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
257 {
258 char *rv;
259 size_t len;
260 ngx_str_t *value, name;
261 ngx_uint_t i;
262 ngx_conf_t save;
263 ngx_pool_t *pool;
264 ngx_array_t *a;
265 ngx_http_variable_t *var;
266 ngx_http_geo_ctx_t *geo;
267 ngx_http_geo_conf_ctx_t ctx;
268
269 value = cf->args->elts;
270
271 geo = ngx_palloc(cf->pool, sizeof(ngx_http_geo_ctx_t));
272 if (geo == NULL) {
273 return NGX_CONF_ERROR;
274 }
275
276 name = value[1];
277 name.len--;
278 name.data++;
279
280 if (cf->args->nelts == 3) {
281
282 geo->index = ngx_http_get_variable_index(cf, &name);
283 if (geo->index == NGX_ERROR) {
284 return NGX_CONF_ERROR;
285 }
286
287 name = value[2];
288 name.len--;
289 name.data++;
290
291 } else {
292 geo->index = -1;
293 }
294
295 var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
296 if (var == NULL) {
297 return NGX_CONF_ERROR;
298 }
299
300 pool = ngx_create_pool(16384, cf->log);
301 if (pool == NULL) {
302 return NGX_CONF_ERROR;
303 }
304
305 ctx.temp_pool = ngx_create_pool(16384, cf->log);
306 if (ctx.temp_pool == NULL) {
307 return NGX_CONF_ERROR;
308 }
309
310 ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel,
311 ngx_http_variable_value_rbtree_insert);
312
313 ctx.high = NULL;
314 ctx.tree = NULL;
315 ctx.proxies = NULL;
316 ctx.pool = cf->pool;
317
318 save = *cf;
319 cf->pool = pool;
320 cf->ctx = &ctx;
321 cf->handler = ngx_http_geo;
322 cf->handler_conf = conf;
323
324 rv = ngx_conf_parse(cf, NULL);
325
326 *cf = save;
327
328 geo->proxies = ctx.proxies;
329
330 if (ctx.high) {
331
332 for (i = 0; i < 0x10000; i++) {
333 a = (ngx_array_t *) ctx.high->low[i].ranges;
334
335 if (a == NULL || a->nelts == 0) {
336 continue;
337 }
338
339 ctx.high->low[i].n = a->nelts;
340
341 len = a->nelts * sizeof(ngx_http_geo_range_t);
342
343 ctx.high->low[i].ranges = ngx_palloc(cf->pool, len);
344 if (ctx.high->low[i].ranges == NULL ){
345 return NGX_CONF_ERROR;
346 }
347
348 ngx_memcpy(ctx.high->low[i].ranges, a->elts, len);
349 }
350
351 geo->u.high = ctx.high;
352
353 var->get_handler = ngx_http_geo_range_variable;
354 var->data = (uintptr_t) geo;
355
356 ngx_destroy_pool(ctx.temp_pool);
357 ngx_destroy_pool(pool);
358
359 if (ctx.high->default_value == NULL) {
360 ctx.high->default_value = &ngx_http_variable_null_value;
361 }
362
363 } else {
364 if (ctx.tree == NULL) {
365 ctx.tree = ngx_radix_tree_create(cf->pool, -1);
366 if (ctx.tree == NULL) {
367 return NGX_CONF_ERROR;
368 }
369 }
370
371 geo->u.tree = ctx.tree;
372
373 var->get_handler = ngx_http_geo_cidr_variable;
374 var->data = (uintptr_t) geo;
375
376 ngx_destroy_pool(ctx.temp_pool);
377 ngx_destroy_pool(pool);
378
379 if (ngx_radix32tree_find(ctx.tree, 0) != NGX_RADIX_NO_VALUE) {
380 return rv;
381 }
382
383 if (ngx_radix32tree_insert(ctx.tree, 0, 0,
384 (uintptr_t) &ngx_http_variable_null_value)
385 == NGX_ERROR)
386 {
387 return NGX_CONF_ERROR;
388 }
389 }
390
391 return rv;
392 }
393
394
395 static char *
396 ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
397 {
398 char *rv;
399 ngx_str_t *value, file;
400 ngx_cidr_t cidr;
401 ngx_http_geo_conf_ctx_t *ctx;
402
403 ctx = cf->ctx;
404
405 value = cf->args->elts;
406
407 if (cf->args->nelts == 1) {
408
409 if (ngx_strcmp(value[0].data, "ranges") == 0) {
410
411 if (ctx->tree) {
412 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
413 "the \"ranges\" directive must be "
414 "the first directive inside \"geo\" block");
415 goto failed;
416 }
417
418 ctx->high = ngx_pcalloc(ctx->pool,
419 sizeof(ngx_http_geo_high_ranges_t));
420 if (ctx->high == NULL) {
421 goto failed;
422 }
423
424 rv = NGX_CONF_OK;
425
426 goto done;
427 }
428 }
429
430 if (cf->args->nelts != 2) {
431 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
432 "invalid number of the geo parameters");
433 goto failed;
434 }
435
436 if (ngx_strcmp(value[0].data, "include") == 0) {
437
438 file.len = value[1].len++;
439
440 file.data = ngx_pstrdup(ctx->temp_pool, &value[1]);
441 if (file.data == NULL) {
442 goto failed;
443 }
444
445 if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK){
446 goto failed;
447 }
448
449 ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
450
451 rv = ngx_conf_parse(cf, &file);
452
453 goto done;
454
455 } else if (ngx_strcmp(value[0].data, "proxy") == 0) {
456
457 if (ngx_http_geo_cidr_value(cf, &value[1], &cidr) != NGX_OK) {
458 goto failed;
459 }
460
461 rv = ngx_http_geo_add_proxy(cf, ctx, &cidr);
462
463 goto done;
464 }
465
466 if (ctx->high) {
467 rv = ngx_http_geo_range(cf, ctx, value);
468
469 } else {
470 rv = ngx_http_geo_cidr(cf, ctx, value);
471 }
472
473 done:
474
475 ngx_reset_pool(cf->pool);
476
477 return rv;
478
479 failed:
480
481 ngx_reset_pool(cf->pool);
482
483 return NGX_CONF_ERROR;
484 }
485
486
487 static char *
488 ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
489 ngx_str_t *value)
490 {
491 u_char *p, *last;
492 in_addr_t start, end;
493 ngx_str_t *net;
494 ngx_uint_t del;
495 ngx_http_variable_value_t *old;
496
497 if (ngx_strcmp(value[0].data, "default") == 0) {
498
499 old = ctx->high->default_value;
500
501 ctx->high->default_value = ngx_http_geo_value(cf, ctx, &value[1]);
502 if (ctx->high->default_value == NULL) {
503 return NGX_CONF_ERROR;
504 }
505
506 if (old) {
507 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
508 "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
509 &value[0], ctx->high->default_value, old);
510 }
511
512 return NGX_CONF_OK;
513 }
514
515 if (ngx_strcmp(value[0].data, "delete") == 0) {
516 net = &value[1];
517 del = 1;
518
519 } else {
520 net = &value[0];
521 del = 0;
522 }
523
524 last = net->data + net->len;
525
526 p = ngx_strlchr(net->data, last, '-');
527
528 if (p == NULL) {
529 goto invalid;
530 }
531
532 start = ngx_inet_addr(net->data, p - net->data);
533
534 if (start == INADDR_NONE) {
535 goto invalid;
536 }
537
538 start = ntohl(start);
539
540 p++;
541
542 end = ngx_inet_addr(p, last - p);
543
544 if (end == INADDR_NONE) {
545 goto invalid;
546 }
547
548 end = ntohl(end);
549
550 if (start > end) {
551 goto invalid;
552 }
553
554 if (del) {
555 if (ngx_http_geo_delete_range(cf, ctx, start, end)) {
556 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
557 "no address range \"%V\" to delete", net);
558 }
559
560 return NGX_CONF_OK;
561 }
562
563 ctx->value = ngx_http_geo_value(cf, ctx, &value[1]);
564
565 if (ctx->value == NULL) {
566 return NGX_CONF_ERROR;
567 }
568
569 ctx->net = net;
570
571 return ngx_http_geo_add_range(cf, ctx, start, end);
572
573 invalid:
574
575 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid range \"%V\"", net);
576
577 return NGX_CONF_ERROR;
578 }
579
580
581 /* the add procedure is optimized to add a growing up sequence */
582
583 static char *
584 ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
585 in_addr_t start, in_addr_t end)
586 {
587 in_addr_t n;
588 ngx_uint_t h, i, s, e;
589 ngx_array_t *a;
590 ngx_http_geo_range_t *range;
591
592 for (n = start; n <= end; n += 0x10000) {
593
594 h = n >> 16;
595
596 if (n == start) {
597 s = n & 0xffff;
598 } else {
599 s = 0;
600 }
601
602 if ((n | 0xffff) > end) {
603 e = end & 0xffff;
604
605 } else {
606 e = 0xffff;
607 }
608
609 a = (ngx_array_t *) ctx->high->low[h].ranges;
610
611 if (a == NULL) {
612 a = ngx_array_create(ctx->temp_pool, 64,
613 sizeof(ngx_http_geo_range_t));
614 if (a == NULL) {
615 return NGX_CONF_ERROR;
616 }
617
618 ctx->high->low[h].ranges = (ngx_http_geo_range_t *) a;
619 }
620
621 i = a->nelts;
622 range = a->elts;
623
624 while (i) {
625
626 i--;
627
628 if (e < (ngx_uint_t) range[i].start) {
629 continue;
630 }
631
632 if (s > (ngx_uint_t) range[i].end) {
633
634 /* add after the range */
635
636 range = ngx_array_push(a);
637 if (range == NULL) {
638 return NGX_CONF_ERROR;
639 }
640
641 range = a->elts;
642
643 ngx_memcpy(&range[i + 2], &range[i + 1],
644 (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));
645
646 range[i + 1].start = (u_short) s;
647 range[i + 1].end = (u_short) e;
648 range[i + 1].value = ctx->value;
649
650 goto next;
651 }
652
653 if (s == (ngx_uint_t) range[i].start
654 && e == (ngx_uint_t) range[i].end)
655 {
656 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
657 "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
658 ctx->net, ctx->value, range[i].value);
659
660 range[i].value = ctx->value;
661
662 goto next;
663 }
664
665 if (s > (ngx_uint_t) range[i].start
666 && e < (ngx_uint_t) range[i].end)
667 {
668 /* split the range and insert the new one */
669
670 range = ngx_array_push(a);
671 if (range == NULL) {
672 return NGX_CONF_ERROR;
673 }
674
675 range = ngx_array_push(a);
676 if (range == NULL) {
677 return NGX_CONF_ERROR;
678 }
679
680 range = a->elts;
681
682 ngx_memcpy(&range[i + 3], &range[i + 1],
683 (a->nelts - 3 - i) * sizeof(ngx_http_geo_range_t));
684
685 range[i + 2].start = (u_short) (e + 1);
686 range[i + 2].end = range[i].end;
687 range[i + 2].value = range[i].value;
688
689 range[i + 1].start = (u_short) s;
690 range[i + 1].end = (u_short) e;
691 range[i + 1].value = ctx->value;
692
693 range[i].end = (u_short) (s - 1);
694
695 goto next;
696 }
697
698 if (s == (ngx_uint_t) range[i].start
699 && e < (ngx_uint_t) range[i].end)
700 {
701 /* shift the range start and insert the new range */
702
703 range = ngx_array_push(a);
704 if (range == NULL) {
705 return NGX_CONF_ERROR;
706 }
707
708 range = a->elts;
709
710 ngx_memcpy(&range[i + 1], &range[i],
711 (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));
712
713 range[i + 1].start = (u_short) (e + 1);
714
715 range[i].start = (u_short) s;
716 range[i].end = (u_short) e;
717 range[i].value = ctx->value;
718
719 goto next;
720 }
721
722 if (s > (ngx_uint_t) range[i].start
723 && e == (ngx_uint_t) range[i].end)
724 {
725 /* shift the range end and insert the new range */
726
727 range = ngx_array_push(a);
728 if (range == NULL) {
729 return NGX_CONF_ERROR;
730 }
731
732 range = a->elts;
733
734 ngx_memcpy(&range[i + 2], &range[i + 1],
735 (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));
736
737 range[i + 1].start = (u_short) s;
738 range[i + 1].end = (u_short) e;
739 range[i + 1].value = ctx->value;
740
741 range[i].end = (u_short) (s - 1);
742
743 goto next;
744 }
745
746 s = (ngx_uint_t) range[i].start;
747 e = (ngx_uint_t) range[i].end;
748
749 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
750 "range \"%V\" overlaps \"%d.%d.%d.%d-%d.%d.%d.%d\"",
751 ctx->net,
752 h >> 8, h & 0xff, s >> 8, s & 0xff,
753 h >> 8, h & 0xff, e >> 8, e & 0xff);
754
755 return NGX_CONF_ERROR;
756 }
757
758 /* add the first range */
759
760 range = ngx_array_push(a);
761 if (range == NULL) {
762 return NGX_CONF_ERROR;
763 }
764
765 range->start = (u_short) s;
766 range->end = (u_short) e;
767 range->value = ctx->value;
768
769 next:
770
771 continue;
772 }
773
774 return NGX_CONF_OK;
775 }
776
777
778 static ngx_uint_t
779 ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
780 in_addr_t start, in_addr_t end)
781 {
782 in_addr_t n;
783 ngx_uint_t h, i, s, e, warn;
784 ngx_array_t *a;
785 ngx_http_geo_range_t *range;
786
787 warn = 0;
788
789 for (n = start; n <= end; n += 0x10000) {
790
791 h = n >> 16;
792
793 if (n == start) {
794 s = n & 0xffff;
795 } else {
796 s = 0;
797 }
798
799 if ((n | 0xffff) > end) {
800 e = end & 0xffff;
801
802 } else {
803 e = 0xffff;
804 }
805
806 a = (ngx_array_t *) ctx->high->low[h].ranges;
807
808 if (a == NULL) {
809 warn = 1;
810 continue;
811 }
812
813 range = a->elts;
814 for (i = 0; i < a->nelts; i++) {
815
816 if (s == (ngx_uint_t) range[i].start
817 && e == (ngx_uint_t) range[i].end)
818 {
819 ngx_memcpy(&range[i], &range[i + 1],
820 (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));
821
822 a->nelts--;
823
824 break;
825 }
826
827 if (s != (ngx_uint_t) range[i].start
828 && e != (ngx_uint_t) range[i].end)
829 {
830 continue;
831 }
832
833 warn = 1;
834 }
835 }
836
837 return warn;
838 }
839
840
841 static char *
842 ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
843 ngx_str_t *value)
844 {
845 ngx_int_t rc, del;
846 ngx_str_t *net;
847 ngx_uint_t i;
848 ngx_cidr_t cidr;
849 ngx_http_variable_value_t *val, *old;
850
851 if (ctx->tree == NULL) {
852 ctx->tree = ngx_radix_tree_create(ctx->pool, -1);
853 if (ctx->tree == NULL) {
854 return NGX_CONF_ERROR;
855 }
856 }
857
858 if (ngx_strcmp(value[0].data, "default") == 0) {
859 cidr.u.in.addr = 0;
860 cidr.u.in.mask = 0;
861 net = &value[0];
862
863 } else {
864 if (ngx_strcmp(value[0].data, "delete") == 0) {
865 net = &value[1];
866 del = 1;
867
868 } else {
869 net = &value[0];
870 del = 0;
871 }
872
873 if (ngx_http_geo_cidr_value(cf, net, &cidr) != NGX_OK) {
874 return NGX_CONF_ERROR;
875 }
876
877 if (del) {
878 if (ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr,
879 cidr.u.in.mask)
880 != NGX_OK)
881 {
882 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
883 "no network \"%V\" to delete", net);
884 }
885
886 return NGX_CONF_OK;
887 }
888 }
889
890 val = ngx_http_geo_value(cf, ctx, &value[1]);
891
892 if (val == NULL) {
893 return NGX_CONF_ERROR;
894 }
895
896 for (i = 2; i; i--) {
897 rc = ngx_radix32tree_insert(ctx->tree, cidr.u.in.addr, cidr.u.in.mask,
898 (uintptr_t) val);
899 if (rc == NGX_OK) {
900 return NGX_CONF_OK;
901 }
902
903 if (rc == NGX_ERROR) {
904 return NGX_CONF_ERROR;
905 }
906
907 /* rc == NGX_BUSY */
908
909 old = (ngx_http_variable_value_t *)
910 ngx_radix32tree_find(ctx->tree, cidr.u.in.addr & cidr.u.in.mask);
911
912 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
913 "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
914 net, val, old);
915
916 rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr, cidr.u.in.mask);
917
918 if (rc == NGX_ERROR) {
919 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
920 return NGX_CONF_ERROR;
921 }
922 }
923
924 return NGX_CONF_ERROR;
925 }
926
927
928 static ngx_http_variable_value_t *
929 ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
930 ngx_str_t *value)
931 {
932 uint32_t hash;
933 ngx_http_variable_value_t *val;
934 ngx_http_variable_value_node_t *vvn;
935
936 hash = ngx_crc32_long(value->data, value->len);
937
938 val = ngx_http_variable_value_lookup(&ctx->rbtree, value, hash);
939
940 if (val) {
941 return val;
942 }
943
944 val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));
945 if (val == NULL) {
946 return NULL;
947 }
948
949 val->len = value->len;
950 val->data = ngx_pstrdup(ctx->pool, value);
951 if (val->data == NULL) {
952 return NULL;
953 }
954
955 val->valid = 1;
956 val->no_cacheable = 0;
957 val->not_found = 0;
958
959 vvn = ngx_palloc(ctx->temp_pool, sizeof(ngx_http_variable_value_node_t));
960 if (vvn == NULL) {
961 return NULL;
962 }
963
964 vvn->node.key = hash;
965 vvn->len = val->len;
966 vvn->value = val;
967
968 ngx_rbtree_insert(&ctx->rbtree, &vvn->node);
969
970 return val;
971 }
972
973
974 static char *
975 ngx_http_geo_add_proxy(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
976 ngx_cidr_t *cidr)
977 {
978 ngx_in_cidr_t *c;
979
980 if (ctx->proxies == NULL) {
981 ctx->proxies = ngx_array_create(ctx->pool, 4, sizeof(ngx_in_cidr_t));
982 if (ctx->proxies == NULL) {
983 return NGX_CONF_ERROR;
984 }
985 }
986
987 c = ngx_array_push(ctx->proxies);
988 if (c == NULL) {
989 return NGX_CONF_ERROR;
990 }
991
992 c->addr = cidr->u.in.addr;
993 c->mask = cidr->u.in.mask;
994
995 return NGX_CONF_OK;
996 }
997
998
999 static ngx_int_t
1000 ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr)
1001 {
1002 ngx_int_t rc;
1003
1004 if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
1005 cidr->u.in.addr = 0xffffffff;
1006 cidr->u.in.mask = 0xffffffff;
1007
1008 return NGX_OK;
1009 }
1010
1011 rc = ngx_ptocidr(net, cidr);
1012
1013 if (rc == NGX_ERROR) {
1014 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net);
1015 return NGX_ERROR;
1016 }
1017
1018 if (cidr->family != AF_INET) {
1019 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"geo\" supports IPv4 only");
1020 return NGX_ERROR;
1021 }
1022
1023 if (rc == NGX_DONE) {
1024 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1025 "low address bits of %V are meaningless", net);
1026 }
1027
1028 cidr->u.in.addr = ntohl(cidr->u.in.addr);
1029 cidr->u.in.mask = ntohl(cidr->u.in.mask);
1030
1031 return NGX_OK;
1032 }
1033
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.