# HG changeset patch # User Ruslan Ermilov # Date 1337004017 0 # Node ID 3152e4c371d7df171e3e7848e63c67b807b2bdc6 # Parent d0db70f4c13b6ac738802361eb6b310cc33725aa 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; 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 @@ -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_c 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_com 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 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_geo 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; + + addr.sockaddr = r->connection->sockaddr; + addr.socklen = r->connection->socklen; + /* addr.name = r->connection->addr_text; */ - switch (r->connection->sockaddr->sa_family) { + xfwd = r->headers_in.x_forwarded_for; - case AF_INET: - sin = (struct sockaddr_in *) r->connection->sockaddr; - return ntohl(sin->sin_addr.s_addr); + 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); + } #if (NGX_HAVE_INET6) - case AF_INET6: - sin6 = (struct sockaddr_in6 *) r->connection->sockaddr; + if (addr.sockaddr->sa_family == AF_INET6) { + struct in6_addr *inaddr6; + + inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; - 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 (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; } - 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 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_req 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_ 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 *c return NULL; } + conf->proxy_recursive = NGX_CONF_UNSET; + cln = ngx_pool_cleanup_add(cf->pool, 0); if (cln == NULL) { return NULL; @@ -503,6 +532,17 @@ ngx_http_geoip_create_conf(ngx_conf_t *c 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) { ngx_http_geoip_conf_t *gcf = conf; @@ -652,6 +692,66 @@ ngx_http_geoip_city(ngx_conf_t *cf, ngx_ } +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) {