From d4ba06c31a09fd82a1a2b8046384b6c136df0aae Mon Sep 17 00:00:00 2001 From: Ruslan Ermilov Date: Mon, 14 May 2012 14:00:17 +0000 Subject: [PATCH] geoip: trusted proxies support and partial IPv6 support. The module now supports recursive search of client address through the chain of trusted proxies (closes #100), in the same scope as the geo module. Proxies are listed by the "geoip_proxy" directive, recursive search is enabled by the "geoip_proxy_recursive" directive. IPv6 is partially supported: proxies may be specified with IPv6 addresses. Example: geoip_country .../GeoIP.dat; geoip_proxy 127.0.0.1; geoip_proxy ::1; geoip_proxy 10.0.0.0/8; geoip_proxy_recursive on; --- src/http/modules/ngx_http_geoip_module.c | 174 ++++++++++++++++++----- 1 file changed, 137 insertions(+), 37 deletions(-) diff --git a/src/http/modules/ngx_http_geoip_module.c b/src/http/modules/ngx_http_geoip_module.c index 60d67574d..abc79cdfe 100644 --- a/src/http/modules/ngx_http_geoip_module.c +++ b/src/http/modules/ngx_http_geoip_module.c @@ -14,20 +14,24 @@ typedef struct { - GeoIP *country; - GeoIP *org; - GeoIP *city; + GeoIP *country; + GeoIP *org; + GeoIP *city; + ngx_array_t *proxies; /* array of ngx_cidr_t */ + ngx_flag_t proxy_recursive; } ngx_http_geoip_conf_t; typedef struct { - ngx_str_t *name; - uintptr_t data; + 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); +static u_long ngx_http_geoip_addr(ngx_http_request_t *r, + ngx_http_geoip_conf_t *gcf); 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, @@ -44,12 +48,17 @@ static GeoIPRecord *ngx_http_geoip_get_city_record(ngx_http_request_t *r); 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); static char *ngx_http_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +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, + ngx_cidr_t *cidr); static void ngx_http_geoip_cleanup(void *data); @@ -76,6 +85,20 @@ static ngx_command_t ngx_http_geoip_commands[] = { 0, NULL }, + { ngx_string("geoip_proxy"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, + ngx_http_geoip_proxy, + NGX_HTTP_MAIN_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("geoip_proxy_recursive"), + NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_MAIN_CONF_OFFSET, + offsetof(ngx_http_geoip_conf_t, proxy_recursive), + NULL }, + ngx_null_command }; @@ -85,7 +108,7 @@ static ngx_http_module_t ngx_http_geoip_module_ctx = { NULL, /* postconfiguration */ ngx_http_geoip_create_conf, /* create main configuration */ - NULL, /* init main configuration */ + ngx_http_geoip_init_conf, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ @@ -182,40 +205,44 @@ static ngx_http_variable_t ngx_http_geoip_vars[] = { static u_long -ngx_http_geoip_addr(ngx_http_request_t *r) +ngx_http_geoip_addr(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf) { - struct sockaddr_in *sin; -#if (NGX_HAVE_INET6) - u_char *p; - u_long addr; - struct sockaddr_in6 *sin6; -#endif + ngx_addr_t addr; + ngx_table_elt_t *xfwd; + struct sockaddr_in *sin; - switch (r->connection->sockaddr->sa_family) { + addr.sockaddr = r->connection->sockaddr; + addr.socklen = r->connection->socklen; + /* addr.name = r->connection->addr_text; */ - case AF_INET: - sin = (struct sockaddr_in *) r->connection->sockaddr; - return ntohl(sin->sin_addr.s_addr); + xfwd = r->headers_in.x_forwarded_for; -#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; - } - -#endif + if (xfwd != NULL && gcf->proxies != NULL) { + (void) ngx_http_get_forwarded_addr(r, &addr, xfwd->value.data, + xfwd->value.len, gcf->proxies, + gcf->proxy_recursive); } - return INADDR_NONE; +#if (NGX_HAVE_INET6) + + if (addr.sockaddr->sa_family == AF_INET6) { + struct in6_addr *inaddr6; + + inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; + + if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { + return ntohl(*(in_addr_t *) &inaddr6->s6_addr[12]); + } + } + +#endif + + if (addr.sockaddr->sa_family != AF_INET) { + return INADDR_NONE; + } + + sin = (struct sockaddr_in *) addr.sockaddr; + return ntohl(sin->sin_addr.s_addr); } @@ -235,7 +262,7 @@ ngx_http_geoip_country_variable(ngx_http_request_t *r, goto not_found; } - val = handler(gcf->country, ngx_http_geoip_addr(r)); + val = handler(gcf->country, ngx_http_geoip_addr(r, gcf)); if (val == NULL) { goto not_found; @@ -273,7 +300,7 @@ ngx_http_geoip_org_variable(ngx_http_request_t *r, goto not_found; } - val = handler(gcf->org, ngx_http_geoip_addr(r)); + val = handler(gcf->org, ngx_http_geoip_addr(r, gcf)); if (val == NULL) { goto not_found; @@ -453,7 +480,7 @@ ngx_http_geoip_get_city_record(ngx_http_request_t *r) 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)); + return GeoIP_record_by_ipnum(gcf->city, ngx_http_geoip_addr(r, gcf)); } return NULL; @@ -490,6 +517,8 @@ ngx_http_geoip_create_conf(ngx_conf_t *cf) return NULL; } + conf->proxy_recursive = NGX_CONF_UNSET; + cln = ngx_pool_cleanup_add(cf->pool, 0); if (cln == NULL) { return NULL; @@ -502,6 +531,17 @@ ngx_http_geoip_create_conf(ngx_conf_t *cf) } +static char * +ngx_http_geoip_init_conf(ngx_conf_t *cf, void *conf) +{ + ngx_http_geoip_conf_t *gcf = conf; + + ngx_conf_init_value(gcf->proxy_recursive, 0); + + return NGX_CONF_OK; +} + + static char * ngx_http_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { @@ -652,6 +692,66 @@ ngx_http_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } +static char * +ngx_http_geoip_proxy(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_geoip_conf_t *gcf = conf; + + ngx_str_t *value; + ngx_cidr_t cidr, *c; + + value = cf->args->elts; + + if (ngx_http_geoip_cidr_value(cf, &value[1], &cidr) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (gcf->proxies == NULL) { + gcf->proxies = ngx_array_create(cf->pool, 4, sizeof(ngx_cidr_t)); + if (gcf->proxies == NULL) { + return NGX_CONF_ERROR; + } + } + + c = ngx_array_push(gcf->proxies); + if (c == NULL) { + return NGX_CONF_ERROR; + } + + *c = cidr; + + 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) +{ + ngx_int_t rc; + + if (ngx_strcmp(net->data, "255.255.255.255") == 0) { + cidr->family = AF_INET; + cidr->u.in.addr = 0xffffffff; + cidr->u.in.mask = 0xffffffff; + + return NGX_OK; + } + + rc = ngx_ptocidr(net, cidr); + + if (rc == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net); + return NGX_ERROR; + } + + if (rc == NGX_DONE) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "low address bits of %V are meaningless", net); + } + + return NGX_OK; +} + + static void ngx_http_geoip_cleanup(void *data) {