Add support for retrieving mmdb metadata, specifically build_epoch

This commit is contained in:
Lee Valentine 2018-06-26 01:14:54 +01:00
parent f4364b50b4
commit 4674ba7c9c
3 changed files with 232 additions and 38 deletions

View file

@ -33,7 +33,7 @@ load_module modules/ngx_http_geoip2_module.so;
##### To build as a static module: ##### To build as a static module:
``` ```
./configure --add-module=/path/to/ngx_http_geoip2_module ./configure --add-module=/path/to/ngx_http_geoip2_module
make make
make install make install
``` ```
@ -48,6 +48,7 @@ The free GeoLite2 databases are available from [Maxminds website](http://dev.max
http { http {
... ...
geoip2 /etc/maxmind-country.mmdb { 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_code default=US source=$variable_with_ip country iso_code;
$geoip2_data_country_name country names en; $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 <field>
```
Currently the only metadata field supported is build_epoch.
##### GeoIP:
```
$variable_name [default=<value] [source=$variable_with_ip] path ...
```
If default is not specified, the variable will be empty if not found.
If source is not specified, $remote_addr will be used to perform the lookup.
To find the path of the data you want (eg: country names en), use the [mmdblookup tool](https://maxmind.github.io/libmaxminddb/mmdblookup.html):
``` ```
$ mmdblookup --file /usr/share/GeoIP/GeoIP2-Country.mmdb --ip 8.8.8.8 $ mmdblookup --file /usr/share/GeoIP/GeoIP2-Country.mmdb --ip 8.8.8.8
{ {
"country": "country":
{ {
"geoname_id": "geoname_id":
6252001 <uint32> 6252001 <uint32>
"iso_code": "iso_code":
"US" <utf8_string> "US" <utf8_string>
"names": "names":
{ {
"de": "de":
"USA" <utf8_string> "USA" <utf8_string>
"en": "en":
"United States" <utf8_string> "United States" <utf8_string>
} }
} }
@ -98,3 +114,9 @@ $ mmdblookup --file /usr/share/GeoIP/GeoIP2-Country.mmdb --ip 8.8.8.8 country na
"United States" <utf8_string> "United States" <utf8_string>
``` ```
This translates to:
```
$country_name "default=United States" source=$remote_addr country names en
```

View file

@ -35,15 +35,26 @@ typedef struct {
ngx_http_complex_value_t source; ngx_http_complex_value_t source;
} ngx_http_geoip2_ctx_t; } 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, static ngx_int_t ngx_http_geoip2_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data); 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 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_init_conf(ngx_conf_t *cf, void *conf);
static char *ngx_http_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, static char *ngx_http_geoip2(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf); void *conf);
static char *ngx_http_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, static char *ngx_http_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy,
void *conf); 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, static char *ngx_http_geoip2_proxy(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf); void *conf);
static ngx_int_t ngx_http_geoip2_cidr_value(ngx_conf_t *cf, ngx_str_t *net, 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 * static void *
ngx_http_geoip2_create_conf(ngx_conf_t *cf) 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 * static char *
ngx_http_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) ngx_http_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
{ {
ngx_str_t *value, name, source; ngx_http_geoip2_db_t *database;
ngx_http_geoip2_ctx_t *geoip2; ngx_str_t *value;
ngx_http_variable_t *var; int nelts;
int i, nelts, idx;
ngx_http_compile_complex_value_t ccv;
geoip2 = ngx_pcalloc(cf->pool, sizeof(ngx_http_geoip2_ctx_t)); value = cf->args->elts;
if (geoip2 == NULL) {
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; return NGX_CONF_ERROR;
} }
value = cf->args->elts; value = cf->args->elts;
name = value[0]; name = value[0];
if (name.data[0] != '$') { metadata->database = database;
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, metadata->metavalue = value[2];
"invalid variable name \"%V\"", &name);
var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
if (var == NULL) {
return NGX_CONF_ERROR; return NGX_CONF_ERROR;
} }
name.len--; var->get_handler = ngx_http_geoip2_metadata;
name.data++; 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; nelts = (int) cf->args->nelts;
idx = 1; idx = 1;
geoip2->database = (ngx_http_geoip2_db_t *) conf;
ngx_str_null(&source);
if (nelts > idx) { if (nelts > idx) {
for (i = idx; i < nelts; i++) { for (i = idx; i < nelts; i++) {

View file

@ -34,14 +34,25 @@ typedef struct {
ngx_stream_complex_value_t source; ngx_stream_complex_value_t source;
} ngx_stream_geoip2_ctx_t; } 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, static ngx_int_t ngx_stream_geoip2_variable(ngx_stream_session_t *s,
ngx_stream_variable_value_t *v, uintptr_t data); 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 void *ngx_stream_geoip2_create_conf(ngx_conf_t *cf);
static char *ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, static char *ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf); void *conf);
static char *ngx_stream_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, static char *ngx_stream_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy,
void *conf); 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); 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 * static void *
ngx_stream_geoip2_create_conf(ngx_conf_t *cf) ngx_stream_geoip2_create_conf(ngx_conf_t *cf)
{ {
@ -270,10 +304,10 @@ ngx_stream_geoip2_create_conf(ngx_conf_t *cf)
static char * static char *
ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{ {
int status, nelts, i; int status, nelts, i;
char *rv; char *rv;
ngx_str_t *value; ngx_str_t *value;
ngx_conf_t save; ngx_conf_t save;
ngx_stream_geoip2_db_t *database; ngx_stream_geoip2_db_t *database;
ngx_stream_geoip2_conf_t *gcf = conf; 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 * static char *
ngx_stream_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) ngx_stream_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
{ {
int i, nelts, idx; ngx_stream_geoip2_db_t *database;
ngx_str_t *value, name, source; ngx_str_t *value;
ngx_stream_variable_t *var; int nelts;
ngx_stream_geoip2_ctx_t *geoip2;
ngx_stream_compile_complex_value_t ccv;
geoip2 = ngx_pcalloc(cf->pool, sizeof(ngx_stream_geoip2_ctx_t)); value = cf->args->elts;
if (geoip2 == NULL) {
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; return NGX_CONF_ERROR;
} }
value = cf->args->elts; value = cf->args->elts;
name = value[0]; name = value[0];
if (name.data[0] != '$') { metadata->database = database;
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, metadata->metavalue = value[2];
"invalid variable name \"%V\"", &name);
var = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE);
if (var == NULL) {
return NGX_CONF_ERROR; return NGX_CONF_ERROR;
} }
name.len--; var->get_handler = ngx_stream_geoip2_metadata;
name.data++; 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; nelts = (int) cf->args->nelts;
idx = 1; idx = 1;
geoip2->database = (ngx_stream_geoip2_db_t *) conf;
ngx_str_null(&source);
if (nelts > idx) { if (nelts > idx) {
for (i = idx; i < nelts; i++) { for (i = idx; i < nelts; i++) {