# HG changeset patch # User Ruslan Ermilov # Date 1336998461 0 # Node ID ed3d0cc6de5ae99cf91a314c51e82d911b3cd122 # Parent 0dfdc3f732cb51f98e2230d99edfca8c882c07e6 New function ngx_http_get_forwarded_addr() to look up real client address. On input it takes an original address, string in the X-Forwarded-For format and its length, list of trusted proxies, and a flag indicating to perform the recursive search. On output it returns NGX_OK and the "deepest" valid address in a chain, or NGX_DECLINED. It supports AF_INET and AF_INET6. Additionally, original address and/or proxy may be specified as AF_UNIX. diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -2699,6 +2699,102 @@ ngx_http_set_disable_symlinks(ngx_http_r } +ngx_int_t +ngx_http_get_forwarded_addr(ngx_http_request_t *r, ngx_addr_t *addr, + u_char *xff, size_t xfflen, ngx_array_t *proxies, int recursive) +{ + u_char *p; + in_addr_t *inaddr; + ngx_addr_t paddr; + ngx_cidr_t *cidr; + ngx_uint_t family, i; +#if (NGX_HAVE_INET6) + ngx_uint_t n; + struct in6_addr *inaddr6; +#endif + + family = addr->sockaddr->sa_family; + + if (family == AF_INET) { + inaddr = &((struct sockaddr_in *) addr->sockaddr)->sin_addr.s_addr; + } + +#if (NGX_HAVE_INET6) + else if (family == AF_INET6) { + inaddr6 = &((struct sockaddr_in6 *) addr->sockaddr)->sin6_addr; + + if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { + family = AF_INET; + inaddr = (in_addr_t *) &inaddr6->s6_addr[12]; + } + } +#endif + + for (cidr = proxies->elts, i = 0; i < proxies->nelts; i++) { + if (cidr[i].family != family) { + goto next; + } + + switch (family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + for (n = 0; n < 16; n++) { + if ((inaddr6->s6_addr[n] & cidr[i].u.in6.mask.s6_addr[n]) + != cidr[i].u.in6.addr.s6_addr[n]) + { + goto next; + } + } + break; +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + break; +#endif + + default: /* AF_INET */ + if ((*inaddr & cidr[i].u.in.mask) != cidr[i].u.in.addr) { + goto next; + } + break; + } + + for (p = xff + xfflen - 1; p > xff; p--, xfflen--) { + if (*p != ' ' && *p != ',') { + break; + } + } + + for ( /* void */ ; p > xff; p--) { + if (*p == ' ' || *p == ',') { + p++; + break; + } + } + + if (ngx_parse_addr(r->pool, &paddr, p, xfflen - (p - xff)) != NGX_OK) { + return NGX_DECLINED; + } + + *addr = paddr; + + if (recursive && p > xff) { + (void) ngx_http_get_forwarded_addr(r, addr, xff, p - 1 - xff, + proxies, 1); + } + + return NGX_OK; + + next: + continue; + } + + return NGX_DECLINED; +} + + static char * ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) { diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -513,6 +513,9 @@ ngx_int_t ngx_http_write_filter(ngx_http ngx_int_t ngx_http_set_disable_symlinks(ngx_http_request_t *r, ngx_http_core_loc_conf_t *clcf, ngx_str_t *path, ngx_open_file_info_t *of); +ngx_int_t ngx_http_get_forwarded_addr(ngx_http_request_t *r, ngx_addr_t *addr, + u_char *xff, size_t xfflen, ngx_array_t *proxies, int recursive); + extern ngx_module_t ngx_http_core_module;