From mdounin at mdounin.ru Wed Dec 3 16:31:05 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Wed, 03 Dec 2025 19:31:05 +0300 Subject: [PATCH 1 of 6] GeoIP: added missing empty line Message-ID: # HG changeset patch # User Maxim Dounin # Date 1764678500 -10800 # Tue Dec 02 15:28:20 2025 +0300 # Node ID e83601fadc0a6b7859b6af64626a2b1fb9134ad8 # Parent ff8c3ef06fd552b19750c0af4a28051708a91c40 GeoIP: added missing empty line. 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 @@ -889,6 +889,7 @@ ngx_http_geoip_proxy(ngx_conf_t *cf, ngx return NGX_CONF_OK; } + static ngx_int_t ngx_http_geoip_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr) { From mdounin at mdounin.ru Wed Dec 3 16:31:06 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Wed, 03 Dec 2025 19:31:06 +0300 Subject: [PATCH 2 of 6] GeoIP: removed unused ngx_http_geoip_var_t structure In-Reply-To: References: Message-ID: <354839370549e47835b0.1764779466@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1764679196 -10800 # Tue Dec 02 15:39:56 2025 +0300 # Node ID 354839370549e47835b001649a0f32c76517677c # Parent e83601fadc0a6b7859b6af64626a2b1fb9134ad8 GeoIP: removed unused ngx_http_geoip_var_t structure. It was never used, since introduction of the module in 2985:31af2d1a742e, and hence removed. 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 @@ -32,12 +32,6 @@ typedef struct { } ngx_http_geoip_conf_t; -typedef struct { - ngx_str_t *name; - uintptr_t data; -} ngx_http_geoip_var_t; - - typedef const char *(*ngx_http_geoip_variable_handler_pt)(GeoIP *, u_long addr); 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 @@ -30,12 +30,6 @@ typedef struct { } ngx_stream_geoip_conf_t; -typedef struct { - ngx_str_t *name; - uintptr_t data; -} ngx_stream_geoip_var_t; - - typedef const char *(*ngx_stream_geoip_variable_handler_pt)(GeoIP *, u_long addr); From mdounin at mdounin.ru Wed Dec 3 16:31:07 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Wed, 03 Dec 2025 19:31:07 +0300 Subject: [PATCH 3 of 6] GeoIP: moved ngx_http_geoip_addr() to a better place In-Reply-To: References: Message-ID: <3fb3fd326c10849df11f.1764779467@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1764576361 -10800 # Mon Dec 01 11:06:01 2025 +0300 # Node ID 3fb3fd326c10849df11fa637bf8de2eedec6fb46 # Parent 354839370549e47835b001649a0f32c76517677c GeoIP: moved ngx_http_geoip_addr() to a better place. Per existing style, helper functions are defined after functions that use them, and with appropriate prototypes. 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 @@ -72,6 +72,13 @@ static ngx_int_t ngx_http_geoip_city_int ngx_http_variable_value_t *v, uintptr_t data); static GeoIPRecord *ngx_http_geoip_get_city_record(ngx_http_request_t *r); +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 + static ngx_int_t ngx_http_geoip_add_variables(ngx_conf_t *cf); static void *ngx_http_geoip_create_conf(ngx_conf_t *cf); static char *ngx_http_geoip_init_conf(ngx_conf_t *cf, void *conf); @@ -230,107 +237,6 @@ static ngx_http_variable_t ngx_http_geo }; -static u_long -ngx_http_geoip_addr(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf) -{ - ngx_addr_t addr; - ngx_table_elt_t *xfwd; - struct sockaddr_in *sin; - - addr.sockaddr = r->connection->sockaddr; - addr.socklen = r->connection->socklen; - /* addr.name = r->connection->addr_text; */ - - xfwd = r->headers_in.x_forwarded_for; - - if (xfwd != NULL && gcf->proxies != NULL) { - (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL, - gcf->proxies, gcf->proxy_recursive); - } - -#if (NGX_HAVE_INET6) - - if (addr.sockaddr->sa_family == AF_INET6) { - u_char *p; - in_addr_t inaddr; - struct in6_addr *inaddr6; - - inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; - - if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { - p = inaddr6->s6_addr; - - inaddr = (in_addr_t) p[12] << 24; - inaddr += p[13] << 16; - inaddr += p[14] << 8; - inaddr += p[15]; - - return inaddr; - } - } - -#endif - - if (addr.sockaddr->sa_family != AF_INET) { - return INADDR_NONE; - } - - sin = (struct sockaddr_in *) addr.sockaddr; - return ntohl(sin->sin_addr.s_addr); -} - - -#if (NGX_HAVE_GEOIP_V6) - -static geoipv6_t -ngx_http_geoip_addr_v6(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf) -{ - ngx_addr_t addr; - ngx_table_elt_t *xfwd; - in_addr_t addr4; - struct in6_addr addr6; - struct sockaddr_in *sin; - struct sockaddr_in6 *sin6; - - addr.sockaddr = r->connection->sockaddr; - addr.socklen = r->connection->socklen; - /* addr.name = r->connection->addr_text; */ - - xfwd = r->headers_in.x_forwarded_for; - - if (xfwd != NULL && gcf->proxies != NULL) { - (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL, - gcf->proxies, gcf->proxy_recursive); - } - - switch (addr.sockaddr->sa_family) { - - case AF_INET: - /* Produce IPv4-mapped IPv6 address. */ - sin = (struct sockaddr_in *) addr.sockaddr; - addr4 = ntohl(sin->sin_addr.s_addr); - - ngx_memzero(&addr6, sizeof(struct in6_addr)); - addr6.s6_addr[10] = 0xff; - addr6.s6_addr[11] = 0xff; - addr6.s6_addr[12] = addr4 >> 24; - addr6.s6_addr[13] = addr4 >> 16; - addr6.s6_addr[14] = addr4 >> 8; - addr6.s6_addr[15] = addr4; - return addr6; - - case AF_INET6: - sin6 = (struct sockaddr_in6 *) addr.sockaddr; - return sin6->sin6_addr; - - default: - return in6addr_any; - } -} - -#endif - - static ngx_int_t ngx_http_geoip_country_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) @@ -612,6 +518,107 @@ ngx_http_geoip_get_city_record(ngx_http_ } +static u_long +ngx_http_geoip_addr(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf) +{ + ngx_addr_t addr; + ngx_table_elt_t *xfwd; + struct sockaddr_in *sin; + + addr.sockaddr = r->connection->sockaddr; + addr.socklen = r->connection->socklen; + /* addr.name = r->connection->addr_text; */ + + xfwd = r->headers_in.x_forwarded_for; + + if (xfwd != NULL && gcf->proxies != NULL) { + (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL, + gcf->proxies, gcf->proxy_recursive); + } + +#if (NGX_HAVE_INET6) + + if (addr.sockaddr->sa_family == AF_INET6) { + u_char *p; + in_addr_t inaddr; + struct in6_addr *inaddr6; + + inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; + + if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { + p = inaddr6->s6_addr; + + inaddr = (in_addr_t) p[12] << 24; + inaddr += p[13] << 16; + inaddr += p[14] << 8; + inaddr += p[15]; + + return inaddr; + } + } + +#endif + + if (addr.sockaddr->sa_family != AF_INET) { + return INADDR_NONE; + } + + sin = (struct sockaddr_in *) addr.sockaddr; + return ntohl(sin->sin_addr.s_addr); +} + + +#if (NGX_HAVE_GEOIP_V6) + +static geoipv6_t +ngx_http_geoip_addr_v6(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf) +{ + ngx_addr_t addr; + ngx_table_elt_t *xfwd; + in_addr_t addr4; + struct in6_addr addr6; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + addr.sockaddr = r->connection->sockaddr; + addr.socklen = r->connection->socklen; + /* addr.name = r->connection->addr_text; */ + + xfwd = r->headers_in.x_forwarded_for; + + if (xfwd != NULL && gcf->proxies != NULL) { + (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL, + gcf->proxies, gcf->proxy_recursive); + } + + switch (addr.sockaddr->sa_family) { + + case AF_INET: + /* Produce IPv4-mapped IPv6 address. */ + sin = (struct sockaddr_in *) addr.sockaddr; + addr4 = ntohl(sin->sin_addr.s_addr); + + ngx_memzero(&addr6, sizeof(struct in6_addr)); + addr6.s6_addr[10] = 0xff; + addr6.s6_addr[11] = 0xff; + addr6.s6_addr[12] = addr4 >> 24; + addr6.s6_addr[13] = addr4 >> 16; + addr6.s6_addr[14] = addr4 >> 8; + addr6.s6_addr[15] = addr4; + return addr6; + + case AF_INET6: + sin6 = (struct sockaddr_in6 *) addr.sockaddr; + return sin6->sin6_addr; + + default: + return in6addr_any; + } +} + +#endif + + static ngx_int_t ngx_http_geoip_add_variables(ngx_conf_t *cf) { 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 @@ -72,6 +72,13 @@ static ngx_int_t ngx_stream_geoip_city_i ngx_stream_variable_value_t *v, uintptr_t data); static GeoIPRecord *ngx_stream_geoip_get_city_record(ngx_stream_session_t *s); +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 + static ngx_int_t ngx_stream_geoip_add_variables(ngx_conf_t *cf); static void *ngx_stream_geoip_create_conf(ngx_conf_t *cf); static char *ngx_stream_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, @@ -208,91 +215,6 @@ static ngx_stream_variable_t ngx_stream }; -static u_long -ngx_stream_geoip_addr(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf) -{ - ngx_addr_t addr; - struct sockaddr_in *sin; - - addr.sockaddr = s->connection->sockaddr; - addr.socklen = s->connection->socklen; - /* addr.name = s->connection->addr_text; */ - -#if (NGX_HAVE_INET6) - - if (addr.sockaddr->sa_family == AF_INET6) { - u_char *p; - in_addr_t inaddr; - struct in6_addr *inaddr6; - - inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; - - if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { - p = inaddr6->s6_addr; - - inaddr = (in_addr_t) p[12] << 24; - inaddr += p[13] << 16; - inaddr += p[14] << 8; - inaddr += p[15]; - - return inaddr; - } - } - -#endif - - if (addr.sockaddr->sa_family != AF_INET) { - return INADDR_NONE; - } - - sin = (struct sockaddr_in *) addr.sockaddr; - return ntohl(sin->sin_addr.s_addr); -} - - -#if (NGX_HAVE_GEOIP_V6) - -static geoipv6_t -ngx_stream_geoip_addr_v6(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf) -{ - ngx_addr_t addr; - in_addr_t addr4; - struct in6_addr addr6; - struct sockaddr_in *sin; - struct sockaddr_in6 *sin6; - - addr.sockaddr = s->connection->sockaddr; - addr.socklen = s->connection->socklen; - /* addr.name = s->connection->addr_text; */ - - switch (addr.sockaddr->sa_family) { - - case AF_INET: - /* Produce IPv4-mapped IPv6 address. */ - sin = (struct sockaddr_in *) addr.sockaddr; - addr4 = ntohl(sin->sin_addr.s_addr); - - ngx_memzero(&addr6, sizeof(struct in6_addr)); - addr6.s6_addr[10] = 0xff; - addr6.s6_addr[11] = 0xff; - addr6.s6_addr[12] = addr4 >> 24; - addr6.s6_addr[13] = addr4 >> 16; - addr6.s6_addr[14] = addr4 >> 8; - addr6.s6_addr[15] = addr4; - return addr6; - - case AF_INET6: - sin6 = (struct sockaddr_in6 *) addr.sockaddr; - return sin6->sin6_addr; - - default: - return in6addr_any; - } -} - -#endif - - static ngx_int_t ngx_stream_geoip_country_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data) @@ -574,6 +496,91 @@ ngx_stream_geoip_get_city_record(ngx_str } +static u_long +ngx_stream_geoip_addr(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf) +{ + ngx_addr_t addr; + struct sockaddr_in *sin; + + addr.sockaddr = s->connection->sockaddr; + addr.socklen = s->connection->socklen; + /* addr.name = s->connection->addr_text; */ + +#if (NGX_HAVE_INET6) + + if (addr.sockaddr->sa_family == AF_INET6) { + u_char *p; + in_addr_t inaddr; + struct in6_addr *inaddr6; + + inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; + + if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { + p = inaddr6->s6_addr; + + inaddr = (in_addr_t) p[12] << 24; + inaddr += p[13] << 16; + inaddr += p[14] << 8; + inaddr += p[15]; + + return inaddr; + } + } + +#endif + + if (addr.sockaddr->sa_family != AF_INET) { + return INADDR_NONE; + } + + sin = (struct sockaddr_in *) addr.sockaddr; + return ntohl(sin->sin_addr.s_addr); +} + + +#if (NGX_HAVE_GEOIP_V6) + +static geoipv6_t +ngx_stream_geoip_addr_v6(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf) +{ + ngx_addr_t addr; + in_addr_t addr4; + struct in6_addr addr6; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + addr.sockaddr = s->connection->sockaddr; + addr.socklen = s->connection->socklen; + /* addr.name = s->connection->addr_text; */ + + switch (addr.sockaddr->sa_family) { + + case AF_INET: + /* Produce IPv4-mapped IPv6 address. */ + sin = (struct sockaddr_in *) addr.sockaddr; + addr4 = ntohl(sin->sin_addr.s_addr); + + ngx_memzero(&addr6, sizeof(struct in6_addr)); + addr6.s6_addr[10] = 0xff; + addr6.s6_addr[11] = 0xff; + addr6.s6_addr[12] = addr4 >> 24; + addr6.s6_addr[13] = addr4 >> 16; + addr6.s6_addr[14] = addr4 >> 8; + addr6.s6_addr[15] = addr4; + return addr6; + + case AF_INET6: + sin6 = (struct sockaddr_in6 *) addr.sockaddr; + return sin6->sin6_addr; + + default: + return in6addr_any; + } +} + +#endif + + static ngx_int_t ngx_stream_geoip_add_variables(ngx_conf_t *cf) { From mdounin at mdounin.ru Wed Dec 3 16:31:08 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Wed, 03 Dec 2025 19:31:08 +0300 Subject: [PATCH 4 of 6] GeoIP: introduced ngx_http_geoip_sockaddr() helper In-Reply-To: References: Message-ID: <9be164cc80e638557db1.1764779468@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1764576372 -10800 # Mon Dec 01 11:06:12 2025 +0300 # Node ID 9be164cc80e638557db1f1c6614202b540359ae6 # Parent 3fb3fd326c10849df11fa637bf8de2eedec6fb46 GeoIP: introduced ngx_http_geoip_sockaddr() helper. Such a helper function will be needed for further use with libmaxminddb, where sockaddr is used for both IPv4 and IPv6 lookups. It also reduces amount of code duplication. Stream module counterpart, where direct use of s->connection->sockaddr is sufficient, is adjusted similarly to match the code, but without introducing a helper function. 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 @@ -78,6 +78,8 @@ static u_long ngx_http_geoip_addr(ngx_ht static geoipv6_t ngx_http_geoip_addr_v6(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf); #endif +static struct sockaddr *ngx_http_geoip_sockaddr(ngx_http_request_t *r, + ngx_http_geoip_conf_t *gcf); static ngx_int_t ngx_http_geoip_add_variables(ngx_conf_t *cf); static void *ngx_http_geoip_create_conf(ngx_conf_t *cf); @@ -521,29 +523,19 @@ ngx_http_geoip_get_city_record(ngx_http_ static u_long ngx_http_geoip_addr(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf) { - ngx_addr_t addr; - ngx_table_elt_t *xfwd; + struct sockaddr *sockaddr; struct sockaddr_in *sin; - addr.sockaddr = r->connection->sockaddr; - addr.socklen = r->connection->socklen; - /* addr.name = r->connection->addr_text; */ - - xfwd = r->headers_in.x_forwarded_for; - - if (xfwd != NULL && gcf->proxies != NULL) { - (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL, - gcf->proxies, gcf->proxy_recursive); - } + sockaddr = ngx_http_geoip_sockaddr(r, gcf); #if (NGX_HAVE_INET6) - if (addr.sockaddr->sa_family == AF_INET6) { + if (sockaddr->sa_family == AF_INET6) { u_char *p; in_addr_t inaddr; struct in6_addr *inaddr6; - inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; + inaddr6 = &((struct sockaddr_in6 *) sockaddr)->sin6_addr; if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { p = inaddr6->s6_addr; @@ -559,11 +551,11 @@ ngx_http_geoip_addr(ngx_http_request_t * #endif - if (addr.sockaddr->sa_family != AF_INET) { + if (sockaddr->sa_family != AF_INET) { return INADDR_NONE; } - sin = (struct sockaddr_in *) addr.sockaddr; + sin = (struct sockaddr_in *) sockaddr; return ntohl(sin->sin_addr.s_addr); } @@ -573,13 +565,48 @@ ngx_http_geoip_addr(ngx_http_request_t * static geoipv6_t ngx_http_geoip_addr_v6(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf) { - ngx_addr_t addr; - ngx_table_elt_t *xfwd; in_addr_t addr4; struct in6_addr addr6; + struct sockaddr *sockaddr; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; + sockaddr = ngx_http_geoip_sockaddr(r, gcf); + + switch (sockaddr->sa_family) { + + case AF_INET: + /* Produce IPv4-mapped IPv6 address. */ + sin = (struct sockaddr_in *) sockaddr; + addr4 = ntohl(sin->sin_addr.s_addr); + + ngx_memzero(&addr6, sizeof(struct in6_addr)); + addr6.s6_addr[10] = 0xff; + addr6.s6_addr[11] = 0xff; + addr6.s6_addr[12] = addr4 >> 24; + addr6.s6_addr[13] = addr4 >> 16; + addr6.s6_addr[14] = addr4 >> 8; + addr6.s6_addr[15] = addr4; + return addr6; + + case AF_INET6: + sin6 = (struct sockaddr_in6 *) sockaddr; + return sin6->sin6_addr; + + default: + return in6addr_any; + } +} + +#endif + + +static struct sockaddr * +ngx_http_geoip_sockaddr(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf) +{ + ngx_addr_t addr; + ngx_table_elt_t *xfwd; + addr.sockaddr = r->connection->sockaddr; addr.socklen = r->connection->socklen; /* addr.name = r->connection->addr_text; */ @@ -591,33 +618,9 @@ ngx_http_geoip_addr_v6(ngx_http_request_ gcf->proxies, gcf->proxy_recursive); } - switch (addr.sockaddr->sa_family) { - - case AF_INET: - /* Produce IPv4-mapped IPv6 address. */ - sin = (struct sockaddr_in *) addr.sockaddr; - addr4 = ntohl(sin->sin_addr.s_addr); - - ngx_memzero(&addr6, sizeof(struct in6_addr)); - addr6.s6_addr[10] = 0xff; - addr6.s6_addr[11] = 0xff; - addr6.s6_addr[12] = addr4 >> 24; - addr6.s6_addr[13] = addr4 >> 16; - addr6.s6_addr[14] = addr4 >> 8; - addr6.s6_addr[15] = addr4; - return addr6; - - case AF_INET6: - sin6 = (struct sockaddr_in6 *) addr.sockaddr; - return sin6->sin6_addr; - - default: - return in6addr_any; - } + return addr.sockaddr; } -#endif - static ngx_int_t ngx_http_geoip_add_variables(ngx_conf_t *cf) 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 @@ -499,21 +499,19 @@ ngx_stream_geoip_get_city_record(ngx_str static u_long ngx_stream_geoip_addr(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf) { - ngx_addr_t addr; + struct sockaddr *sockaddr; struct sockaddr_in *sin; - addr.sockaddr = s->connection->sockaddr; - addr.socklen = s->connection->socklen; - /* addr.name = s->connection->addr_text; */ + sockaddr = s->connection->sockaddr; #if (NGX_HAVE_INET6) - if (addr.sockaddr->sa_family == AF_INET6) { + if (sockaddr->sa_family == AF_INET6) { u_char *p; in_addr_t inaddr; struct in6_addr *inaddr6; - inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; + inaddr6 = &((struct sockaddr_in6 *) sockaddr)->sin6_addr; if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { p = inaddr6->s6_addr; @@ -529,11 +527,11 @@ ngx_stream_geoip_addr(ngx_stream_session #endif - if (addr.sockaddr->sa_family != AF_INET) { + if (sockaddr->sa_family != AF_INET) { return INADDR_NONE; } - sin = (struct sockaddr_in *) addr.sockaddr; + sin = (struct sockaddr_in *) sockaddr; return ntohl(sin->sin_addr.s_addr); } @@ -543,21 +541,19 @@ ngx_stream_geoip_addr(ngx_stream_session static geoipv6_t ngx_stream_geoip_addr_v6(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf) { - ngx_addr_t addr; in_addr_t addr4; struct in6_addr addr6; + struct sockaddr *sockaddr; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; - addr.sockaddr = s->connection->sockaddr; - addr.socklen = s->connection->socklen; - /* addr.name = s->connection->addr_text; */ + sockaddr = s->connection->sockaddr; - switch (addr.sockaddr->sa_family) { + switch (sockaddr->sa_family) { case AF_INET: /* Produce IPv4-mapped IPv6 address. */ - sin = (struct sockaddr_in *) addr.sockaddr; + sin = (struct sockaddr_in *) sockaddr; addr4 = ntohl(sin->sin_addr.s_addr); ngx_memzero(&addr6, sizeof(struct in6_addr)); @@ -570,7 +566,7 @@ ngx_stream_geoip_addr_v6(ngx_stream_sess return addr6; case AF_INET6: - sin6 = (struct sockaddr_in6 *) addr.sockaddr; + sin6 = (struct sockaddr_in6 *) sockaddr; return sin6->sin6_addr; default: From mdounin at mdounin.ru Wed Dec 3 16:31:09 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Wed, 03 Dec 2025 19:31:09 +0300 Subject: [PATCH 5 of 6] GeoIP: simplified handling of IPv6 databases In-Reply-To: References: Message-ID: <9d4db0c2b512cb8d5483.1764779469@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1764578741 -10800 # Mon Dec 01 11:45:41 2025 +0300 # Node ID 9d4db0c2b512cb8d548345db2852c2d6a000d49c # Parent 9be164cc80e638557db1f1c6614202b540359ae6 GeoIP: simplified handling of IPv6 databases. 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 @@ -32,32 +32,6 @@ typedef struct { } ngx_http_geoip_conf_t; -typedef const char *(*ngx_http_geoip_variable_handler_pt)(GeoIP *, - u_long addr); - - -ngx_http_geoip_variable_handler_pt ngx_http_geoip_country_functions[] = { - GeoIP_country_code_by_ipnum, - GeoIP_country_code3_by_ipnum, - GeoIP_country_name_by_ipnum, -}; - - -#if (NGX_HAVE_GEOIP_V6) - -typedef const char *(*ngx_http_geoip_variable_handler_v6_pt)(GeoIP *, - geoipv6_t addr); - - -ngx_http_geoip_variable_handler_v6_pt ngx_http_geoip_country_v6_functions[] = { - GeoIP_country_code_by_ipnum_v6, - GeoIP_country_code3_by_ipnum_v6, - GeoIP_country_name_by_ipnum_v6, -}; - -#endif - - static ngx_int_t ngx_http_geoip_country_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_geoip_org_variable(ngx_http_request_t *r, @@ -243,13 +217,6 @@ static ngx_int_t ngx_http_geoip_country_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - ngx_http_geoip_variable_handler_pt handler = - ngx_http_geoip_country_functions[data]; -#if (NGX_HAVE_GEOIP_V6) - ngx_http_geoip_variable_handler_v6_pt handler_v6 = - ngx_http_geoip_country_v6_functions[data]; -#endif - const char *val; ngx_http_geoip_conf_t *gcf; @@ -260,12 +227,35 @@ ngx_http_geoip_country_variable(ngx_http } #if (NGX_HAVE_GEOIP_V6) - val = gcf->country_v6 - ? handler_v6(gcf->country, ngx_http_geoip_addr_v6(r, gcf)) - : handler(gcf->country, ngx_http_geoip_addr(r, gcf)); -#else - val = handler(gcf->country, ngx_http_geoip_addr(r, gcf)); + if (gcf->country_v6) { + if (data == NGX_GEOIP_COUNTRY_CODE) { + val = GeoIP_country_code_by_ipnum_v6(gcf->country, + ngx_http_geoip_addr_v6(r, gcf)); + + } else if (data == NGX_GEOIP_COUNTRY_CODE3) { + val = GeoIP_country_code3_by_ipnum_v6(gcf->country, + ngx_http_geoip_addr_v6(r, gcf)); + + } else { /* NGX_GEOIP_COUNTRY_NAME */ + val = GeoIP_country_name_by_ipnum_v6(gcf->country, + ngx_http_geoip_addr_v6(r, gcf)); + } + } else #endif + { + if (data == NGX_GEOIP_COUNTRY_CODE) { + val = GeoIP_country_code_by_ipnum(gcf->country, + ngx_http_geoip_addr(r, gcf)); + + } else if (data == NGX_GEOIP_COUNTRY_CODE3) { + val = GeoIP_country_code3_by_ipnum(gcf->country, + ngx_http_geoip_addr(r, gcf)); + + } else { /* NGX_GEOIP_COUNTRY_NAME */ + val = GeoIP_country_name_by_ipnum(gcf->country, + ngx_http_geoip_addr(r, gcf)); + } + } if (val == NULL) { goto not_found; @@ -302,14 +292,14 @@ ngx_http_geoip_org_variable(ngx_http_req } #if (NGX_HAVE_GEOIP_V6) - val = gcf->org_v6 - ? GeoIP_name_by_ipnum_v6(gcf->org, - ngx_http_geoip_addr_v6(r, gcf)) - : GeoIP_name_by_ipnum(gcf->org, - ngx_http_geoip_addr(r, gcf)); -#else - val = GeoIP_name_by_ipnum(gcf->org, ngx_http_geoip_addr(r, gcf)); + if (gcf->org_v6) { + val = GeoIP_name_by_ipnum_v6(gcf->org, ngx_http_geoip_addr_v6(r, gcf)); + + } else #endif + { + val = GeoIP_name_by_ipnum(gcf->org, ngx_http_geoip_addr(r, gcf)); + } if (val == NULL) { goto not_found; @@ -506,14 +496,12 @@ ngx_http_geoip_get_city_record(ngx_http_ if (gcf->city) { #if (NGX_HAVE_GEOIP_V6) - return gcf->city_v6 - ? GeoIP_record_by_ipnum_v6(gcf->city, - ngx_http_geoip_addr_v6(r, gcf)) - : GeoIP_record_by_ipnum(gcf->city, - ngx_http_geoip_addr(r, gcf)); -#else + if (gcf->city_v6) { + return GeoIP_record_by_ipnum_v6(gcf->city, + ngx_http_geoip_addr_v6(r, gcf)); + } +#endif return GeoIP_record_by_ipnum(gcf->city, ngx_http_geoip_addr(r, gcf)); -#endif } return NULL; 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 @@ -30,34 +30,6 @@ typedef struct { } ngx_stream_geoip_conf_t; -typedef const char *(*ngx_stream_geoip_variable_handler_pt)(GeoIP *, - u_long addr); - - -ngx_stream_geoip_variable_handler_pt ngx_stream_geoip_country_functions[] = { - GeoIP_country_code_by_ipnum, - GeoIP_country_code3_by_ipnum, - GeoIP_country_name_by_ipnum, -}; - - -#if (NGX_HAVE_GEOIP_V6) - -typedef const char *(*ngx_stream_geoip_variable_handler_v6_pt)(GeoIP *, - geoipv6_t addr); - - -ngx_stream_geoip_variable_handler_v6_pt - ngx_stream_geoip_country_v6_functions[] = -{ - GeoIP_country_code_by_ipnum_v6, - GeoIP_country_code3_by_ipnum_v6, - GeoIP_country_name_by_ipnum_v6, -}; - -#endif - - static ngx_int_t ngx_stream_geoip_country_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_geoip_org_variable(ngx_stream_session_t *s, @@ -219,13 +191,6 @@ static ngx_int_t ngx_stream_geoip_country_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data) { - ngx_stream_geoip_variable_handler_pt handler = - ngx_stream_geoip_country_functions[data]; -#if (NGX_HAVE_GEOIP_V6) - ngx_stream_geoip_variable_handler_v6_pt handler_v6 = - ngx_stream_geoip_country_v6_functions[data]; -#endif - const char *val; ngx_stream_geoip_conf_t *gcf; @@ -236,12 +201,35 @@ ngx_stream_geoip_country_variable(ngx_st } #if (NGX_HAVE_GEOIP_V6) - val = gcf->country_v6 - ? handler_v6(gcf->country, ngx_stream_geoip_addr_v6(s, gcf)) - : handler(gcf->country, ngx_stream_geoip_addr(s, gcf)); -#else - val = handler(gcf->country, ngx_stream_geoip_addr(s, gcf)); + if (gcf->country_v6) { + if (data == NGX_GEOIP_COUNTRY_CODE) { + val = GeoIP_country_code_by_ipnum_v6(gcf->country, + ngx_stream_geoip_addr_v6(s, gcf)); + + } else if (data == NGX_GEOIP_COUNTRY_CODE3) { + val = GeoIP_country_code3_by_ipnum_v6(gcf->country, + ngx_stream_geoip_addr_v6(s, gcf)); + + } else { /* NGX_GEOIP_COUNTRY_NAME */ + val = GeoIP_country_name_by_ipnum_v6(gcf->country, + ngx_stream_geoip_addr_v6(s, gcf)); + } + } else #endif + { + if (data == NGX_GEOIP_COUNTRY_CODE) { + val = GeoIP_country_code_by_ipnum(gcf->country, + ngx_stream_geoip_addr(s, gcf)); + + } else if (data == NGX_GEOIP_COUNTRY_CODE3) { + val = GeoIP_country_code3_by_ipnum(gcf->country, + ngx_stream_geoip_addr(s, gcf)); + + } else { /* NGX_GEOIP_COUNTRY_NAME */ + val = GeoIP_country_name_by_ipnum(gcf->country, + ngx_stream_geoip_addr(s, gcf)); + } + } if (val == NULL) { goto not_found; @@ -278,14 +266,15 @@ ngx_stream_geoip_org_variable(ngx_stream } #if (NGX_HAVE_GEOIP_V6) - val = gcf->org_v6 - ? GeoIP_name_by_ipnum_v6(gcf->org, - ngx_stream_geoip_addr_v6(s, gcf)) - : GeoIP_name_by_ipnum(gcf->org, - ngx_stream_geoip_addr(s, gcf)); -#else - val = GeoIP_name_by_ipnum(gcf->org, ngx_stream_geoip_addr(s, gcf)); + if (gcf->org_v6) { + val = GeoIP_name_by_ipnum_v6(gcf->org, + ngx_stream_geoip_addr_v6(s, gcf)); + + } else #endif + { + val = GeoIP_name_by_ipnum(gcf->org, ngx_stream_geoip_addr(s, gcf)); + } if (val == NULL) { goto not_found; @@ -482,14 +471,12 @@ ngx_stream_geoip_get_city_record(ngx_str if (gcf->city) { #if (NGX_HAVE_GEOIP_V6) - return gcf->city_v6 - ? GeoIP_record_by_ipnum_v6(gcf->city, - ngx_stream_geoip_addr_v6(s, gcf)) - : GeoIP_record_by_ipnum(gcf->city, - ngx_stream_geoip_addr(s, gcf)); -#else + if (gcf->city_v6) { + return GeoIP_record_by_ipnum_v6(gcf->city, + ngx_stream_geoip_addr_v6(s, gcf)); + } +#endif return GeoIP_record_by_ipnum(gcf->city, ngx_stream_geoip_addr(s, gcf)); -#endif } return NULL; From mdounin at mdounin.ru Wed Dec 3 16:31:10 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Wed, 03 Dec 2025 19:31:10 +0300 Subject: [PATCH 6 of 6] GeoIP: support for GeoIP2 databases in the MMDB format In-Reply-To: References: Message-ID: <234a79a752d46a21ecac.1764779470@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # 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 " 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 + #include " + #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 " + 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 - #include " - #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 #include + +#if (NGX_HAVE_GEOIP_LEGACY) + #include #include +#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 + +#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 #include +#if (NGX_HAVE_GEOIP_LEGACY) + #include #include +#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 + +#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 } From mdounin at mdounin.ru Wed Dec 3 16:32:44 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Wed, 03 Dec 2025 19:32:44 +0300 Subject: [PATCH] Tests: tests for GeoIP databases in the MMDB format In-Reply-To: <234a79a752d46a21ecac.1764779470@vm-bsd.mdounin.ru> References: <234a79a752d46a21ecac.1764779470@vm-bsd.mdounin.ru> Message-ID: # HG changeset patch # User Maxim Dounin # Date 1764694278 -10800 # Tue Dec 02 19:51:18 2025 +0300 # Node ID f94d1e13363739c0f2129f404e5672a4ee98146c # Parent 55ff381e5d3a0f0fb2e64b3ec63ec01bf1b6f132 Tests: tests for GeoIP databases in the MMDB format. diff --git a/geoip.t b/geoip.t --- a/geoip.t +++ b/geoip.t @@ -144,7 +144,7 @@ for my $i (0 .. 31) { $t->write_file('org.dat', $data); $t->write_file('index.html', ''); -$t->try_run('no inet6 support')->plan(20); +$t->try_run('no geoip legacy')->plan(20); ############################################################################### diff --git a/geoip.t b/geoip_mmdb.t copy from geoip.t copy to geoip_mmdb.t --- a/geoip.t +++ b/geoip_mmdb.t @@ -1,9 +1,10 @@ #!/usr/bin/perl +# (C) Maxim Dounin # (C) Andrey Zelenkov # (C) Nginx, Inc. -# Tests for geoip module. +# Tests for geoip module with MMDB databases. ############################################################################### @@ -37,9 +38,11 @@ http { geoip_proxy 127.0.0.1/32; - geoip_country %%TESTDIR%%/country.dat; - geoip_city %%TESTDIR%%/city.dat; - geoip_org %%TESTDIR%%/org.dat; + geoip_country test.mmdb; + geoip_city test.mmdb; + geoip_org test.mmdb; + + geoip_set $asn test.mmdb autonomous_system_number; server { listen 127.0.0.1:8080; @@ -47,13 +50,13 @@ http { location / { add_header X-Country-Code $geoip_country_code; - add_header X-Country-Code3 $geoip_country_code3; + add_header X-Country-Code3 $geoip_country_code3:none; add_header X-Country-Name $geoip_country_name; - add_header X-Area-Code $geoip_area_code; + add_header X-Area-Code $geoip_area_code:none; add_header X-C-Continent-Code $geoip_city_continent_code; add_header X-C-Country-Code $geoip_city_country_code; - add_header X-C-Country-Code3 $geoip_city_country_code3; + add_header X-C-Country-Code3 $geoip_city_country_code3:none; add_header X-C-Country-Name $geoip_city_country_name; add_header X-Dma-Code $geoip_dma_code; add_header X-Latitude $geoip_latitude; @@ -64,6 +67,8 @@ http { add_header X-Postal-Code $geoip_postal_code; add_header X-Org $geoip_org; + + add_header X-ASN $asn; } } } @@ -72,127 +77,126 @@ EOF my $d = $t->testdir(); -# country database: -# -# "10.0.0.1","10.0.0.1","RU","Russian Federation" -# "2001:db8::","2001:db8::","US","United States" +# MMDB format specification: +# https://github.com/maxmind/MaxMind-DB/blob/main/MaxMind-DB-spec.md my $data = ''; -for my $i (0 .. 156) { - # skip to offset 32 if 1st bit set in ipv6 address wins - $data .= pack_node($i + 1) . pack_node(32), next if $i == 2; - # otherwise default to RU - $data .= pack_node(0xffffb9) . pack_node(0xffff00), next if $i == 31; - # continue checking bits set in ipv6 address - $data .= pack_node(0xffff00) . pack_node($i + 1), next - if grep $_ == $i, (44, 49, 50, 52, 53, 55, 56, 57); - # last bit set in ipv6 address - $data .= pack_node(0xffffe1) . pack_node(0xffff00), next if $i == 156; - $data .= pack_node($i + 1) . pack_node(0xffff00); -} +# binary search tree +# just one node (two records), 32-bit records + +$data .= pack('NN', 17, 17); + +# data section + +$data .= pack('x16'); + +$data .= pack("B8", "11101000"); + +$data .= pack("B8A*", "01011000", "autonomous_system_number"); +$data .= pack("B8N", "11000100", 64511); + +$data .= pack("B8CA*", "01011101", 30 - 29, "autonomous_system_organization"); +$data .= pack("B8A*", "01001101", "freenginx.org"); -$data .= chr(0x00) x 3; -$data .= chr(0xFF) x 3; -$data .= chr(12); - -$t->write_file('country.dat', $data); +$data .= pack("B8A*", "01000111", "country"); +$data .= pack("B8", "11100010"); +$data .= pack("B8A*", "01001000", "iso_code"); +$data .= pack("B8A*", "01000010", "RU"); +$data .= pack("B8A*", "01000101", "names"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000010", "en"); +$data .= pack("B8A*", "01010010", "Russian Federation"); -# city database: -# -# "167772161","167772161","RU","48","Moscow","119034","55.7543",37.6202",, - -$data = ''; +$data .= pack("B8A*", "01001001", "continent"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000100", "code"); +$data .= pack("B8A*", "01000010", "EU"); -for my $i (0 .. 31) { - $data .= pack_node(32) . pack_node($i + 1), next if $i == 4 or $i == 6; - $data .= pack_node(32) . pack_node($i + 2), next if $i == 31; - $data .= pack_node($i + 1) . pack_node(32); -} +$data .= pack("B8A*", "01001100", "subdivisions"); +$data .= pack("B8C", "00000001", 11 - 7); +$data .= pack("B8", "11100010"); +$data .= pack("B8A*", "01001000", "iso_code"); +$data .= pack("B8A*", "01000011", "MOW"); +$data .= pack("B8A*", "01000101", "names"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000010", "en"); +$data .= pack("B8A*", "01000110", "Moscow"); -$data .= chr(42); -$data .= chr(185); -$data .= pack('Z*', 48); -$data .= pack('Z*', 'Moscow'); -$data .= pack('Z*', 119034); -$data .= pack_node(int((55.7543 + 180) * 10000)); -$data .= pack_node(int((37.6202 + 180) * 10000)); -$data .= chr(0) x 3; -$data .= chr(0xFF) x 3; -$data .= chr(2); -$data .= pack_node(32); +$data .= pack("B8A*", "01000100", "city"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000101", "names"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000010", "en"); +$data .= pack("B8A*", "01000110", "Moscow"); + +$data .= pack("B8A*", "01000110", "postal"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000100", "code"); +$data .= pack("B8A*", "01000110", "119034"); -$t->write_file('city.dat', $data); +$data .= pack("B8A*", "01001000", "location"); +$data .= pack("B8", "11100011"); +$data .= pack("B8A*", "01001000", "latitude"); +$data .= pack("B8d>", "01101000", 55.7543); +$data .= pack("B8A*", "01001001", "longitude"); +$data .= pack("B8d>", "01101000", 37.6202); +$data .= pack("B8A*", "01001010", "metro_code"); +$data .= pack("B8C", "10100001", 0); -# organization database: -# -# "167772161","167772161","Nginx" +# metadata + +$data .= "\xab\xcd\xefMaxMind.com"; -$data = ''; - -for my $i (0 .. 31) { - $data .= pack_org(32) . pack_org($i + 1), next if $i == 4 or $i == 6; - $data .= pack_org(32) . pack_org($i + 2), next if $i == 31; - $data .= pack_org($i + 1) . pack_org(32); -} +$data .= pack("B8", "11101001"); +$data .= pack("B8A*", "01001010", "node_count"); +$data .= pack("B8C", "11000001", 1); +$data .= pack("B8A*", "01001011", "record_size"); +$data .= pack("B8C", "10100001", 32); +$data .= pack("B8A*", "01001010", "ip_version"); +$data .= pack("B8C", "10100001", 6); +$data .= pack("B8A*", "01001101", "database_type"); +$data .= pack("B8A*", "01000100", "test"); +$data .= pack("B8A*", "01001001", "languages"); +$data .= pack("B8B8", "00000001", "00000100"); +$data .= pack("B8A*", "01000100", "test"); +$data .= pack("B8A*", "01011011", "binary_format_major_version"); +$data .= pack("B8C", "10100001", 2); +$data .= pack("B8A*", "01011011", "binary_format_minor_version"); +$data .= pack("B8C", "10100001", 0); +$data .= pack("B8A*", "01001011", "build_epoch"); +$data .= pack("B8B8C", "00000001", "00000010", 1); +$data .= pack("B8A*", "01001011", "description"); +$data .= pack("B8", "11100000"); -$data .= chr(42); -$data .= pack('Z*', 'Nginx'); -$data .= chr(0xFF) x 3; -$data .= chr(5); -$data .= pack_node(32); +$t->write_file('test.mmdb', $data); -$t->write_file('org.dat', $data); $t->write_file('index.html', ''); -$t->try_run('no inet6 support')->plan(20); +$t->try_run('no geoip mmdb')->plan(17); ############################################################################### -my $r = http_xff('10.0.0.1'); +my $r = http_get('/'); + like($r, qr/X-Country-Code: RU/, 'geoip country code'); -like($r, qr/X-Country-Code3: RUS/, 'geoip country code 3'); +like($r, qr/X-Country-Code3: :none/, 'geoip country code 3'); like($r, qr/X-Country-Name: Russian Federation/, 'geoip country name'); -like($r, qr/X-Area-Code: 0/, 'geoip area code'); +like($r, qr/X-Area-Code: :none/, 'geoip area code'); like($r, qr/X-C-Continent-Code: EU/, 'geoip city continent code'); like($r, qr/X-C-Country-Code: RU/, 'geoip city country code'); -like($r, qr/X-C-Country-Code3: RUS/, 'geoip city country code 3'); +like($r, qr/X-C-Country-Code3: :none/, 'geoip city country code 3'); like($r, qr/X-C-Country-Name: Russian Federation/, 'geoip city country name'); like($r, qr/X-Dma-Code: 0/, 'geoip dma code'); like($r, qr/X-Latitude: 55.7543/, 'geoip latitude'); like($r, qr/X-Longitude: 37.6202/, 'geoip longitude'); -like($r, qr/X-Region: 48/, 'geoip region'); -like($r, qr/X-Region-Name: Moscow City/, 'geoip region name'); +like($r, qr/X-Region: MOW/, 'geoip region'); +like($r, qr/X-Region-Name: Moscow/, 'geoip region name'); like($r, qr/X-City: Moscow/, 'geoip city'); like($r, qr/X-Postal-Code: 119034/, 'geoip postal code'); -like($r, qr/X-Org: Nginx/, 'geoip org'); - -like(http_xff('::ffff:10.0.0.1'), qr/X-Org: Nginx/, 'geoip ipv6 ipv4-mapped'); +like($r, qr/X-Org: freenginx.org/, 'geoip org'); -$r = http_xff('2001:db8::'); -like($r, qr/X-Country-Code: US/, 'geoip ipv6 country code'); -like($r, qr/X-Country-Code3: USA/, 'geoip ipv6 country code 3'); -like($r, qr/X-Country-Name: United States/, 'geoip ipv6 country name'); +like($r, qr/X-ASN: 64511/, 'geoip set asn'); ############################################################################### - -sub http_xff { - my ($xff) = @_; - return http(<write_file('org.dat', $data); -$t->try_run('no inet6 support')->plan(20); +$t->try_run('no geoip legacy')->plan(20); ############################################################################### diff --git a/stream_geoip.t b/stream_geoip_mmdb.t copy from stream_geoip.t copy to stream_geoip_mmdb.t --- a/stream_geoip.t +++ b/stream_geoip_mmdb.t @@ -1,9 +1,10 @@ #!/usr/bin/perl +# (C) Maxim Dounin # (C) Andrey Zelenkov # (C) Nginx, Inc. -# Tests for stream geoip module. +# Tests for stream geoip module with MMDB databases. ############################################################################### @@ -25,8 +26,7 @@ use Test::Nginx::Stream qw/ stream /; select STDERR; $| = 1; select STDOUT; $| = 1; -my $t = Test::Nginx->new()->has(qw/stream stream_geoip stream_return/) - ->has('stream_realip'); +my $t = Test::Nginx->new()->has(qw/stream stream_geoip stream_return/); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -40,22 +40,22 @@ events { stream { %%TEST_GLOBALS_STREAM%% - set_real_ip_from 127.0.0.1/32; + geoip_country test.mmdb; + geoip_city test.mmdb; + geoip_org test.mmdb; - geoip_country %%TESTDIR%%/country.dat; - geoip_city %%TESTDIR%%/city.dat; - geoip_org %%TESTDIR%%/org.dat; + geoip_set $asn test.mmdb autonomous_system_number; server { - listen 127.0.0.1:8080 proxy_protocol; + listen 127.0.0.1:8080; return "country_code:$geoip_country_code - country_code3:$geoip_country_code3 + country_code3:$geoip_country_code3:none country_name:$geoip_country_name - area_code:$geoip_area_code + area_code:$geoip_area_code:none city_continent_code:$geoip_city_continent_code city_country_code:$geoip_city_country_code - city_country_code3:$geoip_city_country_code3 + city_country_code3:$geoip_city_country_code3:none city_country_name:$geoip_city_country_name dma_code:$geoip_dma_code latitude:$geoip_latitude @@ -65,7 +65,9 @@ stream { city:$geoip_city postal_code:$geoip_postal_code - org:$geoip_org"; + org:$geoip_org + + asn:$asn"; } } @@ -73,123 +75,125 @@ EOF my $d = $t->testdir(); -# country database: -# -# "10.0.0.1","10.0.0.1","RU","Russian Federation" -# "2001:db8::","2001:db8::","US","United States" +# MMDB format specification: +# https://github.com/maxmind/MaxMind-DB/blob/main/MaxMind-DB-spec.md my $data = ''; -for my $i (0 .. 156) { - # skip to offset 32 if 1st bit set in ipv6 address wins - $data .= pack_node($i + 1) . pack_node(32), next if $i == 2; - # otherwise default to RU - $data .= pack_node(0xffffb9) . pack_node(0xffff00), next if $i == 31; - # continue checking bits set in ipv6 address - $data .= pack_node(0xffff00) . pack_node($i + 1), next - if grep $_ == $i, (44, 49, 50, 52, 53, 55, 56, 57); - # last bit set in ipv6 address - $data .= pack_node(0xffffe1) . pack_node(0xffff00), next if $i == 156; - $data .= pack_node($i + 1) . pack_node(0xffff00); -} +# binary search tree +# just one node (two records), 32-bit records + +$data .= pack('NN', 17, 17); + +# data section + +$data .= pack('x16'); + +$data .= pack("B8", "11101000"); + +$data .= pack("B8A*", "01011000", "autonomous_system_number"); +$data .= pack("B8N", "11000100", 64511); + +$data .= pack("B8CA*", "01011101", 30 - 29, "autonomous_system_organization"); +$data .= pack("B8A*", "01001101", "freenginx.org"); -$data .= chr(0x00) x 3; -$data .= chr(0xFF) x 3; -$data .= chr(12); - -$t->write_file('country.dat', $data); +$data .= pack("B8A*", "01000111", "country"); +$data .= pack("B8", "11100010"); +$data .= pack("B8A*", "01001000", "iso_code"); +$data .= pack("B8A*", "01000010", "RU"); +$data .= pack("B8A*", "01000101", "names"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000010", "en"); +$data .= pack("B8A*", "01010010", "Russian Federation"); -# city database: -# -# "167772161","167772161","RU","48","Moscow","119034","55.7543",37.6202",, - -$data = ''; +$data .= pack("B8A*", "01001001", "continent"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000100", "code"); +$data .= pack("B8A*", "01000010", "EU"); -for my $i (0 .. 31) { - $data .= pack_node(32) . pack_node($i + 1), next if $i == 4 or $i == 6; - $data .= pack_node(32) . pack_node($i + 2), next if $i == 31; - $data .= pack_node($i + 1) . pack_node(32); -} +$data .= pack("B8A*", "01001100", "subdivisions"); +$data .= pack("B8C", "00000001", 11 - 7); +$data .= pack("B8", "11100010"); +$data .= pack("B8A*", "01001000", "iso_code"); +$data .= pack("B8A*", "01000011", "MOW"); +$data .= pack("B8A*", "01000101", "names"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000010", "en"); +$data .= pack("B8A*", "01000110", "Moscow"); -$data .= chr(42); -$data .= chr(185); -$data .= pack('Z*', 48); -$data .= pack('Z*', 'Moscow'); -$data .= pack('Z*', 119034); -$data .= pack_node(int((55.7543 + 180) * 10000)); -$data .= pack_node(int((37.6202 + 180) * 10000)); -$data .= chr(0) x 3; -$data .= chr(0xFF) x 3; -$data .= chr(2); -$data .= pack_node(32); +$data .= pack("B8A*", "01000100", "city"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000101", "names"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000010", "en"); +$data .= pack("B8A*", "01000110", "Moscow"); + +$data .= pack("B8A*", "01000110", "postal"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000100", "code"); +$data .= pack("B8A*", "01000110", "119034"); -$t->write_file('city.dat', $data); +$data .= pack("B8A*", "01001000", "location"); +$data .= pack("B8", "11100011"); +$data .= pack("B8A*", "01001000", "latitude"); +$data .= pack("B8d>", "01101000", 55.7543); +$data .= pack("B8A*", "01001001", "longitude"); +$data .= pack("B8d>", "01101000", 37.6202); +$data .= pack("B8A*", "01001010", "metro_code"); +$data .= pack("B8C", "10100001", 0); + +# metadata + +$data .= "\xab\xcd\xefMaxMind.com"; -# organization database: -# -# "167772161","167772161","Nginx" - -$data = ''; +$data .= pack("B8", "11101001"); +$data .= pack("B8A*", "01001010", "node_count"); +$data .= pack("B8C", "11000001", 1); +$data .= pack("B8A*", "01001011", "record_size"); +$data .= pack("B8C", "10100001", 32); +$data .= pack("B8A*", "01001010", "ip_version"); +$data .= pack("B8C", "10100001", 6); +$data .= pack("B8A*", "01001101", "database_type"); +$data .= pack("B8A*", "01000100", "test"); +$data .= pack("B8A*", "01001001", "languages"); +$data .= pack("B8B8", "00000001", "00000100"); +$data .= pack("B8A*", "01000100", "test"); +$data .= pack("B8A*", "01011011", "binary_format_major_version"); +$data .= pack("B8C", "10100001", 2); +$data .= pack("B8A*", "01011011", "binary_format_minor_version"); +$data .= pack("B8C", "10100001", 0); +$data .= pack("B8A*", "01001011", "build_epoch"); +$data .= pack("B8B8C", "00000001", "00000010", 1); +$data .= pack("B8A*", "01001011", "description"); +$data .= pack("B8", "11100000"); -for my $i (0 .. 31) { - $data .= pack_org(32) . pack_org($i + 1), next if $i == 4 or $i == 6; - $data .= pack_org(32) . pack_org($i + 2), next if $i == 31; - $data .= pack_org($i + 1) . pack_org(32); -} +$t->write_file('test.mmdb', $data); -$data .= chr(42); -$data .= pack('Z*', 'Nginx'); -$data .= chr(0xFF) x 3; -$data .= chr(5); -$data .= pack_node(32); - -$t->write_file('org.dat', $data); -$t->try_run('no inet6 support')->plan(20); +$t->try_run('no geoip mmdb')->plan(17); ############################################################################### -my %data = stream_pp('10.0.0.1') =~ /(\w+):(.*)/g; +my %data = stream('127.0.0.1:' . port(8080))->read() =~ /(\w+):(.*)/g; + is($data{country_code}, 'RU', 'geoip country code'); -is($data{country_code3}, 'RUS', 'geoip country code 3'); +is($data{country_code3}, ':none', 'geoip country code 3'); is($data{country_name}, 'Russian Federation', 'geoip country name'); -is($data{area_code}, 0, 'geoip area code'); +is($data{area_code}, ':none', 'geoip area code'); is($data{city_continent_code}, 'EU', 'geoip city continent code'); is($data{city_country_code}, 'RU', 'geoip city country code'); -is($data{city_country_code3}, 'RUS', 'geoip city country code 3'); +is($data{city_country_code3}, ':none', 'geoip city country code 3'); is($data{city_country_name}, 'Russian Federation', 'geoip city country name'); is($data{dma_code}, 0, 'geoip dma code'); is($data{latitude}, 55.7543, 'geoip latitude'); is($data{longitude}, 37.6202, 'geoip longitude'); -is($data{region}, 48, 'geoip region'); -is($data{region_name}, 'Moscow City', 'geoip region name'); +is($data{region}, 'MOW', 'geoip region'); +is($data{region_name}, 'Moscow', 'geoip region name'); is($data{city}, 'Moscow', 'geoip city'); is($data{postal_code}, 119034, 'geoip postal code'); -is($data{org}, 'Nginx', 'geoip org'); - -like(stream_pp('::ffff:10.0.0.1'), qr/org:Nginx/, 'geoip ipv6 ipv4-mapped'); +is($data{org}, 'freenginx.org', 'geoip org'); -%data = stream_pp('2001:db8::') =~ /(\w+):(.*)/g; -is($data{country_code}, 'US', 'geoip ipv6 country code'); -is($data{country_code3}, 'USA', 'geoip ipv6 country code 3'); -is($data{country_name}, 'United States', 'geoip ipv6 country name'); +is($data{asn}, '64511', 'geoip set asn'); ############################################################################### - -sub stream_pp { - my ($ip) = @_; - my $type = ($ip =~ ':' ? 'TCP6' : 'TCP4'); - return stream('127.0.0.1:' . port(8080)) - ->io("PROXY $type $ip 127.0.0.1 8080 8080${CRLF}"); -} - -sub pack_node { - substr pack('V', shift), 0, 3; -} - -sub pack_org { - pack('V', shift); -} - -############################################################################### From mdounin at mdounin.ru Sat Dec 6 09:10:21 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 06 Dec 2025 12:10:21 +0300 Subject: [PATCH] Adjusted ngx_file_fs_size() workaround for XFS Message-ID: <3a7cbb9c60d5e058b41d.1765012221@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1765007183 -10800 # Sat Dec 06 10:46:23 2025 +0300 # Node ID 3a7cbb9c60d5e058b41d2aae6e7890e89099e556 # Parent 234a79a752d46a21ecaccddfda660ca2cbfe15f4 Adjusted ngx_file_fs_size() workaround for XFS. With XFS, creating a file might result in large preallocation being reported in st_blocks as returned by fstat() till the file is closed, resulting in incorrect cache size calculations and too aggressive cache clearing based on max_size. This was worked around in 7669:52b34c3f89b4 by introducing the arbitrary sanity limit of eight blocks (8 * st_blksize) for the difference between st_size and size in st_blocks. Unfortunately, it turns out that XFS might return very large values in st_blksize as well, notably when using the "largeio" mount option, which makes the limit ineffective. As a workaround, the limit is changed to 32k instead, which is expected to be large enough to cover real differences between st_size and size in st_blocks, yet small enough to effectively suppress bogus st_blocks values with preallocations. It also matches the previous limit in many practical cases (with st_blksize 4096, previous limit was 32k). Reported by Oleg Mamontov. diff --git a/src/os/unix/ngx_files.h b/src/os/unix/ngx_files.h --- a/src/os/unix/ngx_files.h +++ b/src/os/unix/ngx_files.h @@ -187,7 +187,7 @@ ngx_int_t ngx_set_file_time(u_char *name #define ngx_file_size(sb) (sb)->st_size #define ngx_file_fs_size(sb) \ (((sb)->st_blocks * 512 > (sb)->st_size \ - && (sb)->st_blocks * 512 < (sb)->st_size + 8 * (sb)->st_blksize) \ + && (sb)->st_blocks * 512 < (sb)->st_size + 32768) \ ? (sb)->st_blocks * 512 : (sb)->st_size) #define ngx_file_mtime(sb) (sb)->st_mtime #define ngx_file_uniq(sb) (sb)->st_ino From mdounin at mdounin.ru Fri Dec 12 22:49:58 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 13 Dec 2025 01:49:58 +0300 Subject: [PATCH] Documented support for GeoIP2 databases in the MMDB format In-Reply-To: <234a79a752d46a21ecac.1764779470@vm-bsd.mdounin.ru> References: <234a79a752d46a21ecac.1764779470@vm-bsd.mdounin.ru> Message-ID: <4dd62a0a82169fc78d0c.1765579798@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1765579563 -10800 # Sat Dec 13 01:46:03 2025 +0300 # Node ID 4dd62a0a82169fc78d0c8593940ec374c4e2aa2e # Parent b0c7016f88682d92a2892074d783c7a007d8621f Documented support for GeoIP2 databases in the MMDB format. diff --git a/xml/en/docs/configure.xml b/xml/en/docs/configure.xml --- a/xml/en/docs/configure.xml +++ b/xml/en/docs/configure.xml @@ -8,7 +8,7 @@
+ rev="24">
@@ -1341,6 +1341,26 @@ sets additional build options for OpenSS +--without-geoip + + +disables use of the GeoIP library (1.29.4). + + + +--without-libmaxminddb + + +disables use of the libmaxminddb library (1.29.4). + + + + + + + + + --with-debug diff --git a/xml/en/docs/http/ngx_http_geoip_module.xml b/xml/en/docs/http/ngx_http_geoip_module.xml --- a/xml/en/docs/http/ngx_http_geoip_module.xml +++ b/xml/en/docs/http/ngx_http_geoip_module.xml @@ -10,7 +10,7 @@ + rev="6">
@@ -21,7 +21,12 @@ with values depending on the client IP a -When using the databases with IPv6 support (1.3.12, 1.2.7), +The module supports both legacy GeoIP databases +and new GeoIP2 databases in the MaxMind DB (MMDB) file format (1.29.4). + + + +When using legacy databases with IPv6 support (1.3.12, 1.2.7), IPv4 addresses are looked up as IPv4-mapped IPv6 addresses. @@ -31,7 +36,14 @@ This module is not built by default, it configuration parameter. This module requires the -MaxMind GeoIP library. +MaxMind GeoIP +library +to support loading of legacy GeoIP databases, +and the +libmaxminddb +library +to support loading of GeoIP2 databases +in the MaxMind DB (MMDB) file format (1.29.4). @@ -79,6 +91,9 @@ two-letter country code, for example, three-letter country code, for example, ?RUS?, ?USA?. + +This variable is not available when using MMDB databases. + $geoip_country_name @@ -130,6 +145,9 @@ two-letter country code, for example, three-letter country code, for example, ?RUS?, ?USA?. + +This variable is not available when using MMDB databases. + $geoip_city_country_name @@ -154,7 +172,7 @@ in Google AdWords API. $geoip_region -two-symbol country region code (region, territory, state, province, federal land +country region code (region, territory, state, province, federal land and the like), for example, ?48?, ?DC?. @@ -241,6 +259,35 @@ non-trusted address sent in
X-Fo + + + + $variable + file + path + +http +1.29.4 + + +Creates a variable +whose value is determinded +from the specified database file +with the specified data path, +for example: + +geoip_set $country GeoLite2-Country.mmdb country.names.en; +geoip_set $region GeoLite2-City.mmdb subdivisions.0.iso_code; +geoip_set $asn GeoLite2-ASN.mmdb autonomous_system_number; + + + + +This directive only supports MMDB databases. + + + +
diff --git a/xml/en/docs/stream/ngx_stream_geoip_module.xml b/xml/en/docs/stream/ngx_stream_geoip_module.xml --- a/xml/en/docs/stream/ngx_stream_geoip_module.xml +++ b/xml/en/docs/stream/ngx_stream_geoip_module.xml @@ -9,7 +9,7 @@ + rev="2">
@@ -20,7 +20,12 @@ with values depending on the client IP a -When using the databases with IPv6 support, +The module supports both legacy GeoIP databases +and new GeoIP2 databases in the MaxMind DB (MMDB) file format (1.29.4). + + + +When using legacy databases with IPv6 support, IPv4 addresses are looked up as IPv4-mapped IPv6 addresses. @@ -30,7 +35,14 @@ This module is not built by default, it configuration parameter. This module requires the -MaxMind GeoIP library. +MaxMind GeoIP +library +to support loading of legacy GeoIP databases, +and the +libmaxminddb +library +to support loading of GeoIP2 databases +in the MaxMind DB (MMDB) file format (1.29.4). @@ -51,7 +63,7 @@ stream { NA na.example.com; AS as.example.com; } - ... + ... } @@ -83,6 +95,9 @@ two-letter country code, for example, three-letter country code, for example, ?RUS?, ?USA?. + +This variable is not available when using MMDB databases. + $geoip_country_name @@ -134,6 +149,9 @@ two-letter country code, for example, three-letter country code, for example, ?RUS?, ?USA?. + +This variable is not available when using MMDB databases. + $geoip_city_country_name @@ -158,7 +176,7 @@ in Google AdWords API. $geoip_region -two-symbol country region code (region, territory, state, province, federal land +country region code (region, territory, state, province, federal land and the like), for example, ?48?, ?DC?. @@ -208,6 +226,35 @@ organization name, for example, ?The University of Melbourne?. + + + + $variable + file + path + +http +1.29.4 + + +Creates a variable +whose value is determinded +from the specified database file +with the specified data path, +for example: + +geoip_set $country GeoLite2-Country.mmdb country.names.en; +geoip_set $region GeoLite2-City.mmdb subdivisions.0.iso_code; +geoip_set $asn GeoLite2-ASN.mmdb autonomous_system_number; + + + + +This directive only supports MMDB databases. + + + +
diff --git a/xml/ru/docs/configure.xml b/xml/ru/docs/configure.xml --- a/xml/ru/docs/configure.xml +++ b/xml/ru/docs/configure.xml @@ -8,7 +8,7 @@
+ rev="24">
@@ -1321,6 +1321,26 @@ 1.1.3—1.3) ????? ????? ?? ????? +--without-geoip + + +????????? ????????????? ?????????? GeoIP (1.29.4). + + + +--without-libmaxminddb + + +????????? ????????????? ?????????? libmaxminddb (1.29.4). + + + + + + + + + --with-debug diff --git a/xml/ru/docs/http/ngx_http_geoip_module.xml b/xml/ru/docs/http/ngx_http_geoip_module.xml --- a/xml/ru/docs/http/ngx_http_geoip_module.xml +++ b/xml/ru/docs/http/ngx_http_geoip_module.xml @@ -10,7 +10,7 @@ + rev="6">
@@ -21,7 +21,12 @@ -??? ????????????? ??? ?????? ? ?????????? IPv6 (1.3.12, 1.2.7) +?????? ???????????? ??? ???? ?????? GeoIP ? ?????? ???????, +??? ? ????? ???? ?????? GeoIP2 ? ??????? MaxMind DB (MMDB) (1.29.4). + + + +??? ????????????? ??? ?????? ? ?????? ??????? ? ?????????? IPv6 (1.3.12, 1.2.7) IPv4-?????? ?????? ????????????? ?? IPv6. @@ -30,8 +35,12 @@ IPv4-?????? ?????? ????????????? ?? IPv6. ????????? ? ??????? ????????????????? ????????? --with-http_geoip_module. -??? ?????? ? ?????? ????? ?????? ????? ?????????? -MaxMind GeoIP. +??? ?????? ? ?????? ????? ?????? ????????? ??????????: +MaxMind GeoIP +??? ?????? ? ?????? ?????? ? ?????? ??????? +? +libmaxminddb +??? ?????? ? ?????? ?????? ? ??????? MaxMind DB (MMDB) (1.29.4). @@ -79,6 +88,9 @@ http { ????????????? ??? ??????, ????????, ?RUS?, ?USA?. + +??? ?????????? ?????????? ??? ????????????? ??? ?????? ? ??????? MMDB. + $geoip_country_name @@ -131,6 +143,9 @@ http { ????????????? ??? ??????, ????????, ?RUS?, ?USA?. + +??? ?????????? ?????????? ??? ????????????? ??? ?????? ? ??????? MMDB. + $geoip_city_country_name @@ -155,7 +170,7 @@ Google AdWords API. $geoip_region -?????????????? ??? ??????? ?????? (???????, ????, ????, +??? ??????? ?????? (???????, ????, ????, ?????????, ??????????? ????? ? ???? ????????), ????????, ?48?, ?DC?. @@ -240,6 +255,35 @@ Google AdWords API. + + + + $?????????? + ???? + ???? + +http +1.29.4 + + +??????? ??????????, +???????? ??????? ????? ???????????? +?? ???? ?????? ? ????? +?? ?????????? ???? ? ??????, +????????: + +geoip_set $country GeoLite2-Country.mmdb country.names.en; +geoip_set $region GeoLite2-City.mmdb subdivisions.0.iso_code; +geoip_set $asn GeoLite2-ASN.mmdb autonomous_system_number; + + + + +??? ????????? ???????????? ?????? ???? ?????? ? ??????? MMDB. + + + +
diff --git a/xml/ru/docs/stream/ngx_stream_geoip_module.xml b/xml/ru/docs/stream/ngx_stream_geoip_module.xml --- a/xml/ru/docs/stream/ngx_stream_geoip_module.xml +++ b/xml/ru/docs/stream/ngx_stream_geoip_module.xml @@ -9,7 +9,7 @@ + rev="2">
@@ -20,7 +20,12 @@ -??? ????????????? ??? ?????? ? ?????????? IPv6 +?????? ???????????? ??? ???? ?????? GeoIP ? ?????? ???????, +??? ? ????? ???? ?????? GeoIP2 ? ??????? MaxMind DB (MMDB) (1.29.4). + + + +??? ????????????? ??? ?????? ? ?????? ??????? ? ?????????? IPv6 IPv4-?????? ?????? ????????????? ?? IPv6. @@ -29,8 +34,12 @@ IPv4-?????? ?????? ????????????? ?? IPv6. ????????? ? ??????? ????????????????? ????????? --with-stream_geoip_module. -??? ?????? ? ?????? ????? ?????? ????? ?????????? -MaxMind GeoIP. +??? ?????? ? ?????? ????? ?????? ????????? ??????????: +MaxMind GeoIP +??? ?????? ? ?????? ?????? ? ?????? ??????? +? +libmaxminddb +??? ?????? ? ?????? ?????? ? ??????? MaxMind DB (MMDB) (1.29.4). @@ -51,7 +60,7 @@ stream { NA na.example.com; AS as.example.com; } - ... + ... } @@ -83,6 +92,9 @@ stream { ????????????? ??? ??????, ????????, ?RUS?, ?USA?. + +??? ?????????? ?????????? ??? ????????????? ??? ?????? ? ??????? MMDB. + $geoip_country_name @@ -135,6 +147,9 @@ stream { ????????????? ??? ??????, ????????, ?RUS?, ?USA?. + +??? ?????????? ?????????? ??? ????????????? ??? ?????? ? ??????? MMDB. + $geoip_city_country_name @@ -159,7 +174,7 @@ Google AdWords API. $geoip_region -?????????????? ??? ??????? ?????? (???????, ????, ????, +??? ??????? ?????? (???????, ????, ????, ?????????, ??????????? ????? ? ???? ????????), ????????, ?48?, ?DC?. @@ -209,6 +224,35 @@ Google AdWords API. + + + + $?????????? + ???? + ???? + +http +1.29.4 + + +??????? ??????????, +???????? ??????? ????? ???????????? +?? ???? ?????? ? ????? +?? ?????????? ???? ? ??????, +????????: + +geoip_set $country GeoLite2-Country.mmdb country.names.en; +geoip_set $region GeoLite2-City.mmdb subdivisions.0.iso_code; +geoip_set $asn GeoLite2-ASN.mmdb autonomous_system_number; + + + + +??? ????????? ???????????? ?????? ???? ?????? ? ??????? MMDB. + + + +
From osa at freebsd.org.ru Sat Dec 13 17:37:57 2025 From: osa at freebsd.org.ru (Sergey A. Osokin) Date: Sat, 13 Dec 2025 20:37:57 +0300 Subject: [PATCH] Documented support for GeoIP2 databases in the MMDB format In-Reply-To: <4dd62a0a82169fc78d0c.1765579798@vm-bsd.mdounin.ru> References: <234a79a752d46a21ecac.1764779470@vm-bsd.mdounin.ru> <4dd62a0a82169fc78d0c.1765579798@vm-bsd.mdounin.ru> Message-ID: On Sat, Dec 13, 2025 at 01:49:58AM +0300, Maxim Dounin wrote: > # HG changeset patch > # User Maxim Dounin > # Date 1765579563 -10800 > # Sat Dec 13 01:46:03 2025 +0300 > # Node ID 4dd62a0a82169fc78d0c8593940ec374c4e2aa2e > # Parent b0c7016f88682d92a2892074d783c7a007d8621f > Documented support for GeoIP2 databases in the MMDB format. > [...] Looks good to me. -- Sergey A. Osokin From mdounin at mdounin.ru Sat Dec 13 22:12:41 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sun, 14 Dec 2025 01:12:41 +0300 Subject: [nginx] GeoIP: added missing empty line. Message-ID: details: http://freenginx.org/hg/nginx/rev/1370957bd7d7 branches: changeset: 9443:1370957bd7d7 user: Maxim Dounin date: Sun Dec 14 01:10:58 2025 +0300 description: GeoIP: added missing empty line. diffstat: src/http/modules/ngx_http_geoip_module.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diffs (11 lines): 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 @@ -889,6 +889,7 @@ ngx_http_geoip_proxy(ngx_conf_t *cf, ngx return NGX_CONF_OK; } + static ngx_int_t ngx_http_geoip_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr) { From mdounin at mdounin.ru Sat Dec 13 22:12:41 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sun, 14 Dec 2025 01:12:41 +0300 Subject: [nginx] GeoIP: removed unused ngx_http_geoip_var_t structure. Message-ID: details: http://freenginx.org/hg/nginx/rev/82cf82e9379b branches: changeset: 9444:82cf82e9379b user: Maxim Dounin date: Sun Dec 14 01:11:02 2025 +0300 description: GeoIP: removed unused ngx_http_geoip_var_t structure. It was never used, since introduction of the module in 2985:31af2d1a742e, and hence removed. diffstat: src/http/modules/ngx_http_geoip_module.c | 6 ------ src/stream/ngx_stream_geoip_module.c | 6 ------ 2 files changed, 0 insertions(+), 12 deletions(-) diffs (32 lines): 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 @@ -32,12 +32,6 @@ typedef struct { } ngx_http_geoip_conf_t; -typedef struct { - ngx_str_t *name; - uintptr_t data; -} ngx_http_geoip_var_t; - - typedef const char *(*ngx_http_geoip_variable_handler_pt)(GeoIP *, u_long addr); 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 @@ -30,12 +30,6 @@ typedef struct { } ngx_stream_geoip_conf_t; -typedef struct { - ngx_str_t *name; - uintptr_t data; -} ngx_stream_geoip_var_t; - - typedef const char *(*ngx_stream_geoip_variable_handler_pt)(GeoIP *, u_long addr); From mdounin at mdounin.ru Sat Dec 13 22:12:41 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sun, 14 Dec 2025 01:12:41 +0300 Subject: [nginx] GeoIP: moved ngx_http_geoip_addr() to a better place. Message-ID: details: http://freenginx.org/hg/nginx/rev/b86ea67ea8b8 branches: changeset: 9445:b86ea67ea8b8 user: Maxim Dounin date: Sun Dec 14 01:11:07 2025 +0300 description: GeoIP: moved ngx_http_geoip_addr() to a better place. Per existing style, helper functions are defined after functions that use them, and with appropriate prototypes. diffstat: src/http/modules/ngx_http_geoip_module.c | 209 ++++++++++++++++-------------- src/stream/ngx_stream_geoip_module.c | 177 +++++++++++++------------ 2 files changed, 200 insertions(+), 186 deletions(-) diffs (434 lines): 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 @@ -72,6 +72,13 @@ static ngx_int_t ngx_http_geoip_city_int ngx_http_variable_value_t *v, uintptr_t data); static GeoIPRecord *ngx_http_geoip_get_city_record(ngx_http_request_t *r); +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 + static ngx_int_t ngx_http_geoip_add_variables(ngx_conf_t *cf); static void *ngx_http_geoip_create_conf(ngx_conf_t *cf); static char *ngx_http_geoip_init_conf(ngx_conf_t *cf, void *conf); @@ -230,107 +237,6 @@ static ngx_http_variable_t ngx_http_geo }; -static u_long -ngx_http_geoip_addr(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf) -{ - ngx_addr_t addr; - ngx_table_elt_t *xfwd; - struct sockaddr_in *sin; - - addr.sockaddr = r->connection->sockaddr; - addr.socklen = r->connection->socklen; - /* addr.name = r->connection->addr_text; */ - - xfwd = r->headers_in.x_forwarded_for; - - if (xfwd != NULL && gcf->proxies != NULL) { - (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL, - gcf->proxies, gcf->proxy_recursive); - } - -#if (NGX_HAVE_INET6) - - if (addr.sockaddr->sa_family == AF_INET6) { - u_char *p; - in_addr_t inaddr; - struct in6_addr *inaddr6; - - inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; - - if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { - p = inaddr6->s6_addr; - - inaddr = (in_addr_t) p[12] << 24; - inaddr += p[13] << 16; - inaddr += p[14] << 8; - inaddr += p[15]; - - return inaddr; - } - } - -#endif - - if (addr.sockaddr->sa_family != AF_INET) { - return INADDR_NONE; - } - - sin = (struct sockaddr_in *) addr.sockaddr; - return ntohl(sin->sin_addr.s_addr); -} - - -#if (NGX_HAVE_GEOIP_V6) - -static geoipv6_t -ngx_http_geoip_addr_v6(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf) -{ - ngx_addr_t addr; - ngx_table_elt_t *xfwd; - in_addr_t addr4; - struct in6_addr addr6; - struct sockaddr_in *sin; - struct sockaddr_in6 *sin6; - - addr.sockaddr = r->connection->sockaddr; - addr.socklen = r->connection->socklen; - /* addr.name = r->connection->addr_text; */ - - xfwd = r->headers_in.x_forwarded_for; - - if (xfwd != NULL && gcf->proxies != NULL) { - (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL, - gcf->proxies, gcf->proxy_recursive); - } - - switch (addr.sockaddr->sa_family) { - - case AF_INET: - /* Produce IPv4-mapped IPv6 address. */ - sin = (struct sockaddr_in *) addr.sockaddr; - addr4 = ntohl(sin->sin_addr.s_addr); - - ngx_memzero(&addr6, sizeof(struct in6_addr)); - addr6.s6_addr[10] = 0xff; - addr6.s6_addr[11] = 0xff; - addr6.s6_addr[12] = addr4 >> 24; - addr6.s6_addr[13] = addr4 >> 16; - addr6.s6_addr[14] = addr4 >> 8; - addr6.s6_addr[15] = addr4; - return addr6; - - case AF_INET6: - sin6 = (struct sockaddr_in6 *) addr.sockaddr; - return sin6->sin6_addr; - - default: - return in6addr_any; - } -} - -#endif - - static ngx_int_t ngx_http_geoip_country_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) @@ -612,6 +518,107 @@ ngx_http_geoip_get_city_record(ngx_http_ } +static u_long +ngx_http_geoip_addr(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf) +{ + ngx_addr_t addr; + ngx_table_elt_t *xfwd; + struct sockaddr_in *sin; + + addr.sockaddr = r->connection->sockaddr; + addr.socklen = r->connection->socklen; + /* addr.name = r->connection->addr_text; */ + + xfwd = r->headers_in.x_forwarded_for; + + if (xfwd != NULL && gcf->proxies != NULL) { + (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL, + gcf->proxies, gcf->proxy_recursive); + } + +#if (NGX_HAVE_INET6) + + if (addr.sockaddr->sa_family == AF_INET6) { + u_char *p; + in_addr_t inaddr; + struct in6_addr *inaddr6; + + inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; + + if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { + p = inaddr6->s6_addr; + + inaddr = (in_addr_t) p[12] << 24; + inaddr += p[13] << 16; + inaddr += p[14] << 8; + inaddr += p[15]; + + return inaddr; + } + } + +#endif + + if (addr.sockaddr->sa_family != AF_INET) { + return INADDR_NONE; + } + + sin = (struct sockaddr_in *) addr.sockaddr; + return ntohl(sin->sin_addr.s_addr); +} + + +#if (NGX_HAVE_GEOIP_V6) + +static geoipv6_t +ngx_http_geoip_addr_v6(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf) +{ + ngx_addr_t addr; + ngx_table_elt_t *xfwd; + in_addr_t addr4; + struct in6_addr addr6; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + addr.sockaddr = r->connection->sockaddr; + addr.socklen = r->connection->socklen; + /* addr.name = r->connection->addr_text; */ + + xfwd = r->headers_in.x_forwarded_for; + + if (xfwd != NULL && gcf->proxies != NULL) { + (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL, + gcf->proxies, gcf->proxy_recursive); + } + + switch (addr.sockaddr->sa_family) { + + case AF_INET: + /* Produce IPv4-mapped IPv6 address. */ + sin = (struct sockaddr_in *) addr.sockaddr; + addr4 = ntohl(sin->sin_addr.s_addr); + + ngx_memzero(&addr6, sizeof(struct in6_addr)); + addr6.s6_addr[10] = 0xff; + addr6.s6_addr[11] = 0xff; + addr6.s6_addr[12] = addr4 >> 24; + addr6.s6_addr[13] = addr4 >> 16; + addr6.s6_addr[14] = addr4 >> 8; + addr6.s6_addr[15] = addr4; + return addr6; + + case AF_INET6: + sin6 = (struct sockaddr_in6 *) addr.sockaddr; + return sin6->sin6_addr; + + default: + return in6addr_any; + } +} + +#endif + + static ngx_int_t ngx_http_geoip_add_variables(ngx_conf_t *cf) { 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 @@ -72,6 +72,13 @@ static ngx_int_t ngx_stream_geoip_city_i ngx_stream_variable_value_t *v, uintptr_t data); static GeoIPRecord *ngx_stream_geoip_get_city_record(ngx_stream_session_t *s); +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 + static ngx_int_t ngx_stream_geoip_add_variables(ngx_conf_t *cf); static void *ngx_stream_geoip_create_conf(ngx_conf_t *cf); static char *ngx_stream_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, @@ -208,91 +215,6 @@ static ngx_stream_variable_t ngx_stream }; -static u_long -ngx_stream_geoip_addr(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf) -{ - ngx_addr_t addr; - struct sockaddr_in *sin; - - addr.sockaddr = s->connection->sockaddr; - addr.socklen = s->connection->socklen; - /* addr.name = s->connection->addr_text; */ - -#if (NGX_HAVE_INET6) - - if (addr.sockaddr->sa_family == AF_INET6) { - u_char *p; - in_addr_t inaddr; - struct in6_addr *inaddr6; - - inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; - - if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { - p = inaddr6->s6_addr; - - inaddr = (in_addr_t) p[12] << 24; - inaddr += p[13] << 16; - inaddr += p[14] << 8; - inaddr += p[15]; - - return inaddr; - } - } - -#endif - - if (addr.sockaddr->sa_family != AF_INET) { - return INADDR_NONE; - } - - sin = (struct sockaddr_in *) addr.sockaddr; - return ntohl(sin->sin_addr.s_addr); -} - - -#if (NGX_HAVE_GEOIP_V6) - -static geoipv6_t -ngx_stream_geoip_addr_v6(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf) -{ - ngx_addr_t addr; - in_addr_t addr4; - struct in6_addr addr6; - struct sockaddr_in *sin; - struct sockaddr_in6 *sin6; - - addr.sockaddr = s->connection->sockaddr; - addr.socklen = s->connection->socklen; - /* addr.name = s->connection->addr_text; */ - - switch (addr.sockaddr->sa_family) { - - case AF_INET: - /* Produce IPv4-mapped IPv6 address. */ - sin = (struct sockaddr_in *) addr.sockaddr; - addr4 = ntohl(sin->sin_addr.s_addr); - - ngx_memzero(&addr6, sizeof(struct in6_addr)); - addr6.s6_addr[10] = 0xff; - addr6.s6_addr[11] = 0xff; - addr6.s6_addr[12] = addr4 >> 24; - addr6.s6_addr[13] = addr4 >> 16; - addr6.s6_addr[14] = addr4 >> 8; - addr6.s6_addr[15] = addr4; - return addr6; - - case AF_INET6: - sin6 = (struct sockaddr_in6 *) addr.sockaddr; - return sin6->sin6_addr; - - default: - return in6addr_any; - } -} - -#endif - - static ngx_int_t ngx_stream_geoip_country_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data) @@ -574,6 +496,91 @@ ngx_stream_geoip_get_city_record(ngx_str } +static u_long +ngx_stream_geoip_addr(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf) +{ + ngx_addr_t addr; + struct sockaddr_in *sin; + + addr.sockaddr = s->connection->sockaddr; + addr.socklen = s->connection->socklen; + /* addr.name = s->connection->addr_text; */ + +#if (NGX_HAVE_INET6) + + if (addr.sockaddr->sa_family == AF_INET6) { + u_char *p; + in_addr_t inaddr; + struct in6_addr *inaddr6; + + inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; + + if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { + p = inaddr6->s6_addr; + + inaddr = (in_addr_t) p[12] << 24; + inaddr += p[13] << 16; + inaddr += p[14] << 8; + inaddr += p[15]; + + return inaddr; + } + } + +#endif + + if (addr.sockaddr->sa_family != AF_INET) { + return INADDR_NONE; + } + + sin = (struct sockaddr_in *) addr.sockaddr; + return ntohl(sin->sin_addr.s_addr); +} + + +#if (NGX_HAVE_GEOIP_V6) + +static geoipv6_t +ngx_stream_geoip_addr_v6(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf) +{ + ngx_addr_t addr; + in_addr_t addr4; + struct in6_addr addr6; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + addr.sockaddr = s->connection->sockaddr; + addr.socklen = s->connection->socklen; + /* addr.name = s->connection->addr_text; */ + + switch (addr.sockaddr->sa_family) { + + case AF_INET: + /* Produce IPv4-mapped IPv6 address. */ + sin = (struct sockaddr_in *) addr.sockaddr; + addr4 = ntohl(sin->sin_addr.s_addr); + + ngx_memzero(&addr6, sizeof(struct in6_addr)); + addr6.s6_addr[10] = 0xff; + addr6.s6_addr[11] = 0xff; + addr6.s6_addr[12] = addr4 >> 24; + addr6.s6_addr[13] = addr4 >> 16; + addr6.s6_addr[14] = addr4 >> 8; + addr6.s6_addr[15] = addr4; + return addr6; + + case AF_INET6: + sin6 = (struct sockaddr_in6 *) addr.sockaddr; + return sin6->sin6_addr; + + default: + return in6addr_any; + } +} + +#endif + + static ngx_int_t ngx_stream_geoip_add_variables(ngx_conf_t *cf) { From mdounin at mdounin.ru Sat Dec 13 22:12:41 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sun, 14 Dec 2025 01:12:41 +0300 Subject: [nginx] GeoIP: introduced ngx_http_geoip_sockaddr() helper. Message-ID: details: http://freenginx.org/hg/nginx/rev/6967497b5555 branches: changeset: 9446:6967497b5555 user: Maxim Dounin date: Sun Dec 14 01:11:13 2025 +0300 description: GeoIP: introduced ngx_http_geoip_sockaddr() helper. Such a helper function will be needed for further use with libmaxminddb, where sockaddr is used for both IPv4 and IPv6 lookups. It also reduces amount of code duplication. Stream module counterpart, where direct use of s->connection->sockaddr is sufficient, is adjusted similarly to match the code, but without introducing a helper function. diffstat: src/http/modules/ngx_http_geoip_module.c | 89 ++++++++++++++++--------------- src/stream/ngx_stream_geoip_module.c | 26 +++----- 2 files changed, 57 insertions(+), 58 deletions(-) diffs (224 lines): 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 @@ -78,6 +78,8 @@ static u_long ngx_http_geoip_addr(ngx_ht static geoipv6_t ngx_http_geoip_addr_v6(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf); #endif +static struct sockaddr *ngx_http_geoip_sockaddr(ngx_http_request_t *r, + ngx_http_geoip_conf_t *gcf); static ngx_int_t ngx_http_geoip_add_variables(ngx_conf_t *cf); static void *ngx_http_geoip_create_conf(ngx_conf_t *cf); @@ -521,29 +523,19 @@ ngx_http_geoip_get_city_record(ngx_http_ static u_long ngx_http_geoip_addr(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf) { - ngx_addr_t addr; - ngx_table_elt_t *xfwd; + struct sockaddr *sockaddr; struct sockaddr_in *sin; - addr.sockaddr = r->connection->sockaddr; - addr.socklen = r->connection->socklen; - /* addr.name = r->connection->addr_text; */ - - xfwd = r->headers_in.x_forwarded_for; - - if (xfwd != NULL && gcf->proxies != NULL) { - (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL, - gcf->proxies, gcf->proxy_recursive); - } + sockaddr = ngx_http_geoip_sockaddr(r, gcf); #if (NGX_HAVE_INET6) - if (addr.sockaddr->sa_family == AF_INET6) { + if (sockaddr->sa_family == AF_INET6) { u_char *p; in_addr_t inaddr; struct in6_addr *inaddr6; - inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; + inaddr6 = &((struct sockaddr_in6 *) sockaddr)->sin6_addr; if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { p = inaddr6->s6_addr; @@ -559,11 +551,11 @@ ngx_http_geoip_addr(ngx_http_request_t * #endif - if (addr.sockaddr->sa_family != AF_INET) { + if (sockaddr->sa_family != AF_INET) { return INADDR_NONE; } - sin = (struct sockaddr_in *) addr.sockaddr; + sin = (struct sockaddr_in *) sockaddr; return ntohl(sin->sin_addr.s_addr); } @@ -573,13 +565,48 @@ ngx_http_geoip_addr(ngx_http_request_t * static geoipv6_t ngx_http_geoip_addr_v6(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf) { - ngx_addr_t addr; - ngx_table_elt_t *xfwd; in_addr_t addr4; struct in6_addr addr6; + struct sockaddr *sockaddr; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; + sockaddr = ngx_http_geoip_sockaddr(r, gcf); + + switch (sockaddr->sa_family) { + + case AF_INET: + /* Produce IPv4-mapped IPv6 address. */ + sin = (struct sockaddr_in *) sockaddr; + addr4 = ntohl(sin->sin_addr.s_addr); + + ngx_memzero(&addr6, sizeof(struct in6_addr)); + addr6.s6_addr[10] = 0xff; + addr6.s6_addr[11] = 0xff; + addr6.s6_addr[12] = addr4 >> 24; + addr6.s6_addr[13] = addr4 >> 16; + addr6.s6_addr[14] = addr4 >> 8; + addr6.s6_addr[15] = addr4; + return addr6; + + case AF_INET6: + sin6 = (struct sockaddr_in6 *) sockaddr; + return sin6->sin6_addr; + + default: + return in6addr_any; + } +} + +#endif + + +static struct sockaddr * +ngx_http_geoip_sockaddr(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf) +{ + ngx_addr_t addr; + ngx_table_elt_t *xfwd; + addr.sockaddr = r->connection->sockaddr; addr.socklen = r->connection->socklen; /* addr.name = r->connection->addr_text; */ @@ -591,33 +618,9 @@ ngx_http_geoip_addr_v6(ngx_http_request_ gcf->proxies, gcf->proxy_recursive); } - switch (addr.sockaddr->sa_family) { - - case AF_INET: - /* Produce IPv4-mapped IPv6 address. */ - sin = (struct sockaddr_in *) addr.sockaddr; - addr4 = ntohl(sin->sin_addr.s_addr); - - ngx_memzero(&addr6, sizeof(struct in6_addr)); - addr6.s6_addr[10] = 0xff; - addr6.s6_addr[11] = 0xff; - addr6.s6_addr[12] = addr4 >> 24; - addr6.s6_addr[13] = addr4 >> 16; - addr6.s6_addr[14] = addr4 >> 8; - addr6.s6_addr[15] = addr4; - return addr6; - - case AF_INET6: - sin6 = (struct sockaddr_in6 *) addr.sockaddr; - return sin6->sin6_addr; - - default: - return in6addr_any; - } + return addr.sockaddr; } -#endif - static ngx_int_t ngx_http_geoip_add_variables(ngx_conf_t *cf) 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 @@ -499,21 +499,19 @@ ngx_stream_geoip_get_city_record(ngx_str static u_long ngx_stream_geoip_addr(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf) { - ngx_addr_t addr; + struct sockaddr *sockaddr; struct sockaddr_in *sin; - addr.sockaddr = s->connection->sockaddr; - addr.socklen = s->connection->socklen; - /* addr.name = s->connection->addr_text; */ + sockaddr = s->connection->sockaddr; #if (NGX_HAVE_INET6) - if (addr.sockaddr->sa_family == AF_INET6) { + if (sockaddr->sa_family == AF_INET6) { u_char *p; in_addr_t inaddr; struct in6_addr *inaddr6; - inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; + inaddr6 = &((struct sockaddr_in6 *) sockaddr)->sin6_addr; if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { p = inaddr6->s6_addr; @@ -529,11 +527,11 @@ ngx_stream_geoip_addr(ngx_stream_session #endif - if (addr.sockaddr->sa_family != AF_INET) { + if (sockaddr->sa_family != AF_INET) { return INADDR_NONE; } - sin = (struct sockaddr_in *) addr.sockaddr; + sin = (struct sockaddr_in *) sockaddr; return ntohl(sin->sin_addr.s_addr); } @@ -543,21 +541,19 @@ ngx_stream_geoip_addr(ngx_stream_session static geoipv6_t ngx_stream_geoip_addr_v6(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf) { - ngx_addr_t addr; in_addr_t addr4; struct in6_addr addr6; + struct sockaddr *sockaddr; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; - addr.sockaddr = s->connection->sockaddr; - addr.socklen = s->connection->socklen; - /* addr.name = s->connection->addr_text; */ + sockaddr = s->connection->sockaddr; - switch (addr.sockaddr->sa_family) { + switch (sockaddr->sa_family) { case AF_INET: /* Produce IPv4-mapped IPv6 address. */ - sin = (struct sockaddr_in *) addr.sockaddr; + sin = (struct sockaddr_in *) sockaddr; addr4 = ntohl(sin->sin_addr.s_addr); ngx_memzero(&addr6, sizeof(struct in6_addr)); @@ -570,7 +566,7 @@ ngx_stream_geoip_addr_v6(ngx_stream_sess return addr6; case AF_INET6: - sin6 = (struct sockaddr_in6 *) addr.sockaddr; + sin6 = (struct sockaddr_in6 *) sockaddr; return sin6->sin6_addr; default: From mdounin at mdounin.ru Sat Dec 13 22:12:42 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sun, 14 Dec 2025 01:12:42 +0300 Subject: [nginx] GeoIP: simplified handling of IPv6 databases. Message-ID: details: http://freenginx.org/hg/nginx/rev/02b9e07673b0 branches: changeset: 9447:02b9e07673b0 user: Maxim Dounin date: Sun Dec 14 01:11:18 2025 +0300 description: GeoIP: simplified handling of IPv6 databases. diffstat: src/http/modules/ngx_http_geoip_module.c | 92 +++++++++++++----------------- src/stream/ngx_stream_geoip_module.c | 95 +++++++++++++------------------ 2 files changed, 81 insertions(+), 106 deletions(-) diffs (269 lines): 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 @@ -32,32 +32,6 @@ typedef struct { } ngx_http_geoip_conf_t; -typedef const char *(*ngx_http_geoip_variable_handler_pt)(GeoIP *, - u_long addr); - - -ngx_http_geoip_variable_handler_pt ngx_http_geoip_country_functions[] = { - GeoIP_country_code_by_ipnum, - GeoIP_country_code3_by_ipnum, - GeoIP_country_name_by_ipnum, -}; - - -#if (NGX_HAVE_GEOIP_V6) - -typedef const char *(*ngx_http_geoip_variable_handler_v6_pt)(GeoIP *, - geoipv6_t addr); - - -ngx_http_geoip_variable_handler_v6_pt ngx_http_geoip_country_v6_functions[] = { - GeoIP_country_code_by_ipnum_v6, - GeoIP_country_code3_by_ipnum_v6, - GeoIP_country_name_by_ipnum_v6, -}; - -#endif - - static ngx_int_t ngx_http_geoip_country_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_geoip_org_variable(ngx_http_request_t *r, @@ -243,13 +217,6 @@ static ngx_int_t ngx_http_geoip_country_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - ngx_http_geoip_variable_handler_pt handler = - ngx_http_geoip_country_functions[data]; -#if (NGX_HAVE_GEOIP_V6) - ngx_http_geoip_variable_handler_v6_pt handler_v6 = - ngx_http_geoip_country_v6_functions[data]; -#endif - const char *val; ngx_http_geoip_conf_t *gcf; @@ -260,12 +227,35 @@ ngx_http_geoip_country_variable(ngx_http } #if (NGX_HAVE_GEOIP_V6) - val = gcf->country_v6 - ? handler_v6(gcf->country, ngx_http_geoip_addr_v6(r, gcf)) - : handler(gcf->country, ngx_http_geoip_addr(r, gcf)); -#else - val = handler(gcf->country, ngx_http_geoip_addr(r, gcf)); + if (gcf->country_v6) { + if (data == NGX_GEOIP_COUNTRY_CODE) { + val = GeoIP_country_code_by_ipnum_v6(gcf->country, + ngx_http_geoip_addr_v6(r, gcf)); + + } else if (data == NGX_GEOIP_COUNTRY_CODE3) { + val = GeoIP_country_code3_by_ipnum_v6(gcf->country, + ngx_http_geoip_addr_v6(r, gcf)); + + } else { /* NGX_GEOIP_COUNTRY_NAME */ + val = GeoIP_country_name_by_ipnum_v6(gcf->country, + ngx_http_geoip_addr_v6(r, gcf)); + } + } else #endif + { + if (data == NGX_GEOIP_COUNTRY_CODE) { + val = GeoIP_country_code_by_ipnum(gcf->country, + ngx_http_geoip_addr(r, gcf)); + + } else if (data == NGX_GEOIP_COUNTRY_CODE3) { + val = GeoIP_country_code3_by_ipnum(gcf->country, + ngx_http_geoip_addr(r, gcf)); + + } else { /* NGX_GEOIP_COUNTRY_NAME */ + val = GeoIP_country_name_by_ipnum(gcf->country, + ngx_http_geoip_addr(r, gcf)); + } + } if (val == NULL) { goto not_found; @@ -302,14 +292,14 @@ ngx_http_geoip_org_variable(ngx_http_req } #if (NGX_HAVE_GEOIP_V6) - val = gcf->org_v6 - ? GeoIP_name_by_ipnum_v6(gcf->org, - ngx_http_geoip_addr_v6(r, gcf)) - : GeoIP_name_by_ipnum(gcf->org, - ngx_http_geoip_addr(r, gcf)); -#else - val = GeoIP_name_by_ipnum(gcf->org, ngx_http_geoip_addr(r, gcf)); + if (gcf->org_v6) { + val = GeoIP_name_by_ipnum_v6(gcf->org, ngx_http_geoip_addr_v6(r, gcf)); + + } else #endif + { + val = GeoIP_name_by_ipnum(gcf->org, ngx_http_geoip_addr(r, gcf)); + } if (val == NULL) { goto not_found; @@ -506,14 +496,12 @@ ngx_http_geoip_get_city_record(ngx_http_ if (gcf->city) { #if (NGX_HAVE_GEOIP_V6) - return gcf->city_v6 - ? GeoIP_record_by_ipnum_v6(gcf->city, - ngx_http_geoip_addr_v6(r, gcf)) - : GeoIP_record_by_ipnum(gcf->city, - ngx_http_geoip_addr(r, gcf)); -#else + if (gcf->city_v6) { + return GeoIP_record_by_ipnum_v6(gcf->city, + ngx_http_geoip_addr_v6(r, gcf)); + } +#endif return GeoIP_record_by_ipnum(gcf->city, ngx_http_geoip_addr(r, gcf)); -#endif } return NULL; 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 @@ -30,34 +30,6 @@ typedef struct { } ngx_stream_geoip_conf_t; -typedef const char *(*ngx_stream_geoip_variable_handler_pt)(GeoIP *, - u_long addr); - - -ngx_stream_geoip_variable_handler_pt ngx_stream_geoip_country_functions[] = { - GeoIP_country_code_by_ipnum, - GeoIP_country_code3_by_ipnum, - GeoIP_country_name_by_ipnum, -}; - - -#if (NGX_HAVE_GEOIP_V6) - -typedef const char *(*ngx_stream_geoip_variable_handler_v6_pt)(GeoIP *, - geoipv6_t addr); - - -ngx_stream_geoip_variable_handler_v6_pt - ngx_stream_geoip_country_v6_functions[] = -{ - GeoIP_country_code_by_ipnum_v6, - GeoIP_country_code3_by_ipnum_v6, - GeoIP_country_name_by_ipnum_v6, -}; - -#endif - - static ngx_int_t ngx_stream_geoip_country_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_geoip_org_variable(ngx_stream_session_t *s, @@ -219,13 +191,6 @@ static ngx_int_t ngx_stream_geoip_country_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data) { - ngx_stream_geoip_variable_handler_pt handler = - ngx_stream_geoip_country_functions[data]; -#if (NGX_HAVE_GEOIP_V6) - ngx_stream_geoip_variable_handler_v6_pt handler_v6 = - ngx_stream_geoip_country_v6_functions[data]; -#endif - const char *val; ngx_stream_geoip_conf_t *gcf; @@ -236,12 +201,35 @@ ngx_stream_geoip_country_variable(ngx_st } #if (NGX_HAVE_GEOIP_V6) - val = gcf->country_v6 - ? handler_v6(gcf->country, ngx_stream_geoip_addr_v6(s, gcf)) - : handler(gcf->country, ngx_stream_geoip_addr(s, gcf)); -#else - val = handler(gcf->country, ngx_stream_geoip_addr(s, gcf)); + if (gcf->country_v6) { + if (data == NGX_GEOIP_COUNTRY_CODE) { + val = GeoIP_country_code_by_ipnum_v6(gcf->country, + ngx_stream_geoip_addr_v6(s, gcf)); + + } else if (data == NGX_GEOIP_COUNTRY_CODE3) { + val = GeoIP_country_code3_by_ipnum_v6(gcf->country, + ngx_stream_geoip_addr_v6(s, gcf)); + + } else { /* NGX_GEOIP_COUNTRY_NAME */ + val = GeoIP_country_name_by_ipnum_v6(gcf->country, + ngx_stream_geoip_addr_v6(s, gcf)); + } + } else #endif + { + if (data == NGX_GEOIP_COUNTRY_CODE) { + val = GeoIP_country_code_by_ipnum(gcf->country, + ngx_stream_geoip_addr(s, gcf)); + + } else if (data == NGX_GEOIP_COUNTRY_CODE3) { + val = GeoIP_country_code3_by_ipnum(gcf->country, + ngx_stream_geoip_addr(s, gcf)); + + } else { /* NGX_GEOIP_COUNTRY_NAME */ + val = GeoIP_country_name_by_ipnum(gcf->country, + ngx_stream_geoip_addr(s, gcf)); + } + } if (val == NULL) { goto not_found; @@ -278,14 +266,15 @@ ngx_stream_geoip_org_variable(ngx_stream } #if (NGX_HAVE_GEOIP_V6) - val = gcf->org_v6 - ? GeoIP_name_by_ipnum_v6(gcf->org, - ngx_stream_geoip_addr_v6(s, gcf)) - : GeoIP_name_by_ipnum(gcf->org, - ngx_stream_geoip_addr(s, gcf)); -#else - val = GeoIP_name_by_ipnum(gcf->org, ngx_stream_geoip_addr(s, gcf)); + if (gcf->org_v6) { + val = GeoIP_name_by_ipnum_v6(gcf->org, + ngx_stream_geoip_addr_v6(s, gcf)); + + } else #endif + { + val = GeoIP_name_by_ipnum(gcf->org, ngx_stream_geoip_addr(s, gcf)); + } if (val == NULL) { goto not_found; @@ -482,14 +471,12 @@ ngx_stream_geoip_get_city_record(ngx_str if (gcf->city) { #if (NGX_HAVE_GEOIP_V6) - return gcf->city_v6 - ? GeoIP_record_by_ipnum_v6(gcf->city, - ngx_stream_geoip_addr_v6(s, gcf)) - : GeoIP_record_by_ipnum(gcf->city, - ngx_stream_geoip_addr(s, gcf)); -#else + if (gcf->city_v6) { + return GeoIP_record_by_ipnum_v6(gcf->city, + ngx_stream_geoip_addr_v6(s, gcf)); + } +#endif return GeoIP_record_by_ipnum(gcf->city, ngx_stream_geoip_addr(s, gcf)); -#endif } return NULL; From mdounin at mdounin.ru Sat Dec 13 22:12:42 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sun, 14 Dec 2025 01:12:42 +0300 Subject: [nginx] GeoIP: support for GeoIP2 databases in the MMDB format. Message-ID: details: http://freenginx.org/hg/nginx/rev/0a2409a92fa9 branches: changeset: 9448:0a2409a92fa9 user: Maxim Dounin date: Sun Dec 14 01:11:22 2025 +0300 description: 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). diffstat: auto/lib/geoip/conf | 187 +++++-- auto/options | 9 + src/http/modules/ngx_http_geoip_module.c | 770 +++++++++++++++++++++++++++++- src/stream/ngx_stream_geoip_module.c | 766 +++++++++++++++++++++++++++++- 4 files changed, 1607 insertions(+), 125 deletions(-) diffs (2479 lines): 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 " 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 + #include " + #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 " + 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 - #include " - #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 #include + +#if (NGX_HAVE_GEOIP_LEGACY) + #include #include +#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 + +#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 #include +#if (NGX_HAVE_GEOIP_LEGACY) + #include #include +#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 + +#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 } From mdounin at mdounin.ru Sat Dec 13 22:13:29 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sun, 14 Dec 2025 01:13:29 +0300 Subject: [nginx-tests] Tests: tests for GeoIP databases in the MMDB format. Message-ID: details: http://freenginx.org/hg/nginx-tests/rev/cc23719251b0 branches: changeset: 2035:cc23719251b0 user: Maxim Dounin date: Sun Dec 14 01:08:48 2025 +0300 description: Tests: tests for GeoIP databases in the MMDB format. diffstat: geoip.t | 2 +- geoip_mmdb.t | 204 +++++++++++++++++++++++++------------------------ stream_geoip.t | 2 +- stream_geoip_mmdb.t | 210 ++++++++++++++++++++++++++------------------------- 4 files changed, 213 insertions(+), 205 deletions(-) diffs (588 lines): diff --git a/geoip.t b/geoip.t --- a/geoip.t +++ b/geoip.t @@ -144,7 +144,7 @@ for my $i (0 .. 31) { $t->write_file('org.dat', $data); $t->write_file('index.html', ''); -$t->try_run('no inet6 support')->plan(20); +$t->try_run('no geoip legacy')->plan(20); ############################################################################### diff --git a/geoip.t b/geoip_mmdb.t copy from geoip.t copy to geoip_mmdb.t --- a/geoip.t +++ b/geoip_mmdb.t @@ -1,9 +1,10 @@ #!/usr/bin/perl +# (C) Maxim Dounin # (C) Andrey Zelenkov # (C) Nginx, Inc. -# Tests for geoip module. +# Tests for geoip module with MMDB databases. ############################################################################### @@ -37,9 +38,11 @@ http { geoip_proxy 127.0.0.1/32; - geoip_country %%TESTDIR%%/country.dat; - geoip_city %%TESTDIR%%/city.dat; - geoip_org %%TESTDIR%%/org.dat; + geoip_country test.mmdb; + geoip_city test.mmdb; + geoip_org test.mmdb; + + geoip_set $asn test.mmdb autonomous_system_number; server { listen 127.0.0.1:8080; @@ -47,13 +50,13 @@ http { location / { add_header X-Country-Code $geoip_country_code; - add_header X-Country-Code3 $geoip_country_code3; + add_header X-Country-Code3 $geoip_country_code3:none; add_header X-Country-Name $geoip_country_name; - add_header X-Area-Code $geoip_area_code; + add_header X-Area-Code $geoip_area_code:none; add_header X-C-Continent-Code $geoip_city_continent_code; add_header X-C-Country-Code $geoip_city_country_code; - add_header X-C-Country-Code3 $geoip_city_country_code3; + add_header X-C-Country-Code3 $geoip_city_country_code3:none; add_header X-C-Country-Name $geoip_city_country_name; add_header X-Dma-Code $geoip_dma_code; add_header X-Latitude $geoip_latitude; @@ -64,6 +67,8 @@ http { add_header X-Postal-Code $geoip_postal_code; add_header X-Org $geoip_org; + + add_header X-ASN $asn; } } } @@ -72,127 +77,126 @@ EOF my $d = $t->testdir(); -# country database: -# -# "10.0.0.1","10.0.0.1","RU","Russian Federation" -# "2001:db8::","2001:db8::","US","United States" +# MMDB format specification: +# https://github.com/maxmind/MaxMind-DB/blob/main/MaxMind-DB-spec.md my $data = ''; -for my $i (0 .. 156) { - # skip to offset 32 if 1st bit set in ipv6 address wins - $data .= pack_node($i + 1) . pack_node(32), next if $i == 2; - # otherwise default to RU - $data .= pack_node(0xffffb9) . pack_node(0xffff00), next if $i == 31; - # continue checking bits set in ipv6 address - $data .= pack_node(0xffff00) . pack_node($i + 1), next - if grep $_ == $i, (44, 49, 50, 52, 53, 55, 56, 57); - # last bit set in ipv6 address - $data .= pack_node(0xffffe1) . pack_node(0xffff00), next if $i == 156; - $data .= pack_node($i + 1) . pack_node(0xffff00); -} +# binary search tree +# just one node (two records), 32-bit records + +$data .= pack('NN', 17, 17); + +# data section + +$data .= pack('x16'); + +$data .= pack("B8", "11101000"); + +$data .= pack("B8A*", "01011000", "autonomous_system_number"); +$data .= pack("B8N", "11000100", 64511); + +$data .= pack("B8CA*", "01011101", 30 - 29, "autonomous_system_organization"); +$data .= pack("B8A*", "01001101", "freenginx.org"); -$data .= chr(0x00) x 3; -$data .= chr(0xFF) x 3; -$data .= chr(12); - -$t->write_file('country.dat', $data); +$data .= pack("B8A*", "01000111", "country"); +$data .= pack("B8", "11100010"); +$data .= pack("B8A*", "01001000", "iso_code"); +$data .= pack("B8A*", "01000010", "RU"); +$data .= pack("B8A*", "01000101", "names"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000010", "en"); +$data .= pack("B8A*", "01010010", "Russian Federation"); -# city database: -# -# "167772161","167772161","RU","48","Moscow","119034","55.7543",37.6202",, - -$data = ''; +$data .= pack("B8A*", "01001001", "continent"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000100", "code"); +$data .= pack("B8A*", "01000010", "EU"); -for my $i (0 .. 31) { - $data .= pack_node(32) . pack_node($i + 1), next if $i == 4 or $i == 6; - $data .= pack_node(32) . pack_node($i + 2), next if $i == 31; - $data .= pack_node($i + 1) . pack_node(32); -} +$data .= pack("B8A*", "01001100", "subdivisions"); +$data .= pack("B8C", "00000001", 11 - 7); +$data .= pack("B8", "11100010"); +$data .= pack("B8A*", "01001000", "iso_code"); +$data .= pack("B8A*", "01000011", "MOW"); +$data .= pack("B8A*", "01000101", "names"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000010", "en"); +$data .= pack("B8A*", "01000110", "Moscow"); -$data .= chr(42); -$data .= chr(185); -$data .= pack('Z*', 48); -$data .= pack('Z*', 'Moscow'); -$data .= pack('Z*', 119034); -$data .= pack_node(int((55.7543 + 180) * 10000)); -$data .= pack_node(int((37.6202 + 180) * 10000)); -$data .= chr(0) x 3; -$data .= chr(0xFF) x 3; -$data .= chr(2); -$data .= pack_node(32); +$data .= pack("B8A*", "01000100", "city"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000101", "names"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000010", "en"); +$data .= pack("B8A*", "01000110", "Moscow"); + +$data .= pack("B8A*", "01000110", "postal"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000100", "code"); +$data .= pack("B8A*", "01000110", "119034"); -$t->write_file('city.dat', $data); +$data .= pack("B8A*", "01001000", "location"); +$data .= pack("B8", "11100011"); +$data .= pack("B8A*", "01001000", "latitude"); +$data .= pack("B8d>", "01101000", 55.7543); +$data .= pack("B8A*", "01001001", "longitude"); +$data .= pack("B8d>", "01101000", 37.6202); +$data .= pack("B8A*", "01001010", "metro_code"); +$data .= pack("B8C", "10100001", 0); -# organization database: -# -# "167772161","167772161","Nginx" +# metadata + +$data .= "\xab\xcd\xefMaxMind.com"; -$data = ''; - -for my $i (0 .. 31) { - $data .= pack_org(32) . pack_org($i + 1), next if $i == 4 or $i == 6; - $data .= pack_org(32) . pack_org($i + 2), next if $i == 31; - $data .= pack_org($i + 1) . pack_org(32); -} +$data .= pack("B8", "11101001"); +$data .= pack("B8A*", "01001010", "node_count"); +$data .= pack("B8C", "11000001", 1); +$data .= pack("B8A*", "01001011", "record_size"); +$data .= pack("B8C", "10100001", 32); +$data .= pack("B8A*", "01001010", "ip_version"); +$data .= pack("B8C", "10100001", 6); +$data .= pack("B8A*", "01001101", "database_type"); +$data .= pack("B8A*", "01000100", "test"); +$data .= pack("B8A*", "01001001", "languages"); +$data .= pack("B8B8", "00000001", "00000100"); +$data .= pack("B8A*", "01000100", "test"); +$data .= pack("B8A*", "01011011", "binary_format_major_version"); +$data .= pack("B8C", "10100001", 2); +$data .= pack("B8A*", "01011011", "binary_format_minor_version"); +$data .= pack("B8C", "10100001", 0); +$data .= pack("B8A*", "01001011", "build_epoch"); +$data .= pack("B8B8C", "00000001", "00000010", 1); +$data .= pack("B8A*", "01001011", "description"); +$data .= pack("B8", "11100000"); -$data .= chr(42); -$data .= pack('Z*', 'Nginx'); -$data .= chr(0xFF) x 3; -$data .= chr(5); -$data .= pack_node(32); +$t->write_file('test.mmdb', $data); -$t->write_file('org.dat', $data); $t->write_file('index.html', ''); -$t->try_run('no inet6 support')->plan(20); +$t->try_run('no geoip mmdb')->plan(17); ############################################################################### -my $r = http_xff('10.0.0.1'); +my $r = http_get('/'); + like($r, qr/X-Country-Code: RU/, 'geoip country code'); -like($r, qr/X-Country-Code3: RUS/, 'geoip country code 3'); +like($r, qr/X-Country-Code3: :none/, 'geoip country code 3'); like($r, qr/X-Country-Name: Russian Federation/, 'geoip country name'); -like($r, qr/X-Area-Code: 0/, 'geoip area code'); +like($r, qr/X-Area-Code: :none/, 'geoip area code'); like($r, qr/X-C-Continent-Code: EU/, 'geoip city continent code'); like($r, qr/X-C-Country-Code: RU/, 'geoip city country code'); -like($r, qr/X-C-Country-Code3: RUS/, 'geoip city country code 3'); +like($r, qr/X-C-Country-Code3: :none/, 'geoip city country code 3'); like($r, qr/X-C-Country-Name: Russian Federation/, 'geoip city country name'); like($r, qr/X-Dma-Code: 0/, 'geoip dma code'); like($r, qr/X-Latitude: 55.7543/, 'geoip latitude'); like($r, qr/X-Longitude: 37.6202/, 'geoip longitude'); -like($r, qr/X-Region: 48/, 'geoip region'); -like($r, qr/X-Region-Name: Moscow City/, 'geoip region name'); +like($r, qr/X-Region: MOW/, 'geoip region'); +like($r, qr/X-Region-Name: Moscow/, 'geoip region name'); like($r, qr/X-City: Moscow/, 'geoip city'); like($r, qr/X-Postal-Code: 119034/, 'geoip postal code'); -like($r, qr/X-Org: Nginx/, 'geoip org'); - -like(http_xff('::ffff:10.0.0.1'), qr/X-Org: Nginx/, 'geoip ipv6 ipv4-mapped'); +like($r, qr/X-Org: freenginx.org/, 'geoip org'); -$r = http_xff('2001:db8::'); -like($r, qr/X-Country-Code: US/, 'geoip ipv6 country code'); -like($r, qr/X-Country-Code3: USA/, 'geoip ipv6 country code 3'); -like($r, qr/X-Country-Name: United States/, 'geoip ipv6 country name'); +like($r, qr/X-ASN: 64511/, 'geoip set asn'); ############################################################################### - -sub http_xff { - my ($xff) = @_; - return http(<write_file('org.dat', $data); -$t->try_run('no inet6 support')->plan(20); +$t->try_run('no geoip legacy')->plan(20); ############################################################################### diff --git a/stream_geoip.t b/stream_geoip_mmdb.t copy from stream_geoip.t copy to stream_geoip_mmdb.t --- a/stream_geoip.t +++ b/stream_geoip_mmdb.t @@ -1,9 +1,10 @@ #!/usr/bin/perl +# (C) Maxim Dounin # (C) Andrey Zelenkov # (C) Nginx, Inc. -# Tests for stream geoip module. +# Tests for stream geoip module with MMDB databases. ############################################################################### @@ -25,8 +26,7 @@ use Test::Nginx::Stream qw/ stream /; select STDERR; $| = 1; select STDOUT; $| = 1; -my $t = Test::Nginx->new()->has(qw/stream stream_geoip stream_return/) - ->has('stream_realip'); +my $t = Test::Nginx->new()->has(qw/stream stream_geoip stream_return/); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -40,22 +40,22 @@ events { stream { %%TEST_GLOBALS_STREAM%% - set_real_ip_from 127.0.0.1/32; + geoip_country test.mmdb; + geoip_city test.mmdb; + geoip_org test.mmdb; - geoip_country %%TESTDIR%%/country.dat; - geoip_city %%TESTDIR%%/city.dat; - geoip_org %%TESTDIR%%/org.dat; + geoip_set $asn test.mmdb autonomous_system_number; server { - listen 127.0.0.1:8080 proxy_protocol; + listen 127.0.0.1:8080; return "country_code:$geoip_country_code - country_code3:$geoip_country_code3 + country_code3:$geoip_country_code3:none country_name:$geoip_country_name - area_code:$geoip_area_code + area_code:$geoip_area_code:none city_continent_code:$geoip_city_continent_code city_country_code:$geoip_city_country_code - city_country_code3:$geoip_city_country_code3 + city_country_code3:$geoip_city_country_code3:none city_country_name:$geoip_city_country_name dma_code:$geoip_dma_code latitude:$geoip_latitude @@ -65,7 +65,9 @@ stream { city:$geoip_city postal_code:$geoip_postal_code - org:$geoip_org"; + org:$geoip_org + + asn:$asn"; } } @@ -73,123 +75,125 @@ EOF my $d = $t->testdir(); -# country database: -# -# "10.0.0.1","10.0.0.1","RU","Russian Federation" -# "2001:db8::","2001:db8::","US","United States" +# MMDB format specification: +# https://github.com/maxmind/MaxMind-DB/blob/main/MaxMind-DB-spec.md my $data = ''; -for my $i (0 .. 156) { - # skip to offset 32 if 1st bit set in ipv6 address wins - $data .= pack_node($i + 1) . pack_node(32), next if $i == 2; - # otherwise default to RU - $data .= pack_node(0xffffb9) . pack_node(0xffff00), next if $i == 31; - # continue checking bits set in ipv6 address - $data .= pack_node(0xffff00) . pack_node($i + 1), next - if grep $_ == $i, (44, 49, 50, 52, 53, 55, 56, 57); - # last bit set in ipv6 address - $data .= pack_node(0xffffe1) . pack_node(0xffff00), next if $i == 156; - $data .= pack_node($i + 1) . pack_node(0xffff00); -} +# binary search tree +# just one node (two records), 32-bit records + +$data .= pack('NN', 17, 17); + +# data section + +$data .= pack('x16'); + +$data .= pack("B8", "11101000"); + +$data .= pack("B8A*", "01011000", "autonomous_system_number"); +$data .= pack("B8N", "11000100", 64511); + +$data .= pack("B8CA*", "01011101", 30 - 29, "autonomous_system_organization"); +$data .= pack("B8A*", "01001101", "freenginx.org"); -$data .= chr(0x00) x 3; -$data .= chr(0xFF) x 3; -$data .= chr(12); - -$t->write_file('country.dat', $data); +$data .= pack("B8A*", "01000111", "country"); +$data .= pack("B8", "11100010"); +$data .= pack("B8A*", "01001000", "iso_code"); +$data .= pack("B8A*", "01000010", "RU"); +$data .= pack("B8A*", "01000101", "names"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000010", "en"); +$data .= pack("B8A*", "01010010", "Russian Federation"); -# city database: -# -# "167772161","167772161","RU","48","Moscow","119034","55.7543",37.6202",, - -$data = ''; +$data .= pack("B8A*", "01001001", "continent"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000100", "code"); +$data .= pack("B8A*", "01000010", "EU"); -for my $i (0 .. 31) { - $data .= pack_node(32) . pack_node($i + 1), next if $i == 4 or $i == 6; - $data .= pack_node(32) . pack_node($i + 2), next if $i == 31; - $data .= pack_node($i + 1) . pack_node(32); -} +$data .= pack("B8A*", "01001100", "subdivisions"); +$data .= pack("B8C", "00000001", 11 - 7); +$data .= pack("B8", "11100010"); +$data .= pack("B8A*", "01001000", "iso_code"); +$data .= pack("B8A*", "01000011", "MOW"); +$data .= pack("B8A*", "01000101", "names"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000010", "en"); +$data .= pack("B8A*", "01000110", "Moscow"); -$data .= chr(42); -$data .= chr(185); -$data .= pack('Z*', 48); -$data .= pack('Z*', 'Moscow'); -$data .= pack('Z*', 119034); -$data .= pack_node(int((55.7543 + 180) * 10000)); -$data .= pack_node(int((37.6202 + 180) * 10000)); -$data .= chr(0) x 3; -$data .= chr(0xFF) x 3; -$data .= chr(2); -$data .= pack_node(32); +$data .= pack("B8A*", "01000100", "city"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000101", "names"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000010", "en"); +$data .= pack("B8A*", "01000110", "Moscow"); + +$data .= pack("B8A*", "01000110", "postal"); +$data .= pack("B8", "11100001"); +$data .= pack("B8A*", "01000100", "code"); +$data .= pack("B8A*", "01000110", "119034"); -$t->write_file('city.dat', $data); +$data .= pack("B8A*", "01001000", "location"); +$data .= pack("B8", "11100011"); +$data .= pack("B8A*", "01001000", "latitude"); +$data .= pack("B8d>", "01101000", 55.7543); +$data .= pack("B8A*", "01001001", "longitude"); +$data .= pack("B8d>", "01101000", 37.6202); +$data .= pack("B8A*", "01001010", "metro_code"); +$data .= pack("B8C", "10100001", 0); + +# metadata + +$data .= "\xab\xcd\xefMaxMind.com"; -# organization database: -# -# "167772161","167772161","Nginx" - -$data = ''; +$data .= pack("B8", "11101001"); +$data .= pack("B8A*", "01001010", "node_count"); +$data .= pack("B8C", "11000001", 1); +$data .= pack("B8A*", "01001011", "record_size"); +$data .= pack("B8C", "10100001", 32); +$data .= pack("B8A*", "01001010", "ip_version"); +$data .= pack("B8C", "10100001", 6); +$data .= pack("B8A*", "01001101", "database_type"); +$data .= pack("B8A*", "01000100", "test"); +$data .= pack("B8A*", "01001001", "languages"); +$data .= pack("B8B8", "00000001", "00000100"); +$data .= pack("B8A*", "01000100", "test"); +$data .= pack("B8A*", "01011011", "binary_format_major_version"); +$data .= pack("B8C", "10100001", 2); +$data .= pack("B8A*", "01011011", "binary_format_minor_version"); +$data .= pack("B8C", "10100001", 0); +$data .= pack("B8A*", "01001011", "build_epoch"); +$data .= pack("B8B8C", "00000001", "00000010", 1); +$data .= pack("B8A*", "01001011", "description"); +$data .= pack("B8", "11100000"); -for my $i (0 .. 31) { - $data .= pack_org(32) . pack_org($i + 1), next if $i == 4 or $i == 6; - $data .= pack_org(32) . pack_org($i + 2), next if $i == 31; - $data .= pack_org($i + 1) . pack_org(32); -} +$t->write_file('test.mmdb', $data); -$data .= chr(42); -$data .= pack('Z*', 'Nginx'); -$data .= chr(0xFF) x 3; -$data .= chr(5); -$data .= pack_node(32); - -$t->write_file('org.dat', $data); -$t->try_run('no inet6 support')->plan(20); +$t->try_run('no geoip mmdb')->plan(17); ############################################################################### -my %data = stream_pp('10.0.0.1') =~ /(\w+):(.*)/g; +my %data = stream('127.0.0.1:' . port(8080))->read() =~ /(\w+):(.*)/g; + is($data{country_code}, 'RU', 'geoip country code'); -is($data{country_code3}, 'RUS', 'geoip country code 3'); +is($data{country_code3}, ':none', 'geoip country code 3'); is($data{country_name}, 'Russian Federation', 'geoip country name'); -is($data{area_code}, 0, 'geoip area code'); +is($data{area_code}, ':none', 'geoip area code'); is($data{city_continent_code}, 'EU', 'geoip city continent code'); is($data{city_country_code}, 'RU', 'geoip city country code'); -is($data{city_country_code3}, 'RUS', 'geoip city country code 3'); +is($data{city_country_code3}, ':none', 'geoip city country code 3'); is($data{city_country_name}, 'Russian Federation', 'geoip city country name'); is($data{dma_code}, 0, 'geoip dma code'); is($data{latitude}, 55.7543, 'geoip latitude'); is($data{longitude}, 37.6202, 'geoip longitude'); -is($data{region}, 48, 'geoip region'); -is($data{region_name}, 'Moscow City', 'geoip region name'); +is($data{region}, 'MOW', 'geoip region'); +is($data{region_name}, 'Moscow', 'geoip region name'); is($data{city}, 'Moscow', 'geoip city'); is($data{postal_code}, 119034, 'geoip postal code'); -is($data{org}, 'Nginx', 'geoip org'); - -like(stream_pp('::ffff:10.0.0.1'), qr/org:Nginx/, 'geoip ipv6 ipv4-mapped'); +is($data{org}, 'freenginx.org', 'geoip org'); -%data = stream_pp('2001:db8::') =~ /(\w+):(.*)/g; -is($data{country_code}, 'US', 'geoip ipv6 country code'); -is($data{country_code3}, 'USA', 'geoip ipv6 country code 3'); -is($data{country_name}, 'United States', 'geoip ipv6 country name'); +is($data{asn}, '64511', 'geoip set asn'); ############################################################################### - -sub stream_pp { - my ($ip) = @_; - my $type = ($ip =~ ':' ? 'TCP6' : 'TCP4'); - return stream('127.0.0.1:' . port(8080)) - ->io("PROXY $type $ip 127.0.0.1 8080 8080${CRLF}"); -} - -sub pack_node { - substr pack('V', shift), 0, 3; -} - -sub pack_org { - pack('V', shift); -} - -############################################################################### From mdounin at mdounin.ru Sat Dec 13 22:14:26 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sun, 14 Dec 2025 01:14:26 +0300 Subject: [nginx-site] Documented support for GeoIP2 databases in the MMDB... Message-ID: details: http://freenginx.org/hg/nginx-site/rev/857e2f1f9d5b branches: changeset: 3124:857e2f1f9d5b user: Maxim Dounin date: Sun Dec 14 01:09:23 2025 +0300 description: Documented support for GeoIP2 databases in the MMDB format. diffstat: xml/en/docs/configure.xml | 22 +++++++++- xml/en/docs/http/ngx_http_geoip_module.xml | 55 +++++++++++++++++++++++- xml/en/docs/stream/ngx_stream_geoip_module.xml | 57 +++++++++++++++++++++++-- xml/ru/docs/configure.xml | 22 +++++++++- xml/ru/docs/http/ngx_http_geoip_module.xml | 54 ++++++++++++++++++++++-- xml/ru/docs/stream/ngx_stream_geoip_module.xml | 56 ++++++++++++++++++++++-- 6 files changed, 244 insertions(+), 22 deletions(-) diffs (522 lines): diff --git a/xml/en/docs/configure.xml b/xml/en/docs/configure.xml --- a/xml/en/docs/configure.xml +++ b/xml/en/docs/configure.xml @@ -8,7 +8,7 @@
+ rev="24">
@@ -1341,6 +1341,26 @@ sets additional build options for OpenSS +--without-geoip + + +disables use of the GeoIP library (1.29.4). + + + +--without-libmaxminddb + + +disables use of the libmaxminddb library (1.29.4). + + + + + + + + + --with-debug diff --git a/xml/en/docs/http/ngx_http_geoip_module.xml b/xml/en/docs/http/ngx_http_geoip_module.xml --- a/xml/en/docs/http/ngx_http_geoip_module.xml +++ b/xml/en/docs/http/ngx_http_geoip_module.xml @@ -10,7 +10,7 @@ + rev="6">
@@ -21,7 +21,12 @@ with values depending on the client IP a -When using the databases with IPv6 support (1.3.12, 1.2.7), +The module supports both legacy GeoIP databases +and new GeoIP2 databases in the MaxMind DB (MMDB) file format (1.29.4). + + + +When using legacy databases with IPv6 support (1.3.12, 1.2.7), IPv4 addresses are looked up as IPv4-mapped IPv6 addresses. @@ -31,7 +36,14 @@ This module is not built by default, it configuration parameter. This module requires the -MaxMind GeoIP library. +MaxMind GeoIP +library +to support loading of legacy GeoIP databases, +and the +libmaxminddb +library +to support loading of GeoIP2 databases +in the MaxMind DB (MMDB) file format (1.29.4). @@ -79,6 +91,9 @@ two-letter country code, for example, three-letter country code, for example, ?RUS?, ?USA?. + +This variable is not available when using MMDB databases. + $geoip_country_name @@ -130,6 +145,9 @@ two-letter country code, for example, three-letter country code, for example, ?RUS?, ?USA?. + +This variable is not available when using MMDB databases. + $geoip_city_country_name @@ -154,7 +172,7 @@ in Google AdWords API. $geoip_region -two-symbol country region code (region, territory, state, province, federal land +country region code (region, territory, state, province, federal land and the like), for example, ?48?, ?DC?. @@ -241,6 +259,35 @@ non-trusted address sent in
X-Fo + + + + $variable + file + path + +http +1.29.4 + + +Creates a variable +whose value is determinded +from the specified database file +with the specified data path, +for example: + +geoip_set $country GeoLite2-Country.mmdb country.names.en; +geoip_set $region GeoLite2-City.mmdb subdivisions.0.iso_code; +geoip_set $asn GeoLite2-ASN.mmdb autonomous_system_number; + + + + +This directive only supports MMDB databases. + + + +
diff --git a/xml/en/docs/stream/ngx_stream_geoip_module.xml b/xml/en/docs/stream/ngx_stream_geoip_module.xml --- a/xml/en/docs/stream/ngx_stream_geoip_module.xml +++ b/xml/en/docs/stream/ngx_stream_geoip_module.xml @@ -9,7 +9,7 @@ + rev="2">
@@ -20,7 +20,12 @@ with values depending on the client IP a -When using the databases with IPv6 support, +The module supports both legacy GeoIP databases +and new GeoIP2 databases in the MaxMind DB (MMDB) file format (1.29.4). + + + +When using legacy databases with IPv6 support, IPv4 addresses are looked up as IPv4-mapped IPv6 addresses. @@ -30,7 +35,14 @@ This module is not built by default, it configuration parameter. This module requires the -MaxMind GeoIP library. +MaxMind GeoIP +library +to support loading of legacy GeoIP databases, +and the +libmaxminddb +library +to support loading of GeoIP2 databases +in the MaxMind DB (MMDB) file format (1.29.4). @@ -51,7 +63,7 @@ stream { NA na.example.com; AS as.example.com; } - ... + ... } @@ -83,6 +95,9 @@ two-letter country code, for example, three-letter country code, for example, ?RUS?, ?USA?. + +This variable is not available when using MMDB databases. + $geoip_country_name @@ -134,6 +149,9 @@ two-letter country code, for example, three-letter country code, for example, ?RUS?, ?USA?. + +This variable is not available when using MMDB databases. + $geoip_city_country_name @@ -158,7 +176,7 @@ in Google AdWords API. $geoip_region -two-symbol country region code (region, territory, state, province, federal land +country region code (region, territory, state, province, federal land and the like), for example, ?48?, ?DC?. @@ -208,6 +226,35 @@ organization name, for example, ?The University of Melbourne?. + + + + $variable + file + path + +http +1.29.4 + + +Creates a variable +whose value is determinded +from the specified database file +with the specified data path, +for example: + +geoip_set $country GeoLite2-Country.mmdb country.names.en; +geoip_set $region GeoLite2-City.mmdb subdivisions.0.iso_code; +geoip_set $asn GeoLite2-ASN.mmdb autonomous_system_number; + + + + +This directive only supports MMDB databases. + + + +
diff --git a/xml/ru/docs/configure.xml b/xml/ru/docs/configure.xml --- a/xml/ru/docs/configure.xml +++ b/xml/ru/docs/configure.xml @@ -8,7 +8,7 @@
+ rev="24">
@@ -1321,6 +1321,26 @@ 1.1.3—1.3) ????? ????? ?? ????? +--without-geoip + + +????????? ????????????? ?????????? GeoIP (1.29.4). + + + +--without-libmaxminddb + + +????????? ????????????? ?????????? libmaxminddb (1.29.4). + + + + + + + + + --with-debug diff --git a/xml/ru/docs/http/ngx_http_geoip_module.xml b/xml/ru/docs/http/ngx_http_geoip_module.xml --- a/xml/ru/docs/http/ngx_http_geoip_module.xml +++ b/xml/ru/docs/http/ngx_http_geoip_module.xml @@ -10,7 +10,7 @@ + rev="6">
@@ -21,7 +21,12 @@ -??? ????????????? ??? ?????? ? ?????????? IPv6 (1.3.12, 1.2.7) +?????? ???????????? ??? ???? ?????? GeoIP ? ?????? ???????, +??? ? ????? ???? ?????? GeoIP2 ? ??????? MaxMind DB (MMDB) (1.29.4). + + + +??? ????????????? ??? ?????? ? ?????? ??????? ? ?????????? IPv6 (1.3.12, 1.2.7) IPv4-?????? ?????? ????????????? ?? IPv6. @@ -30,8 +35,12 @@ IPv4-?????? ?????? ????????????? ?? IPv6. ????????? ? ??????? ????????????????? ????????? --with-http_geoip_module. -??? ?????? ? ?????? ????? ?????? ????? ?????????? -MaxMind GeoIP. +??? ?????? ? ?????? ????? ?????? ????????? ??????????: +MaxMind GeoIP +??? ?????? ? ?????? ?????? ? ?????? ??????? +? +libmaxminddb +??? ?????? ? ?????? ?????? ? ??????? MaxMind DB (MMDB) (1.29.4). @@ -79,6 +88,9 @@ http { ????????????? ??? ??????, ????????, ?RUS?, ?USA?. + +??? ?????????? ?????????? ??? ????????????? ??? ?????? ? ??????? MMDB. + $geoip_country_name @@ -131,6 +143,9 @@ http { ????????????? ??? ??????, ????????, ?RUS?, ?USA?. + +??? ?????????? ?????????? ??? ????????????? ??? ?????? ? ??????? MMDB. + $geoip_city_country_name @@ -155,7 +170,7 @@ Google AdWords API. $geoip_region -?????????????? ??? ??????? ?????? (???????, ????, ????, +??? ??????? ?????? (???????, ????, ????, ?????????, ??????????? ????? ? ???? ????????), ????????, ?48?, ?DC?. @@ -240,6 +255,35 @@ Google AdWords API. + + + + $?????????? + ???? + ???? + +http +1.29.4 + + +??????? ??????????, +???????? ??????? ????? ???????????? +?? ???? ?????? ? ????? +?? ?????????? ???? ? ??????, +????????: + +geoip_set $country GeoLite2-Country.mmdb country.names.en; +geoip_set $region GeoLite2-City.mmdb subdivisions.0.iso_code; +geoip_set $asn GeoLite2-ASN.mmdb autonomous_system_number; + + + + +??? ????????? ???????????? ?????? ???? ?????? ? ??????? MMDB. + + + +
diff --git a/xml/ru/docs/stream/ngx_stream_geoip_module.xml b/xml/ru/docs/stream/ngx_stream_geoip_module.xml --- a/xml/ru/docs/stream/ngx_stream_geoip_module.xml +++ b/xml/ru/docs/stream/ngx_stream_geoip_module.xml @@ -9,7 +9,7 @@ + rev="2">
@@ -20,7 +20,12 @@ -??? ????????????? ??? ?????? ? ?????????? IPv6 +?????? ???????????? ??? ???? ?????? GeoIP ? ?????? ???????, +??? ? ????? ???? ?????? GeoIP2 ? ??????? MaxMind DB (MMDB) (1.29.4). + + + +??? ????????????? ??? ?????? ? ?????? ??????? ? ?????????? IPv6 IPv4-?????? ?????? ????????????? ?? IPv6. @@ -29,8 +34,12 @@ IPv4-?????? ?????? ????????????? ?? IPv6. ????????? ? ??????? ????????????????? ????????? --with-stream_geoip_module. -??? ?????? ? ?????? ????? ?????? ????? ?????????? -MaxMind GeoIP. +??? ?????? ? ?????? ????? ?????? ????????? ??????????: +MaxMind GeoIP +??? ?????? ? ?????? ?????? ? ?????? ??????? +? +libmaxminddb +??? ?????? ? ?????? ?????? ? ??????? MaxMind DB (MMDB) (1.29.4). @@ -51,7 +60,7 @@ stream { NA na.example.com; AS as.example.com; } - ... + ... } @@ -83,6 +92,9 @@ stream { ????????????? ??? ??????, ????????, ?RUS?, ?USA?. + +??? ?????????? ?????????? ??? ????????????? ??? ?????? ? ??????? MMDB. + $geoip_country_name @@ -135,6 +147,9 @@ stream { ????????????? ??? ??????, ????????, ?RUS?, ?USA?. + +??? ?????????? ?????????? ??? ????????????? ??? ?????? ? ??????? MMDB. + $geoip_city_country_name @@ -159,7 +174,7 @@ Google AdWords API. $geoip_region -?????????????? ??? ??????? ?????? (???????, ????, ????, +??? ??????? ?????? (???????, ????, ????, ?????????, ??????????? ????? ? ???? ????????), ????????, ?48?, ?DC?. @@ -209,6 +224,35 @@ Google AdWords API. + + + + $?????????? + ???? + ???? + +http +1.29.4 + + +??????? ??????????, +???????? ??????? ????? ???????????? +?? ???? ?????? ? ????? +?? ?????????? ???? ? ??????, +????????: + +geoip_set $country GeoLite2-Country.mmdb country.names.en; +geoip_set $region GeoLite2-City.mmdb subdivisions.0.iso_code; +geoip_set $asn GeoLite2-ASN.mmdb autonomous_system_number; + + + + +??? ????????? ???????????? ?????? ???? ?????? ? ??????? MMDB. + + + +
From mdounin at mdounin.ru Sat Dec 13 22:15:43 2025 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sun, 14 Dec 2025 01:15:43 +0300 Subject: [PATCH] Documented support for GeoIP2 databases in the MMDB format In-Reply-To: References: <234a79a752d46a21ecac.1764779470@vm-bsd.mdounin.ru> <4dd62a0a82169fc78d0c.1765579798@vm-bsd.mdounin.ru> Message-ID: Hello! On Sat, Dec 13, 2025 at 08:37:57PM +0300, Sergey A. Osokin wrote: > On Sat, Dec 13, 2025 at 01:49:58AM +0300, Maxim Dounin wrote: > > # HG changeset patch > > # User Maxim Dounin > > # Date 1765579563 -10800 > > # Sat Dec 13 01:46:03 2025 +0300 > > # Node ID 4dd62a0a82169fc78d0c8593940ec374c4e2aa2e > > # Parent b0c7016f88682d92a2892074d783c7a007d8621f > > Documented support for GeoIP2 databases in the MMDB format. > > > > [...] > > Looks good to me. Thanks for looking, committed. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Sun Dec 14 02:59:57 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Sun, 14 Dec 2025 05:59:57 +0300 Subject: [nginx] Adjusted ngx_file_fs_size() workaround for XFS. Message-ID: details: http://freenginx.org/hg/nginx/rev/f5ce4ebcec7e branches: changeset: 9449:f5ce4ebcec7e user: Maxim Dounin date: Sun Dec 14 02:36:34 2025 +0300 description: Adjusted ngx_file_fs_size() workaround for XFS. With XFS, creating a file might result in large preallocation being reported in st_blocks as returned by fstat() till the file is closed, resulting in incorrect cache size calculations and too aggressive cache clearing based on max_size. This was worked around in 7669:52b34c3f89b4 by introducing the arbitrary sanity limit of eight blocks (8 * st_blksize) for the difference between st_size and size in st_blocks. Unfortunately, it turns out that XFS might return very large values in st_blksize as well, notably when using the "largeio" mount option, which makes the limit ineffective. As a workaround, the limit is changed to 32k instead, which is expected to be large enough to cover real differences between st_size and size in st_blocks, yet small enough to effectively suppress bogus st_blocks values with preallocations. It also matches the previous limit in many practical cases (with st_blksize 4096, previous limit was 32k). Reported by Oleg Mamontov. diffstat: src/os/unix/ngx_files.h | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff --git a/src/os/unix/ngx_files.h b/src/os/unix/ngx_files.h --- a/src/os/unix/ngx_files.h +++ b/src/os/unix/ngx_files.h @@ -187,7 +187,7 @@ ngx_int_t ngx_set_file_time(u_char *name #define ngx_file_size(sb) (sb)->st_size #define ngx_file_fs_size(sb) \ (((sb)->st_blocks * 512 > (sb)->st_size \ - && (sb)->st_blocks * 512 < (sb)->st_size + 8 * (sb)->st_blksize) \ + && (sb)->st_blocks * 512 < (sb)->st_size + 32768) \ ? (sb)->st_blocks * 512 : (sb)->st_size) #define ngx_file_mtime(sb) (sb)->st_mtime #define ngx_file_uniq(sb) (sb)->st_ino From osa at freebsd.org.ru Sun Dec 14 19:57:31 2025 From: osa at freebsd.org.ru (Sergey A. Osokin) Date: Sun, 14 Dec 2025 22:57:31 +0300 Subject: Coverity Scan's TOCTOUs Message-ID: Hi, hope you're doing well. I've tried to figure out how to deal with the TOCTOUs, found by Coverity Scan, https://scan.coverity.com/projects/freenginx. So, I've created a PoC for that: it uses *at family functions (conforming to POSIX.1-2008), such as mkdirat(2) and openat(2). I do believe it's possible to fix TOCTOU issues similarly, but before I start my work on that I'd like to hear your thoughts. Thank you. -- Sergey A. Osokin -------------- next part -------------- A non-text attachment was scrubbed... Name: t.c Type: text/x-csrc Size: 1796 bytes Desc: not available URL: From mdounin at mdounin.ru Mon Dec 15 00:32:32 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Mon, 15 Dec 2025 03:32:32 +0300 Subject: [PATCH] GeoIP: rearranged checks to avoid dead code Message-ID: # HG changeset patch # User Maxim Dounin # Date 1765750562 -10800 # Mon Dec 15 01:16:02 2025 +0300 # Node ID b401e3f56bdadb60efc0f51e1a195958d52254c8 # Parent f5ce4ebcec7e85d43a170b07b79a446798e24f83 GeoIP: rearranged checks to avoid dead code. The "ngx_http_geoip_mmdb_file() == NGX_OK" checks are not executed when both the legacy GeoIP library and libmaxminddb available, and Coverity complains that the code is dead. Fix is to rearrange to code so the checks are not compiled at all if both libraries are available. Prodded by Coverity. 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 @@ -1112,14 +1112,16 @@ ngx_http_geoip_country(ngx_conf_t *cf, n #if (NGX_HAVE_GEOIP_LEGACY) +#if (NGX_HAVE_GEOIP_MMDB) + +legacy: + +#else + 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); @@ -1213,14 +1215,16 @@ ngx_http_geoip_org(ngx_conf_t *cf, ngx_c #if (NGX_HAVE_GEOIP_LEGACY) +#if (NGX_HAVE_GEOIP_MMDB) + +legacy: + +#else + 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); @@ -1320,14 +1324,16 @@ ngx_http_geoip_city(ngx_conf_t *cf, ngx_ #if (NGX_HAVE_GEOIP_LEGACY) +#if (NGX_HAVE_GEOIP_MMDB) + +legacy: + +#else + 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); 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 @@ -1049,14 +1049,16 @@ ngx_stream_geoip_country(ngx_conf_t *cf, #if (NGX_HAVE_GEOIP_LEGACY) +#if (NGX_HAVE_GEOIP_MMDB) + +legacy: + +#else + 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); @@ -1150,14 +1152,16 @@ ngx_stream_geoip_org(ngx_conf_t *cf, ngx #if (NGX_HAVE_GEOIP_LEGACY) +#if (NGX_HAVE_GEOIP_MMDB) + +legacy: + +#else + 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); @@ -1257,14 +1261,16 @@ ngx_stream_geoip_city(ngx_conf_t *cf, ng #if (NGX_HAVE_GEOIP_LEGACY) +#if (NGX_HAVE_GEOIP_MMDB) + +legacy: + +#else + 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); From mdounin at mdounin.ru Mon Dec 15 00:36:39 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Mon, 15 Dec 2025 03:36:39 +0300 Subject: [PATCH] Tests: resolved ssl_store_pkcs11.t failures on Debian Message-ID: # HG changeset patch # User Maxim Dounin # Date 1765758980 -10800 # Mon Dec 15 03:36:20 2025 +0300 # Node ID b79ae9f9b554a2a8e1bdc9fde7f5730914b363ee # Parent cc23719251b0c98e556453e0f6f498111da7ec82 Tests: resolved ssl_store_pkcs11.t failures on Debian. There is a race between SoftHSM destructor and OpenSSL's atexit() handler, which results in segmentation faults on process exit when using unpatched SoftHSM 2.6.1 (latest version at the time of writing): https://github.com/latchset/pkcs11-provider/issues/310 https://github.com/openssl/openssl/issues/22508 https://github.com/openssl/project/issues/1719 https://github.com/softhsm/SoftHSMv2/issues/780 In particular, segmentation faults were observed on Debian, which uses some patches for SoftHSM, but not a patch which prevents SoftHSM object destruction as seen on Ubuntu: https://bugs.launchpad.net/ubuntu/+source/softhsm2/+bug/2059340 https://git.launchpad.net/ubuntu/+source/softhsm2/commit/?id=1d7c9001dbb7b89d2f72b920e09353dd90895b43 This can be resolved with the "no-deinit" quirk in pkcs11-provider. diff --git a/ssl_store_pkcs11.t b/ssl_store_pkcs11.t --- a/ssl_store_pkcs11.t +++ b/ssl_store_pkcs11.t @@ -125,7 +125,17 @@ EOF # pkcs11-module-load-behavior = early # pkcs11-module-quirks = no-operation-state # -# No quirks are needed with pkcs11-provider 1.0+. +# Further, there is a race between SoftHSM destructor and OpenSSL's atexit() +# handler, which results in segmentation faults on process exit when using +# unpatched SoftHSM 2.6.1 (latest version at the time of writing) with +# pkcs11-provider: +# +# https://github.com/latchset/pkcs11-provider/issues/310 +# https://github.com/openssl/openssl/issues/22508 +# https://github.com/openssl/project/issues/1719 +# https://github.com/softhsm/SoftHSMv2/issues/780 +# +# This can be resolved with the "no-deinit" quirk. # Libraries on various systems: FreeBSD, Alpine, Debian, Fedora @@ -164,7 +174,7 @@ module = $provider pkcs11-module-path = $softhsm pkcs11-module-token-pin = 1234 pkcs11-module-load-behavior = early -pkcs11-module-quirks = no-operation-state +pkcs11-module-quirks = no-operation-state no-deinit activate = 1 [ req ] From mdounin at mdounin.ru Mon Dec 15 22:37:57 2025 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 16 Dec 2025 01:37:57 +0300 Subject: Coverity Scan's TOCTOUs In-Reply-To: References: Message-ID: Hello! On Sun, Dec 14, 2025 at 10:57:31PM +0300, Sergey A. Osokin wrote: > I've tried to figure out how to deal with the TOCTOUs, found by Coverity > Scan, https://scan.coverity.com/projects/freenginx. > > So, I've created a PoC for that: it uses *at family functions (conforming > to POSIX.1-2008), such as mkdirat(2) and openat(2). > > I do believe it's possible to fix TOCTOU issues similarly, but before > I start my work on that I'd like to hear your thoughts. It is certainly should be possible to resolve these issues with using *at() functions, or even with more common fstat() / fchown() / fchmod() in some cases (e.g., CID 400877 and CID 728359 seems to only require these, and not *at() functions). But doing so will likely require additional code, especially given that *at() functions are not available on some supported platforms, such as old Unix systems, and we generally use this only conditionally, falling back to normal functions if no support available (see src/core/ngx_open_file_cache.c for an example). And at the same time most likely these issues are negligible, and can be safely ignored. As such, my recommendation would be to first focus on thoughtful analysis of these issues. If there are indeed some privilege escalation or other noticeable impact possible, we can consider fixing these on platforms with appropriate functions available. But if there are no real issues, I think it would be better to just ignore these instead of trying to implement multiple alternative code versions (with and without *at()). At the same time, fixing CID 400877 and CID 728359 with fstat() / fchown() / fchmod(), as long as it's possible without multiple code versions, might make sense regardless, just to make sure Coverity (and similar tools) will no longer complain. We already use fstat() unconditionally anyway, and both fchown() and fchmod() are available since 4.2BSD, in POSIX since Issue 4, Version 2, and seems to be present on all meaningful Unix systems (and the code in question is Unix-only anyway). But I would recommend thoughtful analysis as the first step anyway. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Mon Dec 15 22:51:39 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 16 Dec 2025 01:51:39 +0300 Subject: [nginx-tests] Tests: resolved ssl_store_pkcs11.t failures on Deb... Message-ID: details: http://freenginx.org/hg/nginx-tests/rev/b79ae9f9b554 branches: changeset: 2036:b79ae9f9b554 user: Maxim Dounin date: Mon Dec 15 03:36:20 2025 +0300 description: Tests: resolved ssl_store_pkcs11.t failures on Debian. There is a race between SoftHSM destructor and OpenSSL's atexit() handler, which results in segmentation faults on process exit when using unpatched SoftHSM 2.6.1 (latest version at the time of writing): https://github.com/latchset/pkcs11-provider/issues/310 https://github.com/openssl/openssl/issues/22508 https://github.com/openssl/project/issues/1719 https://github.com/softhsm/SoftHSMv2/issues/780 In particular, segmentation faults were observed on Debian, which uses some patches for SoftHSM, but not a patch which prevents SoftHSM object destruction as seen on Ubuntu: https://bugs.launchpad.net/ubuntu/+source/softhsm2/+bug/2059340 https://git.launchpad.net/ubuntu/+source/softhsm2/commit/?id=1d7c9001dbb7b89d2f72b920e09353dd90895b43 This can be resolved with the "no-deinit" quirk in pkcs11-provider. diffstat: ssl_store_pkcs11.t | 14 ++++++++++++-- 1 files changed, 12 insertions(+), 2 deletions(-) diffs (31 lines): diff --git a/ssl_store_pkcs11.t b/ssl_store_pkcs11.t --- a/ssl_store_pkcs11.t +++ b/ssl_store_pkcs11.t @@ -125,7 +125,17 @@ EOF # pkcs11-module-load-behavior = early # pkcs11-module-quirks = no-operation-state # -# No quirks are needed with pkcs11-provider 1.0+. +# Further, there is a race between SoftHSM destructor and OpenSSL's atexit() +# handler, which results in segmentation faults on process exit when using +# unpatched SoftHSM 2.6.1 (latest version at the time of writing) with +# pkcs11-provider: +# +# https://github.com/latchset/pkcs11-provider/issues/310 +# https://github.com/openssl/openssl/issues/22508 +# https://github.com/openssl/project/issues/1719 +# https://github.com/softhsm/SoftHSMv2/issues/780 +# +# This can be resolved with the "no-deinit" quirk. # Libraries on various systems: FreeBSD, Alpine, Debian, Fedora @@ -164,7 +174,7 @@ module = $provider pkcs11-module-path = $softhsm pkcs11-module-token-pin = 1234 pkcs11-module-load-behavior = early -pkcs11-module-quirks = no-operation-state +pkcs11-module-quirks = no-operation-state no-deinit activate = 1 [ req ] From mdounin at mdounin.ru Mon Dec 15 22:51:47 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 16 Dec 2025 01:51:47 +0300 Subject: [nginx] GeoIP: rearranged checks to avoid dead code. Message-ID: details: http://freenginx.org/hg/nginx/rev/b401e3f56bda branches: changeset: 9450:b401e3f56bda user: Maxim Dounin date: Mon Dec 15 01:16:02 2025 +0300 description: GeoIP: rearranged checks to avoid dead code. The "ngx_http_geoip_mmdb_file() == NGX_OK" checks are not executed when both the legacy GeoIP library and libmaxminddb available, and Coverity complains that the code is dead. Fix is to rearrange to code so the checks are not compiled at all if both libraries are available. Prodded by Coverity. diffstat: src/http/modules/ngx_http_geoip_module.c | 30 ++++++++++++++++++------------ src/stream/ngx_stream_geoip_module.c | 30 ++++++++++++++++++------------ 2 files changed, 36 insertions(+), 24 deletions(-) diffs (132 lines): 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 @@ -1112,14 +1112,16 @@ ngx_http_geoip_country(ngx_conf_t *cf, n #if (NGX_HAVE_GEOIP_LEGACY) +#if (NGX_HAVE_GEOIP_MMDB) + +legacy: + +#else + 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); @@ -1213,14 +1215,16 @@ ngx_http_geoip_org(ngx_conf_t *cf, ngx_c #if (NGX_HAVE_GEOIP_LEGACY) +#if (NGX_HAVE_GEOIP_MMDB) + +legacy: + +#else + 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); @@ -1320,14 +1324,16 @@ ngx_http_geoip_city(ngx_conf_t *cf, ngx_ #if (NGX_HAVE_GEOIP_LEGACY) +#if (NGX_HAVE_GEOIP_MMDB) + +legacy: + +#else + 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); 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 @@ -1049,14 +1049,16 @@ ngx_stream_geoip_country(ngx_conf_t *cf, #if (NGX_HAVE_GEOIP_LEGACY) +#if (NGX_HAVE_GEOIP_MMDB) + +legacy: + +#else + 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); @@ -1150,14 +1152,16 @@ ngx_stream_geoip_org(ngx_conf_t *cf, ngx #if (NGX_HAVE_GEOIP_LEGACY) +#if (NGX_HAVE_GEOIP_MMDB) + +legacy: + +#else + 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); @@ -1257,14 +1261,16 @@ ngx_stream_geoip_city(ngx_conf_t *cf, ng #if (NGX_HAVE_GEOIP_LEGACY) +#if (NGX_HAVE_GEOIP_MMDB) + +legacy: + +#else + 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); From mdounin at mdounin.ru Mon Dec 15 22:55:06 2025 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 16 Dec 2025 01:55:06 +0300 Subject: freenginx-1.29.4 changes draft Message-ID: Hello! Changes with freenginx 1.29.4 16 Dec 2025 *) Feature: support for GeoIP2 databases in the MaxMind DB (MMDB) format in the ngx_http_geoip_module and ngx_stream_geoip_module modules; the "geoip_set" directive. *) Bugfix: disk cache size might be calculated incorrectly on the XFS file system with the "largeio" mount option. *) Bugfix: of minor potential bugs. ????????? ? freenginx 1.29.4 16.12.2025 *) ??????????: ????????? ??? ?????? GeoIP2 ? ??????? MaxMind DB (MMDB) ? ??????? ngx_http_geoip_module ? ngx_stream_geoip_module; ????????? geoip_set. *) ???????????: ?????? ???? ?? ????? ??? ????????? ??????????? ??? ????????????? ???????? ??????? XFS ? ?????? ???????????? "largeio". *) ???????????: ?????????????? ????????????? ??????. -- Maxim Dounin http://mdounin.ru/ From osa at freebsd.org.ru Mon Dec 15 23:14:33 2025 From: osa at freebsd.org.ru (Sergey A. Osokin) Date: Tue, 16 Dec 2025 02:14:33 +0300 Subject: freenginx-1.29.4 changes draft In-Reply-To: References: Message-ID: On Tue, Dec 16, 2025 at 01:55:06AM +0300, Maxim Dounin wrote: > Hello! > > > Changes with freenginx 1.29.4 16 Dec 2025 > > *) Feature: support for GeoIP2 databases in the MaxMind DB (MMDB) format > in the ngx_http_geoip_module and ngx_stream_geoip_module modules; the > "geoip_set" directive. > > *) Bugfix: disk cache size might be calculated incorrectly on the XFS > file system with the "largeio" mount option. > > *) Bugfix: of minor potential bugs. > > > ????????? ? freenginx 1.29.4 16.12.2025 > > *) ??????????: ????????? ??? ?????? GeoIP2 ? ??????? MaxMind DB (MMDB) ? > ??????? ngx_http_geoip_module ? ngx_stream_geoip_module; ????????? > geoip_set. > > *) ???????????: ?????? ???? ?? ????? ??? ????????? ??????????? ??? > ????????????? ???????? ??????? XFS ? ?????? ???????????? "largeio". > > *) ???????????: ?????????????? ????????????? ??????. LGTM. -- Sergey A. Osokin osa at FreeBSD.org.ru From mdounin at mdounin.ru Tue Dec 16 13:22:09 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 16 Dec 2025 16:22:09 +0300 Subject: [nginx] freenginx-1.29.4-RELEASE Message-ID: details: http://freenginx.org/hg/nginx/rev/567870bfeb23 branches: changeset: 9451:567870bfeb23 user: Maxim Dounin date: Tue Dec 16 16:08:50 2025 +0300 description: freenginx-1.29.4-RELEASE diffstat: docs/xml/nginx/changes.xml | 38 ++++++++++++++++++++++++++++++++++++++ 1 files changed, 38 insertions(+), 0 deletions(-) diffs (48 lines): diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml --- a/docs/xml/nginx/changes.xml +++ b/docs/xml/nginx/changes.xml @@ -7,6 +7,44 @@
+ + + + +????????? ??? ?????? GeoIP2 ? ??????? MaxMind DB (MMDB) +? ??????? ngx_http_geoip_module ? ngx_stream_geoip_module; +????????? geoip_set. + + +support for GeoIP2 databases in the MaxMind DB (MMDB) format +in the ngx_http_geoip_module and ngx_stream_geoip_module modules; +the "geoip_set" directive. + + + + + +?????? ???? ?? ????? ??? ????????? ??????????? +??? ????????????? ???????? ??????? XFS ? ?????? ???????????? "largeio". + + +disk cache size might be calculated incorrectly +on the XFS file system with the "largeio" mount option. + + + + + +?????????????? ????????????? ??????. + + +of minor potential bugs. + + + + + + From mdounin at mdounin.ru Tue Dec 16 13:22:09 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 16 Dec 2025 16:22:09 +0300 Subject: [nginx] release-1.29.4 tag Message-ID: details: http://freenginx.org/hg/nginx/rev/0b1a383fcbb2 branches: changeset: 9452:0b1a383fcbb2 user: Maxim Dounin date: Tue Dec 16 16:08:51 2025 +0300 description: release-1.29.4 tag diffstat: .hgtags | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diffs (8 lines): diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -489,3 +489,4 @@ 441d59c1052d602143764a5765d65c51388c1d7b 8ccd477736c998c0ea64c477b75c647cb28b356c release-1.29.1 56d817adaa1dd4f522114e921b1ff599c2ab4db2 release-1.29.2 2175d3bea2a84f7265b90c6f9efea7fb9b41bdd7 release-1.29.3 +567870bfeb23f2e9c91e2a110d6d332c27c1ceb1 release-1.29.4 From mdounin at mdounin.ru Tue Dec 16 13:22:32 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 16 Dec 2025 16:22:32 +0300 Subject: [nginx-site] freenginx-1.29.4 Message-ID: details: http://freenginx.org/hg/nginx-site/rev/c6cbc552bd6a branches: changeset: 3125:c6cbc552bd6a user: Maxim Dounin date: Tue Dec 16 16:20:31 2025 +0300 description: freenginx-1.29.4 diffstat: text/en/CHANGES | 12 ++++++++++++ text/ru/CHANGES.ru | 12 ++++++++++++ xml/index.xml | 14 ++++++++++++++ xml/versions.xml | 1 + 4 files changed, 39 insertions(+), 0 deletions(-) diffs (75 lines): diff --git a/text/en/CHANGES b/text/en/CHANGES --- a/text/en/CHANGES +++ b/text/en/CHANGES @@ -1,4 +1,16 @@ +Changes with freenginx 1.29.4 16 Dec 2025 + + *) Feature: support for GeoIP2 databases in the MaxMind DB (MMDB) format + in the ngx_http_geoip_module and ngx_stream_geoip_module modules; the + "geoip_set" directive. + + *) Bugfix: disk cache size might be calculated incorrectly on the XFS + file system with the "largeio" mount option. + + *) Bugfix: of minor potential bugs. + + Changes with freenginx 1.29.3 11 Nov 2025 *) Feature: loading of certificates and secret keys from hardware tokens diff --git a/text/ru/CHANGES.ru b/text/ru/CHANGES.ru --- a/text/ru/CHANGES.ru +++ b/text/ru/CHANGES.ru @@ -1,4 +1,16 @@ +????????? ? freenginx 1.29.4 16.12.2025 + + *) ??????????: ????????? ??? ?????? GeoIP2 ? ??????? MaxMind DB (MMDB) ? + ??????? ngx_http_geoip_module ? ngx_stream_geoip_module; ????????? + geoip_set. + + *) ???????????: ?????? ???? ?? ????? ??? ????????? ??????????? ??? + ????????????? ???????? ??????? XFS ? ?????? ???????????? "largeio". + + *) ???????????: ?????????????? ????????????? ??????. + + ????????? ? freenginx 1.29.3 11.11.2025 *) ??????????: ??????????? ???????? ???????????? ? ????????? ?????? ? diff --git a/xml/index.xml b/xml/index.xml --- a/xml/index.xml +++ b/xml/index.xml @@ -8,6 +8,20 @@ + + +freenginx-1.29.4 +mainline version has been released, +featuring +support for GeoIP2 databases in the MaxMind DB (MMDB) format +in the +GeoIP module +and the + +directive. + + + freenginx-1.29.3 diff --git a/xml/versions.xml b/xml/versions.xml --- a/xml/versions.xml +++ b/xml/versions.xml @@ -9,6 +9,7 @@ + From mdounin at mdounin.ru Sun Dec 28 17:36:47 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sun, 28 Dec 2025 20:36:47 +0300 Subject: [PATCH 1 of 4] Removed unneeded local variable in ngx_libc_crypt() Message-ID: # HG changeset patch # User Maxim Dounin # Date 1766623499 -10800 # Thu Dec 25 03:44:59 2025 +0300 # Node ID c348a55bad1160c1a503f0ae57352eb7ebfab74a # Parent 0b1a383fcbb2aacb1ea3e5b228ea66ee30ec6eb9 Removed unneeded local variable in ngx_libc_crypt(). diff --git a/src/os/unix/ngx_user.c b/src/os/unix/ngx_user.c --- a/src/os/unix/ngx_user.c +++ b/src/os/unix/ngx_user.c @@ -46,9 +46,8 @@ ngx_libc_crypt(ngx_pool_t *pool, u_char ngx_int_t ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted) { - char *value; - size_t len; - ngx_err_t err; + char *value; + size_t len; value = crypt((char *) key, (char *) salt); @@ -64,9 +63,7 @@ ngx_libc_crypt(ngx_pool_t *pool, u_char return NGX_OK; } - err = ngx_errno; - - ngx_log_error(NGX_LOG_CRIT, pool->log, err, "crypt() failed"); + ngx_log_error(NGX_LOG_CRIT, pool->log, ngx_errno, "crypt() failed"); return NGX_ERROR; } From mdounin at mdounin.ru Sun Dec 28 17:36:48 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sun, 28 Dec 2025 20:36:48 +0300 Subject: [PATCH 2 of 4] Fixed unexpected ngx_socket_errno usage In-Reply-To: References: Message-ID: <5b137aa1f392d76b055c.1766943408@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1766623505 -10800 # Thu Dec 25 03:45:05 2025 +0300 # Node ID 5b137aa1f392d76b055c889bf25ebe66a191d343 # Parent c348a55bad1160c1a503f0ae57352eb7ebfab74a Fixed unexpected ngx_socket_errno usage. The particular error is reported because the protocol family of an inherited socket is not supported, and not because of a syscall error, so ngx_socket_errno contains unrelated data and should not be used. diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c --- a/src/core/ngx_connection.c +++ b/src/core/ngx_connection.c @@ -197,7 +197,7 @@ ngx_set_inherited_sockets(ngx_cycle_t *c break; default: - ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno, + ngx_log_error(NGX_LOG_CRIT, cycle->log, 0, "the inherited socket #%d has " "an unsupported protocol family", ls[i].fd); ls[i].ignore = 1; From mdounin at mdounin.ru Sun Dec 28 17:36:49 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sun, 28 Dec 2025 20:36:49 +0300 Subject: [PATCH 3 of 4] Win32: fixed ngx_errno vs. ngx_socket_errno usage In-Reply-To: References: Message-ID: # HG changeset patch # User Maxim Dounin # Date 1766623517 -10800 # Thu Dec 25 03:45:17 2025 +0300 # Node ID b2a44f0a26068f3eb6d66a80d789ac6177672ce7 # Parent 5b137aa1f392d76b055c889bf25ebe66a191d343 Win32: fixed ngx_errno vs. ngx_socket_errno usage. The ngx_socket_errno macro, and not ngx_errno, should be used on Windows to get an error after calling socket functions. And vice versa, after non-socket functions ngx_errno should be used. In modern Windows versions these are believed to be equivalent in practice, as WSAGetLastError() simply calls GetLastError(), so this is a mostly style change[1][2]. The documentation still suggests that WSAGetLastError() should be used after socket functions though. [1] https://devblogs.microsoft.com/oldnewthing/20050908-19/?p=34283 [2] https://github.com/tongzx/nt5src/blob/master/Source/XPSP1/NT/net/sockets/winsock2/ws2_32/src/perthrd.cpp diff --git a/src/event/modules/ngx_win32_poll_module.c b/src/event/modules/ngx_win32_poll_module.c --- a/src/event/modules/ngx_win32_poll_module.c +++ b/src/event/modules/ngx_win32_poll_module.c @@ -284,7 +284,7 @@ ngx_poll_process_events(ngx_cycle_t *cyc ready = WSAPoll(event_list, (u_int) nevents, (int) timer); - err = (ready == -1) ? ngx_errno : 0; + err = (ready == -1) ? ngx_socket_errno : 0; if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) { ngx_time_update(); diff --git a/src/event/ngx_event_connectex.c b/src/event/ngx_event_connectex.c --- a/src/event/ngx_event_connectex.c +++ b/src/event/ngx_event_connectex.c @@ -176,7 +176,7 @@ void ngx_iocp_wait_events(int main) if (PostQueuedCompletionStatus(iocp, 0, NGX_IOCP_CONNECT, &conn[n].write->ovlp) == 0) { - ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno, + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "PostQueuedCompletionStatus() failed"); continue; } diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -2423,7 +2423,7 @@ ngx_ssl_handshake(ngx_connection_t *c) return NGX_AGAIN; } - err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; + err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_socket_errno : 0; c->ssl->no_wait_shutdown = 1; c->ssl->no_send_shutdown = 1; @@ -2570,7 +2570,7 @@ ngx_ssl_try_early_data(ngx_connection_t return NGX_AGAIN; } - err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; + err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_socket_errno : 0; c->ssl->no_wait_shutdown = 1; c->ssl->no_send_shutdown = 1; @@ -3039,7 +3039,7 @@ ngx_ssl_handle_recv(ngx_connection_t *c, sslerr = SSL_get_error(c->ssl->connection, n); - err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; + err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_socket_errno : 0; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr); @@ -3363,7 +3363,7 @@ ngx_ssl_write(ngx_connection_t *c, u_cha sslerr = SSL_ERROR_SYSCALL; } - err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; + err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_socket_errno : 0; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr); @@ -3467,7 +3467,7 @@ ngx_ssl_write_early(ngx_connection_t *c, sslerr = SSL_get_error(c->ssl->connection, n); - err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; + err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_socket_errno : 0; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr); @@ -3624,7 +3624,7 @@ ngx_ssl_sendfile(ngx_connection_t *c, ng if (sslerr == SSL_ERROR_SSL && ERR_GET_REASON(ERR_peek_error()) == SSL_R_UNINITIALIZED - && ngx_errno != 0) + && ngx_socket_errno != 0) { /* * OpenSSL fails to return SSL_ERROR_SYSCALL if an error @@ -3635,7 +3635,7 @@ ngx_ssl_sendfile(ngx_connection_t *c, ng sslerr = SSL_ERROR_SYSCALL; } - err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; + err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_socket_errno : 0; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr); @@ -3656,7 +3656,7 @@ ngx_ssl_sendfile(ngx_connection_t *c, ng #if (NGX_HAVE_SENDFILE_NODISKIO) - if (ngx_errno == EBUSY) { + if (ngx_socket_errno == EBUSY) { c->busy_count++; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, @@ -3848,7 +3848,7 @@ ngx_ssl_shutdown(ngx_connection_t *c) goto done; } - err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; + err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_socket_errno : 0; ngx_ssl_connection_error(c, sslerr, err, "SSL_shutdown() failed"); From mdounin at mdounin.ru Sun Dec 28 17:36:50 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sun, 28 Dec 2025 20:36:50 +0300 Subject: [PATCH 4 of 4] Cleaned up unsafe ngx_errno and ngx_socket_errno usage In-Reply-To: References: Message-ID: <353f1106597651ab6d43.1766943410@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1766836715 -10800 # Sat Dec 27 14:58:35 2025 +0300 # Node ID 353f1106597651ab6d43ff2039801001603add51 # Parent b2a44f0a26068f3eb6d66a80d789ac6177672ce7 Cleaned up unsafe ngx_errno and ngx_socket_errno usage. Typical incorrect pattern is to call a function, then log some debugging information with a ngx_log_debugN() call, and then use ngx_errno if the function returned an error. This might lead to incorrect ngx_errno value being checked or logged, since ngx_log_debugN() might clobber errno if debugging is enabled. Fix is to save ngx_errno (and ngx_socket_errno) before debug logging, similarly to what we already do in many other places, or to reorder the code if appropriate. diff --git a/src/core/ngx_cycle.c b/src/core/ngx_cycle.c --- a/src/core/ngx_cycle.c +++ b/src/core/ngx_cycle.c @@ -42,8 +42,9 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) { void *rv; char **senv; + ngx_err_t err; + ngx_log_t *log; ngx_uint_t i, n; - ngx_log_t *log; ngx_time_t *tp; ngx_conf_t conf; ngx_pool_t *pool; @@ -387,12 +388,14 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) NGX_FILE_CREATE_OR_OPEN, NGX_FILE_DEFAULT_ACCESS); + err = ngx_errno; + ngx_log_debug3(NGX_LOG_DEBUG_CORE, log, 0, "log: %p %d \"%s\"", &file[i], file[i].fd, file[i].name.data); if (file[i].fd == NGX_INVALID_FILE) { - ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + ngx_log_error(NGX_LOG_EMERG, log, err, ngx_open_file_n " \"%s\" failed", file[i].name.data); goto failed; @@ -1238,6 +1241,7 @@ void ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user) { ngx_fd_t fd; + ngx_err_t err; ngx_uint_t i; ngx_list_part_t *part; ngx_open_file_t *file; @@ -1267,12 +1271,14 @@ ngx_reopen_files(ngx_cycle_t *cycle, ngx fd = ngx_open_file(file[i].name.data, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN, NGX_FILE_DEFAULT_ACCESS); + err = ngx_errno; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reopen file \"%s\", old:%d new:%d", file[i].name.data, file[i].fd, fd); if (fd == NGX_INVALID_FILE) { - ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + ngx_log_error(NGX_LOG_EMERG, cycle->log, err, ngx_open_file_n " \"%s\" failed", file[i].name.data); continue; } diff --git a/src/core/ngx_file.c b/src/core/ngx_file.c --- a/src/core/ngx_file.c +++ b/src/core/ngx_file.c @@ -201,6 +201,8 @@ ngx_create_temp_file(ngx_file_t *file, n file->fd = ngx_open_tempfile(file->name.data, persistent, access); + err = ngx_errno; + ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0, "temp fd:%d", file->fd); @@ -216,8 +218,6 @@ ngx_create_temp_file(ngx_file_t *file, n return NGX_OK; } - err = ngx_errno; - if (err == NGX_EEXIST_FILE) { n = (uint32_t) ngx_next_temp_number(1); continue; diff --git a/src/core/ngx_log.c b/src/core/ngx_log.c --- a/src/core/ngx_log.c +++ b/src/core/ngx_log.c @@ -387,8 +387,9 @@ ngx_log_check_rate(ngx_log_t *log, ngx_u ngx_log_t * ngx_log_init(u_char *prefix, u_char *error_log) { - u_char *p, *name; - size_t nlen, plen; + u_char *p, *name; + size_t nlen, plen; + ngx_err_t err; ngx_log.file = &ngx_log_file; ngx_log.log_level = NGX_LOG_NOTICE; @@ -448,11 +449,13 @@ ngx_log_init(u_char *prefix, u_char *err NGX_FILE_DEFAULT_ACCESS); if (ngx_log_file.fd == NGX_INVALID_FILE) { - ngx_log_stderr(ngx_errno, + err = ngx_errno; + + ngx_log_stderr(err, "[alert] could not open error log file: " ngx_open_file_n " \"%s\" failed", name); #if (NGX_WIN32) - ngx_event_log(ngx_errno, + ngx_event_log(err, "could not open error log file: " ngx_open_file_n " \"%s\" failed", name); #endif diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c --- a/src/core/ngx_resolver.c +++ b/src/core/ngx_resolver.c @@ -4465,14 +4465,14 @@ ngx_udp_connect(ngx_resolver_connection_ s = ngx_socket(rec->sockaddr->sa_family, SOCK_DGRAM, 0); - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &rec->log, 0, "UDP socket %d", s); - if (s == (ngx_socket_t) -1) { ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno, ngx_socket_n " failed"); return NGX_ERROR; } + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &rec->log, 0, "UDP socket %d", s); + c = ngx_get_connection(s, &rec->log); if (c == NULL) { @@ -4553,14 +4553,14 @@ ngx_tcp_connect(ngx_resolver_connection_ s = ngx_socket(rec->sockaddr->sa_family, SOCK_STREAM, 0); - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &rec->log, 0, "TCP socket %d", s); - if (s == (ngx_socket_t) -1) { ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno, ngx_socket_n " failed"); return NGX_ERROR; } + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &rec->log, 0, "TCP socket %d", s); + c = ngx_get_connection(s, &rec->log); if (c == NULL) { diff --git a/src/event/modules/ngx_epoll_module.c b/src/event/modules/ngx_epoll_module.c --- a/src/event/modules/ngx_epoll_module.c +++ b/src/event/modules/ngx_epoll_module.c @@ -980,6 +980,8 @@ ngx_epoll_eventfd_handler(ngx_event_t *e events = io_getevents(ngx_aio_ctx, 1, 64, event, &ts); + err = ngx_errno; + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0, "io_getevents: %d", events); @@ -1013,7 +1015,7 @@ ngx_epoll_eventfd_handler(ngx_event_t *e } /* events == -1 */ - ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, + ngx_log_error(NGX_LOG_ALERT, ev->log, err, "io_getevents() failed"); return; } diff --git a/src/event/ngx_event_acceptex.c b/src/event/ngx_event_acceptex.c --- a/src/event/ngx_event_acceptex.c +++ b/src/event/ngx_event_acceptex.c @@ -106,16 +106,15 @@ ngx_event_post_acceptex(ngx_listening_t s = ngx_socket(ls->sockaddr->sa_family, ls->type, 0); - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &ls->log, 0, - ngx_socket_n " s:%d", s); - if (s == (ngx_socket_t) -1) { ngx_log_error(NGX_LOG_ALERT, &ls->log, ngx_socket_errno, ngx_socket_n " failed"); - return NGX_ERROR; } + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &ls->log, 0, + ngx_socket_n " s:%d", s); + c = ngx_get_connection(s, &ls->log); if (c == NULL) { diff --git a/src/event/ngx_event_connect.c b/src/event/ngx_event_connect.c --- a/src/event/ngx_event_connect.c +++ b/src/event/ngx_event_connect.c @@ -40,15 +40,15 @@ ngx_event_connect_peer(ngx_peer_connecti s = ngx_socket(pc->sockaddr->sa_family, type, 0); - ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pc->log, 0, "%s socket %d", - (type == SOCK_STREAM) ? "stream" : "dgram", s); - if (s == (ngx_socket_t) -1) { ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, ngx_socket_n " failed"); return NGX_ERROR; } + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pc->log, 0, "%s socket %d", + (type == SOCK_STREAM) ? "stream" : "dgram", s); + c = ngx_get_connection(s, pc->log); diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -38,7 +38,8 @@ static void ngx_ssl_handshake_handler(ng static ssize_t ngx_ssl_recv_early(ngx_connection_t *c, u_char *buf, size_t size); #endif -static ngx_int_t ngx_ssl_handle_recv(ngx_connection_t *c, int n); +static ngx_int_t ngx_ssl_handle_recv(ngx_connection_t *c, int n, + ngx_err_t err); static void ngx_ssl_write_handler(ngx_event_t *wev); #ifdef SSL_READ_EARLY_DATA_SUCCESS static ssize_t ngx_ssl_write_early(ngx_connection_t *c, u_char *data, @@ -2324,6 +2325,8 @@ ngx_ssl_handshake(ngx_connection_t *c) n = SSL_do_handshake(c->ssl->connection); + err = ngx_socket_errno; + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); if (n == 1) { @@ -2423,7 +2426,9 @@ ngx_ssl_handshake(ngx_connection_t *c) return NGX_AGAIN; } - err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_socket_errno : 0; + if (sslerr != SSL_ERROR_SYSCALL) { + err = 0; + } c->ssl->no_wait_shutdown = 1; c->ssl->no_send_shutdown = 1; @@ -2468,6 +2473,8 @@ ngx_ssl_try_early_data(ngx_connection_t n = SSL_read_early_data(c->ssl->connection, &buf, 1, &readbytes); + err = ngx_socket_errno; + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_read_early_data: %d, %uz", n, readbytes); @@ -2570,7 +2577,9 @@ ngx_ssl_try_early_data(ngx_connection_t return NGX_AGAIN; } - err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_socket_errno : 0; + if (sslerr != SSL_ERROR_SYSCALL) { + err = 0; + } c->ssl->no_wait_shutdown = 1; c->ssl->no_send_shutdown = 1; @@ -2738,7 +2747,8 @@ ngx_ssl_recv_chain(ngx_connection_t *c, ssize_t ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size) { - int n, bytes; + int n, bytes; + ngx_err_t err; #ifdef SSL_READ_EARLY_DATA_SUCCESS if (c->ssl->in_early) { @@ -2771,13 +2781,15 @@ ngx_ssl_recv(ngx_connection_t *c, u_char n = SSL_read(c->ssl->connection, buf, size); + err = ngx_socket_errno; + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_read: %d", n); if (n > 0) { bytes += n; } - c->ssl->last = ngx_ssl_handle_recv(c, n); + c->ssl->last = ngx_ssl_handle_recv(c, n, err); if (c->ssl->last == NGX_OK) { @@ -2870,6 +2882,7 @@ ngx_ssl_recv_early(ngx_connection_t *c, { int n, bytes; size_t readbytes; + ngx_err_t err; if (c->ssl->last == NGX_ERROR) { c->read->ready = 0; @@ -2919,12 +2932,14 @@ ngx_ssl_recv_early(ngx_connection_t *c, n = SSL_read_early_data(c->ssl->connection, buf, size, &readbytes); + err = ngx_socket_errno; + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_read_early_data: %d, %uz", n, readbytes); if (n == SSL_READ_EARLY_DATA_SUCCESS) { - c->ssl->last = ngx_ssl_handle_recv(c, 1); + c->ssl->last = ngx_ssl_handle_recv(c, 1, 0); bytes += readbytes; size -= readbytes; @@ -2941,7 +2956,7 @@ ngx_ssl_recv_early(ngx_connection_t *c, if (n == SSL_READ_EARLY_DATA_FINISH) { - c->ssl->last = ngx_ssl_handle_recv(c, 1); + c->ssl->last = ngx_ssl_handle_recv(c, 1, 0); c->ssl->in_early = 0; if (bytes) { @@ -2954,7 +2969,7 @@ ngx_ssl_recv_early(ngx_connection_t *c, /* SSL_READ_EARLY_DATA_ERROR */ - c->ssl->last = ngx_ssl_handle_recv(c, 0); + c->ssl->last = ngx_ssl_handle_recv(c, 0, err); if (bytes) { if (c->ssl->last != NGX_AGAIN) { @@ -2987,10 +3002,9 @@ ngx_ssl_recv_early(ngx_connection_t *c, static ngx_int_t -ngx_ssl_handle_recv(ngx_connection_t *c, int n) +ngx_ssl_handle_recv(ngx_connection_t *c, int n, ngx_err_t err) { - int sslerr; - ngx_err_t err; + int sslerr; #if (!defined SSL_OP_NO_RENEGOTIATION \ && !defined SSL_OP_NO_CLIENT_RENEGOTIATION) @@ -3039,8 +3053,6 @@ ngx_ssl_handle_recv(ngx_connection_t *c, sslerr = SSL_get_error(c->ssl->connection, n); - err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_socket_errno : 0; - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr); if (sslerr == SSL_ERROR_WANT_READ) { @@ -3085,6 +3097,10 @@ ngx_ssl_handle_recv(ngx_connection_t *c, return NGX_AGAIN; } + if (sslerr != SSL_ERROR_SYSCALL) { + err = 0; + } + c->ssl->no_wait_shutdown = 1; c->ssl->no_send_shutdown = 1; @@ -3327,6 +3343,8 @@ ngx_ssl_write(ngx_connection_t *c, u_cha n = SSL_write(c->ssl->connection, data, size); + err = ngx_socket_errno; + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_write: %d", n); if (n > 0) { @@ -3363,8 +3381,6 @@ ngx_ssl_write(ngx_connection_t *c, u_cha sslerr = SSL_ERROR_SYSCALL; } - err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_socket_errno : 0; - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr); if (sslerr == SSL_ERROR_WANT_WRITE) { @@ -3410,6 +3426,10 @@ ngx_ssl_write(ngx_connection_t *c, u_cha return NGX_AGAIN; } + if (sslerr != SSL_ERROR_SYSCALL) { + err = 0; + } + c->ssl->no_wait_shutdown = 1; c->ssl->no_send_shutdown = 1; c->write->error = 1; @@ -3437,6 +3457,8 @@ ngx_ssl_write_early(ngx_connection_t *c, n = SSL_write_early_data(c->ssl->connection, data, size, &written); + err = ngx_socket_errno; + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_write_early_data: %d, %uz", n, written); @@ -3467,8 +3489,6 @@ ngx_ssl_write_early(ngx_connection_t *c, sslerr = SSL_get_error(c->ssl->connection, n); - err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_socket_errno : 0; - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr); if (sslerr == SSL_ERROR_WANT_WRITE) { @@ -3525,6 +3545,10 @@ ngx_ssl_write_early(ngx_connection_t *c, return NGX_AGAIN; } + if (sslerr != SSL_ERROR_SYSCALL) { + err = 0; + } + c->ssl->no_wait_shutdown = 1; c->ssl->no_send_shutdown = 1; c->write->error = 1; @@ -3569,6 +3593,8 @@ ngx_ssl_sendfile(ngx_connection_t *c, ng n = SSL_sendfile(c->ssl->connection, file->file->fd, file->file_pos, size, flags); + err = ngx_socket_errno; + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_sendfile: %z", n); if (n > 0) { @@ -3624,7 +3650,7 @@ ngx_ssl_sendfile(ngx_connection_t *c, ng if (sslerr == SSL_ERROR_SSL && ERR_GET_REASON(ERR_peek_error()) == SSL_R_UNINITIALIZED - && ngx_socket_errno != 0) + && err != 0) { /* * OpenSSL fails to return SSL_ERROR_SYSCALL if an error @@ -3635,8 +3661,6 @@ ngx_ssl_sendfile(ngx_connection_t *c, ng sslerr = SSL_ERROR_SYSCALL; } - err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_socket_errno : 0; - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr); if (sslerr == SSL_ERROR_WANT_WRITE) { @@ -3656,7 +3680,7 @@ ngx_ssl_sendfile(ngx_connection_t *c, ng #if (NGX_HAVE_SENDFILE_NODISKIO) - if (ngx_socket_errno == EBUSY) { + if (err == EBUSY) { c->busy_count++; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, @@ -3699,6 +3723,10 @@ ngx_ssl_sendfile(ngx_connection_t *c, ng return NGX_AGAIN; } + if (sslerr != SSL_ERROR_SYSCALL) { + err = 0; + } + c->ssl->no_wait_shutdown = 1; c->ssl->no_send_shutdown = 1; c->write->error = 1; @@ -3803,6 +3831,8 @@ ngx_ssl_shutdown(ngx_connection_t *c) n = SSL_shutdown(c->ssl->connection); + err = ngx_socket_errno; + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_shutdown: %d", n); if (n == 1) { @@ -3848,7 +3878,9 @@ ngx_ssl_shutdown(ngx_connection_t *c) goto done; } - err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_socket_errno : 0; + if (sslerr != SSL_ERROR_SYSCALL) { + err = 0; + } ngx_ssl_connection_error(c, sslerr, err, "SSL_shutdown() failed"); diff --git a/src/os/unix/ngx_file_aio_read.c b/src/os/unix/ngx_file_aio_read.c --- a/src/os/unix/ngx_file_aio_read.c +++ b/src/os/unix/ngx_file_aio_read.c @@ -153,11 +153,12 @@ ngx_file_aio_result(ngx_file_t *file, ng n = aio_error(&aio->aiocb); + err = ngx_errno; + ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0, "aio_error: fd:%d %d", file->fd, n); if (n == -1) { - err = ngx_errno; aio->err = err; ngx_log_error(NGX_LOG_ALERT, file->log, err, diff --git a/src/os/unix/ngx_freebsd_init.c b/src/os/unix/ngx_freebsd_init.c --- a/src/os/unix/ngx_freebsd_init.c +++ b/src/os/unix/ngx_freebsd_init.c @@ -109,10 +109,12 @@ ngx_os_specific_init(ngx_log_t *log) size = sizeof(ngx_freebsd_kern_ostype); if (sysctlbyname("kern.ostype", ngx_freebsd_kern_ostype, &size, NULL, 0) == -1) { - ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + err = ngx_errno; + + ngx_log_error(NGX_LOG_ALERT, log, err, "sysctlbyname(kern.ostype) failed"); - if (ngx_errno != NGX_ENOMEM) { + if (err != NGX_ENOMEM) { return NGX_ERROR; } @@ -122,7 +124,9 @@ ngx_os_specific_init(ngx_log_t *log) size = sizeof(ngx_freebsd_kern_osrelease); if (sysctlbyname("kern.osrelease", ngx_freebsd_kern_osrelease, &size, NULL, 0) == -1) { - ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + err = ngx_errno; + + ngx_log_error(NGX_LOG_ALERT, log, err, "sysctlbyname(kern.osrelease) failed"); if (ngx_errno != NGX_ENOMEM) { diff --git a/src/os/unix/ngx_recv.c b/src/os/unix/ngx_recv.c --- a/src/os/unix/ngx_recv.c +++ b/src/os/unix/ngx_recv.c @@ -70,6 +70,8 @@ ngx_unix_recv(ngx_connection_t *c, u_cha do { n = recv(c->fd, buf, size, 0); + err = ngx_socket_errno; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, "recv: fd:%d %z of %uz", c->fd, n, size); @@ -179,8 +181,6 @@ ngx_unix_recv(ngx_connection_t *c, u_cha return n; } - err = ngx_socket_errno; - if (err == NGX_EAGAIN || err == NGX_EINTR) { ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, "recv() not ready"); diff --git a/src/os/unix/ngx_send.c b/src/os/unix/ngx_send.c --- a/src/os/unix/ngx_send.c +++ b/src/os/unix/ngx_send.c @@ -33,6 +33,8 @@ ngx_unix_send(ngx_connection_t *c, u_cha for ( ;; ) { n = send(c->fd, buf, size, 0); + err = ngx_socket_errno; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, "send: fd:%d %z of %uz", c->fd, n, size); @@ -46,8 +48,6 @@ ngx_unix_send(ngx_connection_t *c, u_cha return n; } - err = ngx_socket_errno; - if (n == 0) { ngx_log_error(NGX_LOG_ALERT, c->log, err, "send() returned zero"); wev->ready = 0; diff --git a/src/os/unix/ngx_udp_recv.c b/src/os/unix/ngx_udp_recv.c --- a/src/os/unix/ngx_udp_recv.c +++ b/src/os/unix/ngx_udp_recv.c @@ -22,6 +22,8 @@ ngx_udp_unix_recv(ngx_connection_t *c, u do { n = recv(c->fd, buf, size, 0); + err = ngx_socket_errno; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, "recv: fd:%d %z of %uz", c->fd, n, size); @@ -48,8 +50,6 @@ ngx_udp_unix_recv(ngx_connection_t *c, u return n; } - err = ngx_socket_errno; - if (err == NGX_EAGAIN || err == NGX_EINTR) { ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, "recv() not ready"); diff --git a/src/os/unix/ngx_udp_send.c b/src/os/unix/ngx_udp_send.c --- a/src/os/unix/ngx_udp_send.c +++ b/src/os/unix/ngx_udp_send.c @@ -22,6 +22,8 @@ ngx_udp_unix_send(ngx_connection_t *c, u for ( ;; ) { n = sendto(c->fd, buf, size, 0, c->sockaddr, c->socklen); + err = ngx_socket_errno; + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, "sendto: fd:%d %z of %uz to \"%V\"", c->fd, n, size, &c->addr_text); @@ -38,8 +40,6 @@ ngx_udp_unix_send(ngx_connection_t *c, u return n; } - err = ngx_socket_errno; - if (err == NGX_EAGAIN) { wev->ready = 0; ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, NGX_EAGAIN, diff --git a/src/os/unix/ngx_writev_chain.c b/src/os/unix/ngx_writev_chain.c --- a/src/os/unix/ngx_writev_chain.c +++ b/src/os/unix/ngx_writev_chain.c @@ -188,12 +188,12 @@ eintr: n = writev(c->fd, vec->iovs, vec->count); + err = ngx_errno; + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "writev: %z of %uz", n, vec->size); if (n == -1) { - err = ngx_errno; - switch (err) { case NGX_EAGAIN: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, diff --git a/src/os/win32/ngx_process.c b/src/os/win32/ngx_process.c --- a/src/os/win32/ngx_process.c +++ b/src/os/win32/ngx_process.c @@ -23,6 +23,7 @@ ngx_spawn_process(ngx_cycle_t *cycle, ch u_long rc, n, code; ngx_int_t s; ngx_pid_t pid; + ngx_err_t err; ngx_exec_ctx_t ctx; HANDLE events[2]; char file[MAX_PATH + 1]; @@ -86,6 +87,8 @@ ngx_spawn_process(ngx_cycle_t *cycle, ch rc = WaitForMultipleObjects(2, events, 0, 5000); + err = ngx_errno; + ngx_time_update(); ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0, @@ -150,7 +153,7 @@ ngx_spawn_process(ngx_cycle_t *cycle, ch goto failed; case WAIT_FAILED: - ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_log_error(NGX_LOG_ALERT, cycle->log, err, "WaitForSingleObject(\"%s\") failed", ngx_master_process_event_name); diff --git a/src/os/win32/ngx_shmem.c b/src/os/win32/ngx_shmem.c --- a/src/os/win32/ngx_shmem.c +++ b/src/os/win32/ngx_shmem.c @@ -73,12 +73,12 @@ ngx_shm_alloc(ngx_shm_t *shm) return NGX_ERROR; } - ngx_free(name); - if (ngx_errno == ERROR_ALREADY_EXISTS) { shm->exists = 1; } + ngx_free(name); + shm->addr = MapViewOfFileEx(shm->handle, FILE_MAP_WRITE, 0, 0, 0, base); if (shm->addr != NULL) { diff --git a/src/os/win32/ngx_udp_wsarecv.c b/src/os/win32/ngx_udp_wsarecv.c --- a/src/os/win32/ngx_udp_wsarecv.c +++ b/src/os/win32/ngx_udp_wsarecv.c @@ -26,6 +26,8 @@ ngx_udp_wsarecv(ngx_connection_t *c, u_c rc = WSARecv(c->fd, wsabuf, 1, &bytes, &flags, NULL, NULL); + err = ngx_socket_errno; + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, "WSARecv: fd:%d rc:%d %ul of %z", c->fd, rc, bytes, size); @@ -33,7 +35,6 @@ ngx_udp_wsarecv(ngx_connection_t *c, u_c if (rc == -1) { rev->ready = 0; - err = ngx_socket_errno; if (err == WSAEWOULDBLOCK) { ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, @@ -112,13 +113,13 @@ ngx_udp_overlapped_wsarecv(ngx_connectio rc = WSARecv(c->fd, wsabuf, 1, &bytes, &flags, ovlp, NULL); rev->complete = 0; + err = ngx_socket_errno; ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, "WSARecv ovlp: fd:%d rc:%d %ul of %z", c->fd, rc, bytes, size); if (rc == -1) { - err = ngx_socket_errno; if (err == WSA_IO_PENDING) { rev->active = 1; ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, diff --git a/src/os/win32/ngx_wsarecv.c b/src/os/win32/ngx_wsarecv.c --- a/src/os/win32/ngx_wsarecv.c +++ b/src/os/win32/ngx_wsarecv.c @@ -27,6 +27,8 @@ ngx_wsarecv(ngx_connection_t *c, u_char rc = WSARecv(c->fd, wsabuf, 1, &bytes, &flags, NULL, NULL); + err = ngx_socket_errno; + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, "WSARecv: fd:%d rc:%d %ul of %z", c->fd, rc, bytes, size); @@ -34,7 +36,6 @@ ngx_wsarecv(ngx_connection_t *c, u_char if (rc == -1) { rev->ready = 0; - err = ngx_socket_errno; if (err == WSAEWOULDBLOCK) { ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, @@ -166,13 +167,13 @@ ngx_overlapped_wsarecv(ngx_connection_t rc = WSARecv(c->fd, wsabuf, 1, &bytes, &flags, ovlp, NULL); rev->complete = 0; + err = ngx_socket_errno; ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, "WSARecv ovlp: fd:%d rc:%d %ul of %z", c->fd, rc, bytes, size); if (rc == -1) { - err = ngx_socket_errno; if (err == WSA_IO_PENDING) { rev->active = 1; ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, diff --git a/src/os/win32/ngx_wsasend.c b/src/os/win32/ngx_wsasend.c --- a/src/os/win32/ngx_wsasend.c +++ b/src/os/win32/ngx_wsasend.c @@ -37,6 +37,8 @@ ngx_wsasend(ngx_connection_t *c, u_char n = WSASend(c->fd, &wsabuf, 1, &sent, 0, NULL, NULL); + err = ngx_socket_errno; + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, "WSASend: fd:%d, %d, %ul of %uz", c->fd, n, sent, size); @@ -50,8 +52,6 @@ ngx_wsasend(ngx_connection_t *c, u_char return sent; } - err = ngx_socket_errno; - if (err == WSAEWOULDBLOCK) { ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, "WSASend() not ready"); wev->ready = 0; @@ -103,11 +103,12 @@ ngx_overlapped_wsasend(ngx_connection_t n = WSASend(c->fd, &wsabuf, 1, &sent, 0, ovlp, NULL); + wev->complete = 0; + err = ngx_socket_errno; + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, "WSASend: fd:%d, %d, %ul of %uz", c->fd, n, sent, size); - wev->complete = 0; - if (n == 0) { if (ngx_event_flags & NGX_USE_IOCP_EVENT) { @@ -130,8 +131,6 @@ ngx_overlapped_wsasend(ngx_connection_t return sent; } - err = ngx_socket_errno; - if (err == WSA_IO_PENDING) { ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, "WSASend() posted");