[PATCH 6 of 6] GeoIP: support for GeoIP2 databases in the MMDB format
Maxim Dounin
mdounin at mdounin.ru
Wed Dec 3 16:31:10 UTC 2025
# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1764579291 -10800
# Mon Dec 01 11:54:51 2025 +0300
# Node ID 234a79a752d46a21ecaccddfda660ca2cbfe15f4
# Parent 9d4db0c2b512cb8d548345db2852c2d6a000d49c
GeoIP: support for GeoIP2 databases in the MMDB format.
With this change, the ngx_http_geoip_module can be compiled with the
GeoIP library (legacy), or the libmaxminddb library, or both. Usage
of specific libraries can be disabled during compilation with new configure
options, "--without-geoip" and "--without-libmaxminddb".
Existing directives (geoip_country, geoip_org, geoip_city) support both
file formats, and existing variables are mapped to the appropriate data
paths in the MaxMind GeoIP2 databases, for example:
geoip_country /path/to/GeoLite2-Country.mmdb;
Notable exceptions: the $geoip_country_code3, $geoip_city_country_code3,
and $geoip_area_code variables are no longer available if GeoIP2 databases
are used, because the underlying fields are not available in the new
databases.
Additionally, the geoip_set directive is introduced. It can be used to
access arbitrary data paths within an MMDB database, for example:
geoip_set $country /path/to/GeoLite2-Country.mmdb country.names.en;
geoip_set $asn /path/to/GeoLite2-ASN.mmdb autonomous_system_number;
The last parameter is the data path to look up in the database, similarly
to the one accepted by the mmdblookup utility (but with dots between
path components).
diff --git a/auto/lib/geoip/conf b/auto/lib/geoip/conf
--- a/auto/lib/geoip/conf
+++ b/auto/lib/geoip/conf
@@ -1,10 +1,15 @@
# Copyright (C) Igor Sysoev
+# Copyright (C) Maxim Dounin
# Copyright (C) Nginx, Inc.
+ngx_geoip_found=no
+
+if [ $GEOIP_LEGACY != DISABLED ]; then
+
ngx_feature="GeoIP library"
- ngx_feature_name=
+ ngx_feature_name="NGX_HAVE_GEOIP_LEGACY"
ngx_feature_run=no
ngx_feature_incs="#include <GeoIP.h>"
ngx_feature_path=
@@ -12,84 +17,160 @@
ngx_feature_test="GeoIP_open(NULL, 0)"
. auto/feature
+ if [ $ngx_found = no ]; then
-if [ $ngx_found = no ]; then
+ # FreeBSD port
+
+ ngx_feature="GeoIP library in /usr/local/"
+ ngx_feature_path="/usr/local/include"
- # FreeBSD port
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lGeoIP"
+ else
+ ngx_feature_libs="-L/usr/local/lib -lGeoIP"
+ fi
- ngx_feature="GeoIP library in /usr/local/"
- ngx_feature_path="/usr/local/include"
+ . auto/feature
+ fi
+
+ if [ $ngx_found = no ]; then
+
+ # NetBSD port
- if [ $NGX_RPATH = YES ]; then
- ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lGeoIP"
- else
- ngx_feature_libs="-L/usr/local/lib -lGeoIP"
+ ngx_feature="GeoIP library in /usr/pkg/"
+ ngx_feature_path="/usr/pkg/include"
+
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lGeoIP"
+ else
+ ngx_feature_libs="-L/usr/pkg/lib -lGeoIP"
+ fi
+
+ . auto/feature
fi
- . auto/feature
-fi
-
+ if [ $ngx_found = no ]; then
-if [ $ngx_found = no ]; then
+ # MacPorts
- # NetBSD port
+ ngx_feature="GeoIP library in /opt/local/"
+ ngx_feature_path="/opt/local/include"
- ngx_feature="GeoIP library in /usr/pkg/"
- ngx_feature_path="/usr/pkg/include"
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lGeoIP"
+ else
+ ngx_feature_libs="-L/opt/local/lib -lGeoIP"
+ fi
- if [ $NGX_RPATH = YES ]; then
- ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lGeoIP"
- else
- ngx_feature_libs="-L/usr/pkg/lib -lGeoIP"
+ . auto/feature
fi
- . auto/feature
+ if [ $ngx_found = yes ]; then
+
+ CORE_INCS="$CORE_INCS $ngx_feature_path"
+
+ if [ $USE_GEOIP = YES ]; then
+ CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
+ fi
+
+ NGX_LIB_GEOIP=$ngx_feature_libs
+
+ ngx_feature="GeoIP IPv6 support"
+ ngx_feature_name="NGX_HAVE_GEOIP_V6"
+ ngx_feature_run=no
+ ngx_feature_incs="#include <stdio.h>
+ #include <GeoIP.h>"
+ #ngx_feature_path=
+ #ngx_feature_libs=
+ ngx_feature_test="printf(\"%d\", GEOIP_CITY_EDITION_REV0_V6);"
+ . auto/feature
+
+ ngx_geoip_found=yes
+ fi
fi
-if [ $ngx_found = no ]; then
+if [ $GEOIP_MMDB != DISABLED ]; then
- # MacPorts
+ ngx_feature="libmaxminddb"
+ ngx_feature_name="NGX_HAVE_GEOIP_MMDB"
+ ngx_feature_run=no
+ ngx_feature_incs="#include <maxminddb.h>"
+ ngx_feature_path=
+ ngx_feature_libs="-lmaxminddb"
+ ngx_feature_test="MMDB_open(NULL, 0, NULL)"
+ . auto/feature
+
+ if [ $ngx_found = no ]; then
- ngx_feature="GeoIP library in /opt/local/"
- ngx_feature_path="/opt/local/include"
+ # FreeBSD port
+
+ ngx_feature="libmaxminddb in /usr/local/"
+ ngx_feature_path="/usr/local/include"
- if [ $NGX_RPATH = YES ]; then
- ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lGeoIP"
- else
- ngx_feature_libs="-L/opt/local/lib -lGeoIP"
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lmaxminddb"
+ else
+ ngx_feature_libs="-L/usr/local/lib -lmaxminddb"
+ fi
+
+ . auto/feature
fi
- . auto/feature
+ if [ $ngx_found = no ]; then
+
+ # NetBSD port
+
+ ngx_feature="libmaxminddb in /usr/pkg/"
+ ngx_feature_path="/usr/pkg/include"
+
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lmaxminddb"
+ else
+ ngx_feature_libs="-L/usr/pkg/lib -lmaxminddb"
+ fi
+
+ . auto/feature
+ fi
+
+ if [ $ngx_found = no ]; then
+
+ # MacPorts
+
+ ngx_feature="libmaxminddb in /opt/local/"
+ ngx_feature_path="/opt/local/include"
+
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lmaxminddb"
+ else
+ ngx_feature_libs="-L/opt/local/lib -lmaxminddb"
+ fi
+
+ . auto/feature
+ fi
+
+ if [ $ngx_found = yes ]; then
+
+ ngx_geoip_found=yes
+
+ CORE_INCS="$CORE_INCS $ngx_feature_path"
+
+ if [ $USE_GEOIP = YES ]; then
+ CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
+ fi
+
+ NGX_LIB_GEOIP="$NGX_LIB_GEOIP $ngx_feature_libs"
+ fi
fi
-if [ $ngx_found = yes ]; then
-
- CORE_INCS="$CORE_INCS $ngx_feature_path"
-
- if [ $USE_GEOIP = YES ]; then
- CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
- fi
-
- NGX_LIB_GEOIP=$ngx_feature_libs
-
- ngx_feature="GeoIP IPv6 support"
- ngx_feature_name="NGX_HAVE_GEOIP_V6"
- ngx_feature_run=no
- ngx_feature_incs="#include <stdio.h>
- #include <GeoIP.h>"
- #ngx_feature_path=
- #ngx_feature_libs=
- ngx_feature_test="printf(\"%d\", GEOIP_CITY_EDITION_REV0_V6);"
- . auto/feature
-
-else
+if [ $ngx_geoip_found != yes ]; then
cat << END
-$0: error: the GeoIP module requires the GeoIP library.
-You can either do not enable the module or install the library.
+$0: error: the GeoIP modules require the GeoIP library or
+the libmaxminddb library (or both). You can either do not enable
+the modules or install at least one of the libraries.
END
diff --git a/auto/options b/auto/options
--- a/auto/options
+++ b/auto/options
@@ -165,7 +165,10 @@ NGX_PERL=perl
USE_LIBXSLT=NO
USE_LIBGD=NO
+
USE_GEOIP=NO
+GEOIP_LEGACY=YES
+GEOIP_MMDB=YES
NGX_GOOGLE_PERFTOOLS=NO
NGX_CPP_TEST=NO
@@ -405,6 +408,9 @@ use the \"--with-mail_ssl_module\" optio
--with-libatomic) NGX_LIBATOMIC=YES ;;
--with-libatomic=*) NGX_LIBATOMIC="$value" ;;
+ --without-geoip) GEOIP_LEGACY=DISABLED ;;
+ --without-libmaxminddb) GEOIP_MMDB=DISABLED ;;
+
--test-build-devpoll) NGX_TEST_BUILD_DEVPOLL=YES ;;
--test-build-eventport) NGX_TEST_BUILD_EVENTPORT=YES ;;
--test-build-epoll) NGX_TEST_BUILD_EPOLL=YES ;;
@@ -601,6 +607,9 @@ cat << END
--with-openssl=DIR set path to OpenSSL library sources
--with-openssl-opt=OPTIONS set additional build options for OpenSSL
+ --without-geoip do not use GeoIP library
+ --without-libmaxminddb do not use libmaxminddb library
+
--with-debug enable debug logging
END
diff --git a/src/http/modules/ngx_http_geoip_module.c b/src/http/modules/ngx_http_geoip_module.c
--- a/src/http/modules/ngx_http_geoip_module.c
+++ b/src/http/modules/ngx_http_geoip_module.c
@@ -1,6 +1,7 @@
/*
* Copyright (C) Igor Sysoev
+ * Copyright (C) Maxim Dounin
* Copyright (C) Nginx, Inc.
*/
@@ -9,27 +10,59 @@
#include <ngx_core.h>
#include <ngx_http.h>
+
+#if (NGX_HAVE_GEOIP_LEGACY)
+
#include <GeoIP.h>
#include <GeoIPCity.h>
+#endif
-#define NGX_GEOIP_COUNTRY_CODE 0
-#define NGX_GEOIP_COUNTRY_CODE3 1
-#define NGX_GEOIP_COUNTRY_NAME 2
+#if (NGX_HAVE_GEOIP_MMDB)
+
+#include <maxminddb.h>
+
+#endif
+
+
+#define NGX_GEOIP_COUNTRY_CODE 0
+#define NGX_GEOIP_COUNTRY_CODE3 1
+#define NGX_GEOIP_COUNTRY_NAME 2
+#define NGX_GEOIP_CONTINENT_CODE 3
+#define NGX_GEOIP_REGION 4
+#define NGX_GEOIP_REGION_NAME 5
+#define NGX_GEOIP_CITY 6
+#define NGX_GEOIP_POSTAL_CODE 7
+#define NGX_GEOIP_LATITUDE 8
+#define NGX_GEOIP_LONGITUDE 9
+#define NGX_GEOIP_DMA_CODE 10
+#define NGX_GEOIP_AREA_CODE 11
typedef struct {
- GeoIP *country;
- GeoIP *org;
- GeoIP *city;
+ void *country;
+ void *org;
+ void *city;
ngx_array_t *proxies; /* array of ngx_cidr_t */
+ ngx_array_t *mmdb; /* array of MMDB_s */
ngx_flag_t proxy_recursive;
-#if (NGX_HAVE_GEOIP_V6)
unsigned country_v6:1;
+ unsigned country_mmdb:1;
unsigned org_v6:1;
+ unsigned org_mmdb:1;
unsigned city_v6:1;
+ unsigned city_mmdb:1;
+} ngx_http_geoip_conf_t;
+
+
+#if (NGX_HAVE_GEOIP_MMDB)
+
+typedef struct {
+ MMDB_s *mmdb;
+ const char **path;
+} ngx_http_geoip_variable_t;
+
#endif
-} ngx_http_geoip_conf_t;
static ngx_int_t ngx_http_geoip_country_variable(ngx_http_request_t *r,
@@ -44,14 +77,25 @@ static ngx_int_t ngx_http_geoip_city_flo
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_geoip_city_int_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
+#if (NGX_HAVE_GEOIP_LEGACY)
static GeoIPRecord *ngx_http_geoip_get_city_record(ngx_http_request_t *r);
+#endif
+#if (NGX_HAVE_GEOIP_MMDB)
+static ngx_int_t ngx_http_geoip_mmdb_city_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_geoip_mmdb_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+#endif
+
+#if (NGX_HAVE_GEOIP_LEGACY)
static u_long ngx_http_geoip_addr(ngx_http_request_t *r,
ngx_http_geoip_conf_t *gcf);
#if (NGX_HAVE_GEOIP_V6)
static geoipv6_t ngx_http_geoip_addr_v6(ngx_http_request_t *r,
ngx_http_geoip_conf_t *gcf);
#endif
+#endif
static struct sockaddr *ngx_http_geoip_sockaddr(ngx_http_request_t *r,
ngx_http_geoip_conf_t *gcf);
@@ -64,6 +108,15 @@ static char *ngx_http_geoip_org(ngx_conf
void *conf);
static char *ngx_http_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
+static char *ngx_http_geoip_set(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+#if (NGX_HAVE_GEOIP_MMDB)
+static MMDB_s *ngx_http_geoip_mmdb_open(ngx_conf_t *cf,
+ ngx_http_geoip_conf_t *gcf, ngx_str_t *file);
+#endif
+#if (NGX_HAVE_GEOIP_LEGACY)
+static ngx_int_t ngx_http_geoip_mmdb_file(ngx_str_t *file);
+#endif
static char *ngx_http_geoip_proxy(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_int_t ngx_http_geoip_cidr_value(ngx_conf_t *cf, ngx_str_t *net,
@@ -94,6 +147,13 @@ static ngx_command_t ngx_http_geoip_com
0,
NULL },
+ { ngx_string("geoip_set"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3,
+ ngx_http_geoip_set,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ 0,
+ NULL },
+
{ ngx_string("geoip_proxy"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
ngx_http_geoip_proxy,
@@ -163,61 +223,103 @@ static ngx_http_variable_t ngx_http_geo
{ ngx_string("geoip_city_continent_code"), NULL,
ngx_http_geoip_city_variable,
- offsetof(GeoIPRecord, continent_code), 0, 0 },
+ NGX_GEOIP_CONTINENT_CODE, 0, 0 },
{ ngx_string("geoip_city_country_code"), NULL,
ngx_http_geoip_city_variable,
- offsetof(GeoIPRecord, country_code), 0, 0 },
+ NGX_GEOIP_COUNTRY_CODE, 0, 0 },
{ ngx_string("geoip_city_country_code3"), NULL,
ngx_http_geoip_city_variable,
- offsetof(GeoIPRecord, country_code3), 0, 0 },
+ NGX_GEOIP_COUNTRY_CODE3, 0, 0 },
{ ngx_string("geoip_city_country_name"), NULL,
ngx_http_geoip_city_variable,
- offsetof(GeoIPRecord, country_name), 0, 0 },
+ NGX_GEOIP_COUNTRY_NAME, 0, 0 },
{ ngx_string("geoip_region"), NULL,
ngx_http_geoip_city_variable,
- offsetof(GeoIPRecord, region), 0, 0 },
+ NGX_GEOIP_REGION, 0, 0 },
{ ngx_string("geoip_region_name"), NULL,
ngx_http_geoip_region_name_variable,
- 0, 0, 0 },
+ NGX_GEOIP_REGION_NAME, 0, 0 },
{ ngx_string("geoip_city"), NULL,
ngx_http_geoip_city_variable,
- offsetof(GeoIPRecord, city), 0, 0 },
+ NGX_GEOIP_CITY, 0, 0 },
{ ngx_string("geoip_postal_code"), NULL,
ngx_http_geoip_city_variable,
- offsetof(GeoIPRecord, postal_code), 0, 0 },
+ NGX_GEOIP_POSTAL_CODE, 0, 0 },
{ ngx_string("geoip_latitude"), NULL,
ngx_http_geoip_city_float_variable,
- offsetof(GeoIPRecord, latitude), 0, 0 },
+ NGX_GEOIP_LATITUDE, 0, 0 },
{ ngx_string("geoip_longitude"), NULL,
ngx_http_geoip_city_float_variable,
- offsetof(GeoIPRecord, longitude), 0, 0 },
+ NGX_GEOIP_LONGITUDE, 0, 0 },
{ ngx_string("geoip_dma_code"), NULL,
ngx_http_geoip_city_int_variable,
- offsetof(GeoIPRecord, dma_code), 0, 0 },
+ NGX_GEOIP_DMA_CODE, 0, 0 },
{ ngx_string("geoip_area_code"), NULL,
ngx_http_geoip_city_int_variable,
- offsetof(GeoIPRecord, area_code), 0, 0 },
+ NGX_GEOIP_AREA_CODE, 0, 0 },
ngx_http_null_variable
};
+#if (NGX_HAVE_GEOIP_LEGACY)
+
+static uintptr_t ngx_http_geoip_city_offsets[] = {
+ offsetof(GeoIPRecord, country_code),
+ offsetof(GeoIPRecord, country_code3),
+ offsetof(GeoIPRecord, country_name),
+ offsetof(GeoIPRecord, continent_code),
+ offsetof(GeoIPRecord, region),
+ 0, /* region name */
+ offsetof(GeoIPRecord, city),
+ offsetof(GeoIPRecord, postal_code),
+ offsetof(GeoIPRecord, latitude),
+ offsetof(GeoIPRecord, longitude),
+ offsetof(GeoIPRecord, dma_code),
+ offsetof(GeoIPRecord, area_code)
+};
+
+#endif
+
+
+#if (NGX_HAVE_GEOIP_MMDB)
+
+static const char *ngx_http_geoip_mmdb_paths[][5] = {
+ { "country", "iso_code", NULL, NULL, NULL },
+ { NULL, NULL, NULL, NULL, NULL }, /* country code3 */
+ { "country", "names", "en", NULL, NULL },
+ { "continent", "code", NULL, NULL, NULL },
+ { "subdivisions", "0", "iso_code", NULL, NULL },
+ { "subdivisions", "0", "names", "en", NULL },
+ { "city", "names", "en", NULL, NULL },
+ { "postal", "code", NULL, NULL, NULL },
+ { "location", "latitude", NULL, NULL, NULL },
+ { "location", "longitude", NULL, NULL, NULL },
+ { "location", "metro_code", NULL, NULL, NULL },
+ { NULL, NULL, NULL, NULL, NULL } /* area code */
+};
+
+#endif
+
+
static ngx_int_t
ngx_http_geoip_country_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
+#if (NGX_HAVE_GEOIP_LEGACY)
const char *val;
+#endif
ngx_http_geoip_conf_t *gcf;
gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
@@ -226,6 +328,21 @@ ngx_http_geoip_country_variable(ngx_http
goto not_found;
}
+#if (NGX_HAVE_GEOIP_MMDB)
+
+ if (gcf->country_mmdb) {
+ ngx_http_geoip_variable_t gv;
+
+ gv.mmdb = gcf->country;
+ gv.path = ngx_http_geoip_mmdb_paths[data];
+
+ return ngx_http_geoip_mmdb_variable(r, v, (uintptr_t) &gv);
+ }
+
+#endif
+
+#if (NGX_HAVE_GEOIP_LEGACY)
+
#if (NGX_HAVE_GEOIP_V6)
if (gcf->country_v6) {
if (data == NGX_GEOIP_COUNTRY_CODE) {
@@ -269,6 +386,8 @@ ngx_http_geoip_country_variable(ngx_http
return NGX_OK;
+#endif
+
not_found:
v->not_found = 1;
@@ -281,8 +400,10 @@ static ngx_int_t
ngx_http_geoip_org_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
+#if (NGX_HAVE_GEOIP_LEGACY)
size_t len;
char *val;
+#endif
ngx_http_geoip_conf_t *gcf;
gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
@@ -291,6 +412,32 @@ ngx_http_geoip_org_variable(ngx_http_req
goto not_found;
}
+#if (NGX_HAVE_GEOIP_MMDB)
+
+ if (gcf->org_mmdb) {
+ ngx_int_t rc;
+ ngx_http_geoip_variable_t gv;
+
+ static const char *org[] = { "organization", NULL };
+ static const char *asorg[] = { "autonomous_system_organization", NULL };
+
+ gv.mmdb = gcf->org;
+ gv.path = org;
+
+ rc = ngx_http_geoip_mmdb_variable(r, v, (uintptr_t) &gv);
+
+ if (rc == NGX_OK && v->not_found) {
+ gv.path = asorg;
+ rc = ngx_http_geoip_mmdb_variable(r, v, (uintptr_t) &gv);
+ }
+
+ return rc;
+ }
+
+#endif
+
+#if (NGX_HAVE_GEOIP_LEGACY)
+
#if (NGX_HAVE_GEOIP_V6)
if (gcf->org_v6) {
val = GeoIP_name_by_ipnum_v6(gcf->org, ngx_http_geoip_addr_v6(r, gcf));
@@ -323,6 +470,8 @@ ngx_http_geoip_org_variable(ngx_http_req
return NGX_OK;
+#endif
+
not_found:
v->not_found = 1;
@@ -335,16 +484,32 @@ static ngx_int_t
ngx_http_geoip_city_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
+#if (NGX_HAVE_GEOIP_LEGACY)
char *val;
size_t len;
GeoIPRecord *gr;
+#endif
+
+#if (NGX_HAVE_GEOIP_MMDB)
+
+ ngx_int_t rc;
+
+ rc = ngx_http_geoip_mmdb_city_variable(r, v, data);
+
+ if (rc != NGX_DECLINED) {
+ return rc;
+ }
+
+#endif
+
+#if (NGX_HAVE_GEOIP_LEGACY)
gr = ngx_http_geoip_get_city_record(r);
if (gr == NULL) {
goto not_found;
}
- val = *(char **) ((char *) gr + data);
+ val = *(char **) ((char *) gr + ngx_http_geoip_city_offsets[data]);
if (val == NULL) {
goto no_value;
}
@@ -373,6 +538,8 @@ no_value:
not_found:
+#endif
+
v->not_found = 1;
return NGX_OK;
@@ -383,9 +550,25 @@ static ngx_int_t
ngx_http_geoip_region_name_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
+#if (NGX_HAVE_GEOIP_LEGACY)
size_t len;
const char *val;
GeoIPRecord *gr;
+#endif
+
+#if (NGX_HAVE_GEOIP_MMDB)
+
+ ngx_int_t rc;
+
+ rc = ngx_http_geoip_mmdb_city_variable(r, v, data);
+
+ if (rc != NGX_DECLINED) {
+ return rc;
+ }
+
+#endif
+
+#if (NGX_HAVE_GEOIP_LEGACY)
gr = ngx_http_geoip_get_city_record(r);
if (gr == NULL) {
@@ -417,6 +600,8 @@ ngx_http_geoip_region_name_variable(ngx_
not_found:
+#endif
+
v->not_found = 1;
return NGX_OK;
@@ -427,13 +612,28 @@ static ngx_int_t
ngx_http_geoip_city_float_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
+#if (NGX_HAVE_GEOIP_LEGACY)
float val;
GeoIPRecord *gr;
+#endif
+
+#if (NGX_HAVE_GEOIP_MMDB)
+
+ ngx_int_t rc;
+
+ rc = ngx_http_geoip_mmdb_city_variable(r, v, data);
+
+ if (rc != NGX_DECLINED) {
+ return rc;
+ }
+
+#endif
+
+#if (NGX_HAVE_GEOIP_LEGACY)
gr = ngx_http_geoip_get_city_record(r);
if (gr == NULL) {
- v->not_found = 1;
- return NGX_OK;
+ goto not_found;
}
v->data = ngx_pnalloc(r->pool, NGX_INT64_LEN + 5);
@@ -442,7 +642,7 @@ ngx_http_geoip_city_float_variable(ngx_h
return NGX_ERROR;
}
- val = *(float *) ((char *) gr + data);
+ val = *(float *) ((char *) gr + ngx_http_geoip_city_offsets[data]);
v->len = ngx_sprintf(v->data, "%.4f", val) - v->data;
v->valid = 1;
@@ -452,6 +652,14 @@ ngx_http_geoip_city_float_variable(ngx_h
GeoIPRecord_delete(gr);
return NGX_OK;
+
+not_found:
+
+#endif
+
+ v->not_found = 1;
+
+ return NGX_OK;
}
@@ -459,13 +667,28 @@ static ngx_int_t
ngx_http_geoip_city_int_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
+#if (NGX_HAVE_GEOIP_LEGACY)
int val;
GeoIPRecord *gr;
+#endif
+
+#if (NGX_HAVE_GEOIP_MMDB)
+
+ ngx_int_t rc;
+
+ rc = ngx_http_geoip_mmdb_city_variable(r, v, data);
+
+ if (rc != NGX_DECLINED) {
+ return rc;
+ }
+
+#endif
+
+#if (NGX_HAVE_GEOIP_LEGACY)
gr = ngx_http_geoip_get_city_record(r);
if (gr == NULL) {
- v->not_found = 1;
- return NGX_OK;
+ goto not_found;
}
v->data = ngx_pnalloc(r->pool, NGX_INT64_LEN);
@@ -474,7 +697,7 @@ ngx_http_geoip_city_int_variable(ngx_htt
return NGX_ERROR;
}
- val = *(int *) ((char *) gr + data);
+ val = *(int *) ((char *) gr + ngx_http_geoip_city_offsets[data]);
v->len = ngx_sprintf(v->data, "%d", val) - v->data;
v->valid = 1;
@@ -484,9 +707,19 @@ ngx_http_geoip_city_int_variable(ngx_htt
GeoIPRecord_delete(gr);
return NGX_OK;
+
+not_found:
+
+#endif
+
+ v->not_found = 1;
+
+ return NGX_OK;
}
+#if (NGX_HAVE_GEOIP_LEGACY)
+
static GeoIPRecord *
ngx_http_geoip_get_city_record(ngx_http_request_t *r)
{
@@ -507,6 +740,172 @@ ngx_http_geoip_get_city_record(ngx_http_
return NULL;
}
+#endif
+
+
+#if (NGX_HAVE_GEOIP_MMDB)
+
+static ngx_int_t
+ngx_http_geoip_mmdb_city_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_http_geoip_conf_t *gcf;
+ ngx_http_geoip_variable_t gv;
+
+ gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
+
+ if (!gcf->city_mmdb) {
+ return NGX_DECLINED;
+ }
+
+ gv.mmdb = gcf->city;
+ gv.path = ngx_http_geoip_mmdb_paths[data];
+
+ return ngx_http_geoip_mmdb_variable(r, v, (uintptr_t) &gv);
+}
+
+
+static ngx_int_t
+ngx_http_geoip_mmdb_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_http_geoip_variable_t *gv = (ngx_http_geoip_variable_t *) data;
+
+ int status;
+ struct sockaddr *sockaddr;
+ MMDB_entry_data_s entry;
+ MMDB_lookup_result_s result;
+ ngx_http_geoip_conf_t *gcf;
+
+ if (gv->path[0] == NULL) {
+ goto not_found;
+ }
+
+ gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
+
+ sockaddr = ngx_http_geoip_sockaddr(r, gcf);
+
+ if (sockaddr->sa_family != AF_INET
+#if (NGX_HAVE_INET6)
+ && sockaddr->sa_family != AF_INET6
+#endif
+ )
+ {
+ goto not_found;
+ }
+
+ result = MMDB_lookup_sockaddr(gv->mmdb, sockaddr, &status);
+
+ if (status != MMDB_SUCCESS) {
+ if (status == MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR) {
+ goto not_found;
+ }
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "MMDB_lookup_sockaddr() failed: %s",
+ MMDB_strerror(status));
+ return NGX_ERROR;
+ }
+
+ if (!result.found_entry) {
+ goto not_found;
+ }
+
+ status = MMDB_aget_value(&result.entry, &entry, gv->path);
+
+ if (status != MMDB_SUCCESS) {
+ if (status == MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR) {
+ goto not_found;
+ }
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "MMDB_aget_value() failed: %s",
+ MMDB_strerror(status));
+ return NGX_ERROR;
+ }
+
+ if (!entry.has_data) {
+ goto not_found;
+ }
+
+ if (entry.type == MMDB_DATA_TYPE_UTF8_STRING) {
+ v->len = entry.data_size;
+ v->data = (u_char *) entry.utf8_string;
+
+ } else if (entry.type == MMDB_DATA_TYPE_BYTES) {
+ v->len = entry.data_size;
+ v->data = (u_char *) entry.bytes;
+
+ } else if (entry.type == MMDB_DATA_TYPE_DOUBLE
+ || entry.type == MMDB_DATA_TYPE_FLOAT)
+ {
+ v->data = ngx_pnalloc(r->pool, NGX_INT64_LEN + 5);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (entry.type == MMDB_DATA_TYPE_DOUBLE) {
+ v->len = ngx_sprintf(v->data, "%.4f", entry.double_value)
+ - v->data;
+
+ } else { /* MMDB_DATA_TYPE_FLOAT */
+ v->len = ngx_sprintf(v->data, "%.4f", (double) entry.float_value)
+ - v->data;
+ }
+
+ } else if (entry.type == MMDB_DATA_TYPE_INT32
+ || entry.type == MMDB_DATA_TYPE_UINT16
+ || entry.type == MMDB_DATA_TYPE_UINT32
+ || entry.type == MMDB_DATA_TYPE_UINT64
+ || entry.type == MMDB_DATA_TYPE_BOOLEAN)
+ {
+ v->data = ngx_pnalloc(r->pool, NGX_INT64_LEN);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (entry.type == MMDB_DATA_TYPE_INT32) {
+ v->len = ngx_sprintf(v->data, "%D", entry.int32) - v->data;
+
+ } else if (entry.type == MMDB_DATA_TYPE_UINT16) {
+ v->len = ngx_sprintf(v->data, "%uD", (uint32_t) entry.uint16)
+ - v->data;
+
+ } else if (entry.type == MMDB_DATA_TYPE_UINT32) {
+ v->len = ngx_sprintf(v->data, "%uD", entry.uint32) - v->data;
+
+ } else if (entry.type == MMDB_DATA_TYPE_UINT64) {
+ v->len = ngx_sprintf(v->data, "%uL", entry.uint64) - v->data;
+
+ } else { /* MMDB_DATA_TYPE_BOOLEAN */
+ v->len = ngx_sprintf(v->data, "%uD", (uint32_t) entry.boolean)
+ - v->data;
+ }
+
+ } else {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "MMDB_aget_value(): unexpected entry type, %d",
+ entry.type);
+ goto not_found;
+ }
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ return NGX_OK;
+
+not_found:
+
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+
+#endif
+
+
+#if (NGX_HAVE_GEOIP_LEGACY)
static u_long
ngx_http_geoip_addr(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf)
@@ -587,6 +986,7 @@ ngx_http_geoip_addr_v6(ngx_http_request_
}
#endif
+#endif
static struct sockaddr *
@@ -682,6 +1082,46 @@ ngx_http_geoip_country(ngx_conf_t *cf, n
return NGX_CONF_ERROR;
}
+#if (NGX_HAVE_GEOIP_MMDB)
+
+#if (NGX_HAVE_GEOIP_LEGACY)
+
+ if (ngx_http_geoip_mmdb_file(&value[1]) != NGX_OK) {
+ goto legacy;
+ }
+
+#endif
+
+ gcf->country = ngx_http_geoip_mmdb_open(cf, gcf, &value[1]);
+
+ if (gcf->country == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ gcf->country_mmdb = 1;
+
+ if (cf->args->nelts == 3) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+
+#endif
+
+#if (NGX_HAVE_GEOIP_LEGACY)
+
+ if (ngx_http_geoip_mmdb_file(&value[1]) == NGX_OK) {
+ return "does not support mmdb databases on this platform";
+ }
+
+#if (NGX_HAVE_GEOIP_MMDB)
+
+legacy:
+
+#endif
+
gcf->country = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
if (gcf->country == NULL) {
@@ -702,7 +1142,7 @@ ngx_http_geoip_country(ngx_conf_t *cf, n
}
}
- switch (gcf->country->databaseType) {
+ switch (((GeoIP *) gcf->country)->databaseType) {
case GEOIP_COUNTRY_EDITION:
@@ -718,9 +1158,11 @@ ngx_http_geoip_country(ngx_conf_t *cf, n
default:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid GeoIP database \"%V\" type:%d",
- &value[1], gcf->country->databaseType);
+ &value[1], ((GeoIP *) gcf->country)->databaseType);
return NGX_CONF_ERROR;
}
+
+#endif
}
@@ -741,6 +1183,46 @@ ngx_http_geoip_org(ngx_conf_t *cf, ngx_c
return NGX_CONF_ERROR;
}
+#if (NGX_HAVE_GEOIP_MMDB)
+
+#if (NGX_HAVE_GEOIP_LEGACY)
+
+ if (ngx_http_geoip_mmdb_file(&value[1]) != NGX_OK) {
+ goto legacy;
+ }
+
+#endif
+
+ gcf->org = ngx_http_geoip_mmdb_open(cf, gcf, &value[1]);
+
+ if (gcf->org == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ gcf->org_mmdb = 1;
+
+ if (cf->args->nelts == 3) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+
+#endif
+
+#if (NGX_HAVE_GEOIP_LEGACY)
+
+ if (ngx_http_geoip_mmdb_file(&value[1]) == NGX_OK) {
+ return "does not support mmdb databases on this platform";
+ }
+
+#if (NGX_HAVE_GEOIP_MMDB)
+
+legacy:
+
+#endif
+
gcf->org = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
if (gcf->org == NULL) {
@@ -761,7 +1243,7 @@ ngx_http_geoip_org(ngx_conf_t *cf, ngx_c
}
}
- switch (gcf->org->databaseType) {
+ switch (((GeoIP *) gcf->org)->databaseType) {
case GEOIP_ISP_EDITION:
case GEOIP_ORG_EDITION:
@@ -783,9 +1265,11 @@ ngx_http_geoip_org(ngx_conf_t *cf, ngx_c
default:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid GeoIP database \"%V\" type:%d",
- &value[1], gcf->org->databaseType);
+ &value[1], ((GeoIP *) gcf->org)->databaseType);
return NGX_CONF_ERROR;
}
+
+#endif
}
@@ -806,6 +1290,46 @@ ngx_http_geoip_city(ngx_conf_t *cf, ngx_
return NGX_CONF_ERROR;
}
+#if (NGX_HAVE_GEOIP_MMDB)
+
+#if (NGX_HAVE_GEOIP_LEGACY)
+
+ if (ngx_http_geoip_mmdb_file(&value[1]) != NGX_OK) {
+ goto legacy;
+ }
+
+#endif
+
+ gcf->city = ngx_http_geoip_mmdb_open(cf, gcf, &value[1]);
+
+ if (gcf->city == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ gcf->city_mmdb = 1;
+
+ if (cf->args->nelts == 3) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+
+#endif
+
+#if (NGX_HAVE_GEOIP_LEGACY)
+
+ if (ngx_http_geoip_mmdb_file(&value[1]) == NGX_OK) {
+ return "does not support mmdb databases on this platform";
+ }
+
+#if (NGX_HAVE_GEOIP_MMDB)
+
+legacy:
+
+#endif
+
gcf->city = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
if (gcf->city == NULL) {
@@ -826,7 +1350,7 @@ ngx_http_geoip_city(ngx_conf_t *cf, ngx_
}
}
- switch (gcf->city->databaseType) {
+ switch (((GeoIP *) gcf->city)->databaseType) {
case GEOIP_CITY_EDITION_REV0:
case GEOIP_CITY_EDITION_REV1:
@@ -844,12 +1368,168 @@ ngx_http_geoip_city(ngx_conf_t *cf, ngx_
default:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid GeoIP City database \"%V\" type:%d",
- &value[1], gcf->city->databaseType);
+ &value[1], ((GeoIP *) gcf->city)->databaseType);
+ return NGX_CONF_ERROR;
+ }
+
+#endif
+}
+
+
+static char *
+ngx_http_geoip_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+#if (NGX_HAVE_GEOIP_MMDB)
+
+ ngx_http_geoip_conf_t *gcf = conf;
+
+ u_char *key;
+ ngx_str_t *value;
+ ngx_uint_t i, n;
+ ngx_http_variable_t *v;
+ ngx_http_geoip_variable_t *gv;
+
+ value = cf->args->elts;
+
+ if (value[1].data[0] != '$') {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid variable name \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ value[1].len--;
+ value[1].data++;
+
+ v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE);
+ if (v == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ gv = ngx_palloc(cf->pool, sizeof(ngx_http_geoip_variable_t));
+ if (gv == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ v->get_handler = ngx_http_geoip_mmdb_variable;
+ v->data = (uintptr_t) gv;
+
+ /* database file name */
+
+ if (ngx_conf_full_name(cf->cycle, &value[2], 0) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ gv->mmdb = ngx_http_geoip_mmdb_open(cf, gcf, &value[2]);
+
+ if (gv->mmdb == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ /* data path specification */
+
+ n = 0;
+ for (i = 0; i < value[3].len; i++) {
+ if (value[3].data[i] == '.') {
+ n++;
+ }
+ }
+
+ gv->path = ngx_pcalloc(cf->pool, (n + 2) * sizeof(char *));
+ if (gv->path == NULL) {
return NGX_CONF_ERROR;
}
+
+ n = 0;
+ key = &value[3].data[0];
+
+ for (i = 0; i < value[3].len; i++) {
+ if (value[3].data[i] != '.') {
+ continue;
+ }
+
+ value[3].data[i] = '\0';
+ gv->path[n++] = (char *) key;
+ key = &value[3].data[i + 1];
+ }
+
+ gv->path[n++] = (char *) key;
+ gv->path[n] = NULL;
+
+ return NGX_CONF_OK;
+
+#else
+ return "is not supported on this platform";
+#endif
}
+#if (NGX_HAVE_GEOIP_MMDB)
+
+static MMDB_s *
+ngx_http_geoip_mmdb_open(ngx_conf_t *cf, ngx_http_geoip_conf_t *gcf,
+ ngx_str_t *file)
+{
+ int status;
+ MMDB_s *mmdb;
+ ngx_uint_t i;
+
+ if (gcf->mmdb == NULL) {
+ gcf->mmdb = ngx_array_create(cf->pool, 1, sizeof(MMDB_s));
+ if (gcf->mmdb == NULL) {
+ return NULL;
+ }
+ }
+
+ mmdb = gcf->mmdb->elts;
+ for (i = 0; i < gcf->mmdb->nelts; i++) {
+ if (ngx_strcmp(mmdb[i].filename, file->data) == 0) {
+ return &mmdb[i];
+ }
+ }
+
+ mmdb = ngx_array_push(gcf->mmdb);
+ if (mmdb == NULL) {
+ return NULL;
+ }
+
+ status = MMDB_open((char *) file->data, 0, mmdb);
+
+ if (status != MMDB_SUCCESS) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf,
+ (status == MMDB_IO_ERROR) ? ngx_errno : 0,
+ "MMDB_open(\"%V\") failed: %s",
+ file, MMDB_strerror(status));
+
+ gcf->mmdb->nelts--;
+
+ return NULL;
+ }
+
+ return mmdb;
+}
+
+#endif
+
+
+#if (NGX_HAVE_GEOIP_LEGACY)
+
+static ngx_int_t
+ngx_http_geoip_mmdb_file(ngx_str_t *file)
+{
+ if (file->len < sizeof(".mmdb") - 1
+ || ngx_strncasecmp(file->data + file->len - (sizeof(".mmdb") - 1),
+ (u_char *) ".mmdb", sizeof(".mmdb") - 1)
+ != 0)
+ {
+ return NGX_DECLINED;
+ }
+
+ return NGX_OK;
+}
+
+#endif
+
+
static char *
ngx_http_geoip_proxy(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
@@ -916,15 +1596,33 @@ ngx_http_geoip_cleanup(void *data)
{
ngx_http_geoip_conf_t *gcf = data;
- if (gcf->country) {
+#if (NGX_HAVE_GEOIP_LEGACY)
+
+ if (gcf->country && !gcf->country_mmdb) {
GeoIP_delete(gcf->country);
}
- if (gcf->org) {
+ if (gcf->org && !gcf->org_mmdb) {
GeoIP_delete(gcf->org);
}
- if (gcf->city) {
+ if (gcf->city && !gcf->city_mmdb) {
GeoIP_delete(gcf->city);
}
+
+#endif
+
+#if (NGX_HAVE_GEOIP_MMDB)
+
+ if (gcf->mmdb) {
+ MMDB_s *mmdb;
+ ngx_uint_t i;
+
+ mmdb = gcf->mmdb->elts;
+ for (i = 0; i < gcf->mmdb->nelts; i++) {
+ MMDB_close(&mmdb[i]);
+ }
+ }
+
+#endif
}
diff --git a/src/stream/ngx_stream_geoip_module.c b/src/stream/ngx_stream_geoip_module.c
--- a/src/stream/ngx_stream_geoip_module.c
+++ b/src/stream/ngx_stream_geoip_module.c
@@ -1,6 +1,7 @@
/*
* Copyright (C) Igor Sysoev
+ * Copyright (C) Maxim Dounin
* Copyright (C) Nginx, Inc.
*/
@@ -9,25 +10,56 @@
#include <ngx_core.h>
#include <ngx_stream.h>
+#if (NGX_HAVE_GEOIP_LEGACY)
+
#include <GeoIP.h>
#include <GeoIPCity.h>
+#endif
-#define NGX_GEOIP_COUNTRY_CODE 0
-#define NGX_GEOIP_COUNTRY_CODE3 1
-#define NGX_GEOIP_COUNTRY_NAME 2
+#if (NGX_HAVE_GEOIP_MMDB)
+
+#include <maxminddb.h>
+
+#endif
+
+
+#define NGX_GEOIP_COUNTRY_CODE 0
+#define NGX_GEOIP_COUNTRY_CODE3 1
+#define NGX_GEOIP_COUNTRY_NAME 2
+#define NGX_GEOIP_CONTINENT_CODE 3
+#define NGX_GEOIP_REGION 4
+#define NGX_GEOIP_REGION_NAME 5
+#define NGX_GEOIP_CITY 6
+#define NGX_GEOIP_POSTAL_CODE 7
+#define NGX_GEOIP_LATITUDE 8
+#define NGX_GEOIP_LONGITUDE 9
+#define NGX_GEOIP_DMA_CODE 10
+#define NGX_GEOIP_AREA_CODE 11
typedef struct {
- GeoIP *country;
- GeoIP *org;
- GeoIP *city;
-#if (NGX_HAVE_GEOIP_V6)
+ void *country;
+ void *org;
+ void *city;
+ ngx_array_t *mmdb; /* array of MMDB_s */
unsigned country_v6:1;
+ unsigned country_mmdb:1;
unsigned org_v6:1;
+ unsigned org_mmdb:1;
unsigned city_v6:1;
+ unsigned city_mmdb:1;
+} ngx_stream_geoip_conf_t;
+
+
+#if (NGX_HAVE_GEOIP_MMDB)
+
+typedef struct {
+ MMDB_s *mmdb;
+ const char **path;
+} ngx_stream_geoip_variable_t;
+
#endif
-} ngx_stream_geoip_conf_t;
static ngx_int_t ngx_stream_geoip_country_variable(ngx_stream_session_t *s,
@@ -42,14 +74,25 @@ static ngx_int_t ngx_stream_geoip_city_f
ngx_stream_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_stream_geoip_city_int_variable(ngx_stream_session_t *s,
ngx_stream_variable_value_t *v, uintptr_t data);
+#if (NGX_HAVE_GEOIP_LEGACY)
static GeoIPRecord *ngx_stream_geoip_get_city_record(ngx_stream_session_t *s);
+#endif
+#if (NGX_HAVE_GEOIP_MMDB)
+static ngx_int_t ngx_stream_geoip_mmdb_city_variable(ngx_stream_session_t *s,
+ ngx_stream_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_stream_geoip_mmdb_variable(ngx_stream_session_t *s,
+ ngx_stream_variable_value_t *v, uintptr_t data);
+#endif
+
+#if (NGX_HAVE_GEOIP_LEGACY)
static u_long ngx_stream_geoip_addr(ngx_stream_session_t *s,
ngx_stream_geoip_conf_t *gcf);
#if (NGX_HAVE_GEOIP_V6)
static geoipv6_t ngx_stream_geoip_addr_v6(ngx_stream_session_t *s,
ngx_stream_geoip_conf_t *gcf);
#endif
+#endif
static ngx_int_t ngx_stream_geoip_add_variables(ngx_conf_t *cf);
static void *ngx_stream_geoip_create_conf(ngx_conf_t *cf);
@@ -59,6 +102,15 @@ static char *ngx_stream_geoip_org(ngx_co
void *conf);
static char *ngx_stream_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
+static char *ngx_stream_geoip_set(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+#if (NGX_HAVE_GEOIP_MMDB)
+static MMDB_s *ngx_stream_geoip_mmdb_open(ngx_conf_t *cf,
+ ngx_stream_geoip_conf_t *gcf, ngx_str_t *file);
+#endif
+#if (NGX_HAVE_GEOIP_LEGACY)
+static ngx_int_t ngx_stream_geoip_mmdb_file(ngx_str_t *file);
+#endif
static void ngx_stream_geoip_cleanup(void *data);
@@ -85,6 +137,13 @@ static ngx_command_t ngx_stream_geoip_c
0,
NULL },
+ { ngx_string("geoip_set"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE3,
+ ngx_stream_geoip_set,
+ NGX_STREAM_MAIN_CONF_OFFSET,
+ 0,
+ NULL },
+
ngx_null_command
};
@@ -137,61 +196,103 @@ static ngx_stream_variable_t ngx_stream
{ ngx_string("geoip_city_continent_code"), NULL,
ngx_stream_geoip_city_variable,
- offsetof(GeoIPRecord, continent_code), 0, 0 },
+ NGX_GEOIP_CONTINENT_CODE, 0, 0 },
{ ngx_string("geoip_city_country_code"), NULL,
ngx_stream_geoip_city_variable,
- offsetof(GeoIPRecord, country_code), 0, 0 },
+ NGX_GEOIP_COUNTRY_CODE, 0, 0 },
{ ngx_string("geoip_city_country_code3"), NULL,
ngx_stream_geoip_city_variable,
- offsetof(GeoIPRecord, country_code3), 0, 0 },
+ NGX_GEOIP_COUNTRY_CODE3, 0, 0 },
{ ngx_string("geoip_city_country_name"), NULL,
ngx_stream_geoip_city_variable,
- offsetof(GeoIPRecord, country_name), 0, 0 },
+ NGX_GEOIP_COUNTRY_NAME, 0, 0 },
{ ngx_string("geoip_region"), NULL,
ngx_stream_geoip_city_variable,
- offsetof(GeoIPRecord, region), 0, 0 },
+ NGX_GEOIP_REGION, 0, 0 },
{ ngx_string("geoip_region_name"), NULL,
ngx_stream_geoip_region_name_variable,
- 0, 0, 0 },
+ NGX_GEOIP_REGION_NAME, 0, 0 },
{ ngx_string("geoip_city"), NULL,
ngx_stream_geoip_city_variable,
- offsetof(GeoIPRecord, city), 0, 0 },
+ NGX_GEOIP_CITY, 0, 0 },
{ ngx_string("geoip_postal_code"), NULL,
ngx_stream_geoip_city_variable,
- offsetof(GeoIPRecord, postal_code), 0, 0 },
+ NGX_GEOIP_POSTAL_CODE, 0, 0 },
{ ngx_string("geoip_latitude"), NULL,
ngx_stream_geoip_city_float_variable,
- offsetof(GeoIPRecord, latitude), 0, 0 },
+ NGX_GEOIP_LATITUDE, 0, 0 },
{ ngx_string("geoip_longitude"), NULL,
ngx_stream_geoip_city_float_variable,
- offsetof(GeoIPRecord, longitude), 0, 0 },
+ NGX_GEOIP_LONGITUDE, 0, 0 },
{ ngx_string("geoip_dma_code"), NULL,
ngx_stream_geoip_city_int_variable,
- offsetof(GeoIPRecord, dma_code), 0, 0 },
+ NGX_GEOIP_DMA_CODE, 0, 0 },
{ ngx_string("geoip_area_code"), NULL,
ngx_stream_geoip_city_int_variable,
- offsetof(GeoIPRecord, area_code), 0, 0 },
+ NGX_GEOIP_AREA_CODE, 0, 0 },
ngx_stream_null_variable
};
+#if (NGX_HAVE_GEOIP_LEGACY)
+
+static uintptr_t ngx_stream_geoip_city_offsets[] = {
+ offsetof(GeoIPRecord, country_code),
+ offsetof(GeoIPRecord, country_code3),
+ offsetof(GeoIPRecord, country_name),
+ offsetof(GeoIPRecord, continent_code),
+ offsetof(GeoIPRecord, region),
+ 0, /* region name */
+ offsetof(GeoIPRecord, city),
+ offsetof(GeoIPRecord, postal_code),
+ offsetof(GeoIPRecord, latitude),
+ offsetof(GeoIPRecord, longitude),
+ offsetof(GeoIPRecord, dma_code),
+ offsetof(GeoIPRecord, area_code)
+};
+
+#endif
+
+
+#if (NGX_HAVE_GEOIP_MMDB)
+
+static const char *ngx_stream_geoip_mmdb_paths[][5] = {
+ { "country", "iso_code", NULL, NULL, NULL },
+ { NULL, NULL, NULL, NULL, NULL }, /* country code3 */
+ { "country", "names", "en", NULL, NULL },
+ { "continent", "code", NULL, NULL, NULL },
+ { "subdivisions", "0", "iso_code", NULL, NULL },
+ { "subdivisions", "0", "names", "en", NULL },
+ { "city", "names", "en", NULL, NULL },
+ { "postal", "code", NULL, NULL, NULL },
+ { "location", "latitude", NULL, NULL, NULL },
+ { "location", "longitude", NULL, NULL, NULL },
+ { "location", "metro_code", NULL, NULL, NULL },
+ { NULL, NULL, NULL, NULL, NULL } /* area code */
+};
+
+#endif
+
+
static ngx_int_t
ngx_stream_geoip_country_variable(ngx_stream_session_t *s,
ngx_stream_variable_value_t *v, uintptr_t data)
{
+#if (NGX_HAVE_GEOIP_LEGACY)
const char *val;
+#endif
ngx_stream_geoip_conf_t *gcf;
gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module);
@@ -200,6 +301,21 @@ ngx_stream_geoip_country_variable(ngx_st
goto not_found;
}
+#if (NGX_HAVE_GEOIP_MMDB)
+
+ if (gcf->country_mmdb) {
+ ngx_stream_geoip_variable_t gv;
+
+ gv.mmdb = gcf->country;
+ gv.path = ngx_stream_geoip_mmdb_paths[data];
+
+ return ngx_stream_geoip_mmdb_variable(s, v, (uintptr_t) &gv);
+ }
+
+#endif
+
+#if (NGX_HAVE_GEOIP_LEGACY)
+
#if (NGX_HAVE_GEOIP_V6)
if (gcf->country_v6) {
if (data == NGX_GEOIP_COUNTRY_CODE) {
@@ -243,6 +359,8 @@ ngx_stream_geoip_country_variable(ngx_st
return NGX_OK;
+#endif
+
not_found:
v->not_found = 1;
@@ -255,8 +373,10 @@ static ngx_int_t
ngx_stream_geoip_org_variable(ngx_stream_session_t *s,
ngx_stream_variable_value_t *v, uintptr_t data)
{
+#if (NGX_HAVE_GEOIP_LEGACY)
size_t len;
char *val;
+#endif
ngx_stream_geoip_conf_t *gcf;
gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module);
@@ -265,6 +385,32 @@ ngx_stream_geoip_org_variable(ngx_stream
goto not_found;
}
+#if (NGX_HAVE_GEOIP_MMDB)
+
+ if (gcf->org_mmdb) {
+ ngx_int_t rc;
+ ngx_stream_geoip_variable_t gv;
+
+ static const char *org[] = { "organization", NULL };
+ static const char *asorg[] = { "autonomous_system_organization", NULL };
+
+ gv.mmdb = gcf->org;
+ gv.path = org;
+
+ rc = ngx_stream_geoip_mmdb_variable(s, v, (uintptr_t) &gv);
+
+ if (rc == NGX_OK && v->not_found) {
+ gv.path = asorg;
+ rc = ngx_stream_geoip_mmdb_variable(s, v, (uintptr_t) &gv);
+ }
+
+ return rc;
+ }
+
+#endif
+
+#if (NGX_HAVE_GEOIP_LEGACY)
+
#if (NGX_HAVE_GEOIP_V6)
if (gcf->org_v6) {
val = GeoIP_name_by_ipnum_v6(gcf->org,
@@ -298,6 +444,8 @@ ngx_stream_geoip_org_variable(ngx_stream
return NGX_OK;
+#endif
+
not_found:
v->not_found = 1;
@@ -310,16 +458,32 @@ static ngx_int_t
ngx_stream_geoip_city_variable(ngx_stream_session_t *s,
ngx_stream_variable_value_t *v, uintptr_t data)
{
+#if (NGX_HAVE_GEOIP_LEGACY)
char *val;
size_t len;
GeoIPRecord *gr;
+#endif
+
+#if (NGX_HAVE_GEOIP_MMDB)
+
+ ngx_int_t rc;
+
+ rc = ngx_stream_geoip_mmdb_city_variable(s, v, data);
+
+ if (rc != NGX_DECLINED) {
+ return rc;
+ }
+
+#endif
+
+#if (NGX_HAVE_GEOIP_LEGACY)
gr = ngx_stream_geoip_get_city_record(s);
if (gr == NULL) {
goto not_found;
}
- val = *(char **) ((char *) gr + data);
+ val = *(char **) ((char *) gr + ngx_stream_geoip_city_offsets[data]);
if (val == NULL) {
goto no_value;
}
@@ -348,6 +512,8 @@ no_value:
not_found:
+#endif
+
v->not_found = 1;
return NGX_OK;
@@ -358,9 +524,25 @@ static ngx_int_t
ngx_stream_geoip_region_name_variable(ngx_stream_session_t *s,
ngx_stream_variable_value_t *v, uintptr_t data)
{
+#if (NGX_HAVE_GEOIP_LEGACY)
size_t len;
const char *val;
GeoIPRecord *gr;
+#endif
+
+#if (NGX_HAVE_GEOIP_MMDB)
+
+ ngx_int_t rc;
+
+ rc = ngx_stream_geoip_mmdb_city_variable(s, v, data);
+
+ if (rc != NGX_DECLINED) {
+ return rc;
+ }
+
+#endif
+
+#if (NGX_HAVE_GEOIP_LEGACY)
gr = ngx_stream_geoip_get_city_record(s);
if (gr == NULL) {
@@ -392,6 +574,8 @@ ngx_stream_geoip_region_name_variable(ng
not_found:
+#endif
+
v->not_found = 1;
return NGX_OK;
@@ -402,13 +586,28 @@ static ngx_int_t
ngx_stream_geoip_city_float_variable(ngx_stream_session_t *s,
ngx_stream_variable_value_t *v, uintptr_t data)
{
+#if (NGX_HAVE_GEOIP_LEGACY)
float val;
GeoIPRecord *gr;
+#endif
+
+#if (NGX_HAVE_GEOIP_MMDB)
+
+ ngx_int_t rc;
+
+ rc = ngx_stream_geoip_mmdb_city_variable(s, v, data);
+
+ if (rc != NGX_DECLINED) {
+ return rc;
+ }
+
+#endif
+
+#if (NGX_HAVE_GEOIP_LEGACY)
gr = ngx_stream_geoip_get_city_record(s);
if (gr == NULL) {
- v->not_found = 1;
- return NGX_OK;
+ goto not_found;
}
v->data = ngx_pnalloc(s->connection->pool, NGX_INT64_LEN + 5);
@@ -417,7 +616,7 @@ ngx_stream_geoip_city_float_variable(ngx
return NGX_ERROR;
}
- val = *(float *) ((char *) gr + data);
+ val = *(float *) ((char *) gr + ngx_stream_geoip_city_offsets[data]);
v->len = ngx_sprintf(v->data, "%.4f", val) - v->data;
v->valid = 1;
@@ -427,6 +626,14 @@ ngx_stream_geoip_city_float_variable(ngx
GeoIPRecord_delete(gr);
return NGX_OK;
+
+not_found:
+
+#endif
+
+ v->not_found = 1;
+
+ return NGX_OK;
}
@@ -434,13 +641,28 @@ static ngx_int_t
ngx_stream_geoip_city_int_variable(ngx_stream_session_t *s,
ngx_stream_variable_value_t *v, uintptr_t data)
{
+#if (NGX_HAVE_GEOIP_LEGACY)
int val;
GeoIPRecord *gr;
+#endif
+
+#if (NGX_HAVE_GEOIP_MMDB)
+
+ ngx_int_t rc;
+
+ rc = ngx_stream_geoip_mmdb_city_variable(s, v, data);
+
+ if (rc != NGX_DECLINED) {
+ return rc;
+ }
+
+#endif
+
+#if (NGX_HAVE_GEOIP_LEGACY)
gr = ngx_stream_geoip_get_city_record(s);
if (gr == NULL) {
- v->not_found = 1;
- return NGX_OK;
+ goto not_found;
}
v->data = ngx_pnalloc(s->connection->pool, NGX_INT64_LEN);
@@ -449,7 +671,7 @@ ngx_stream_geoip_city_int_variable(ngx_s
return NGX_ERROR;
}
- val = *(int *) ((char *) gr + data);
+ val = *(int *) ((char *) gr + ngx_stream_geoip_city_offsets[data]);
v->len = ngx_sprintf(v->data, "%d", val) - v->data;
v->valid = 1;
@@ -459,9 +681,19 @@ ngx_stream_geoip_city_int_variable(ngx_s
GeoIPRecord_delete(gr);
return NGX_OK;
+
+not_found:
+
+#endif
+
+ v->not_found = 1;
+
+ return NGX_OK;
}
+#if (NGX_HAVE_GEOIP_LEGACY)
+
static GeoIPRecord *
ngx_stream_geoip_get_city_record(ngx_stream_session_t *s)
{
@@ -482,6 +714,169 @@ ngx_stream_geoip_get_city_record(ngx_str
return NULL;
}
+#endif
+
+
+#if (NGX_HAVE_GEOIP_MMDB)
+
+static ngx_int_t
+ngx_stream_geoip_mmdb_city_variable(ngx_stream_session_t *s,
+ ngx_stream_variable_value_t *v, uintptr_t data)
+{
+ ngx_stream_geoip_conf_t *gcf;
+ ngx_stream_geoip_variable_t gv;
+
+ gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module);
+
+ if (!gcf->city_mmdb) {
+ return NGX_DECLINED;
+ }
+
+ gv.mmdb = gcf->city;
+ gv.path = ngx_stream_geoip_mmdb_paths[data];
+
+ return ngx_stream_geoip_mmdb_variable(s, v, (uintptr_t) &gv);
+}
+
+
+static ngx_int_t
+ngx_stream_geoip_mmdb_variable(ngx_stream_session_t *s,
+ ngx_stream_variable_value_t *v, uintptr_t data)
+{
+ ngx_stream_geoip_variable_t *gv = (ngx_stream_geoip_variable_t *) data;
+
+ int status;
+ struct sockaddr *sockaddr;
+ MMDB_entry_data_s entry;
+ MMDB_lookup_result_s result;
+
+ if (gv->path[0] == NULL) {
+ goto not_found;
+ }
+
+ sockaddr = s->connection->sockaddr;
+
+ if (sockaddr->sa_family != AF_INET
+#if (NGX_HAVE_INET6)
+ && sockaddr->sa_family != AF_INET6
+#endif
+ )
+ {
+ goto not_found;
+ }
+
+ result = MMDB_lookup_sockaddr(gv->mmdb, sockaddr, &status);
+
+ if (status != MMDB_SUCCESS) {
+ if (status == MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR) {
+ goto not_found;
+ }
+
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "MMDB_lookup_sockaddr() failed: %s",
+ MMDB_strerror(status));
+ return NGX_ERROR;
+ }
+
+ if (!result.found_entry) {
+ goto not_found;
+ }
+
+ status = MMDB_aget_value(&result.entry, &entry, gv->path);
+
+ if (status != MMDB_SUCCESS) {
+ if (status == MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR) {
+ goto not_found;
+ }
+
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "MMDB_aget_value() failed: %s",
+ MMDB_strerror(status));
+ return NGX_ERROR;
+ }
+
+ if (!entry.has_data) {
+ goto not_found;
+ }
+
+ if (entry.type == MMDB_DATA_TYPE_UTF8_STRING) {
+ v->len = entry.data_size;
+ v->data = (u_char *) entry.utf8_string;
+
+ } else if (entry.type == MMDB_DATA_TYPE_BYTES) {
+ v->len = entry.data_size;
+ v->data = (u_char *) entry.bytes;
+
+ } else if (entry.type == MMDB_DATA_TYPE_DOUBLE
+ || entry.type == MMDB_DATA_TYPE_FLOAT)
+ {
+ v->data = ngx_pnalloc(s->connection->pool, NGX_INT64_LEN + 5);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (entry.type == MMDB_DATA_TYPE_DOUBLE) {
+ v->len = ngx_sprintf(v->data, "%.4f", entry.double_value)
+ - v->data;
+
+ } else { /* MMDB_DATA_TYPE_FLOAT */
+ v->len = ngx_sprintf(v->data, "%.4f", (double) entry.float_value)
+ - v->data;
+ }
+
+ } else if (entry.type == MMDB_DATA_TYPE_INT32
+ || entry.type == MMDB_DATA_TYPE_UINT16
+ || entry.type == MMDB_DATA_TYPE_UINT32
+ || entry.type == MMDB_DATA_TYPE_UINT64
+ || entry.type == MMDB_DATA_TYPE_BOOLEAN)
+ {
+ v->data = ngx_pnalloc(s->connection->pool, NGX_INT64_LEN);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (entry.type == MMDB_DATA_TYPE_INT32) {
+ v->len = ngx_sprintf(v->data, "%D", entry.int32) - v->data;
+
+ } else if (entry.type == MMDB_DATA_TYPE_UINT16) {
+ v->len = ngx_sprintf(v->data, "%uD", (uint32_t) entry.uint16)
+ - v->data;
+
+ } else if (entry.type == MMDB_DATA_TYPE_UINT32) {
+ v->len = ngx_sprintf(v->data, "%uD", entry.uint32) - v->data;
+
+ } else if (entry.type == MMDB_DATA_TYPE_UINT64) {
+ v->len = ngx_sprintf(v->data, "%uL", entry.uint64) - v->data;
+
+ } else { /* MMDB_DATA_TYPE_BOOLEAN */
+ v->len = ngx_sprintf(v->data, "%uD", (uint32_t) entry.boolean)
+ - v->data;
+ }
+
+ } else {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, s->connection->log, 0,
+ "MMDB_aget_value(): unexpected entry type, %d",
+ entry.type);
+ goto not_found;
+ }
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ return NGX_OK;
+
+not_found:
+
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+
+#endif
+
+
+#if (NGX_HAVE_GEOIP_LEGACY)
static u_long
ngx_stream_geoip_addr(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf)
@@ -562,6 +957,7 @@ ngx_stream_geoip_addr_v6(ngx_stream_sess
}
#endif
+#endif
static ngx_int_t
@@ -623,6 +1019,46 @@ ngx_stream_geoip_country(ngx_conf_t *cf,
return NGX_CONF_ERROR;
}
+#if (NGX_HAVE_GEOIP_MMDB)
+
+#if (NGX_HAVE_GEOIP_LEGACY)
+
+ if (ngx_stream_geoip_mmdb_file(&value[1]) != NGX_OK) {
+ goto legacy;
+ }
+
+#endif
+
+ gcf->country = ngx_stream_geoip_mmdb_open(cf, gcf, &value[1]);
+
+ if (gcf->country == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ gcf->country_mmdb = 1;
+
+ if (cf->args->nelts == 3) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+
+#endif
+
+#if (NGX_HAVE_GEOIP_LEGACY)
+
+ if (ngx_stream_geoip_mmdb_file(&value[1]) == NGX_OK) {
+ return "does not support mmdb databases on this platform";
+ }
+
+#if (NGX_HAVE_GEOIP_MMDB)
+
+legacy:
+
+#endif
+
gcf->country = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
if (gcf->country == NULL) {
@@ -643,7 +1079,7 @@ ngx_stream_geoip_country(ngx_conf_t *cf,
}
}
- switch (gcf->country->databaseType) {
+ switch (((GeoIP *) gcf->country)->databaseType) {
case GEOIP_COUNTRY_EDITION:
@@ -659,9 +1095,11 @@ ngx_stream_geoip_country(ngx_conf_t *cf,
default:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid GeoIP database \"%V\" type:%d",
- &value[1], gcf->country->databaseType);
+ &value[1], ((GeoIP *) gcf->country)->databaseType);
return NGX_CONF_ERROR;
}
+
+#endif
}
@@ -682,6 +1120,46 @@ ngx_stream_geoip_org(ngx_conf_t *cf, ngx
return NGX_CONF_ERROR;
}
+#if (NGX_HAVE_GEOIP_MMDB)
+
+#if (NGX_HAVE_GEOIP_LEGACY)
+
+ if (ngx_stream_geoip_mmdb_file(&value[1]) != NGX_OK) {
+ goto legacy;
+ }
+
+#endif
+
+ gcf->org = ngx_stream_geoip_mmdb_open(cf, gcf, &value[1]);
+
+ if (gcf->org == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ gcf->org_mmdb = 1;
+
+ if (cf->args->nelts == 3) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+
+#endif
+
+#if (NGX_HAVE_GEOIP_LEGACY)
+
+ if (ngx_stream_geoip_mmdb_file(&value[1]) == NGX_OK) {
+ return "does not support mmdb databases on this platform";
+ }
+
+#if (NGX_HAVE_GEOIP_MMDB)
+
+legacy:
+
+#endif
+
gcf->org = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
if (gcf->org == NULL) {
@@ -702,7 +1180,7 @@ ngx_stream_geoip_org(ngx_conf_t *cf, ngx
}
}
- switch (gcf->org->databaseType) {
+ switch (((GeoIP *) gcf->org)->databaseType) {
case GEOIP_ISP_EDITION:
case GEOIP_ORG_EDITION:
@@ -724,9 +1202,11 @@ ngx_stream_geoip_org(ngx_conf_t *cf, ngx
default:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid GeoIP database \"%V\" type:%d",
- &value[1], gcf->org->databaseType);
+ &value[1], ((GeoIP *) gcf->org)->databaseType);
return NGX_CONF_ERROR;
}
+
+#endif
}
@@ -747,6 +1227,46 @@ ngx_stream_geoip_city(ngx_conf_t *cf, ng
return NGX_CONF_ERROR;
}
+#if (NGX_HAVE_GEOIP_MMDB)
+
+#if (NGX_HAVE_GEOIP_LEGACY)
+
+ if (ngx_stream_geoip_mmdb_file(&value[1]) != NGX_OK) {
+ goto legacy;
+ }
+
+#endif
+
+ gcf->city = ngx_stream_geoip_mmdb_open(cf, gcf, &value[1]);
+
+ if (gcf->city == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ gcf->city_mmdb = 1;
+
+ if (cf->args->nelts == 3) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+
+#endif
+
+#if (NGX_HAVE_GEOIP_LEGACY)
+
+ if (ngx_stream_geoip_mmdb_file(&value[1]) == NGX_OK) {
+ return "does not support mmdb databases on this platform";
+ }
+
+#if (NGX_HAVE_GEOIP_MMDB)
+
+legacy:
+
+#endif
+
gcf->city = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
if (gcf->city == NULL) {
@@ -767,7 +1287,7 @@ ngx_stream_geoip_city(ngx_conf_t *cf, ng
}
}
- switch (gcf->city->databaseType) {
+ switch (((GeoIP *) gcf->city)->databaseType) {
case GEOIP_CITY_EDITION_REV0:
case GEOIP_CITY_EDITION_REV1:
@@ -785,26 +1305,200 @@ ngx_stream_geoip_city(ngx_conf_t *cf, ng
default:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid GeoIP City database \"%V\" type:%d",
- &value[1], gcf->city->databaseType);
+ &value[1], ((GeoIP *) gcf->city)->databaseType);
+ return NGX_CONF_ERROR;
+ }
+
+#endif
+}
+
+
+static char *
+ngx_stream_geoip_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+#if (NGX_HAVE_GEOIP_MMDB)
+
+ ngx_stream_geoip_conf_t *gcf = conf;
+
+ u_char *key;
+ ngx_str_t *value;
+ ngx_uint_t i, n;
+ ngx_stream_variable_t *v;
+ ngx_stream_geoip_variable_t *gv;
+
+ value = cf->args->elts;
+
+ if (value[1].data[0] != '$') {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid variable name \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ value[1].len--;
+ value[1].data++;
+
+ v = ngx_stream_add_variable(cf, &value[1], NGX_STREAM_VAR_CHANGEABLE);
+ if (v == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ gv = ngx_palloc(cf->pool, sizeof(ngx_stream_geoip_variable_t));
+ if (gv == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ v->get_handler = ngx_stream_geoip_mmdb_variable;
+ v->data = (uintptr_t) gv;
+
+ /* database file name */
+
+ if (ngx_conf_full_name(cf->cycle, &value[2], 0) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ gv->mmdb = ngx_stream_geoip_mmdb_open(cf, gcf, &value[2]);
+
+ if (gv->mmdb == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ /* data path specification */
+
+ n = 0;
+ for (i = 0; i < value[3].len; i++) {
+ if (value[3].data[i] == '.') {
+ n++;
+ }
+ }
+
+ gv->path = ngx_pcalloc(cf->pool, (n + 2) * sizeof(char *));
+ if (gv->path == NULL) {
return NGX_CONF_ERROR;
}
+
+ n = 0;
+ key = &value[3].data[0];
+
+ for (i = 0; i < value[3].len; i++) {
+ if (value[3].data[i] != '.') {
+ continue;
+ }
+
+ value[3].data[i] = '\0';
+ gv->path[n++] = (char *) key;
+ key = &value[3].data[i + 1];
+ }
+
+ gv->path[n++] = (char *) key;
+ gv->path[n] = NULL;
+
+ return NGX_CONF_OK;
+
+#else
+ return "is not supported on this platform";
+#endif
}
+#if (NGX_HAVE_GEOIP_MMDB)
+
+static MMDB_s *
+ngx_stream_geoip_mmdb_open(ngx_conf_t *cf, ngx_stream_geoip_conf_t *gcf,
+ ngx_str_t *file)
+{
+ int status;
+ MMDB_s *mmdb;
+ ngx_uint_t i;
+
+ if (gcf->mmdb == NULL) {
+ gcf->mmdb = ngx_array_create(cf->pool, 1, sizeof(MMDB_s));
+ if (gcf->mmdb == NULL) {
+ return NULL;
+ }
+ }
+
+ mmdb = gcf->mmdb->elts;
+ for (i = 0; i < gcf->mmdb->nelts; i++) {
+ if (ngx_strcmp(mmdb[i].filename, file->data) == 0) {
+ return &mmdb[i];
+ }
+ }
+
+ mmdb = ngx_array_push(gcf->mmdb);
+ if (mmdb == NULL) {
+ return NULL;
+ }
+
+ status = MMDB_open((char *) file->data, 0, mmdb);
+
+ if (status != MMDB_SUCCESS) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf,
+ (status == MMDB_IO_ERROR) ? ngx_errno : 0,
+ "MMDB_open(\"%V\") failed: %s",
+ file, MMDB_strerror(status));
+
+ gcf->mmdb->nelts--;
+
+ return NULL;
+ }
+
+ return mmdb;
+}
+
+#endif
+
+
+#if (NGX_HAVE_GEOIP_LEGACY)
+
+static ngx_int_t
+ngx_stream_geoip_mmdb_file(ngx_str_t *file)
+{
+ if (file->len < sizeof(".mmdb") - 1
+ || ngx_strncasecmp(file->data + file->len - (sizeof(".mmdb") - 1),
+ (u_char *) ".mmdb", sizeof(".mmdb") - 1)
+ != 0)
+ {
+ return NGX_DECLINED;
+ }
+
+ return NGX_OK;
+}
+
+#endif
+
+
static void
ngx_stream_geoip_cleanup(void *data)
{
ngx_stream_geoip_conf_t *gcf = data;
- if (gcf->country) {
+#if (NGX_HAVE_GEOIP_LEGACY)
+
+ if (gcf->country && !gcf->country_mmdb) {
GeoIP_delete(gcf->country);
}
- if (gcf->org) {
+ if (gcf->org && !gcf->org_mmdb) {
GeoIP_delete(gcf->org);
}
- if (gcf->city) {
+ if (gcf->city && !gcf->city_mmdb) {
GeoIP_delete(gcf->city);
}
+
+#endif
+
+#if (NGX_HAVE_GEOIP_MMDB)
+
+ if (gcf->mmdb) {
+ MMDB_s *mmdb;
+ ngx_uint_t i;
+
+ mmdb = gcf->mmdb->elts;
+ for (i = 0; i < gcf->mmdb->nelts; i++) {
+ MMDB_close(&mmdb[i]);
+ }
+ }
+
+#endif
}
More information about the nginx-devel
mailing list