From 4674ba7c9ca6dd0b5783e808597eb632cce425a6 Mon Sep 17 00:00:00 2001 From: Lee Valentine Date: Tue, 26 Jun 2018 01:14:54 +0100 Subject: [PATCH] Add support for retrieving mmdb metadata, specifically build_epoch --- README.md | 38 +++++++++--- ngx_http_geoip2_module.c | 114 ++++++++++++++++++++++++++++++----- ngx_stream_geoip2_module.c | 118 ++++++++++++++++++++++++++++++++----- 3 files changed, 232 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 31f4484..9cf430d 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ load_module modules/ngx_http_geoip2_module.so; ##### To build as a static module: ``` ./configure --add-module=/path/to/ngx_http_geoip2_module -make +make make install ``` @@ -48,6 +48,7 @@ The free GeoLite2 databases are available from [Maxminds website](http://dev.max http { ... geoip2 /etc/maxmind-country.mmdb { + $geoip2_metadata_country_build metadata build_epoch; $geoip2_data_country_code default=US source=$variable_with_ip country iso_code; $geoip2_data_country_name country names en; } @@ -72,23 +73,38 @@ stream { } ``` -To find the path of the data you want (eg: city names en), use the [mmdblookup tool](https://maxmind.github.io/libmaxminddb/mmdblookup.html): +##### Metadata: +Retrieve metadata regarding the geoip database. +``` +$variable_name metadata +``` +Currently the only metadata field supported is build_epoch. + +##### GeoIP: +``` +$variable_name [default= - "iso_code": + "iso_code": "US" - "names": + "names": { - "de": + "de": "USA" - "en": + "en": "United States" } } @@ -98,3 +114,9 @@ $ mmdblookup --file /usr/share/GeoIP/GeoIP2-Country.mmdb --ip 8.8.8.8 country na "United States" ``` + +This translates to: + +``` +$country_name "default=United States" source=$remote_addr country names en +``` diff --git a/ngx_http_geoip2_module.c b/ngx_http_geoip2_module.c index 0814e5b..b64afee 100644 --- a/ngx_http_geoip2_module.c +++ b/ngx_http_geoip2_module.c @@ -35,15 +35,26 @@ typedef struct { ngx_http_complex_value_t source; } ngx_http_geoip2_ctx_t; +typedef struct { + ngx_http_geoip2_db_t *database; + ngx_str_t metavalue; +} ngx_http_geoip2_metadata_t; + static ngx_int_t ngx_http_geoip2_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_geoip2_metadata(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(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); +static char *ngx_http_geoip2_add_variable_geodata(ngx_conf_t *cf, + ngx_http_geoip2_db_t *database); +static char *ngx_http_geoip2_add_variable_metadata(ngx_conf_t *cf, + ngx_http_geoip2_db_t *database); static char *ngx_http_geoip2_proxy(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_int_t ngx_http_geoip2_cidr_value(ngx_conf_t *cf, ngx_str_t *net, @@ -275,6 +286,29 @@ not_found: } +static ngx_int_t +ngx_http_geoip2_metadata(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + ngx_http_geoip2_metadata_t *metadata = (ngx_http_geoip2_metadata_t *) data; + ngx_http_geoip2_db_t *database = metadata->database; + u_char *p; + + if (ngx_strncmp(metadata->metavalue.data, "build_epoch", 11) == 0) { + FORMAT("%uL", database->mmdb.metadata.build_epoch); + } else { + v->not_found = 1; + return NGX_OK; + } + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; +} + + static void * ngx_http_geoip2_create_conf(ngx_conf_t *cf) { @@ -370,32 +404,84 @@ ngx_http_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) static char * ngx_http_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) { - ngx_str_t *value, name, source; - ngx_http_geoip2_ctx_t *geoip2; - ngx_http_variable_t *var; - int i, nelts, idx; - ngx_http_compile_complex_value_t ccv; + ngx_http_geoip2_db_t *database; + ngx_str_t *value; + int nelts; - geoip2 = ngx_pcalloc(cf->pool, sizeof(ngx_http_geoip2_ctx_t)); - if (geoip2 == NULL) { + value = cf->args->elts; + + if (value[0].data[0] != '$') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"%V\"", &value[0]); + return NGX_CONF_ERROR; + } + + value[0].len--; + value[0].data++; + + nelts = (int) cf->args->nelts; + database = (ngx_http_geoip2_db_t *) conf; + + if (nelts > 0 && value[1].len == 8 && ngx_strncmp(value[1].data, "metadata", 8) == 0) { + return ngx_http_geoip2_add_variable_metadata(cf, database); + } + + return ngx_http_geoip2_add_variable_geodata(cf, database); +} + + +static char * +ngx_http_geoip2_add_variable_metadata(ngx_conf_t *cf, ngx_http_geoip2_db_t *database) +{ + ngx_http_geoip2_metadata_t *metadata; + ngx_str_t *value, name; + ngx_http_variable_t *var; + + metadata = ngx_pcalloc(cf->pool, sizeof(ngx_http_geoip2_metadata_t)); + if (metadata == NULL) { return NGX_CONF_ERROR; } value = cf->args->elts; name = value[0]; - if (name.data[0] != '$') { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid variable name \"%V\"", &name); + metadata->database = database; + metadata->metavalue = value[2]; + + var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE); + if (var == NULL) { return NGX_CONF_ERROR; } - name.len--; - name.data++; + var->get_handler = ngx_http_geoip2_metadata; + var->data = (uintptr_t) metadata; + + return NGX_CONF_OK; +} + + +static char * +ngx_http_geoip2_add_variable_geodata(ngx_conf_t *cf, ngx_http_geoip2_db_t *database) +{ + ngx_http_geoip2_ctx_t *geoip2; + ngx_http_compile_complex_value_t ccv; + ngx_str_t *value, name, source; + ngx_http_variable_t *var; + int i, nelts, idx; + + geoip2 = ngx_pcalloc(cf->pool, sizeof(ngx_http_geoip2_ctx_t)); + if (geoip2 == NULL) { + return NGX_CONF_ERROR; + } + + geoip2->database = database; + ngx_str_null(&source); + + value = cf->args->elts; + name = value[0]; + nelts = (int) cf->args->nelts; idx = 1; - geoip2->database = (ngx_http_geoip2_db_t *) conf; - ngx_str_null(&source); if (nelts > idx) { for (i = idx; i < nelts; i++) { diff --git a/ngx_stream_geoip2_module.c b/ngx_stream_geoip2_module.c index 1bdeff8..4952b45 100644 --- a/ngx_stream_geoip2_module.c +++ b/ngx_stream_geoip2_module.c @@ -34,14 +34,25 @@ typedef struct { ngx_stream_complex_value_t source; } ngx_stream_geoip2_ctx_t; +typedef struct { + ngx_stream_geoip2_db_t *database; + ngx_str_t metavalue; +} ngx_stream_geoip2_metadata_t; + static ngx_int_t ngx_stream_geoip2_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_geoip2_metadata(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); static void *ngx_stream_geoip2_create_conf(ngx_conf_t *cf); static char *ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_stream_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); +static char *ngx_stream_geoip2_add_variable_geodata(ngx_conf_t *cf, + ngx_stream_geoip2_db_t *database); +static char *ngx_stream_geoip2_add_variable_metadata(ngx_conf_t *cf, + ngx_stream_geoip2_db_t *database); static void ngx_stream_geoip2_cleanup(void *data); @@ -244,6 +255,29 @@ not_found: } +static ngx_int_t +ngx_stream_geoip2_metadata(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, + uintptr_t data) +{ + ngx_stream_geoip2_metadata_t *metadata = (ngx_stream_geoip2_metadata_t *) data; + ngx_stream_geoip2_db_t *database = metadata->database; + u_char *p; + + if (ngx_strncmp(metadata->metavalue.data, "build_epoch", 11) == 0) { + FORMAT("%uL", database->mmdb.metadata.build_epoch); + } else { + v->not_found = 1; + return NGX_OK; + } + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; +} + + static void * ngx_stream_geoip2_create_conf(ngx_conf_t *cf) { @@ -270,10 +304,10 @@ ngx_stream_geoip2_create_conf(ngx_conf_t *cf) static char * ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - int status, nelts, i; + int status, nelts, i; char *rv; ngx_str_t *value; - ngx_conf_t save; + ngx_conf_t save; ngx_stream_geoip2_db_t *database; ngx_stream_geoip2_conf_t *gcf = conf; @@ -337,32 +371,84 @@ ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) static char * ngx_stream_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) { - int i, nelts, idx; - ngx_str_t *value, name, source; - ngx_stream_variable_t *var; - ngx_stream_geoip2_ctx_t *geoip2; - ngx_stream_compile_complex_value_t ccv; + ngx_stream_geoip2_db_t *database; + ngx_str_t *value; + int nelts; - geoip2 = ngx_pcalloc(cf->pool, sizeof(ngx_stream_geoip2_ctx_t)); - if (geoip2 == NULL) { + value = cf->args->elts; + + if (value[0].data[0] != '$') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"%V\"", &value[0]); + return NGX_CONF_ERROR; + } + + value[0].len--; + value[0].data++; + + nelts = (int) cf->args->nelts; + database = (ngx_stream_geoip2_db_t *) conf; + + if (nelts > 0 && value[1].len == 8 && ngx_strncmp(value[1].data, "metadata", 8) == 0) { + return ngx_stream_geoip2_add_variable_metadata(cf, database); + } + + return ngx_stream_geoip2_add_variable_geodata(cf, database); +} + + +static char * +ngx_stream_geoip2_add_variable_metadata(ngx_conf_t *cf, ngx_stream_geoip2_db_t *database) +{ + ngx_stream_geoip2_metadata_t *metadata; + ngx_str_t *value, name; + ngx_stream_variable_t *var; + + metadata = ngx_pcalloc(cf->pool, sizeof(ngx_stream_geoip2_metadata_t)); + if (metadata == NULL) { return NGX_CONF_ERROR; } value = cf->args->elts; name = value[0]; - if (name.data[0] != '$') { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid variable name \"%V\"", &name); + metadata->database = database; + metadata->metavalue = value[2]; + + var = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE); + if (var == NULL) { return NGX_CONF_ERROR; } - name.len--; - name.data++; + var->get_handler = ngx_stream_geoip2_metadata; + var->data = (uintptr_t) metadata; + + return NGX_CONF_OK; +} + + +static char * +ngx_stream_geoip2_add_variable_geodata(ngx_conf_t *cf, ngx_stream_geoip2_db_t *database) +{ + ngx_stream_geoip2_ctx_t *geoip2; + ngx_stream_compile_complex_value_t ccv; + ngx_str_t *value, name, source; + ngx_stream_variable_t *var; + int i, nelts, idx; + + geoip2 = ngx_pcalloc(cf->pool, sizeof(ngx_stream_geoip2_ctx_t)); + if (geoip2 == NULL) { + return NGX_CONF_ERROR; + } + + geoip2->database = database; + ngx_str_null(&source); + + value = cf->args->elts; + name = value[0]; + nelts = (int) cf->args->nelts; idx = 1; - geoip2->database = (ngx_stream_geoip2_db_t *) conf; - ngx_str_null(&source); if (nelts > idx) { for (i = idx; i < nelts; i++) {