diff --git a/README b/README index 18adfc5..6e5a9c3 100644 --- a/README +++ b/README @@ -6,9 +6,9 @@ Name installation instructions. Version - This document describes ngx_set_misc v0.22 - () released on 8 - September 2013. + This document describes ngx_set_misc v0.24 + () released on 10 + January 2014. Synopsis location /foo { @@ -84,6 +84,15 @@ Synopsis # $b == 'abcde' } + location /base64url { + set $a 'abcde'; + set_encode_base64url $a; + set_decode_base64url $b $a; + + # now $a == 'YWJjZGU' and + # $b == 'abcde' + } + location /hex { set $a 'abcde'; set_encode_hex $a; @@ -125,6 +134,29 @@ Synopsis echo $signature; } + # GET /secure?e=1893456000&n=MC4wLjAuMC8w&s=CyTCGzrXeRqq9_MvY1hm6ZvqwmY + # returns 403 when signature on the arguments is not correct OR + # when expire time is passed or network does not match. + # It is an alternative to the HttpSecureLinkModule in Nginx. + # This example has expiry "2030-01-01" and network "0.0.0.0/0". + location /secure { + set_hmac_sha1 $signature 'secret-key' "$arg_e&$arg_n"; + set_encode_base64url $signature; + if ($signature != $arg_s) { + return 403; + } + set_expired $expired $arg_e; + if ($expired) { + return 403; + } + set_decode_base64url $network $arg_n; + set_ip_matches $ip_matches $network $remote_ip; + if ($ip_matches = 0) { + return 403; + } + echo "OK"; + } + location = /rand { set $from 3; set $to 15; @@ -545,6 +577,40 @@ Directives opposite operation, .i.e, decoding a base64 digest into its original form. + set_encode_base64url + syntax: *set_encode_base64url $dst * + + syntax: *set_encode_base64url $dst* + + default: *no* + + context: *location, location if* + + phase: *rewrite* + + category: *ndk_set_var_value* + + Similar to the set_encode_base64 directive, but uses URL safe base64 + variant, '+' becomes '-', '/' becomes '_' and there is no padding with + '=' characters. + + set_decode_base64url + syntax: *set_decode_base64url $dst * + + syntax: *set_decode_base64url $dst* + + default: *no* + + context: *location, location if* + + phase: *rewrite* + + category: *ndk_set_var_value* + + Similar to the set_encode_base64url directive, but does exactly the the + opposite operation, .i.e, decoding a base64url digest into its original + form. + set_encode_hex syntax: *set_encode_hex $dst * @@ -760,6 +826,34 @@ Directives (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 * @@ -824,7 +918,7 @@ Directives then request "GET /test" will output a string like "ivVVRP2DGaAqDmdf3Rv4ZDJ7k0gOfASz". - This function depends on the presence of the "/dev/urandom" device, + This functionality depends on the presence of the "/dev/urandom" device, available on most UNIX-like systems. See also set_secure_random_lcalpha and set_random. @@ -856,7 +950,7 @@ Directives then request "GET /test" will output a string like "kcuxcddktffsippuekhshdaclaquiusj". - This function depends on the presence of the "/dev/urandom" device, + This functionality depends on the presence of the "/dev/urandom" device, available on most UNIX-like systems. This directive was first introduced in the "v0.22rc8" release. @@ -914,6 +1008,23 @@ Directives This directive was first introduced in the "v0.22rc7" release. + set_expired + syntax: *set_expired $dst * + + default: *no* + + context: *location, location if* + + phase: *rewrite* + + Sets $dst to either 1 or 0, dependent on whether or not the timestamp as + defined in "timestamp" (seconds since 1970-01-01 00:00:00) is or is not + in the past. + + Behind the scene, this directive utilizes the "ngx_time" API in the + Nginx core, so usually no syscall is involved due to the time caching + mechanism in the Nginx core. + set_local_today syntax: *set_local_today $dst* @@ -943,6 +1054,70 @@ Directives Nginx core, so usually no syscall is involved due to the time caching mechanism in the Nginx core. + set_formatted_gmt_time + syntax: *set_formatted_gmt_time $res <time-format>* + + default: *no* + + context: *location, location if* + + phase: *rewrite* + + Set a formatted GMT time to variable $res (as the first argument) using + the format string in the second argument. + + All the conversion specification notations in the standard C function + "strftime" are supported, like %Y (for 4-digit years) and %M (for + minutes in decimal). See http://linux.die.net/man/3/strftime for a + complete list of conversion specification symbols. + + Below is an example: + + location = /t { + set_formatted_gmt_time $timestr "%a %b %e %H:%M:%S %Y GMT"; + echo $timestr; + } + + Accessing "/t" yields the output + + Fri Dec 13 15:34:37 2013 GMT + + This directive was first added in the 0.23 release. + + See also set_formatted_local_time. + + set_formatted_local_time + syntax: *set_formatted_local_time $res <time-format>* + + default: *no* + + context: *location, location if* + + phase: *rewrite* + + Set a formatted local time to variable $res (as the first argument) + using the format string in the second argument. + + All the conversion specification notations in the standard C function + "strftime" are supported, like %Y (for 4-digit years) and %M (for + minutes in decimal). See http://linux.die.net/man/3/strftime for a + complete list of conversion specification symbols. + + Below is an example: + + location = /t { + set_formatted_local_time $timestr "%a %b %e %H:%M:%S %Y %Z"; + echo $timestr; + } + + Accessing "/t" yields the output + + Fri Dec 13 15:42:15 2013 PST + + This directive was first added in the 0.23 release. + + See also set_formatted_gmt_time. + Caveats Do not use $arg_PARAMETER, $cookie_COOKIE, $http_HEADER or other special variables defined in the Nginx core module as the target variable in @@ -959,12 +1134,12 @@ Installation below: Grab the nginx source code from nginx.org (), for - example, the version 1.4.2 (see nginx compatibility), and then build the + example, the version 1.5.8 (see nginx compatibility), and then build the source with this module: - wget 'http://nginx.org/download/nginx-1.4.2.tar.gz' - tar -xzvf nginx-1.4.2.tar.gz - cd nginx-1.4.2/ + wget 'http://nginx.org/download/nginx-1.5.8.tar.gz' + tar -xzvf nginx-1.5.8.tar.gz + cd nginx-1.5.8/ # Here we assume you would install you nginx under /opt/nginx/. ./configure --prefix=/opt/nginx \ @@ -987,7 +1162,9 @@ Installation Compatibility The following versions of Nginx should work with this module: - * 1.4.x (last tested: 1.4.2) + * 1.5.x (last tested: 1.5.8) + + * 1.4.x (last tested: 1.4.4) * 1.2.x (last tested: 1.2.9) @@ -1057,7 +1234,8 @@ Author is encouraged to improve this page as well. Copyright & License - Copyright (C) 2009-2013, Yichun Zhang (章亦春) . + Copyright (C) 2009-2014, Yichun Zhang (章亦春) , + CloudFlare Inc. This module is licensed under the terms of the BSD license. diff --git a/README.markdown b/README.markdown index 5cddd89..3447d36 100644 --- a/README.markdown +++ b/README.markdown @@ -29,15 +29,19 @@ Table of Contents * [set_decode_base32](#set_decode_base32) * [set_encode_base64](#set_encode_base64) * [set_decode_base64](#set_decode_base64) + * [set_encode_base64url](#set_encode_base64url) + * [set_decode_base64url](#set_decode_base64url) * [set_encode_hex](#set_encode_hex) * [set_decode_hex](#set_decode_hex) * [set_sha1](#set_sha1) * [set_md5](#set_md5) * [set_hmac_sha1](#set_hmac_sha1) + * [set_ip_matches](#set_ip_matches) * [set_random](#set_random) * [set_secure_random_alphanum](#set_secure_random_alphanum) * [set_secure_random_lcalpha](#set_secure_random_lcalpha) * [set_rotate](#set_rotate) + * [set_expired](#set_expired) * [set_local_today](#set_local_today) * [set_formatted_gmt_time](#set_formatted_gmt_time) * [set_formatted_local_time](#set_formatted_local_time) @@ -136,6 +140,15 @@ location /base64 { # $b == 'abcde' } +location /base64url { + set $a 'abcde'; + set_encode_base64url $a; + set_decode_base64url $b $a; + + # now $a == 'YWJjZGU' and + # $b == 'abcde' +} + location /hex { set $a 'abcde'; set_encode_hex $a; @@ -177,6 +190,29 @@ location /signature { echo $signature; } +# GET /secure?e=1893456000&n=MC4wLjAuMC8w&s=CyTCGzrXeRqq9_MvY1hm6ZvqwmY +# returns 403 when signature on the arguments is not correct OR +# when expire time is passed or network does not match. +# It is an alternative to the HttpSecureLinkModule in Nginx. +# This example has expiry "2030-01-01" and network "0.0.0.0/0". +location /secure { + set_hmac_sha1 $signature 'secret-key' "$arg_e&$arg_n"; + set_encode_base64url $signature; + if ($signature != $arg_s) { + return 403; + } + set_expired $expired $arg_e; + if ($expired) { + return 403; + } + set_decode_base64url $network $arg_n; + set_ip_matches $ip_matches $network $remote_ip; + if ($ip_matches = 0) { + return 403; + } + echo "OK"; +} + location = /rand { set $from 3; set $to 15; @@ -627,6 +663,42 @@ Similar to the [set_encode_base64](#set_encode_base64) directive, but does exact [Back to TOC](#table-of-contents) +set_encode_base64url +-------------------- +**syntax:** *set_encode_base64url $dst <src>* + +**syntax:** *set_encode_base64url $dst* + +**default:** *no* + +**context:** *location, location if* + +**phase:** *rewrite* + +**category:** *ndk_set_var_value* + +Similar to the [set_encode_base64](#set_encode_base64) directive, but uses URL safe base64 variant, '+' becomes '-', '/' becomes '_' and there is no padding with '=' characters. + +[Back to TOC](#table-of-contents) + +set_decode_base64url +-------------------- +**syntax:** *set_decode_base64url $dst <src>* + +**syntax:** *set_decode_base64url $dst* + +**default:** *no* + +**context:** *location, location if* + +**phase:** *rewrite* + +**category:** *ndk_set_var_value* + +Similar to the [set_encode_base64url](#set_encode_base64url) directive, but does exactly the the opposite operation, .i.e, decoding a base64url digest into its original form. + +[Back to TOC](#table-of-contents) + set_encode_hex -------------- **syntax:** *set_encode_hex $dst <src>* @@ -846,6 +918,35 @@ This directive requires the OpenSSL library enabled in your Nignx build (usually [Back to TOC](#table-of-contents) +set_ip_matches +-------------- +**syntax:** *set_ip_matches $dst <network> <ip>* + +**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, + +```nginx + +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. + +[Back to TOC](#table-of-contents) + set_random ---------- **syntax:** *set_random $res <from> <to>* @@ -1010,6 +1111,23 @@ This directive was first introduced in the `v0.22rc7` release. [Back to TOC](#table-of-contents) +set_expired +----------- +**syntax:** *set_expired $dst <timestamp>* + +**default:** *no* + +**context:** *location, location if* + +**phase:** *rewrite* + +Sets `$dst` to either `1` or `0`, dependent on whether or not the +timestamp as defined in `timestamp` (seconds since 1970-01-01 00:00:00) is or is not in the past. + +Behind the scene, this directive utilizes the `ngx_time` API in the Nginx core, so usually no syscall is involved due to the time caching mechanism in the Nginx core. + +[Back to TOC](#table-of-contents) + set_local_today --------------- **syntax:** *set_local_today $dst* diff --git a/config b/config index 4f046aa..26efd92 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_expired.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_base64url.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_expired.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_base64url.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..8d1617e 100644 --- a/doc/HttpSetMiscModule.wiki +++ b/doc/HttpSetMiscModule.wiki @@ -84,6 +84,15 @@ This document describes ngx_set_misc [https://github.com/agentzh/set-misc-nginx- # $b == 'abcde' } + location /base64url { + set $a 'abcde'; + set_encode_base64url $a; + set_decode_base64url $b $a; + + # now $a == 'YWJjZGU' and + # $b == 'abcde' + } + location /hex { set $a 'abcde'; set_encode_hex $a; @@ -125,6 +134,29 @@ This document describes ngx_set_misc [https://github.com/agentzh/set-misc-nginx- echo $signature; } + # GET /secure?e=1893456000&n=MC4wLjAuMC8w&s=CyTCGzrXeRqq9_MvY1hm6ZvqwmY + # returns 403 when signature on the arguments is not correct OR + # when expire time is passed or network does not match. + # It is an alternative to the HttpSecureLinkModule in Nginx. + # This example has expiry "2030-01-01" and network "0.0.0.0/0". + location /secure { + set_hmac_sha1 $signature 'secret-key' "$arg_e&$arg_n"; + set_encode_base64url $signature; + if ($signature != $arg_s) { + return 403; + } + set_expired $expired $arg_e; + if ($expired) { + return 403; + } + set_decode_base64url $network $arg_n; + set_ip_matches $ip_matches $network $remote_ip; + if ($ip_matches = 0) { + return 403; + } + echo "OK"; + } + location = /rand { set $from 3; set $to 15; @@ -518,6 +550,36 @@ This directive can be invoked by [[HttpLuaModule]]'s [[HttpLuaModule#ndk.set_var Similar to the [[#set_encode_base64|set_encode_base64]] directive, but does exactly the the opposite operation, .i.e, decoding a base64 digest into its original form. +== set_encode_base64url == +'''syntax:''' ''set_encode_base64url $dst '' + +'''syntax:''' ''set_encode_base64url $dst'' + +'''default:''' ''no'' + +'''context:''' ''location, location if'' + +'''phase:''' ''rewrite'' + +'''category:''' ''ndk_set_var_value'' + +Similar to the [[#set_encode_base64|set_encode_base64]] directive, but uses URL safe base64 variant, '+' becomes '-', '/' becomes '_' and there is no padding with '=' characters. + +== set_decode_base64url == +'''syntax:''' ''set_decode_base64url $dst '' + +'''syntax:''' ''set_decode_base64url $dst'' + +'''default:''' ''no'' + +'''context:''' ''location, location if'' + +'''phase:''' ''rewrite'' + +'''category:''' ''ndk_set_var_value'' + +Similar to the [[#set_encode_base64url|set_encode_base64url]] directive, but does exactly the the opposite operation, .i.e, decoding a base64url digest into its original form. + == set_encode_hex == '''syntax:''' ''set_encode_hex $dst '' @@ -715,6 +777,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 '' @@ -862,6 +949,20 @@ And accessing /rotate will also output integer sequence 0, 1, 2, 3, This directive was first introduced in the v0.22rc7 release. +== set_expired == +'''syntax:''' ''set_expired $dst '' + +'''default:''' ''no'' + +'''context:''' ''location, location if'' + +'''phase:''' ''rewrite'' + +Sets $dst to either 1 or 0, dependent on whether or not the +timestamp as defined in timestamp (seconds since 1970-01-01 00:00:00) is or is not in the past. + +Behind the scene, this directive utilizes the ngx_time API in the Nginx core, so usually no syscall is involved due to the time caching mechanism in the Nginx core. + == set_local_today == '''syntax:''' ''set_local_today $dst'' diff --git a/src/ngx_http_set_base64url.c b/src/ngx_http_set_base64url.c new file mode 100644 index 0000000..30bcf49 --- /dev/null +++ b/src/ngx_http_set_base64url.c @@ -0,0 +1,81 @@ +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + +#include +#include "ngx_http_set_base64url.h" + + +ngx_int_t +ngx_http_set_misc_set_decode_base64url(ngx_http_request_t *r, ngx_str_t *res, + ngx_http_variable_value_t *v) +{ + u_int i; + ngx_str_t src; + + src.len = v->len; + src.data = v->data; + + res->len = ngx_base64url_decoded_length(v->len); + res->data = ngx_palloc(r->pool, res->len); + if (res->data == NULL) { + return NGX_ERROR; + } + +#if defined(nginx_version) && nginx_version >= 1005010 + if (ngx_decode_base64url(res, &src) != NGX_OK) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "set_decode_base64url: invalid value"); + return NGX_ERROR; + } +#else + for (i=0;iconnection->log, 0, + "set_decode_base64url: invalid value"); + return NGX_ERROR; + } +#endif + + return NGX_OK; +} + + +ngx_int_t +ngx_http_set_misc_set_encode_base64url(ngx_http_request_t *r, ngx_str_t *res, + ngx_http_variable_value_t *v) +{ + u_int i; + ngx_str_t src; + + src.len = v->len; + src.data = v->data; + + res->len = ngx_base64url_encoded_length(v->len); + res->data = ngx_palloc(r->pool, res->len); + if (res->data == NULL) { + return NGX_ERROR; + } + +#if defined(nginx_version) && nginx_version >= 1005010 + ngx_encode_base64url(res, &src); +#else + ngx_encode_base64(res, &src); + for (i=0;ilen;i++) { + switch (res->data[i]) { + case '+': res->data[i]='-'; break; + case '/': res->data[i]='_'; break; + case '=': res->len--; break; + } + } +#endif + + return NGX_OK; +} + diff --git a/src/ngx_http_set_base64url.h b/src/ngx_http_set_base64url.h new file mode 100644 index 0000000..e8aeb06 --- /dev/null +++ b/src/ngx_http_set_base64url.h @@ -0,0 +1,22 @@ +#ifndef NGX_HTTP_SET_BASE64URL +#define NGX_HTTP_SET_BASE64URL + +#include +#include +#include + +#ifndef ngx_base64url_decoded_length +#define ngx_base64url_decoded_length(len) ((((len + 3) / 4) * 3) - (len % 4 ? 4 - len % 4 : 0)) +#endif + +#ifndef ngx_base64url_encoded_length +#define ngx_base64url_encoded_length(len) ((((len + 2) / 3) * 4) + (len % 3 ? len % 3 + 1 : 0)) +#endif + +ngx_int_t ngx_http_set_misc_set_encode_base64url(ngx_http_request_t *r, + ngx_str_t *res, ngx_http_variable_value_t *v); + +ngx_int_t ngx_http_set_misc_set_decode_base64url(ngx_http_request_t *r, + ngx_str_t *res, ngx_http_variable_value_t *v); + +#endif /* NGX_HTTP_SET_BASE64URL */ diff --git a/src/ngx_http_set_expired.c b/src/ngx_http_set_expired.c new file mode 100644 index 0000000..17db8a9 --- /dev/null +++ b/src/ngx_http_set_expired.c @@ -0,0 +1,32 @@ +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + +#include + +#include "ngx_http_set_expired.h" + +ngx_int_t +ngx_http_set_misc_set_expired(ngx_http_request_t *r, ngx_str_t *res, + ngx_http_variable_value_t *v) +{ + ngx_http_variable_value_t *expires; + time_t now, deadline; + + expires = v; + dd("expires=%.*s",(int) expires->len, expires->data); + + now = ngx_time(); + deadline = ngx_atotm(expires->data, expires->len); + dd("now=%lld, deadline=%lld", (long long) now, (long long) deadline); + + res->len = 1; + res->data = ngx_palloc(r->pool, res->len); + if (res->data == NULL) { + return NGX_ERROR; + } + res->data[0] = (u_char) ((deadline && now < deadline) ? '0' : '1'); + + return NGX_OK; + } diff --git a/src/ngx_http_set_expired.h b/src/ngx_http_set_expired.h new file mode 100644 index 0000000..d6d67ca --- /dev/null +++ b/src/ngx_http_set_expired.h @@ -0,0 +1,11 @@ +#ifndef NGX_HTTP_SET_EXPIRED +#define NGX_HTTP_SET_EXPIRED + +#include +#include +#include + +ngx_int_t ngx_http_set_misc_set_expired(ngx_http_request_t *r, + ngx_str_t *res, ngx_http_variable_value_t *v); + +#endif /* NGX_HTTP_SET_EXPIRED */ 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..35d6d07 100755 --- a/src/ngx_http_set_misc_module.c +++ b/src/ngx_http_set_misc_module.c @@ -12,10 +12,13 @@ #include "ngx_http_set_quote_sql.h" #include "ngx_http_set_quote_json.h" #include "ngx_http_set_escape_uri.h" +#include "ngx_http_set_expired.h" #include "ngx_http_set_local_today.h" #include "ngx_http_set_hash.h" #include "ngx_http_set_hex.h" #include "ngx_http_set_base64.h" +#include "ngx_http_set_base64url.h" +#include "ngx_http_set_ip_matches.h" #if NGX_OPENSSL #include "ngx_http_set_hmac.h" #endif @@ -47,6 +50,21 @@ static ndk_set_var_t ngx_http_set_misc_set_decode_base64_filter = { }; +static ndk_set_var_t ngx_http_set_misc_set_encode_base64url_filter = { + NDK_SET_VAR_VALUE, + (void *) ngx_http_set_misc_set_encode_base64url, + 1, + NULL +}; + +static ndk_set_var_t ngx_http_set_misc_set_decode_base64url_filter = { + NDK_SET_VAR_VALUE, + (void *) ngx_http_set_misc_set_decode_base64url, + 1, + NULL +}; + + static ndk_set_var_t ngx_http_set_misc_set_decode_hex_filter = { NDK_SET_VAR_VALUE, (void *) ngx_http_set_misc_set_decode_hex, @@ -62,6 +80,12 @@ 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 = { @@ -72,7 +96,6 @@ static ndk_set_var_t ngx_http_set_misc_set_hmac_sha1_filter = { }; #endif - #ifndef NGX_HTTP_SET_HASH static ndk_set_var_t ngx_http_set_misc_set_md5_filter = { NDK_SET_VAR_VALUE, @@ -108,6 +131,12 @@ static ndk_set_var_t ngx_http_set_misc_escape_uri_filter = { NULL }; +static ndk_set_var_t ngx_http_set_misc_set_expired_filter = { + NDK_SET_VAR_VALUE, + (void *) ngx_http_set_misc_set_expired, + 1, + NULL +}; static ndk_set_var_t ngx_http_set_misc_decode_base32_filter = { NDK_SET_VAR_VALUE, @@ -214,6 +243,22 @@ static ngx_command_t ngx_http_set_misc_commands[] = { 0, &ngx_http_set_misc_set_decode_base64_filter }, + { ngx_string ("set_encode_base64url"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF + |NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12, + ndk_set_var_value, + 0, + 0, + &ngx_http_set_misc_set_encode_base64url_filter + }, + { ngx_string ("set_decode_base64url"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF + |NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12, + ndk_set_var_value, + 0, + 0, + &ngx_http_set_misc_set_decode_base64url_filter + }, { ngx_string ("set_decode_hex"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF |NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12, @@ -279,6 +324,15 @@ static ngx_command_t ngx_http_set_misc_commands[] = { 0, &ngx_http_set_misc_escape_uri_filter }, + { + ngx_string ("set_expired"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF + |NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12, + ndk_set_var_value, + 0, + 0, + &ngx_http_set_misc_set_expired_filter + }, { ngx_string ("set_quote_sql_str"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF @@ -315,6 +369,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/base64url.t b/t/base64url.t new file mode 100644 index 0000000..c0e5908 --- /dev/null +++ b/t/base64url.t @@ -0,0 +1,71 @@ +# 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: base64 encode binary +--- config + location /bar { + set_decode_hex $out "f8f9fafbfcfdfeff"; + set_encode_base64 $out; + echo $out; + } +--- request + GET /bar +--- response_body ++Pn6+/z9/v8= + + + +=== TEST 2: base64 decode binary +--- config + location /bar { + set_decode_base64 $out "+Pn6+/z9/v8="; + set_encode_hex $out; + echo $out; + } +--- request + GET /bar +--- response_body +f8f9fafbfcfdfeff + + + +=== TEST 3: base64url encode binary (url safe) +--- config + location /bar { + set_decode_hex $out "f8f9fafbfcfdfeff"; + set_encode_base64url $out; + echo $out; + } +--- request + GET /bar +--- response_body +-Pn6-_z9_v8 + + + +=== TEST 4: base64url decode binary (url safe) +--- config + location /bar { + set_decode_base64url $out "-Pn6-_z9_v8"; + set_encode_hex $out; + echo $out; + } +--- request + GET /bar +--- response_body +f8f9fafbfcfdfeff + diff --git a/t/expired.t b/t/expired.t new file mode 100644 index 0000000..585bf2c --- /dev/null +++ b/t/expired.t @@ -0,0 +1,67 @@ +# 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: expired for 1970-01-01 00:00:00 +--- config + location /bar { + set_expired $expired 0; + echo $expired; + } +--- request + GET /bar +--- response_body +1 + + + +=== TEST 2: expired for 1970-01-01 00:00:01 +--- config + location /bar { + set_expired $expired 1; + echo $expired; + } +--- request + GET /bar +--- response_body +1 + + + +=== TEST 3: expired for 2020-01-01 00:00:00 +--- config + location /bar { + set_expired $expired 1577836800; + echo $expired; + } +--- request + GET /bar +--- response_body +0 + + + +=== TEST 4: expired for 9999-01-01 00:00:00 +--- config + location /bar { + set_expired $expired 253370764800; + echo $expired; + } +--- request + GET /bar +--- response_body +0 + 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 +