aboutsummaryrefslogtreecommitdiff
path: root/src/http/modules/ngx_http_geo_module.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/http/modules/ngx_http_geo_module.c')
-rw-r--r--src/http/modules/ngx_http_geo_module.c252
1 files changed, 252 insertions, 0 deletions
diff --git a/src/http/modules/ngx_http_geo_module.c b/src/http/modules/ngx_http_geo_module.c
new file mode 100644
index 000000000..cd774894c
--- /dev/null
+++ b/src/http/modules/ngx_http_geo_module.c
@@ -0,0 +1,252 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ ngx_radix_tree_t *tree;
+ ngx_pool_t *pool;
+ ngx_array_t values;
+} ngx_http_geo_conf_t;
+
+
+static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
+
+
+static ngx_command_t ngx_http_geo_commands[] = {
+
+ { ngx_string("geo"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1,
+ ngx_http_geo_block,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_geo_module_ctx = {
+ NULL, /* pre conf */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_geo_module = {
+ NGX_MODULE,
+ &ngx_http_geo_module_ctx, /* module context */
+ ngx_http_geo_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init module */
+ NULL /* init process */
+};
+
+
+static ngx_http_variable_value_t ngx_http_geo_null_value =
+ { 0, ngx_string("0") };
+
+
+/* AF_INET only */
+
+static ngx_http_variable_value_t *ngx_http_geo_variable(ngx_http_request_t *r,
+ void *data)
+{
+ ngx_radix_tree_t *tree = data;
+
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in *) r->connection->sockaddr;
+
+ return (ngx_http_variable_value_t *)
+ ngx_radix32tree_find(tree, ntohl(sin->sin_addr.s_addr));
+}
+
+
+static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *rv;
+ ngx_str_t *value;
+ ngx_conf_t save;
+ ngx_pool_t *pool;
+ ngx_radix_tree_t *tree;
+ ngx_http_geo_conf_t geo;
+ ngx_http_variable_t *var;
+
+ if (!(var = ngx_http_add_variable(cf))) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (!(tree = ngx_radix_tree_create(cf->pool))) {
+ return NGX_CONF_ERROR;
+ }
+
+ value = cf->args->elts;
+
+ var->name = value[1];
+ var->handler = ngx_http_geo_variable;
+ var->data = tree;
+
+ /*
+ * create the temporary pool of a huge initial size
+ * to process quickly a large number of geo lines
+ */
+
+ if (!(pool = ngx_create_pool(512 * 1024, cf->log))) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_array_init(&geo.values, pool, 512,
+ sizeof(ngx_http_variable_value_t *)) == NGX_ERROR)
+ {
+ ngx_destroy_pool(pool);
+ return NGX_CONF_ERROR;
+ }
+
+ geo.tree = tree;
+ geo.pool = cf->pool;
+
+ save = *cf;
+ cf->pool = pool;
+ cf->ctx = &geo;
+ cf->handler = ngx_http_geo;
+ cf->handler_conf = conf;
+
+ rv = ngx_conf_parse(cf, NULL);
+
+ *cf = save;
+
+ ngx_destroy_pool(pool);
+
+ if (ngx_radix32tree_find(tree, 0) != NGX_RADIX_NO_VALUE) {
+ return rv;
+ }
+
+ if (ngx_radix32tree_insert(tree, 0, 0,
+ (uintptr_t) &ngx_http_geo_null_value) == NGX_ERROR)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ return rv;
+}
+
+
+/* AF_INET only */
+
+static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
+{
+ ngx_int_t rc, n;
+ ngx_uint_t i;
+ ngx_str_t *value, file;
+ ngx_inet_cidr_t cidrin;
+ ngx_http_geo_conf_t *geo;
+ ngx_http_variable_value_t *var, **v;
+
+ geo = cf->ctx;
+
+ if (cf->args->nelts != 2) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid number of the geo parameters");
+ return NGX_CONF_ERROR;
+ }
+
+ value = cf->args->elts;
+
+ if (ngx_strcmp(value[0].data, "include") == 0) {
+ file = value[1];
+
+ if (ngx_conf_full_name(cf->cycle, &file) == NGX_ERROR){
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
+
+ return ngx_conf_parse(cf, &file);
+ }
+
+ if (ngx_strcmp(value[0].data, "default") == 0) {
+ cidrin.addr = 0;
+ cidrin.mask = 0;
+
+ } else {
+ if (ngx_ptocidr(&value[0], &cidrin) == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[0]);
+ return NGX_CONF_ERROR;
+ }
+
+ cidrin.addr = ntohl(cidrin.addr);
+ cidrin.mask = ntohl(cidrin.mask);
+ }
+
+ n = ngx_atoi(value[1].data, value[1].len);
+
+ var = NULL;
+ v = geo->values.elts;
+
+ if (n == NGX_ERROR) {
+ for (i = 0; i < geo->values.nelts; i++) {
+ if (ngx_strcmp(value[1].data, v[i]->text.data) == 0) {
+ var = v[i];
+ break;
+ }
+ }
+
+ } else {
+ for (i = 0; i < geo->values.nelts; i++) {
+ if (v[i]->value == (ngx_uint_t) n) {
+ var = v[i];
+ break;
+ }
+ }
+ }
+
+ if (i == geo->values.nelts) {
+ var = ngx_palloc(geo->pool, sizeof(ngx_http_variable_value_t));
+ if (var == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ var->text.len = value[1].len;
+ if (!(var->text.data = ngx_pstrdup(geo->pool, &value[1]))) {
+ return NGX_CONF_ERROR;
+ }
+
+ var->value = (n == NGX_ERROR) ? 0 : n;
+
+ if (!(v = ngx_array_push(&geo->values))) {
+ return NGX_CONF_ERROR;
+ }
+
+ *v = var;
+ }
+
+ rc = ngx_radix32tree_insert(geo->tree, cidrin.addr, cidrin.mask,
+ (uintptr_t) var);
+ if (rc == NGX_ERROR) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (rc == NGX_BUSY) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "duplicate parameter \"%V\"",
+ &value[0]);
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}