diff --git a/config b/config index 4f046aa..eab5c72 100755 --- a/config +++ b/config @@ -7,8 +7,8 @@ fi ngx_addon_name=ngx_http_set_misc_module HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http_set_misc_module" -NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_http_set_base32.c $ngx_addon_dir/src/ngx_http_set_default_value.c $ngx_addon_dir/src/ngx_http_set_hashed_upstream.c $ngx_addon_dir/src/ngx_http_set_quote_sql.c $ngx_addon_dir/src/ngx_http_set_quote_json.c $ngx_addon_dir/src/ngx_http_set_unescape_uri.c $ngx_addon_dir/src/ngx_http_set_misc_module.c $ngx_addon_dir/src/ngx_http_set_escape_uri.c $ngx_addon_dir/src/ngx_http_set_hash.c $ngx_addon_dir/src/ngx_http_set_local_today.c $ngx_addon_dir/src/ngx_http_set_hex.c $ngx_addon_dir/src/ngx_http_set_base64.c $ngx_addon_dir/src/ngx_http_set_random.c $ngx_addon_dir/src/ngx_http_set_secure_random.c $ngx_addon_dir/src/ngx_http_set_rotate.c" -NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/src/ddebug.h $ngx_addon_dir/src/ngx_http_set_default_value.h $ngx_addon_dir/src/ngx_http_set_hashed_upstream.h $ngx_addon_dir/src/ngx_http_set_quote_sql.h $ngx_addon_dir/src/ngx_http_set_quote_json.h $ngx_addon_dir/src/ngx_http_set_unescape_uri.h $ngx_addon_dir/src/ngx_http_set_escape_uri.h $ngx_addon_dir/src/ngx_http_set_hash.h $ngx_addon_dir/src/ngx_http_set_local_today.h $ngx_addon_dir/src/ngx_http_set_hex.h $ngx_addon_dir/src/ngx_http_set_base64.h $ngx_addon_dir/src/ngx_http_set_random.h $ngx_addon_dir/src/ngx_http_set_rotate.h $ngx_addon_dir/src/ngx_http_set_secure_random.h $ngx_addon_dir/src/ngx_http_set_misc_module.h" +NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_http_set_base32.c $ngx_addon_dir/src/ngx_http_set_default_value.c $ngx_addon_dir/src/ngx_http_set_hashed_upstream.c $ngx_addon_dir/src/ngx_http_set_quote_sql.c $ngx_addon_dir/src/ngx_http_set_quote_json.c $ngx_addon_dir/src/ngx_http_set_unescape_uri.c $ngx_addon_dir/src/ngx_http_set_misc_module.c $ngx_addon_dir/src/ngx_http_set_escape_uri.c $ngx_addon_dir/src/ngx_http_set_hash.c $ngx_addon_dir/src/ngx_http_set_local_today.c $ngx_addon_dir/src/ngx_http_set_hex.c $ngx_addon_dir/src/ngx_http_set_ip_matches.c $ngx_addon_dir/src/ngx_http_set_base64.c $ngx_addon_dir/src/ngx_http_set_random.c $ngx_addon_dir/src/ngx_http_set_secure_random.c $ngx_addon_dir/src/ngx_http_set_rotate.c" +NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/src/ddebug.h $ngx_addon_dir/src/ngx_http_set_default_value.h $ngx_addon_dir/src/ngx_http_set_hashed_upstream.h $ngx_addon_dir/src/ngx_http_set_quote_sql.h $ngx_addon_dir/src/ngx_http_set_quote_json.h $ngx_addon_dir/src/ngx_http_set_unescape_uri.h $ngx_addon_dir/src/ngx_http_set_escape_uri.h $ngx_addon_dir/src/ngx_http_set_hash.h $ngx_addon_dir/src/ngx_http_set_local_today.h $ngx_addon_dir/src/ngx_http_set_hex.h $ngx_addon_dir/src/ngx_http_set_ip_matches.h $ngx_addon_dir/src/ngx_http_set_base64.h $ngx_addon_dir/src/ngx_http_set_random.h $ngx_addon_dir/src/ngx_http_set_rotate.h $ngx_addon_dir/src/ngx_http_set_secure_random.h $ngx_addon_dir/src/ngx_http_set_misc_module.h" if [ $USE_OPENSSL = YES ]; then NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/src/ngx_http_set_hmac.h" diff --git a/doc/HttpSetMiscModule.wiki b/doc/HttpSetMiscModule.wiki index 59a5f76..70c30d9 100644 --- a/doc/HttpSetMiscModule.wiki +++ b/doc/HttpSetMiscModule.wiki @@ -715,6 +715,31 @@ Please note that we're using [[HttpEchoModule]]'s [[HttpEchoModule#echo|echo dir This directive requires the OpenSSL library enabled in your Nignx build (usually by passing the --with-http_ssl_module option to the ./configure script). +== set_ip_matches == +'''syntax:''' ''set_ip_matches $dst '' + +'''default:''' ''no'' + +'''context:''' ''location, location if'' + +'''phase:''' ''rewrite'' + +Sets $dst to either 1 or 0, dependent on whether or not the IP address (either IPv4 or IPv6) as defined in ip matches the network defined in network. The network can be specified as a single IP address or using CIDR notation. + +For instance, + + + location /test { + set_ip_matches $r1 10.0.0.0/8 10.0.2.101; + set_ip_matches $r2 10.0.0.0/24 10.0.2.101; + echo "r1=$r1, r2=$r2"; + } + + +then request GET /test will output r1=1, r2=0. + +This directive looks a lot like the "allow" directive, but it executes in the "rewrite" phase and allows for custom handling of matches and mismatches. + == set_random == '''syntax:''' ''set_random $res '' diff --git a/src/ngx_http_set_ip_matches.c b/src/ngx_http_set_ip_matches.c new file mode 100644 index 0000000..d667dd7 --- /dev/null +++ b/src/ngx_http_set_ip_matches.c @@ -0,0 +1,85 @@ +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + +#include + +#include "ngx_http_set_ip_matches.h" + +ngx_int_t +ngx_http_set_misc_set_ip_matches(ngx_http_request_t *r, ngx_str_t *res, + ngx_http_variable_value_t *v) +{ + ngx_http_variable_value_t *network_var, *ip_var; + ngx_str_t network_str, ip_str; + ngx_cidr_t network, ip; + size_t len; + u_char *ip_addr, *ip_mask, *network_addr, *network_mask, result; + u_int i; + + network_var = v; + ip_var = v+1; + + network_str.len = network_var->len; + network_str.data = network_var->data; + + ip_str.len = ip_var->len; + ip_str.data = ip_var->data; + + if (ngx_ptocidr(&network_str, &network) == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "network: invalid address or cidr"); + return NGX_ERROR; + } + + if (ngx_ptocidr(&ip_str, &ip) == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ip: invalid address"); + return NGX_ERROR; + } + + if (network.family != ip.family) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "IPv4 and IPv6 cannot be mixed"); + return NGX_ERROR; + } + + switch (ip.family) { + case AF_INET: + len = 4; + ip_addr = (u_char *) &ip.u.in.addr; + ip_mask = (u_char *) &ip.u.in.mask; + network_addr = (u_char *) &network.u.in.addr; + network_mask = (u_char *) &network.u.in.mask; + break; + case AF_INET6: + len = 16; + ip_addr = ip.u.in6.addr.__in6_u.__u6_addr8; + ip_mask = ip.u.in6.mask.__in6_u.__u6_addr8; + network_addr = network.u.in6.addr.__in6_u.__u6_addr8; + network_mask = network.u.in6.mask.__in6_u.__u6_addr8; + break; + default: + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "invalid address family"); + return NGX_ERROR; + } + + for (i=0;iconnection->log, 0, "ip: cidr notation not allowed"); + return NGX_ERROR; + } + } + + result = 0; + for (i=0;ilen = 1; + res->data = ngx_palloc(r->pool, res->len); + if (res->data == NULL) { + return NGX_ERROR; + } + res->data[0] = result==0 ? '1' : '0'; + + return NGX_OK; + } diff --git a/src/ngx_http_set_ip_matches.h b/src/ngx_http_set_ip_matches.h new file mode 100644 index 0000000..987f19a --- /dev/null +++ b/src/ngx_http_set_ip_matches.h @@ -0,0 +1,11 @@ +#ifndef NGX_HTTP_SET_IP_MATCHES +#define NGX_HTTP_SET_IP_MATCHES + +#include +#include +#include + +ngx_int_t ngx_http_set_misc_set_ip_matches(ngx_http_request_t *r, + ngx_str_t *res, ngx_http_variable_value_t *v); + +#endif /* NGX_HTTP_SET_IP_MATCHES */ diff --git a/src/ngx_http_set_misc_module.c b/src/ngx_http_set_misc_module.c index c57aac7..c01018b 100755 --- a/src/ngx_http_set_misc_module.c +++ b/src/ngx_http_set_misc_module.c @@ -16,6 +16,7 @@ #include "ngx_http_set_hash.h" #include "ngx_http_set_hex.h" #include "ngx_http_set_base64.h" +#include "ngx_http_set_ip_matches.h" #if NGX_OPENSSL #include "ngx_http_set_hmac.h" #endif @@ -62,6 +63,13 @@ static ndk_set_var_t ngx_http_set_misc_set_encode_hex_filter = { NULL }; +static ndk_set_var_t ngx_http_set_misc_set_ip_matches_filter = { + NDK_SET_VAR_MULTI_VALUE, + (void *) ngx_http_set_misc_set_ip_matches, + 2, + NULL +}; + #if NGX_OPENSSL static ndk_set_var_t ngx_http_set_misc_set_hmac_sha1_filter = { @@ -315,6 +323,15 @@ static ngx_command_t ngx_http_set_misc_commands[] = { 0, NULL }, + { + ngx_string ("set_ip_matches"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF + |NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE3, + ndk_set_var_multi_value, + 0, + 0, + &ngx_http_set_misc_set_ip_matches_filter + }, { ngx_string("set_hashed_upstream"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF diff --git a/t/ip_matches.t b/t/ip_matches.t new file mode 100644 index 0000000..9e4e87b --- /dev/null +++ b/t/ip_matches.t @@ -0,0 +1,116 @@ +# vi:filetype= + +use lib 'lib'; +use Test::Nginx::Socket; + +#repeat_each(3); + +plan tests => repeat_each() * 2 * blocks(); + +no_long_string(); + +run_tests(); + +#no_diff(); + +__DATA__ + +=== TEST 1: IPv4 address matches IPv4 CIDR +--- config + location /bar { + set_ip_matches $ip_matches 10.0.0.0/8 10.1.0.101; + echo $ip_matches; + } +--- request + GET /bar +--- response_body +1 + + + +=== TEST 2: IPv4 address does not match IPv4 CIDR +--- config + location /bar { + set_ip_matches $ip_matches 10.0.0.0/24 10.1.0.101; + echo $ip_matches; + } +--- request + GET /bar +--- response_body +0 + + +=== TEST 3: IPv4 address matches IPv4 address +--- config + location /bar { + set_ip_matches $ip_matches 10.1.0.101 10.1.0.101; + echo $ip_matches; + } +--- request + GET /bar +--- response_body +1 + + + +=== TEST 4: IPv4 address does not match IPv4 address +--- config + location /bar { + set_ip_matches $ip_matches 10.1.0.102 10.1.0.101 ; + echo $ip_matches; + } +--- request + GET /bar +--- response_body +0 + + + +=== TEST 5: IPv6 address matches IPv6 CIDR +--- config + location /bar { + set_ip_matches $ip_matches 3ffe::/16 3ffe:1900:4545:3:200:f8ff::67cf; + echo $ip_matches; + } +--- request + GET /bar +--- response_body +1 + + + +=== TEST 6: IPv6 address does not match IPv6 CIDR +--- config + location /bar { + set_ip_matches $ip_matches 3fff::/16 3ffe:1900:4545:3:200:f8ff::67cf; + echo $ip_matches; + } +--- request + GET /bar +--- response_body +0 + + +=== TEST 7: IPv6 address matches IPv6 address +--- config + location /bar { + set_ip_matches $ip_matches 3ffe:1900:4545:3:200:f8ff::67cf 3ffe:1900:4545:3:200:f8ff::67cf; + echo $ip_matches; + } +--- request + GET /bar +--- response_body +1 + + +=== TEST 7: IPv6 address does not match IPv6 address +--- config + location /bar { + set_ip_matches $ip_matches 3ffe:1900:4545:3:200:f8ff::67d0 3ffe:1900:4545:3:200:f8ff::67cf; + echo $ip_matches; + } +--- request + GET /bar +--- response_body +0 +