diff --git a/Makefile.am b/Makefile.am index 7f77c79..a02bdb3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -68,6 +68,7 @@ handwritten_tests = \ t/is-and-extract-ipv6.pl \ t/is-and-extract-ipv6-global.pl \ t/parse_assignments.pl \ + t/skip.pl \ t/ssl-validate.pl \ t/write_cache.pl generated_tests = \ diff --git a/ddclient.in b/ddclient.in index 58f47e7..fcc3a95 100755 --- a/ddclient.in +++ b/ddclient.in @@ -95,7 +95,7 @@ sub T_IPV6 { 'ipv6' } sub T_POSTS { 'postscript' } ## strategies for obtaining an ip address. -my %builtinweb = ( +our %builtinweb = ( 'dyndns' => {'url' => 'http://checkip.dyndns.org/', 'skip' => 'Current IP Address:'}, 'freedns' => {'url' => 'https://freedns.afraid.org/dynamic/check.php'}, 'googledomains' => {'url' => 'https://domains.google.com/checkip'}, # Deprecated! See https://github.com/ddclient/ddclient/issues/622 for more details @@ -112,7 +112,7 @@ my %builtinweb = ( 'nsupdate.info-ipv6' => {'url' => 'https://ipv6.nsupdate.info/myip'}, 'zoneedit' => {'url' => 'https://dynamic.zoneedit.com/checkip.html'}, ); -my %builtinfw = ( +our %builtinfw = ( '2wire' => { 'name' => '2Wire 1701HG Gateway', 'url' => '/xslt?PAGE=B01', @@ -429,21 +429,21 @@ my %variables = ( 'ifv4' => setv(T_IF, 0, 0, 'default', undef), 'ifv6' => setv(T_IF, 0, 0, 'default', undef), 'web' => setv(T_STRING,0, 0, 'dyndns', undef), - 'web-skip' => setv(T_STRING,1, 0, '', undef), + 'web-skip' => setv(T_STRING,0, 0, undef, undef), 'webv4' => setv(T_STRING,0, 0, 'ipify-ipv4', undef), - 'webv4-skip' => setv(T_STRING,1, 0, '', undef), + 'webv4-skip' => setv(T_STRING,0, 0, undef, undef), 'webv6' => setv(T_STRING,0, 0, 'ipify-ipv6', undef), - 'webv6-skip' => setv(T_STRING,1, 0, '', undef), + 'webv6-skip' => setv(T_STRING,0, 0, undef, undef), 'fw' => setv(T_ANY, 0, 0, '', undef), - 'fw-skip' => setv(T_STRING,1, 0, '', undef), + 'fw-skip' => setv(T_STRING,0, 0, undef, undef), 'fwv4' => setv(T_ANY, 0, 0, '', undef), - 'fwv4-skip' => setv(T_STRING,1, 0, '', undef), + 'fwv4-skip' => setv(T_STRING,0, 0, undef, undef), 'fwv6' => setv(T_ANY, 0, 0, '', undef), - 'fwv6-skip' => setv(T_STRING,1, 0, '', undef), + 'fwv6-skip' => setv(T_STRING,0, 0, undef, undef), 'fw-login' => setv(T_LOGIN, 1, 0, '', undef), 'fw-password' => setv(T_PASSWD,1, 0, '', undef), 'cmd' => setv(T_PROG, 0, 0, '', undef), - 'cmd-skip' => setv(T_STRING,1, 0, '', undef), + 'cmd-skip' => setv(T_STRING,0, 0, undef, undef), 'cmdv4' => setv(T_PROG, 0, 0, '', undef), 'cmdv6' => setv(T_PROG, 0, 0, '', undef), @@ -484,23 +484,23 @@ my %variables = ( 'ifv4' => setv(T_IF, 0, 0, 'default', undef), 'ifv6' => setv(T_IF, 0, 0, 'default', undef), 'web' => setv(T_STRING,0, 0, 'dyndns', undef), - 'web-skip' => setv(T_STRING,0, 0, '', undef), + 'web-skip' => setv(T_STRING,0, 0, undef, undef), 'web-ssl-validate' => setv(T_BOOL, 0, 0, 1, undef), 'webv4' => setv(T_STRING,0, 0, 'ipify-ipv4', undef), - 'webv4-skip' => setv(T_STRING,1, 0, '', undef), + 'webv4-skip' => setv(T_STRING,0, 0, undef, undef), 'webv6' => setv(T_STRING,0, 0, 'ipify-ipv6', undef), - 'webv6-skip' => setv(T_STRING,1, 0, '', undef), + 'webv6-skip' => setv(T_STRING,0, 0, undef, undef), 'fw' => setv(T_ANY, 0, 0, '', undef), - 'fw-skip' => setv(T_STRING,0, 0, '', undef), + 'fw-skip' => setv(T_STRING,0, 0, undef, undef), 'fw-login' => setv(T_LOGIN, 0, 0, '', undef), 'fw-password' => setv(T_PASSWD,0, 0, '', undef), 'fw-ssl-validate' => setv(T_BOOL, 0, 0, 1, undef), 'fwv4' => setv(T_ANY, 0, 0, '', undef), - 'fwv4-skip' => setv(T_STRING,1, 0, '', undef), + 'fwv4-skip' => setv(T_STRING,0, 0, undef, undef), 'fwv6' => setv(T_ANY, 0, 0, '', undef), - 'fwv6-skip' => setv(T_STRING,1, 0, '', undef), + 'fwv6-skip' => setv(T_STRING,0, 0, undef, undef), 'cmd' => setv(T_PROG, 0, 0, '', undef), - 'cmd-skip' => setv(T_STRING,0, 0, '', undef), + 'cmd-skip' => setv(T_STRING,0, 0, undef, undef), 'cmdv4' => setv(T_PROG, 0, 0, '', undef), 'cmdv6' => setv(T_PROG, 0, 0, '', undef), @@ -2728,19 +2728,19 @@ sub get_ip { } elsif ($use eq 'cmd') { if ($arg) { - $skip = opt('cmd-skip', $h) // ''; + $skip = opt('cmd-skip', $h); $reply = `$arg`; $reply = '' if $?; } } elsif ($use eq 'web') { $url = opt('web', $h) // ''; - $skip = opt('web-skip', $h) // ''; + $skip = opt('web-skip', $h); if (exists $builtinweb{$url}) { warning("googledomains is deprecated! See https://github.com/ddclient/ddclient/issues/622 for more info.") if ($url eq 'googledomains'); - $skip = $builtinweb{$url}->{'skip'} unless $skip; + $skip //= $builtinweb{$url}->{'skip'}; $url = $builtinweb{$url}->{'url'}; } $arg = $url; @@ -2758,7 +2758,7 @@ sub get_ip { # User fw-login should only have level 1 access to prevent # password theft. This is pretty harmless. my $queryif = opt('if', $h); - $skip = opt('fw-skip', $h) // ''; + $skip = opt('fw-skip', $h); # Convert slashes to protected value "\/" $queryif =~ s%\/%\\\/%g; @@ -2781,7 +2781,7 @@ sub get_ip { # User fw-login should only have level 1 access to prevent # password theft. This is pretty harmless. my $queryif = opt('if', $h); - $skip = opt('fw-skip', $h) // ''; + $skip = opt('fw-skip', $h); # Convert slashes to protected value "\/" $queryif =~ s%\/%\\\/%g; @@ -2807,10 +2807,10 @@ sub get_ip { # Note that --use=firewallname uses --fw=arg, not --firewallname=arg. $arg = opt('fw', $h) // ''; $url = $arg; - $skip = opt('fw-skip', $h) // ''; + $skip = opt('fw-skip', $h); if (exists $builtinfw{$use}) { - $skip = $builtinfw{$use}->{'skip'} unless $skip; + $skip //= $builtinfw{$use}->{'skip'}; $url = "http://${url}" . $builtinfw{$use}->{'url'} unless $url =~ /\//; } @@ -3152,7 +3152,7 @@ sub get_ipv4 { my $ipv4 = undef; ## Found IPv4 address my $reply = ''; ## Text returned from various methods my $url = ''; ## URL of website or firewall - my $skip = ''; ## Regex of pattern to skip before looking for IP + my $skip = undef; ## Regex of pattern to skip before looking for IP my $arg = opt($usev4, $h) // ''; ## Value assigned to the "usev4" method if ($usev4 eq 'ipv4') { @@ -3180,11 +3180,11 @@ sub get_ipv4 { } elsif ($usev4 eq 'webv4') { ## Obtain IPv4 address by accessing website at url in "webv4=" $url = $arg; - $skip = opt('webv4-skip', $h) // ''; + $skip = opt('webv4-skip', $h); if (exists $builtinweb{$url}) { warning("googledomains is deprecated! See https://github.com/ddclient/ddclient/issues/622 for more info.") if ($url eq 'googledomains'); - $skip = $builtinweb{$url}->{'skip'} unless $skip; + $skip //= $builtinweb{$url}->{'skip'}; $url = $builtinweb{$url}->{'url'}; $arg = $url; } @@ -3207,7 +3207,7 @@ sub get_ipv4 { warning("'--fw-skip' is deprecated for '--usev4=$usev4'; use '--fwv4-skip' instead") if (!defined(opt('fwv4-skip', $h)) && defined(opt('fw-skip', $h))); my $queryif = opt('ifv4', $h) // opt('if', $h); - $skip = opt('fwv4-skip', $h) // opt('fw-skip', $h) // ''; + $skip = opt('fwv4-skip', $h) // opt('fw-skip', $h); # Convert slashes to protected value "\/" $queryif =~ s%\/%\\\/%g; # Protect special HTML characters (like '?') @@ -3239,10 +3239,10 @@ sub get_ipv4 { # Note that --usev4=firewallname uses --fwv4=arg (or --fw=arg), not --firewallname=arg. $arg = opt('fwv4', $h) // opt('fw', $h) // ''; $url = $arg; - $skip = opt('fwv4-skip', $h) // opt('fw-skip', $h) // ''; + $skip = opt('fwv4-skip', $h) // opt('fw-skip', $h); if (exists $builtinfw{$usev4}) { - $skip = $builtinfw{$usev4}->{'skip'} unless $skip; + $skip //= $builtinfw{$usev4}->{'skip'}; $url = "http://${url}" . $builtinfw{$usev4}->{'url'} unless $url =~ /\//; } if ($url) { @@ -3282,7 +3282,7 @@ sub get_ipv6 { my $ipv6 = undef; ## Found IPv6 address my $reply = ''; ## Text returned from various methods my $url = ''; ## URL of website or firewall - my $skip = ''; ## Regex of pattern to skip before looking for IP + my $skip = undef; ## Regex of pattern to skip before looking for IP my $arg = opt($usev6, $h) // ''; ## Value assigned to the "usev6" method if ($usev6 eq 'ipv6' || $usev6 eq 'ip') { @@ -3328,11 +3328,11 @@ sub get_ipv6 { warning("'--web-skip' ignored for '--usev6=$usev6'; use '--webv6-skip' instead") if (!defined(opt('webv6-skip', $h)) && defined(opt('web-skip', $h))); $url = $arg; - $skip = opt('webv6-skip', $h) // ''; + $skip = opt('webv6-skip', $h); if (exists $builtinweb{$url}) { warning("googledomains is deprecated! See https://github.com/ddclient/ddclient/issues/622 for more info.") if ($url eq 'googledomains'); - $skip = $builtinweb{$url}->{'skip'} unless $skip; + $skip //= $builtinweb{$url}->{'skip'}; $url = $builtinweb{$url}->{'url'}; $arg = $url; } diff --git a/t/skip.pl b/t/skip.pl new file mode 100644 index 0000000..4e44a81 --- /dev/null +++ b/t/skip.pl @@ -0,0 +1,167 @@ +use Test::More; +eval { require ddclient::Test::Fake::HTTPD; } or plan(skip_all => $@); +SKIP: { eval { require Test::Warnings; } or skip($@, 1); } +eval { require 'ddclient'; } or BAIL_OUT($@); +my $ipv6_supported = eval { + require IO::Socket::IP; + my $ipv6_socket = IO::Socket::IP->new( + Domain => 'PF_INET6', + LocalHost => '::1', + Listen => 1, + ); + defined($ipv6_socket); +}; +my $http_daemon_supports_ipv6 = eval { + require HTTP::Daemon; + HTTP::Daemon->VERSION(6.12); +}; + +sub run_httpd { + my ($ipv6) = @_; + return undef if $ipv6 && (!$ipv6_supported || !$http_daemon_supports_ipv6); + my $httpd = ddclient::Test::Fake::HTTPD->new( + host => $ipv6 ? '::1' : '127.0.0.1', + scheme => 'http', + daemon_args => {V6Only => 1}, + ); + my $out = $ipv6 ? '::1 skip ::2' : '127.0.0.1 skip 127.0.0.2'; + $httpd->run(sub { + return [200, ['Content-Type' => 'text/plain'], [$out]]; + }); + diag(sprintf("started IPv%s SSL server running at %s", $ipv6 ? '6' : '4', $httpd->endpoint())); + return $httpd; +} +my %httpd = ( + '4' => run_httpd(0), + '6' => run_httpd(1), +); + +my $builtinwebv4 = 't/skip.pl webv4'; +my $builtinwebv6 = 't/skip.pl webv6'; +my $builtinfw = 't/skip.pl fw'; + +$ddclient::builtinweb{$builtinwebv4} = {'url' => $httpd{'4'}->endpoint(), 'skip' => 'skip'}; +$ddclient::builtinweb{$builtinwebv6} = {'url' => $httpd{'6'}->endpoint(), 'skip' => 'skip'} + if $httpd{'6'}; +$ddclient::builtinfw{$builtinfw} = {name => 'test', skip => 'skip'}; +%ddclient::builtinfw if 0; # suppress spurious warning "Name used only once: possible typo" + +sub run_test_case { + my %tc = @_; + SKIP: { + skip("IPv6 not supported on this system", 1) if $tc{ipv6} && !$ipv6_supported; + skip("HTTP::Daemon too old for IPv6 support", 1) + if $tc{ipv6} && !$http_daemon_supports_ipv6; + my $h = 't/skip.pl'; + $ddclient::config{$h} = $tc{cfg}; + %ddclient::config if 0; # suppress spurious warning "Name used only once: possible typo" + is(ddclient::get_ip($tc{cfg}{use}, $h), $tc{want}, $tc{desc}) if ($tc{cfg}{use}); + is(ddclient::get_ipv4($tc{cfg}{usev4}, $h), $tc{want}, $tc{desc}) if ($tc{cfg}{usev4}); + is(ddclient::get_ipv6($tc{cfg}{usev6}, $h), $tc{want}, $tc{desc}) if ($tc{cfg}{usev6}); + } +} + +subtest "use=web web='$builtinwebv4'" => sub { + run_test_case( + desc => "web-skip='' cancels built-in skip", + cfg => { + 'use' => 'web', + 'web' => $builtinwebv4, + 'web-skip' => '', + }, + want => '127.0.0.1', + ); + run_test_case( + desc => 'web-skip=undef uses built-in skip', + cfg => { + 'use' => 'web', + 'web' => $builtinwebv4, + 'web-skip' => undef, + }, + want => '127.0.0.2', + ); +}; +subtest "usev4=webv4 webv4='$builtinwebv4'" => sub { + run_test_case( + desc => "webv4-skip='' cancels built-in skip", + cfg => { + 'usev4' => 'webv4', + 'webv4' => $builtinwebv4, + 'webv4-skip' => '', + }, + want => '127.0.0.1', + ); + run_test_case( + desc => 'webv4-skip=undef uses built-in skip', + cfg => { + 'usev4' => 'webv4', + 'webv4' => $builtinwebv4, + 'webv4-skip' => undef, + }, + want => '127.0.0.2', + ); +}; +subtest "usev6=webv6 webv6='$builtinwebv6'" => sub { + run_test_case( + desc => "webv6-skip='' cancels built-in skip", + cfg => { + 'usev6' => 'webv6', + 'webv6' => $builtinwebv6, + 'webv6-skip' => '', + }, + ipv6 => 1, + want => '::1', + ); + run_test_case( + desc => 'webv6-skip=undef uses built-in skip', + cfg => { + 'usev6' => 'webv6', + 'webv6' => $builtinwebv6, + 'webv6-skip' => undef, + }, + ipv6 => 1, + want => '::2', + ); +}; +subtest "use='$builtinfw'" => sub { + run_test_case( + desc => "fw-skip='' cancels built-in skip", + cfg => { + 'fw' => $httpd{'4'}->endpoint(), + 'fw-skip' => '', + 'use' => $builtinfw, + }, + want => '127.0.0.1', + ); + run_test_case( + desc => 'fw-skip=undef uses built-in skip', + cfg => { + 'fw' => $httpd{'4'}->endpoint(), + 'fw-skip' => undef, + 'use' => $builtinfw, + }, + want => '127.0.0.2', + ); +}; +subtest "usev4='$builtinfw'" => sub { + run_test_case( + desc => "fwv4-skip='' cancels built-in skip", + cfg => { + 'fwv4' => $httpd{'4'}->endpoint(), + 'fwv4-skip' => '', + 'usev4' => $builtinfw, + }, + want => '127.0.0.1', + ); + run_test_case( + desc => 'fwv4-skip=undef uses built-in skip', + cfg => { + 'fwv4' => $httpd{'4'}->endpoint(), + 'fwv4-skip' => undef, + 'usev4' => $builtinfw, + }, + want => '127.0.0.2', + ); +}; + +done_testing();