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