From bd530bc5f34e912e2eff325b495132a6a765b696 Mon Sep 17 00:00:00 2001 From: Lee Valentine Date: Sat, 26 Apr 2014 11:26:37 +0100 Subject: [PATCH] Added support for using multiple maxmind databases --- README.md | 15 +-- ngx_http_geoip2_module.c | 207 ++++++++++++++++++++++----------------- 2 files changed, 125 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index f29b43a..1bf43e5 100644 --- a/README.md +++ b/README.md @@ -24,13 +24,14 @@ The free GeoLite2 databases are available from [Maxminds website](http://dev.max ``` http { ... - geoip2_mmdb /etc/maxmind-city.mmdb; - geoip2_data $geoip2_data_country_code country iso_code; - geoip2_data $geoip2_data_country_name country names en; - geoip2_data $geoip2_data_city_name city names en; - geoip2_data $geoip2_data_geoname_id country geoname_id; - geoip2_data $geoip2_data_latitude location latitude; - geoip2_data $geoip2_data_country_code default=US country iso_code; + geoip2 /etc/maxmind-country.mmdb { + $geoip2_data_country_code default=US country iso_code; + $geoip2_data_country_name country names en; + } + + geoip2 /etc/maxmind-city.mmdb { + $geoip2_data_city_name default=London city names en; + } .... } ``` diff --git a/ngx_http_geoip2_module.c b/ngx_http_geoip2_module.c index fede9df..51baadb 100644 --- a/ngx_http_geoip2_module.c +++ b/ngx_http_geoip2_module.c @@ -13,20 +13,23 @@ typedef struct { - MMDB_s database; - int initialized; - ngx_array_t *proxies; - ngx_flag_t proxy_recursive; + MMDB_s mmdb; MMDB_lookup_result_s result; #if (NGX_HAVE_INET6) uint8_t address[16]; #else unsigned long address; #endif -} ngx_http_geoip2_conf_t; - +} ngx_http_geoip2_db_t; typedef struct { + ngx_array_t *databases; + ngx_array_t *proxies; + ngx_flag_t proxy_recursive; +} ngx_http_geoip2_conf_t; + +typedef struct { + ngx_http_geoip2_db_t *database; const char **lookup; ngx_str_t default_value; } ngx_http_geoip2_ctx_t; @@ -36,9 +39,9 @@ static ngx_int_t ngx_http_geoip2_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static void *ngx_http_geoip2_create_conf(ngx_conf_t *cf); static char *ngx_http_geoip2_init_conf(ngx_conf_t *cf, void *conf); -static char *ngx_http_geoip2_data(ngx_conf_t *cf, ngx_command_t *cmd, +static char *ngx_http_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static char *ngx_http_geoip2_mmdb(ngx_conf_t *cf, ngx_command_t *cmd, +static char *ngx_http_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); static char *ngx_http_geoip2_proxy(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -49,16 +52,9 @@ static void ngx_http_geoip2_cleanup(void *data); static ngx_command_t ngx_http_geoip2_commands[] = { - { ngx_string("geoip2_mmdb"), - NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, - ngx_http_geoip2_mmdb, - NGX_HTTP_MAIN_CONF_OFFSET, - 0, - NULL }, - - { ngx_string("geoip2_data"), - NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE, - ngx_http_geoip2_data, + { ngx_string("geoip2"), + NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1, + ngx_http_geoip2, NGX_HTTP_MAIN_CONF_OFFSET, 0, NULL }, @@ -117,6 +113,7 @@ ngx_http_geoip2_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { ngx_http_geoip2_ctx_t *geoip2 = (ngx_http_geoip2_ctx_t *) data; + ngx_http_geoip2_db_t *database = geoip2->database; int mmdb_error; MMDB_entry_data_s entry_data; ngx_http_geoip2_conf_t *gcf; @@ -131,11 +128,6 @@ ngx_http_geoip2_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, #endif gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip2_module); - - if (!gcf->initialized) { - goto not_found; - } - addr.sockaddr = r->connection->sockaddr; addr.socklen = r->connection->socklen; @@ -167,23 +159,23 @@ ngx_http_geoip2_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, } #if (NGX_HAVE_INET6) - if (ngx_memcmp(&address, &gcf->address, sizeof(address)) != 0) { + if (ngx_memcmp(&address, &database->address, sizeof(address)) + != 0) { #else - if (address != gcf->address) { + if (address != database->address) { #endif - memcpy(&gcf->address, &address, sizeof(address)); - gcf->result = MMDB_lookup_sockaddr(&gcf->database, addr.sockaddr, - &mmdb_error); + memcpy(&database->address, &address, sizeof(address)); + database->result = MMDB_lookup_sockaddr(&database->mmdb, + addr.sockaddr, &mmdb_error); if (mmdb_error != MMDB_SUCCESS) { goto not_found; } } - if (!gcf->result.found_entry || MMDB_aget_value(&gcf->result.entry, - &entry_data, - geoip2->lookup) - != MMDB_SUCCESS) { + if (!database->result.found_entry + || MMDB_aget_value(&database->result.entry, &entry_data, + geoip2->lookup) != MMDB_SUCCESS) { goto not_found; } @@ -242,7 +234,6 @@ ngx_http_geoip2_create_conf(ngx_conf_t *cf) } conf->proxy_recursive = NGX_CONF_UNSET; - conf->initialized = 0; cln = ngx_pool_cleanup_add(cf->pool, 0); if (cln == NULL) { @@ -257,12 +248,73 @@ ngx_http_geoip2_create_conf(ngx_conf_t *cf) static char * -ngx_http_geoip2_data(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +ngx_http_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_geoip2_conf_t *gcf = conf; + ngx_str_t *value; + int status, nelts, i; + ngx_http_geoip2_db_t *database; + char *rv; + ngx_conf_t save; + + value = cf->args->elts; + + if (gcf->databases == NULL) { + gcf->databases = ngx_array_create(cf->pool, 2, + sizeof(ngx_http_geoip2_db_t)); + if (gcf->databases == NULL) { + return NGX_CONF_ERROR; + } + } else { + nelts = (int) gcf->databases->nelts; + database = gcf->databases->elts; + + for (i = 0; i < nelts; i++) { + if (ngx_strcmp(value[1].data, database[i].mmdb.filename) == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "Duplicate GeoIP2 mmdb - %V", &value[1]); + return NGX_CONF_ERROR; + } + } + } + + database = ngx_array_push(gcf->databases); + if (database == NULL) { + return NGX_CONF_ERROR; + } + + status = MMDB_open((char *) value[1].data, MMDB_MODE_MMAP, &database->mmdb); + + if (status != MMDB_SUCCESS) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "MMDB_open(\"%V\") failed - %s", &value[1], + MMDB_strerror(status)); + return NGX_CONF_ERROR; + } + +#if (NGX_HAVE_INET6) + ngx_memset(&database->address, 0, sizeof(database->address)); +#else + database->address = 0; +#endif + + save = *cf; + cf->handler = ngx_http_geoip2_add_variable; + cf->handler_conf = (void *) database; + + rv = ngx_conf_parse(cf, NULL); + *cf = save; + return rv; +} + + +static char * +ngx_http_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) { ngx_str_t *value, name; ngx_http_geoip2_ctx_t *geoip2; ngx_http_variable_t *var; - int i, nelts, lookup_idx; + int i, nelts, idx; char *prefix = "default="; size_t prefix_len = sizeof("default=") - 1; @@ -271,15 +323,8 @@ ngx_http_geoip2_data(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_ERROR; } - geoip2->lookup = ngx_pcalloc(cf->pool, - sizeof(const char *) * (cf->args->nelts - 1)); - if (geoip2->lookup == NULL) { - return NGX_CONF_ERROR; - } - value = cf->args->elts; - name = value[1]; - nelts = (int) cf->args->nelts; + name = value[0]; if (name.data[0] != '$') { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, @@ -289,13 +334,15 @@ ngx_http_geoip2_data(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) name.len--; name.data++; + nelts = (int) cf->args->nelts; + idx = 1; + geoip2->database = (ngx_http_geoip2_db_t *) conf; - lookup_idx = 2; - if (nelts > 2 && value[2].len >= prefix_len && - ngx_strncmp(value[2].data, prefix, prefix_len) == 0) { - geoip2->default_value.len = value[2].len - prefix_len; - geoip2->default_value.data = value[2].data + prefix_len; - lookup_idx++; + if (nelts > idx && value[idx].len >= prefix_len && + ngx_strncmp(value[idx].data, prefix, prefix_len) == 0) { + geoip2->default_value.len = value[idx].len - prefix_len; + geoip2->default_value.data = value[idx].data + prefix_len; + idx++; } var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE); @@ -303,10 +350,17 @@ ngx_http_geoip2_data(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_ERROR; } - for (i = lookup_idx; i < nelts; i++) { - geoip2->lookup[i-lookup_idx] = (char *) value[i].data; + geoip2->lookup = ngx_pcalloc(cf->pool, sizeof(const char *) * + (cf->args->nelts - (idx - 1))); + + if (geoip2->lookup == NULL) { + return NGX_CONF_ERROR; } - geoip2->lookup[i] = NULL; + + for (i = idx; i < nelts; i++) { + geoip2->lookup[i - idx] = (char *) value[i].data; + } + geoip2->lookup[i - idx] = NULL; var->get_handler = ngx_http_geoip2_variable; var->data = (uintptr_t) geoip2; @@ -319,42 +373,7 @@ static char * ngx_http_geoip2_init_conf(ngx_conf_t *cf, void *conf) { ngx_http_geoip2_conf_t *gcf = conf; - ngx_conf_init_value(gcf->proxy_recursive, 0); - -#if (NGX_HAVE_INET6) - ngx_memset(&gcf->address, 0, sizeof(gcf->address)); -#else - gcf->address = 0; -#endif - - return NGX_CONF_OK; -} - - -static char * -ngx_http_geoip2_mmdb(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - ngx_http_geoip2_conf_t *gcf = conf; - ngx_str_t *value; - - if (gcf->initialized) { - return "is duplicate"; - } - - value = cf->args->elts; - - int status = MMDB_open((char *) value[1].data, - MMDB_MODE_MMAP, &gcf->database); - - if (status != MMDB_SUCCESS) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "MMDB_open(\"%V\") failed - %s", &value[1], - MMDB_strerror(status)); - return NGX_CONF_ERROR; - } - - gcf->initialized = 1; return NGX_CONF_OK; } @@ -389,6 +408,7 @@ ngx_http_geoip2_proxy(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_OK; } + static ngx_int_t ngx_http_geoip2_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr) { @@ -423,9 +443,16 @@ static void ngx_http_geoip2_cleanup(void *data) { ngx_http_geoip2_conf_t *gcf = data; + ngx_uint_t i; + ngx_http_geoip2_db_t *database; - if (gcf->initialized) { - MMDB_close(&gcf->database); - gcf->initialized = 0; + if (gcf->databases != NULL) { + database = gcf->databases->elts; + + for (i = 0; i < gcf->databases->nelts; i++) { + MMDB_close(&database[i].mmdb); + } + + ngx_array_destroy(gcf->databases); } }