Mercurial > hg > nginx
comparison src/http/modules/ngx_http_geo_module.c @ 4626:d0db70f4c13b
geo: chains of trusted proxies and partial IPv6 support.
The module now supports recursive search of client address through
the chain of trusted proxies, controlled by the "proxy_recursive"
directive in the "geo" block. It also gets partial IPv6 support:
now proxies may be specified with IPv6 addresses.
Example:
geo $test {
...
proxy 127.0.0.1;
proxy ::1;
proxy_recursive;
}
There's also a slight change in behavior. When original client
address (as specified by the "geo" directive) is one of the
trusted proxies, and the value of the X-Forwarded-For request
header cannot not be parsed as a valid address, an original client
address will be used for lookup. Previously, 255.255.255.255 was
used in this case.
author | Ruslan Ermilov <ru@nginx.com> |
---|---|
date | Mon, 14 May 2012 13:53:22 +0000 |
parents | 834049edae24 |
children | bb37a9cc08fb |
comparison
equal
deleted
inserted
replaced
4625:3709ce127763 | 4626:d0db70f4c13b |
---|---|
49 | 49 |
50 unsigned ranges:1; | 50 unsigned ranges:1; |
51 unsigned outside_entries:1; | 51 unsigned outside_entries:1; |
52 unsigned allow_binary_include:1; | 52 unsigned allow_binary_include:1; |
53 unsigned binary_include:1; | 53 unsigned binary_include:1; |
54 unsigned proxy_recursive:1; | |
54 } ngx_http_geo_conf_ctx_t; | 55 } ngx_http_geo_conf_ctx_t; |
55 | 56 |
56 | 57 |
57 typedef struct { | 58 typedef struct { |
58 union { | 59 union { |
59 ngx_radix_tree_t *tree; | 60 ngx_radix_tree_t *tree; |
60 ngx_http_geo_high_ranges_t high; | 61 ngx_http_geo_high_ranges_t high; |
61 } u; | 62 } u; |
62 | 63 |
63 ngx_array_t *proxies; | 64 ngx_array_t *proxies; |
65 unsigned proxy_recursive:1; | |
64 | 66 |
65 ngx_int_t index; | 67 ngx_int_t index; |
66 } ngx_http_geo_ctx_t; | 68 } ngx_http_geo_ctx_t; |
67 | 69 |
68 | 70 |
69 static in_addr_t ngx_http_geo_addr(ngx_http_request_t *r, | 71 static in_addr_t ngx_http_geo_addr(ngx_http_request_t *r, |
70 ngx_http_geo_ctx_t *ctx); | 72 ngx_http_geo_ctx_t *ctx); |
71 static in_addr_t ngx_http_geo_real_addr(ngx_http_request_t *r, | 73 static ngx_int_t ngx_http_geo_real_addr(ngx_http_request_t *r, |
72 ngx_http_geo_ctx_t *ctx); | 74 ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr); |
73 static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); | 75 static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); |
74 static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); | 76 static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); |
75 static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, | 77 static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, |
76 ngx_str_t *value); | 78 ngx_str_t *value); |
77 static char *ngx_http_geo_add_range(ngx_conf_t *cf, | 79 static char *ngx_http_geo_add_range(ngx_conf_t *cf, |
210 | 212 |
211 | 213 |
212 static in_addr_t | 214 static in_addr_t |
213 ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx) | 215 ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx) |
214 { | 216 { |
215 u_char *p, *ip; | 217 ngx_addr_t addr; |
216 size_t len; | 218 ngx_table_elt_t *xfwd; |
217 in_addr_t addr; | 219 struct sockaddr_in *sin; |
218 ngx_uint_t i, n; | 220 |
219 ngx_in_cidr_t *proxies; | 221 if (ngx_http_geo_real_addr(r, ctx, &addr) != NGX_OK) { |
220 ngx_table_elt_t *xfwd; | 222 return INADDR_NONE; |
221 | 223 } |
222 addr = ngx_http_geo_real_addr(r, ctx); | |
223 | 224 |
224 xfwd = r->headers_in.x_forwarded_for; | 225 xfwd = r->headers_in.x_forwarded_for; |
225 | 226 |
226 if (xfwd == NULL || ctx->proxies == NULL) { | 227 if (xfwd != NULL && ctx->proxies != NULL) { |
227 return addr; | 228 (void) ngx_http_get_forwarded_addr(r, &addr, xfwd->value.data, |
228 } | 229 xfwd->value.len, ctx->proxies, |
229 | 230 ctx->proxy_recursive); |
230 proxies = ctx->proxies->elts; | 231 } |
231 n = ctx->proxies->nelts; | 232 |
232 | 233 #if (NGX_HAVE_INET6) |
233 for (i = 0; i < n; i++) { | 234 |
234 if ((addr & proxies[i].mask) == proxies[i].addr) { | 235 if (addr.sockaddr->sa_family == AF_INET6) { |
235 | 236 struct in6_addr *inaddr6; |
236 len = xfwd->value.len; | 237 |
237 ip = xfwd->value.data; | 238 inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; |
238 | 239 |
239 for (p = ip + len - 1; p > ip; p--) { | 240 if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { |
240 if (*p == ' ' || *p == ',') { | 241 return ntohl(*(in_addr_t *) &inaddr6->s6_addr[12]); |
241 p++; | 242 } |
242 len -= p - ip; | 243 } |
243 ip = p; | 244 |
244 break; | 245 #endif |
245 } | 246 |
246 } | 247 if (addr.sockaddr->sa_family != AF_INET) { |
247 | 248 return INADDR_NONE; |
248 return ntohl(ngx_inet_addr(ip, len)); | 249 } |
249 } | 250 |
250 } | 251 sin = (struct sockaddr_in *) addr.sockaddr; |
251 | 252 return ntohl(sin->sin_addr.s_addr); |
252 return addr; | 253 } |
253 } | 254 |
254 | 255 |
255 | 256 static ngx_int_t |
256 static in_addr_t | 257 ngx_http_geo_real_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx, |
257 ngx_http_geo_real_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx) | 258 ngx_addr_t *addr) |
258 { | 259 { |
259 struct sockaddr_in *sin; | |
260 ngx_http_variable_value_t *v; | 260 ngx_http_variable_value_t *v; |
261 #if (NGX_HAVE_INET6) | |
262 u_char *p; | |
263 in_addr_t addr; | |
264 struct sockaddr_in6 *sin6; | |
265 #endif | |
266 | 261 |
267 if (ctx->index == -1) { | 262 if (ctx->index == -1) { |
268 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | 263 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
269 "http geo started: %V", &r->connection->addr_text); | 264 "http geo started: %V", &r->connection->addr_text); |
270 | 265 |
271 switch (r->connection->sockaddr->sa_family) { | 266 addr->sockaddr = r->connection->sockaddr; |
272 | 267 addr->socklen = r->connection->socklen; |
273 case AF_INET: | 268 /* addr->name = r->connection->addr_text; */ |
274 sin = (struct sockaddr_in *) r->connection->sockaddr; | 269 |
275 return ntohl(sin->sin_addr.s_addr); | 270 return NGX_OK; |
276 | |
277 #if (NGX_HAVE_INET6) | |
278 | |
279 case AF_INET6: | |
280 sin6 = (struct sockaddr_in6 *) r->connection->sockaddr; | |
281 | |
282 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { | |
283 p = sin6->sin6_addr.s6_addr; | |
284 addr = p[12] << 24; | |
285 addr += p[13] << 16; | |
286 addr += p[14] << 8; | |
287 addr += p[15]; | |
288 | |
289 return addr; | |
290 } | |
291 | |
292 #endif | |
293 } | |
294 | |
295 return INADDR_NONE; | |
296 } | 271 } |
297 | 272 |
298 v = ngx_http_get_flushed_variable(r, ctx->index); | 273 v = ngx_http_get_flushed_variable(r, ctx->index); |
299 | 274 |
300 if (v == NULL || v->not_found) { | 275 if (v == NULL || v->not_found) { |
301 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | 276 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
302 "http geo not found"); | 277 "http geo not found"); |
303 | 278 |
304 return 0; | 279 return NGX_ERROR; |
305 } | 280 } |
306 | 281 |
307 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | 282 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
308 "http geo started: %v", v); | 283 "http geo started: %v", v); |
309 | 284 |
310 return ntohl(ngx_inet_addr(v->data, v->len)); | 285 if (ngx_parse_addr(r->pool, addr, v->data, v->len) == NGX_OK) { |
286 return NGX_OK; | |
287 } | |
288 | |
289 return NGX_ERROR; | |
311 } | 290 } |
312 | 291 |
313 | 292 |
314 static char * | 293 static char * |
315 ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | 294 ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) |
386 rv = ngx_conf_parse(cf, NULL); | 365 rv = ngx_conf_parse(cf, NULL); |
387 | 366 |
388 *cf = save; | 367 *cf = save; |
389 | 368 |
390 geo->proxies = ctx.proxies; | 369 geo->proxies = ctx.proxies; |
370 geo->proxy_recursive = ctx.proxy_recursive; | |
391 | 371 |
392 if (ctx.high.low) { | 372 if (ctx.high.low) { |
393 | 373 |
394 if (!ctx.binary_include) { | 374 if (!ctx.binary_include) { |
395 for (i = 0; i < 0x10000; i++) { | 375 for (i = 0; i < 0x10000; i++) { |
491 | 471 |
492 rv = NGX_CONF_OK; | 472 rv = NGX_CONF_OK; |
493 | 473 |
494 goto done; | 474 goto done; |
495 } | 475 } |
476 | |
477 else if (ngx_strcmp(value[0].data, "proxy_recursive") == 0) { | |
478 ctx->proxy_recursive = 1; | |
479 rv = NGX_CONF_OK; | |
480 goto done; | |
481 } | |
496 } | 482 } |
497 | 483 |
498 if (cf->args->nelts != 2) { | 484 if (cf->args->nelts != 2) { |
499 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | 485 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, |
500 "invalid number of the geo parameters"); | 486 "invalid number of the geo parameters"); |
924 return NGX_CONF_ERROR; | 910 return NGX_CONF_ERROR; |
925 } | 911 } |
926 } | 912 } |
927 | 913 |
928 if (ngx_strcmp(value[0].data, "default") == 0) { | 914 if (ngx_strcmp(value[0].data, "default") == 0) { |
915 /* cidr.family = AF_INET; */ | |
929 cidr.u.in.addr = 0; | 916 cidr.u.in.addr = 0; |
930 cidr.u.in.mask = 0; | 917 cidr.u.in.mask = 0; |
931 net = &value[0]; | 918 net = &value[0]; |
932 | 919 |
933 } else { | 920 } else { |
941 } | 928 } |
942 | 929 |
943 if (ngx_http_geo_cidr_value(cf, net, &cidr) != NGX_OK) { | 930 if (ngx_http_geo_cidr_value(cf, net, &cidr) != NGX_OK) { |
944 return NGX_CONF_ERROR; | 931 return NGX_CONF_ERROR; |
945 } | 932 } |
933 | |
934 if (cidr.family != AF_INET) { | |
935 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
936 "\"geo\" supports IPv4 only"); | |
937 return NGX_CONF_ERROR; | |
938 } | |
939 | |
940 cidr.u.in.addr = ntohl(cidr.u.in.addr); | |
941 cidr.u.in.mask = ntohl(cidr.u.in.mask); | |
946 | 942 |
947 if (del) { | 943 if (del) { |
948 if (ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr, | 944 if (ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr, |
949 cidr.u.in.mask) | 945 cidr.u.in.mask) |
950 != NGX_OK) | 946 != NGX_OK) |
1050 | 1046 |
1051 static char * | 1047 static char * |
1052 ngx_http_geo_add_proxy(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, | 1048 ngx_http_geo_add_proxy(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, |
1053 ngx_cidr_t *cidr) | 1049 ngx_cidr_t *cidr) |
1054 { | 1050 { |
1055 ngx_in_cidr_t *c; | 1051 ngx_cidr_t *c; |
1056 | 1052 |
1057 if (ctx->proxies == NULL) { | 1053 if (ctx->proxies == NULL) { |
1058 ctx->proxies = ngx_array_create(ctx->pool, 4, sizeof(ngx_in_cidr_t)); | 1054 ctx->proxies = ngx_array_create(ctx->pool, 4, sizeof(ngx_cidr_t)); |
1059 if (ctx->proxies == NULL) { | 1055 if (ctx->proxies == NULL) { |
1060 return NGX_CONF_ERROR; | 1056 return NGX_CONF_ERROR; |
1061 } | 1057 } |
1062 } | 1058 } |
1063 | 1059 |
1064 c = ngx_array_push(ctx->proxies); | 1060 c = ngx_array_push(ctx->proxies); |
1065 if (c == NULL) { | 1061 if (c == NULL) { |
1066 return NGX_CONF_ERROR; | 1062 return NGX_CONF_ERROR; |
1067 } | 1063 } |
1068 | 1064 |
1069 c->addr = cidr->u.in.addr; | 1065 *c = *cidr; |
1070 c->mask = cidr->u.in.mask; | |
1071 | 1066 |
1072 return NGX_CONF_OK; | 1067 return NGX_CONF_OK; |
1073 } | 1068 } |
1074 | 1069 |
1075 | 1070 |
1077 ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr) | 1072 ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr) |
1078 { | 1073 { |
1079 ngx_int_t rc; | 1074 ngx_int_t rc; |
1080 | 1075 |
1081 if (ngx_strcmp(net->data, "255.255.255.255") == 0) { | 1076 if (ngx_strcmp(net->data, "255.255.255.255") == 0) { |
1077 cidr->family = AF_INET; | |
1082 cidr->u.in.addr = 0xffffffff; | 1078 cidr->u.in.addr = 0xffffffff; |
1083 cidr->u.in.mask = 0xffffffff; | 1079 cidr->u.in.mask = 0xffffffff; |
1084 | 1080 |
1085 return NGX_OK; | 1081 return NGX_OK; |
1086 } | 1082 } |
1090 if (rc == NGX_ERROR) { | 1086 if (rc == NGX_ERROR) { |
1091 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net); | 1087 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net); |
1092 return NGX_ERROR; | 1088 return NGX_ERROR; |
1093 } | 1089 } |
1094 | 1090 |
1095 if (cidr->family != AF_INET) { | |
1096 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"geo\" supports IPv4 only"); | |
1097 return NGX_ERROR; | |
1098 } | |
1099 | |
1100 if (rc == NGX_DONE) { | 1091 if (rc == NGX_DONE) { |
1101 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | 1092 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, |
1102 "low address bits of %V are meaningless", net); | 1093 "low address bits of %V are meaningless", net); |
1103 } | 1094 } |
1104 | |
1105 cidr->u.in.addr = ntohl(cidr->u.in.addr); | |
1106 cidr->u.in.mask = ntohl(cidr->u.in.mask); | |
1107 | 1095 |
1108 return NGX_OK; | 1096 return NGX_OK; |
1109 } | 1097 } |
1110 | 1098 |
1111 | 1099 |