[PATCH] Add IPv6 support to GeoIP module

Bruno Prémont bruno.premont at restena.lu
Tue Oct 4 07:22:31 UTC 2011


Hello all,


geoip module currently only supports a single country and a single city
MaxMind database that must cover IPv4 address range.

Below patch allows setting up to two city and country databases, one
for IPv4 and one for IPv6. It then looks-up the requesting IP address in
the appropriate database.

Please review and consider applying,

Bruno




diff -NurpP nginx-1.0.4.orig/src/http/modules/ngx_http_geoip_module.c nginx-1.0.4/src/http/modules/ngx_http_geoip_module.c
--- nginx-1.0.4.orig/src/http/modules/ngx_http_geoip_module.c	2011-05-16 15:50:58.000000000 +0200
+++ nginx-1.0.4/src/http/modules/ngx_http_geoip_module.c	2011-09-30 10:30:18.785083263 +0200
@@ -16,6 +16,9 @@ typedef struct {
     GeoIP      *country;
     GeoIP      *org;
     GeoIP      *city;
+    GeoIP      *country_v6;
+    GeoIP      *org_v6;
+    GeoIP      *city_v6;
 } ngx_http_geoip_conf_t;
 
 
@@ -26,8 +29,13 @@ typedef struct {
 
 
 typedef const char *(*ngx_http_geoip_variable_handler_pt)(GeoIP *, u_long addr);
+typedef const char *(*ngx_http_geoip_variable_handler6_pt)(GeoIP *, struct in6_addr addr);
 
-static ngx_int_t ngx_http_geoip_country_variable(ngx_http_request_t *r,
+static ngx_int_t ngx_http_geoip_country_code_variable(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_geoip_country_code3_variable(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_geoip_country_name_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,
     ngx_http_variable_value_t *v, uintptr_t data);
@@ -113,16 +121,16 @@ ngx_module_t  ngx_http_geoip_module = {
 static ngx_http_variable_t  ngx_http_geoip_vars[] = {
 
     { ngx_string("geoip_country_code"), NULL,
-      ngx_http_geoip_country_variable,
-      (uintptr_t) GeoIP_country_code_by_ipnum, 0, 0 },
+      ngx_http_geoip_country_code_variable,
+      0, 0, 0 },
 
     { ngx_string("geoip_country_code3"), NULL,
-      ngx_http_geoip_country_variable,
-      (uintptr_t) GeoIP_country_code3_by_ipnum, 0, 0 },
+      ngx_http_geoip_country_code3_variable,
+      0, 0, 0 },
 
     { ngx_string("geoip_country_name"), NULL,
-      ngx_http_geoip_country_variable,
-      (uintptr_t) GeoIP_country_name_by_ipnum, 0, 0 },
+      ngx_http_geoip_country_name_variable,
+      0, 0, 0 },
 
     { ngx_string("geoip_org"), NULL,
       ngx_http_geoip_org_variable,
@@ -180,117 +188,146 @@ static ngx_http_variable_t  ngx_http_geo
 };
 
 
-static u_long
-ngx_http_geoip_addr(ngx_http_request_t *r)
+static ngx_int_t
+ngx_http_geoip_get_char(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+    GeoIP *src, ngx_http_geoip_variable_handler_pt handler,
+    GeoIP *src_v6, ngx_http_geoip_variable_handler6_pt handler_v6)
 {
-    struct sockaddr_in   *sin;
-#if (NGX_HAVE_INET6)
-    u_char               *p;
-    u_long                addr;
-    struct sockaddr_in6  *sin6;
-#endif
-
+    const char *ret = NULL;
     switch (r->connection->sockaddr->sa_family) {
-
     case AF_INET:
-        sin = (struct sockaddr_in *) r->connection->sockaddr;
-        return ntohl(sin->sin_addr.s_addr);
-
+        if (src) {
+            struct sockaddr_in *sin = (struct sockaddr_in *) r->connection->sockaddr;
+            ret = handler(src, ntohl(sin->sin_addr.s_addr));
+        }
+        break;
 #if (NGX_HAVE_INET6)
-
     case AF_INET6:
-        sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
-
-        if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
-            p = sin6->sin6_addr.s6_addr;
-            addr = p[12] << 24;
-            addr += p[13] << 16;
-            addr += p[14] << 8;
-            addr += p[15];
-
-            return addr;
+        if (src_v6) {
+            struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
+            if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+                u_char *p = sin6->sin6_addr.s6_addr;
+                ret = src ? handler(src, (p[12] << 24) + (p[13] << 16) + (p[14] << 8) + p[15]) : NULL;
+            } else
+                ret = handler_v6(src_v6, sin6->sin6_addr);
         }
-
+        break;
 #endif
     }
 
-    return INADDR_NONE;
+    if (ret == NULL) {
+        goto not_found;
+    }
+
+    v->len = ngx_strlen(ret);
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
+    v->data = (u_char *) ret;
+
+    return NGX_OK;
+
+not_found:
+
+    v->not_found = 1;
+
+    return NGX_OK;
 }
 
 
 static ngx_int_t
-ngx_http_geoip_country_variable(ngx_http_request_t *r,
+ngx_http_geoip_country_code_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_variable_handler_pt) data;
-
-    const char             *val;
     ngx_http_geoip_conf_t  *gcf;
 
     gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
 
-    if (gcf->country == NULL) {
-        goto not_found;
+    if (gcf->country == NULL && gcf->country_v6 == NULL) {
+        v->not_found = 1;
+
+        return NGX_OK;
     }
 
-    val = handler(gcf->country, ngx_http_geoip_addr(r));
+    return ngx_http_geoip_get_char(r, v, gcf->country, &GeoIP_country_code_by_ipnum,
+#if (NGX_HAVE_INET6)
+                     gcf->country_v6, &GeoIP_country_code_by_ipnum_v6
+#else
+                     NULL, NULL
+#endif
+                     );
+}
 
-    if (val == NULL) {
-        goto not_found;
-    }
 
-    v->len = ngx_strlen(val);
-    v->valid = 1;
-    v->no_cacheable = 0;
-    v->not_found = 0;
-    v->data = (u_char *) val;
+static ngx_int_t
+ngx_http_geoip_country_code3_variable(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    ngx_http_geoip_conf_t  *gcf;
 
-    return NGX_OK;
+    gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
 
-not_found:
+    if (gcf->country == NULL && gcf->country_v6 == NULL) {
+        v->not_found = 1;
 
-    v->not_found = 1;
+        return NGX_OK;
+    }
 
-    return NGX_OK;
+    return ngx_http_geoip_get_char(r, v, gcf->country, &GeoIP_country_code3_by_ipnum,
+#if (NGX_HAVE_INET6)
+                     gcf->country_v6, &GeoIP_country_code3_by_ipnum_v6
+#else
+                     NULL, NULL
+#endif
+                     );
 }
 
 
 static ngx_int_t
-ngx_http_geoip_org_variable(ngx_http_request_t *r,
+ngx_http_geoip_country_name_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_variable_handler_pt) data;
-
-    const char             *val;
     ngx_http_geoip_conf_t  *gcf;
 
     gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
 
-    if (gcf->org == NULL) {
-        goto not_found;
+    if (gcf->country == NULL && gcf->country_v6 == NULL) {
+        v->not_found = 1;
+
+        return NGX_OK;
     }
 
-    val = handler(gcf->org, ngx_http_geoip_addr(r));
+    return ngx_http_geoip_get_char(r, v, gcf->country, &GeoIP_country_name_by_ipnum,
+#if (NGX_HAVE_INET6)
+                     gcf->country_v6, &GeoIP_country_name_by_ipnum_v6
+#else
+                     NULL, NULL
+#endif
+                     );
+}
 
-    if (val == NULL) {
-        goto not_found;
-    }
 
-    v->len = ngx_strlen(val);
-    v->valid = 1;
-    v->no_cacheable = 0;
-    v->not_found = 0;
-    v->data = (u_char *) val;
+static ngx_int_t
+ngx_http_geoip_org_variable(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    ngx_http_geoip_conf_t  *gcf;
 
-    return NGX_OK;
+    gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
 
-not_found:
+    if (gcf->org == NULL && gcf->org_v6 == NULL) {
+        v->not_found = 1;
 
-    v->not_found = 1;
+        return NGX_OK;
+    }
 
-    return NGX_OK;
+    return ngx_http_geoip_get_char(r, v, gcf->org, (ngx_http_geoip_variable_handler_pt)&GeoIP_name_by_ipnum,
+#if (NGX_HAVE_INET6)
+                     gcf->org_v6, (ngx_http_geoip_variable_handler6_pt)&GeoIP_name_by_ipnum_v6
+#else
+                     NULL, NULL
+#endif
+                     );
 }
 
 
@@ -451,8 +488,26 @@ ngx_http_geoip_get_city_record(ngx_http_
 
     gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
 
-    if (gcf->city) {
-        return GeoIP_record_by_ipnum(gcf->city, ngx_http_geoip_addr(r));
+    switch (r->connection->sockaddr->sa_family) {
+    case AF_INET:
+        if (gcf->city) {
+            struct sockaddr_in *sin = (struct sockaddr_in *) r->connection->sockaddr;
+            return GeoIP_record_by_ipnum(gcf->city, ntohl(sin->sin_addr.s_addr));
+        }
+        return NULL;
+#if (NGX_HAVE_INET6)
+    case AF_INET6:
+        if (gcf->city_v6) {
+            struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
+            if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+                u_char *p = sin6->sin6_addr.s6_addr;
+                return gcf->city ? GeoIP_record_by_ipnum(gcf->city,
+                             (p[12] << 24) + (p[13] << 16) + (p[14] << 8) + p[15]) : NULL;
+            } else
+                return GeoIP_record_by_ipnum_v6(gcf->city_v6, sin6->sin6_addr);
+        }
+        return NULL;
+#endif
     }
 
     return NULL;
@@ -505,18 +560,19 @@ static char *
 ngx_http_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     ngx_http_geoip_conf_t  *gcf = conf;
+    GeoIP *tmp;
 
     ngx_str_t  *value;
 
-    if (gcf->country) {
+    if (gcf->country && gcf->country_v6) {
         return "is duplicate";
     }
 
     value = cf->args->elts;
 
-    gcf->country = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
+    tmp = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
 
-    if (gcf->country == NULL) {
+    if (tmp == NULL) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "GeoIP_open(\"%V\") failed", &value[1]);
 
@@ -525,7 +581,7 @@ ngx_http_geoip_country(ngx_conf_t *cf, n
 
     if (cf->args->nelts == 3) {
         if (ngx_strcmp(value[2].data, "utf8") == 0) {
-            GeoIP_set_charset (gcf->country, GEOIP_CHARSET_UTF8);
+            GeoIP_set_charset (tmp, GEOIP_CHARSET_UTF8);
 
         } else {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
@@ -534,18 +590,33 @@ ngx_http_geoip_country(ngx_conf_t *cf, n
         }
     }
 
-    switch (gcf->country->databaseType) {
+    switch (tmp->databaseType) {
 
     case GEOIP_COUNTRY_EDITION:
     case GEOIP_PROXY_EDITION:
     case GEOIP_NETSPEED_EDITION:
+        if (gcf->country) {
+            GeoIP_delete(tmp);
+            return "is duplicate";
+        } else
+            gcf->country = tmp;
+        return NGX_CONF_OK;
 
+#if (NGX_HAVE_INET6)
+    case GEOIP_COUNTRY_EDITION_V6:
+        if (gcf->country_v6) {
+            GeoIP_delete(tmp);
+            return "is duplicate";
+        } else
+            gcf->country_v6 = tmp;
         return NGX_CONF_OK;
+#endif
 
     default:
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "invalid GeoIP database \"%V\" type:%d",
-                           &value[1], gcf->country->databaseType);
+                           &value[1], tmp->databaseType);
+        GeoIP_delete(tmp);
         return NGX_CONF_ERROR;
     }
 }
@@ -555,18 +626,19 @@ static char *
 ngx_http_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     ngx_http_geoip_conf_t  *gcf = conf;
+    GeoIP *tmp;
 
     ngx_str_t  *value;
 
-    if (gcf->org) {
+    if (gcf->org && gcf->org_v6) {
         return "is duplicate";
     }
 
     value = cf->args->elts;
 
-    gcf->org = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
+    tmp = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
 
-    if (gcf->org == NULL) {
+    if (tmp == NULL) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "GeoIP_open(\"%V\") failed", &value[1]);
 
@@ -575,7 +647,7 @@ ngx_http_geoip_org(ngx_conf_t *cf, ngx_c
 
     if (cf->args->nelts == 3) {
         if (ngx_strcmp(value[2].data, "utf8") == 0) {
-            GeoIP_set_charset (gcf->org, GEOIP_CHARSET_UTF8);
+            GeoIP_set_charset (tmp, GEOIP_CHARSET_UTF8);
 
         } else {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
@@ -584,19 +656,37 @@ ngx_http_geoip_org(ngx_conf_t *cf, ngx_c
         }
     }
 
-    switch (gcf->org->databaseType) {
+    switch (tmp->databaseType) {
 
     case GEOIP_ISP_EDITION:
     case GEOIP_ORG_EDITION:
     case GEOIP_DOMAIN_EDITION:
     case GEOIP_ASNUM_EDITION:
+        if (gcf->org) {
+            GeoIP_delete(tmp);
+            return "is duplicate";
+        } else
+            gcf->org = tmp;
+        return NGX_CONF_OK;
 
+#if (NGX_HAVE_INET6)
+    case GEOIP_ISP_EDITION_V6:
+    case GEOIP_ORG_EDITION_V6:
+    case GEOIP_DOMAIN_EDITION_V6:
+    case GEOIP_ASNUM_EDITION_V6:
+        if (gcf->org_v6) {
+            GeoIP_delete(tmp);
+            return "is duplicate";
+        } else
+            gcf->org_v6 = tmp;
         return NGX_CONF_OK;
+#endif
 
     default:
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "invalid GeoIP database \"%V\" type:%d",
-                           &value[1], gcf->org->databaseType);
+                           &value[1], tmp->databaseType);
+        GeoIP_delete(tmp);
         return NGX_CONF_ERROR;
     }
 }
@@ -606,18 +696,19 @@ static char *
 ngx_http_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     ngx_http_geoip_conf_t  *gcf = conf;
+    GeoIP *tmp;
 
     ngx_str_t  *value;
 
-    if (gcf->city) {
+    if (gcf->city && gcf->city_v6) {
         return "is duplicate";
     }
 
     value = cf->args->elts;
 
-    gcf->city = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
+    tmp = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
 
-    if (gcf->city == NULL) {
+    if (tmp == NULL) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "GeoIP_open(\"%V\") failed", &value[1]);
 
@@ -626,7 +717,7 @@ ngx_http_geoip_city(ngx_conf_t *cf, ngx_
 
     if (cf->args->nelts == 3) {
         if (ngx_strcmp(value[2].data, "utf8") == 0) {
-            GeoIP_set_charset (gcf->city, GEOIP_CHARSET_UTF8);
+            GeoIP_set_charset (tmp, GEOIP_CHARSET_UTF8);
 
         } else {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
@@ -635,17 +726,32 @@ ngx_http_geoip_city(ngx_conf_t *cf, ngx_
         }
     }
 
-    switch (gcf->city->databaseType) {
+    switch (tmp->databaseType) {
 
     case GEOIP_CITY_EDITION_REV0:
     case GEOIP_CITY_EDITION_REV1:
+        if (gcf->city) {
+            GeoIP_delete(tmp);
+            return "is duplicate";
+        } else
+            gcf->city = tmp;
+        return NGX_CONF_OK;
 
+#if (NGX_HAVE_INET6)
+    case GEOIP_CITY_EDITION_REV0_V6:
+    case GEOIP_CITY_EDITION_REV1_V6:
+        if (gcf->city_v6) {
+            GeoIP_delete(tmp);
+            return "is duplicate";
+        } else
+            gcf->city_v6 = tmp;
         return NGX_CONF_OK;
+#endif
 
     default:
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "invalid GeoIP City database \"%V\" type:%d",
-                           &value[1], gcf->city->databaseType);
+                           &value[1], tmp->databaseType);
         return NGX_CONF_ERROR;
     }
 }
@@ -659,12 +765,21 @@ ngx_http_geoip_cleanup(void *data)
     if (gcf->country) {
         GeoIP_delete(gcf->country);
     }
+    if (gcf->country_v6) {
+        GeoIP_delete(gcf->country_v6);
+    }
 
     if (gcf->org) {
         GeoIP_delete(gcf->org);
     }
+    if (gcf->org_v6) {
+        GeoIP_delete(gcf->org_v6);
+    }
 
     if (gcf->city) {
         GeoIP_delete(gcf->city);
     }
+    if (gcf->city_v6) {
+        GeoIP_delete(gcf->city_v6);
+    }
 }



More information about the nginx mailing list