changeset 2992:2465ff3da161

geo module supports trusted proxies
author Igor Sysoev <igor@sysoev.ru>
date Wed, 22 Jul 2009 09:43:14 +0000
parents 6ae141e270ad
children f592d466bbda
files src/http/modules/ngx_http_geo_module.c src/http/ngx_http_request.c src/http/ngx_http_request.h
diffstat 3 files changed, 132 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/src/http/modules/ngx_http_geo_module.c
+++ b/src/http/modules/ngx_http_geo_module.c
@@ -35,6 +35,7 @@ typedef struct {
     ngx_radix_tree_t                *tree;
     ngx_rbtree_t                     rbtree;
     ngx_rbtree_node_t                sentinel;
+    ngx_array_t                     *proxies;
     ngx_pool_t                      *pool;
     ngx_pool_t                      *temp_pool;
 } ngx_http_geo_conf_ctx_t;
@@ -46,12 +47,16 @@ typedef struct {
         ngx_http_geo_high_ranges_t  *high;
     } u;
 
+    ngx_array_t                     *proxies;
+
     ngx_int_t                        index;
 } 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 in_addr_t ngx_http_geo_real_addr(ngx_http_request_t *r,
+    ngx_http_geo_ctx_t *ctx);
 static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
 static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
@@ -64,6 +69,10 @@ static char *ngx_http_geo_cidr(ngx_conf_
     ngx_str_t *value);
 static ngx_http_variable_value_t *ngx_http_geo_value(ngx_conf_t *cf,
     ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value);
+static char *ngx_http_geo_add_proxy(ngx_conf_t *cf,
+    ngx_http_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr);
+static ngx_int_t ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net,
+    ngx_cidr_t *cidr);
 
 
 static ngx_command_t  ngx_http_geo_commands[] = {
@@ -168,6 +177,50 @@ 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)
 {
+    u_char           *p, *ip;
+    size_t            len;
+    in_addr_t         addr;
+    ngx_uint_t        i, n;
+    ngx_in_cidr_t    *proxies;
+    ngx_table_elt_t  *xfwd;
+
+    addr = ngx_http_geo_real_addr(r, ctx);
+
+    xfwd = r->headers_in.x_forwarded_for;
+
+    if (xfwd == NULL || ctx->proxies == NULL) {
+        return addr;
+    }
+
+    proxies = ctx->proxies->elts;
+    n = ctx->proxies->nelts;
+
+    for (i = 0; i < n; i++) {
+        if ((addr & proxies[i].mask) == proxies[i].addr) {
+
+            len = xfwd->value.len;
+            ip = xfwd->value.data;
+
+            for (p = ip + len - 1; p > ip; p--) {
+                if (*p == ' ' || *p == ',') {
+                    p++;
+                    len -= p - ip;
+                    ip = p;
+                    break;
+                }
+            }
+
+            return ntohl(ngx_inet_addr(ip, len));
+        }
+    }
+
+    return addr;
+}
+
+
+static in_addr_t
+ngx_http_geo_real_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx)
+{
     struct sockaddr_in         *sin;
     ngx_http_variable_value_t  *v;
 
@@ -259,6 +312,7 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c
 
     ctx.high = NULL;
     ctx.tree = NULL;
+    ctx.proxies = NULL;
     ctx.pool = cf->pool;
 
     save = *cf;
@@ -271,6 +325,8 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c
 
     *cf = save;
 
+    geo->proxies = ctx.proxies;
+
     if (ctx.high) {
 
         for (i = 0; i < 0x10000; i++) {
@@ -341,6 +397,7 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command
 {
     char                     *rv;
     ngx_str_t                *value, file;
+    ngx_cidr_t                cidr;
     ngx_http_geo_conf_ctx_t  *ctx;
 
     ctx = cf->ctx;
@@ -394,6 +451,16 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command
         rv = ngx_conf_parse(cf, &file);
 
         goto done;
+
+    } else if (ngx_strcmp(value[0].data, "proxy") == 0) {
+
+        if (ngx_http_geo_cidr_value(cf, &value[1], &cidr) != NGX_OK) {
+            goto failed;
+        }
+
+        rv = ngx_http_geo_add_proxy(cf, ctx, &cidr);
+
+        goto done;
     }
 
     if (ctx->high) {
@@ -803,33 +870,8 @@ ngx_http_geo_cidr(ngx_conf_t *cf, ngx_ht
             del = 0;
         }
 
-        if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
-            cidr.u.in.addr = 0xffffffff;
-            cidr.u.in.mask = 0xffffffff;
-
-        } else {
-            rc = ngx_ptocidr(net, &cidr);
-
-            if (rc == NGX_ERROR) {
-                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                                   "invalid network \"%V\"", net);
-                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 (rc == NGX_DONE) {
-                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
-                                   "low address bits of %V are meaningless",
-                                   net);
-            }
-
-            cidr.u.in.addr = ntohl(cidr.u.in.addr);
-            cidr.u.in.mask = ntohl(cidr.u.in.mask);
+        if (ngx_http_geo_cidr_value(cf, net, &cidr) != NGX_OK) {
+            return NGX_CONF_ERROR;
         }
 
         if (del) {
@@ -927,3 +969,64 @@ ngx_http_geo_value(ngx_conf_t *cf, ngx_h
 
     return val;
 }
+
+
+static char *
+ngx_http_geo_add_proxy(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+    ngx_cidr_t *cidr)
+{
+    ngx_in_cidr_t  *c;
+
+    if (ctx->proxies == NULL) {
+        ctx->proxies = ngx_array_create(ctx->pool, 4, sizeof(ngx_in_cidr_t));
+        if (ctx->proxies == NULL) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    c = ngx_array_push(ctx->proxies);
+    if (c == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    c->addr = cidr->u.in.addr;
+    c->mask = cidr->u.in.mask;
+
+    return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_geo_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->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 (cidr->family != AF_INET) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"geo\" supports IPv4 only");
+        return NGX_ERROR;
+    }
+
+    if (rc == NGX_DONE) {
+        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                           "low address bits of %V are meaningless", net);
+    }
+
+    cidr->u.in.addr = ntohl(cidr->u.in.addr);
+    cidr->u.in.mask = ntohl(cidr->u.in.mask);
+
+    return NGX_OK;
+}
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -129,7 +129,7 @@ ngx_http_header_t  ngx_http_headers_in[]
     { ngx_string("Keep-Alive"), offsetof(ngx_http_headers_in_t, keep_alive),
                  ngx_http_process_header_line },
 
-#if (NGX_HTTP_PROXY || NGX_HTTP_REALIP)
+#if (NGX_HTTP_PROXY || NGX_HTTP_REALIP || NGX_HTTP_GEO)
     { ngx_string("X-Forwarded-For"),
                  offsetof(ngx_http_headers_in_t, x_forwarded_for),
                  ngx_http_process_header_line },
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -184,7 +184,7 @@ typedef struct {
 
     ngx_table_elt_t                  *keep_alive;
 
-#if (NGX_HTTP_PROXY || NGX_HTTP_REALIP)
+#if (NGX_HTTP_PROXY || NGX_HTTP_REALIP || NGX_HTTP_GEO)
     ngx_table_elt_t                  *x_forwarded_for;
 #endif