Mercurial > hg > nginx-vendor-current
diff src/http/modules/ngx_http_geo_module.c @ 694:88a1b4797f2e NGINX_1_3_10
nginx 1.3.10
*) Change: domain names specified in configuration file are now resolved
to IPv6 addresses as well as IPv4 ones.
*) Change: now if the "include" directive with mask is used on Unix
systems, included files are sorted in alphabetical order.
*) Change: the "add_header" directive adds headers to 201 responses.
*) Feature: the "geo" directive now supports IPv6 addresses in CIDR
notation.
*) Feature: the "flush" and "gzip" parameters of the "access_log"
directive.
*) Feature: variables support in the "auth_basic" directive.
*) Bugfix: nginx could not be built with the ngx_http_perl_module in
some cases.
*) Bugfix: a segmentation fault might occur in a worker process if the
ngx_http_xslt_module was used.
*) Bugfix: nginx could not be built on MacOSX in some cases.
Thanks to Piotr Sikora.
*) Bugfix: the "limit_rate" directive with high rates might result in
truncated responses on 32-bit platforms.
Thanks to Alexey Antropov.
*) Bugfix: a segmentation fault might occur in a worker process if the
"if" directive was used.
Thanks to Piotr Sikora.
*) Bugfix: a "100 Continue" response was issued with "413 Request Entity
Too Large" responses.
*) Bugfix: the "image_filter", "image_filter_jpeg_quality" and
"image_filter_sharpen" directives might be inherited incorrectly.
Thanks to Ian Babrou.
*) Bugfix: "crypt_r() failed" errors might appear if the "auth_basic"
directive was used on Linux.
*) Bugfix: in backup servers handling.
Thanks to Thomas Chen.
*) Bugfix: proxied HEAD requests might return incorrect response if the
"gzip" directive was used.
author | Igor Sysoev <http://sysoev.ru> |
---|---|
date | Tue, 25 Dec 2012 00:00:00 +0400 |
parents | 2e8a942c8872 |
children |
line wrap: on
line diff
--- a/src/http/modules/ngx_http_geo_module.c +++ b/src/http/modules/ngx_http_geo_module.c @@ -18,6 +18,14 @@ typedef struct { typedef struct { + ngx_radix_tree_t *tree; +#if (NGX_HAVE_INET6) + ngx_radix_tree_t *tree6; +#endif +} ngx_http_geo_trees_t; + + +typedef struct { ngx_http_geo_range_t **low; ngx_http_variable_value_t *default_value; } ngx_http_geo_high_ranges_t; @@ -35,6 +43,9 @@ typedef struct { ngx_str_t *net; ngx_http_geo_high_ranges_t high; ngx_radix_tree_t *tree; +#if (NGX_HAVE_INET6) + ngx_radix_tree_t *tree6; +#endif ngx_rbtree_t rbtree; ngx_rbtree_node_t sentinel; ngx_array_t *proxies; @@ -57,7 +68,7 @@ typedef struct { typedef struct { union { - ngx_radix_tree_t *tree; + ngx_http_geo_trees_t trees; ngx_http_geo_high_ranges_t high; } u; @@ -68,8 +79,8 @@ typedef struct { } ngx_http_geo_ctx_t; -static in_addr_t ngx_http_geo_addr(ngx_http_request_t *r, - ngx_http_geo_ctx_t *ctx); +static ngx_int_t ngx_http_geo_addr(ngx_http_request_t *r, + ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr); static ngx_int_t ngx_http_geo_real_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr); static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -155,7 +166,7 @@ static ngx_http_geo_header_t ngx_http_g }; -/* AF_INET only */ +/* geo range is AF_INET only */ static ngx_int_t ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, @@ -163,10 +174,56 @@ ngx_http_geo_cidr_variable(ngx_http_requ { ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data; + in_addr_t inaddr; + ngx_addr_t addr; + struct sockaddr_in *sin; ngx_http_variable_value_t *vv; +#if (NGX_HAVE_INET6) + u_char *p; + struct in6_addr *inaddr6; +#endif + + if (ngx_http_geo_addr(r, ctx, &addr) != NGX_OK) { + vv = (ngx_http_variable_value_t *) + ngx_radix32tree_find(ctx->u.trees.tree, INADDR_NONE); + goto done; + } + + switch (addr.sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; + p = inaddr6->s6_addr; - vv = (ngx_http_variable_value_t *) - ngx_radix32tree_find(ctx->u.tree, ngx_http_geo_addr(r, ctx)); + if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { + inaddr = p[12] << 24; + inaddr += p[13] << 16; + inaddr += p[14] << 8; + inaddr += p[15]; + + vv = (ngx_http_variable_value_t *) + ngx_radix32tree_find(ctx->u.trees.tree, inaddr); + + } else { + vv = (ngx_http_variable_value_t *) + ngx_radix128tree_find(ctx->u.trees.tree6, p); + } + + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) addr.sockaddr; + inaddr = ntohl(sin->sin_addr.s_addr); + + vv = (ngx_http_variable_value_t *) + ngx_radix32tree_find(ctx->u.trees.tree, inaddr); + + break; + } + +done: *v = *vv; @@ -183,25 +240,65 @@ ngx_http_geo_range_variable(ngx_http_req { ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data; - in_addr_t addr; + in_addr_t inaddr; + ngx_addr_t addr; ngx_uint_t n; + struct sockaddr_in *sin; ngx_http_geo_range_t *range; +#if (NGX_HAVE_INET6) + u_char *p; + struct in6_addr *inaddr6; +#endif *v = *ctx->u.high.default_value; - addr = ngx_http_geo_addr(r, ctx); + if (ngx_http_geo_addr(r, ctx, &addr) == NGX_OK) { + + switch (addr.sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; + + if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { + p = inaddr6->s6_addr; - range = ctx->u.high.low[addr >> 16]; + inaddr = p[12] << 24; + inaddr += p[13] << 16; + inaddr += p[14] << 8; + inaddr += p[15]; + + } else { + inaddr = INADDR_NONE; + } + + break; +#endif - if (range) { - n = addr & 0xffff; - do { - if (n >= (ngx_uint_t) range->start && n <= (ngx_uint_t) range->end) - { - *v = *range->value; - break; - } - } while ((++range)->value); + default: /* AF_INET */ + sin = (struct sockaddr_in *) addr.sockaddr; + inaddr = ntohl(sin->sin_addr.s_addr); + break; + } + + } else { + inaddr = INADDR_NONE; + } + + if (ctx->u.high.low) { + range = ctx->u.high.low[inaddr >> 16]; + + if (range) { + n = inaddr & 0xffff; + do { + if (n >= (ngx_uint_t) range->start + && n <= (ngx_uint_t) range->end) + { + *v = *range->value; + break; + } + } while ((++range)->value); + } } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -211,54 +308,25 @@ ngx_http_geo_range_variable(ngx_http_req } -static in_addr_t -ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx) +static ngx_int_t +ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx, + ngx_addr_t *addr) { - ngx_addr_t addr; - ngx_table_elt_t *xfwd; - struct sockaddr_in *sin; + ngx_table_elt_t *xfwd; - if (ngx_http_geo_real_addr(r, ctx, &addr) != NGX_OK) { - return INADDR_NONE; + if (ngx_http_geo_real_addr(r, ctx, addr) != NGX_OK) { + return NGX_ERROR; } xfwd = r->headers_in.x_forwarded_for; if (xfwd != NULL && ctx->proxies != NULL) { - (void) ngx_http_get_forwarded_addr(r, &addr, xfwd->value.data, + (void) ngx_http_get_forwarded_addr(r, addr, xfwd->value.data, xfwd->value.len, ctx->proxies, ctx->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 = 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); + return NGX_OK; } @@ -303,7 +371,6 @@ static char * ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *rv; - void **p; size_t len; ngx_str_t *value, name; ngx_uint_t i; @@ -313,6 +380,9 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c ngx_http_variable_t *var; ngx_http_geo_ctx_t *geo; ngx_http_geo_conf_ctx_t ctx; +#if (NGX_HAVE_INET6) + static struct in6_addr zero; +#endif value = cf->args->elts; @@ -322,6 +392,13 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c } name = value[1]; + + if (name.data[0] != '$') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"%V\"", &name); + return NGX_CONF_ERROR; + } + name.len--; name.data++; @@ -333,6 +410,13 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c } name = value[2]; + + if (name.data[0] != '$') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"%V\"", &name); + return NGX_CONF_ERROR; + } + name.len--; name.data++; @@ -378,9 +462,9 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c geo->proxies = ctx.proxies; geo->proxy_recursive = ctx.proxy_recursive; - if (ctx.high.low) { + if (ctx.ranges) { - if (!ctx.binary_include) { + if (ctx.high.low && !ctx.binary_include) { for (i = 0; i < 0x10000; i++) { a = (ngx_array_t *) ctx.high.low[i]; @@ -395,8 +479,8 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c return NGX_CONF_ERROR; } - p = (void **) ngx_cpymem(ctx.high.low[i], a->elts, len); - *p = NULL; + ngx_memcpy(ctx.high.low[i], a->elts, len); + ctx.high.low[i][a->nelts].value = NULL; ctx.data_size += len + sizeof(void *); } @@ -429,7 +513,18 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c } } - geo->u.tree = ctx.tree; + geo->u.trees.tree = ctx.tree; + +#if (NGX_HAVE_INET6) + if (ctx.tree6 == NULL) { + ctx.tree6 = ngx_radix_tree_create(cf->pool, -1); + if (ctx.tree6 == NULL) { + return NGX_CONF_ERROR; + } + } + + geo->u.trees.tree6 = ctx.tree6; +#endif var->get_handler = ngx_http_geo_cidr_variable; var->data = (uintptr_t) geo; @@ -437,16 +532,23 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c ngx_destroy_pool(ctx.temp_pool); ngx_destroy_pool(pool); - if (ngx_radix32tree_find(ctx.tree, 0) != NGX_RADIX_NO_VALUE) { - return rv; - } - if (ngx_radix32tree_insert(ctx.tree, 0, 0, (uintptr_t) &ngx_http_variable_null_value) == NGX_ERROR) { return NGX_CONF_ERROR; } + + /* NGX_BUSY is okay (default was set explicitly) */ + +#if (NGX_HAVE_INET6) + if (ngx_radix128tree_insert(ctx.tree6, zero.s6_addr, zero.s6_addr, + (uintptr_t) &ngx_http_variable_null_value) + == NGX_ERROR) + { + return NGX_CONF_ERROR; + } +#endif } return rv; @@ -469,7 +571,12 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command if (ngx_strcmp(value[0].data, "ranges") == 0) { - if (ctx->tree) { + if (ctx->tree +#if (NGX_HAVE_INET6) + || ctx->tree6 +#endif + ) + { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "the \"ranges\" directive must be " "the first directive inside \"geo\" block"); @@ -920,8 +1027,17 @@ ngx_http_geo_cidr(ngx_conf_t *cf, ngx_ht } } +#if (NGX_HAVE_INET6) + if (ctx->tree6 == NULL) { + ctx->tree6 = ngx_radix_tree_create(ctx->pool, -1); + if (ctx->tree6 == NULL) { + return NGX_CONF_ERROR; + } + } +#endif + if (ngx_strcmp(value[0].data, "default") == 0) { - /* cidr.family = AF_INET; */ + cidr.family = AF_INET; cidr.u.in.addr = 0; cidr.u.in.mask = 0; net = &value[0]; @@ -940,20 +1056,29 @@ ngx_http_geo_cidr(ngx_conf_t *cf, ngx_ht return NGX_CONF_ERROR; } - if (cidr.family != AF_INET) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"geo\" supports IPv4 only"); - return NGX_CONF_ERROR; + if (cidr.family == AF_INET) { + cidr.u.in.addr = ntohl(cidr.u.in.addr); + cidr.u.in.mask = ntohl(cidr.u.in.mask); } - cidr.u.in.addr = ntohl(cidr.u.in.addr); - cidr.u.in.mask = ntohl(cidr.u.in.mask); + if (del) { + switch (cidr.family) { - if (del) { - if (ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr, - cidr.u.in.mask) - != NGX_OK) - { +#if (NGX_HAVE_INET6) + case AF_INET6: + rc = ngx_radix128tree_delete(ctx->tree6, + cidr.u.in6.addr.s6_addr, + cidr.u.in6.mask.s6_addr); + break; +#endif + + default: /* AF_INET */ + rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr, + cidr.u.in.mask); + break; + } + + if (rc != NGX_OK) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "no network \"%V\" to delete", net); } @@ -968,32 +1093,78 @@ ngx_http_geo_cidr(ngx_conf_t *cf, ngx_ht return NGX_CONF_ERROR; } - for (i = 2; i; i--) { - rc = ngx_radix32tree_insert(ctx->tree, cidr.u.in.addr, cidr.u.in.mask, - (uintptr_t) val); - if (rc == NGX_OK) { - return NGX_CONF_OK; - } + switch (cidr.family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + for (i = 2; i; i--) { + rc = ngx_radix128tree_insert(ctx->tree6, cidr.u.in6.addr.s6_addr, + cidr.u.in6.mask.s6_addr, + (uintptr_t) val); + + if (rc == NGX_OK) { + return NGX_CONF_OK; + } + + if (rc == NGX_ERROR) { + return NGX_CONF_ERROR; + } - if (rc == NGX_ERROR) { - return NGX_CONF_ERROR; + /* rc == NGX_BUSY */ + + old = (ngx_http_variable_value_t *) + ngx_radix128tree_find(ctx->tree6, + cidr.u.in6.addr.s6_addr); + + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "duplicate network \"%V\", value: \"%v\", old value: \"%v\"", + net, val, old); + + rc = ngx_radix128tree_delete(ctx->tree6, + cidr.u.in6.addr.s6_addr, + cidr.u.in6.mask.s6_addr); + + if (rc == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree"); + return NGX_CONF_ERROR; + } } - /* rc == NGX_BUSY */ + break; +#endif + + default: /* AF_INET */ + for (i = 2; i; i--) { + rc = ngx_radix32tree_insert(ctx->tree, cidr.u.in.addr, + cidr.u.in.mask, (uintptr_t) val); - old = (ngx_http_variable_value_t *) - ngx_radix32tree_find(ctx->tree, cidr.u.in.addr & cidr.u.in.mask); + if (rc == NGX_OK) { + return NGX_CONF_OK; + } + + if (rc == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + /* rc == NGX_BUSY */ - ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "duplicate network \"%V\", value: \"%v\", old value: \"%v\"", - net, val, old); + old = (ngx_http_variable_value_t *) + ngx_radix32tree_find(ctx->tree, cidr.u.in.addr); + + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "duplicate network \"%V\", value: \"%v\", old value: \"%v\"", + net, val, old); - rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr, cidr.u.in.mask); + rc = ngx_radix32tree_delete(ctx->tree, + cidr.u.in.addr, cidr.u.in.mask); - if (rc == NGX_ERROR) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree"); - return NGX_CONF_ERROR; + if (rc == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree"); + return NGX_CONF_ERROR; + } } + + break; } return NGX_CONF_ERROR;