From d07b52f75ad60f4118131ebe149602a0ba22ccef Mon Sep 17 00:00:00 2001 From: David Kerr Date: Thu, 17 Mar 2016 23:07:46 -0400 Subject: [PATCH 1/9] Updates here are based on work I have done for the AstLinux project (AstLinux.org) Adding comprehensive framework for IPv6 support. IPv6 and IPv4 exist side-by-side The same network interface may have both IPv4 and IPv6. Therefore to support both we add the following options. usev6 = [ ip, if, cmd, web ] ipv6 = [ set an IPv6 address (usev6=ip) ] webv6 = [ url of a web server that will return your IPv6 address (usev6=web)] webv6-skip = [ string to skip to before looking for IPv6 address] cmdv6 = [ system command to execute that will return an IPv6 address (usev6=cmd)] cmdv6-skip = [ string to skip to before looking for IPv6 address ] There is no ifv6. It is assumed that if=interface will be the same for both IPv4 and IPv6 You can mix methods for IPv4 and IPv6 for the same interface. For example use=web usev6=if if=eth0 web=http://ipdetect.dnspark.com/ Would be a valid method for a system behind an IPv4 NAT connection, IPv6 would be obtained from the eth0 interface (globaly routable address) and the public IPv4 from external web site. Support for updating AAAA (IPv6) records is added for freedns.afraid.org. The existing IPv6 nsupdate method was updated to account for new design. Optional support is added for cURL as some embedded systems do not have perl NET6 and SSL packages installed. Set with option curl=yes Submitted code is ready for code review and comment. --- ddclient | 635 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 464 insertions(+), 171 deletions(-) diff --git a/ddclient b/ddclient index 8eda1c5..258eb8b 100755 --- a/ddclient +++ b/ddclient @@ -24,7 +24,6 @@ use strict; use Getopt::Long; use Sys::Hostname; use IO::Socket; -use Data::Validate::IP; my $version = "3.8.3"; my $programd = $0; @@ -59,17 +58,20 @@ sub T_FILE {'file name'}; sub T_FQDNP {'fully qualified host name and optional port number'}; sub T_PROTO {'protocol'} sub T_USE {'ip strategy'} +sub T_USEV6 {'ipv6 strategy'} sub T_IF {'interface'} sub T_PROG {'program name'} sub T_IP {'ip'} +sub T_IPV6 {'ipv6'} sub T_POSTS {'postscript'}; ## strategies for obtaining an ip address. my %builtinweb = ( - 'dyndns' => { 'url' => 'http://checkip.dyndns.org/', 'skip' => - 'Current IP Address:', }, + 'dyndns' => { 'url' => 'http://checkip.dyndns.org/', 'skip' => 'Current IP Address:', }, 'dnspark' => { 'url' => 'http://ipdetect.dnspark.com/', 'skip' => 'Current Address:', }, 'loopia' => { 'url' => 'http://dns.loopia.se/checkip/checkip.php', 'skip' => 'Current IP Address:', }, + 'whatismyv6' => { 'url' => 'http://whatismyv6.com/', 'skip' => 'Address of:', }, + 'nsupdate.info' => { 'url' => 'https://ipv6.nsupdate.info/myip', , }, ); my %builtinfw = ( 'watchguard-soho' => { @@ -302,6 +304,17 @@ sub ip_strategies_usage { return map { sprintf(" -use=%-22s %s.", $_, $ip_strategies{$_}) } sort keys %ip_strategies; } + +my %ipv6_strategies = ( + 'ip' => ": obtain IP from -ipv6 {address}", + 'if' => ": obtain IP from the -if {interface}", + 'cmd' => ": obtain IP from the -cmdv6 {external-command}", + 'web' => ": obtain IP from an IP discovery page on the web" +); +sub ipv6_strategies_usage { + return map { sprintf(" -usev6=%-22s %s.", $_, $ipv6_strategies{$_}) } sort keys %ipv6_strategies; +} + my %web_strategies = ( 'dyndns'=> 1, 'dnspark'=> 1, @@ -329,11 +342,15 @@ my %variables = ( 'protocol' => setv(T_PROTO, 0, 0, 1, 'dyndns2', undef), 'use' => setv(T_USE, 0, 0, 1, 'ip', undef), + 'usev6' => setv(T_USEV6, 0, 0, 1, undef, undef), 'ip' => setv(T_IP, 0, 0, 1, undef, undef), + 'ipv6' => setv(T_IPV6, 0, 0, 1, undef, undef), 'if' => setv(T_IF, 0, 0, 1, 'ppp0', undef), 'if-skip' => setv(T_STRING,1, 0, 1, '', undef), 'web' => setv(T_STRING,0, 0, 1, 'dyndns', undef), 'web-skip' => setv(T_STRING,1, 0, 1, '', undef), + 'webv6' => setv(T_STRING,0, 0, 1, '', undef), + 'webv6-skip' => setv(T_STRING,1, 0, 1, '', undef), 'fw' => setv(T_ANY, 0, 0, 1, '', undef), 'fw-skip' => setv(T_STRING,1, 0, 1, '', undef), 'fw-banlocal' => setv(T_BOOL, 0, 0, 1, 0, undef), @@ -341,12 +358,15 @@ my %variables = ( 'fw-password' => setv(T_PASSWD,1, 0, 1, '', undef), 'cmd' => setv(T_PROG, 0, 0, 1, '', undef), 'cmd-skip' => setv(T_STRING,1, 0, 1, '', undef), + 'cmdv6' => setv(T_PROG, 0, 0, 1, '', undef), + 'cmdv6-skip' => setv(T_STRING,1, 0, 1, '', undef), 'timeout' => setv(T_DELAY, 0, 0, 1, interval('120s'), interval('120s')), 'retry' => setv(T_BOOL, 0, 0, 0, 0, undef), 'force' => setv(T_BOOL, 0, 0, 0, 0, undef), 'ssl' => setv(T_BOOL, 0, 0, 0, 0, undef), - 'ipv6' => setv(T_BOOL, 0, 0, 0, 0, undef), + 'curl' => setv(T_BOOL, 0, 0, 0, 0, undef), + 'syslog' => setv(T_BOOL, 0, 0, 1, 0, undef), 'facility' => setv(T_STRING,0, 0, 1, 'daemon', undef), 'priority' => setv(T_STRING,0, 0, 1, 'notice', undef), @@ -370,10 +390,13 @@ my %variables = ( 'host' => setv(T_STRING, 1, 1, 1, '', undef), 'use' => setv(T_USE, 0, 0, 1, 'ip', undef), + 'usev6' => setv(T_USE, 0, 0, 1, undef, undef), 'if' => setv(T_IF, 0, 0, 1, 'ppp0', undef), 'if-skip' => setv(T_STRING,0, 0, 1, '', undef), 'web' => setv(T_STRING,0, 0, 1, 'dyndns', undef), 'web-skip' => setv(T_STRING,0, 0, 1, '', undef), + 'webv6' => setv(T_STRING,0, 0, 1, '', undef), + 'webv6-skip' => setv(T_STRING,0, 0, 1, '', undef), 'fw' => setv(T_ANY, 0, 0, 1, '', undef), 'fw-skip' => setv(T_STRING,0, 0, 1, '', undef), 'fw-banlocal' => setv(T_BOOL, 0, 0, 1, 0, undef), @@ -381,8 +404,12 @@ my %variables = ( 'fw-password' => setv(T_PASSWD,0, 0, 1, '', undef), 'cmd' => setv(T_PROG, 0, 0, 1, '', undef), 'cmd-skip' => setv(T_STRING,0, 0, 1, '', undef), - 'ipv6' => setv(T_BOOL, 0, 0, 0, 0, undef), + 'cmdv6' => setv(T_PROG, 0, 0, 1, '', undef), + 'cmdv6-skip' => setv(T_STRING,0, 0, 1, '', undef), + + 'ip' => setv(T_IP, 0, 1, 0, undef, undef), + 'ipv6' => setv(T_IPV6, 0, 1, 0, undef, undef), 'wtime' => setv(T_DELAY, 0, 1, 1, 0, interval('30s')), 'mtime' => setv(T_NUMBER, 0, 1, 0, 0, undef), 'atime' => setv(T_NUMBER, 0, 1, 0, 0, undef), @@ -418,6 +445,7 @@ my %variables = ( 'password' => setv(T_PASSWD, 1, 0, 1, '', undef), 'host' => setv(T_STRING, 1, 1, 1, '', undef), 'ip' => setv(T_IP, 0, 1, 0, undef, undef), + 'ipv6' => setv(T_IPV6, 0, 1, 0, undef, undef), 'wtime' => setv(T_DELAY, 0, 1, 1, 0, interval('30s')), 'mtime' => setv(T_NUMBER, 0, 1, 0, 0, undef), 'atime' => setv(T_NUMBER, 0, 1, 0, 0, undef), @@ -693,14 +721,19 @@ my @opt = ( "", [ "use", "=s", "-use which : how the should IP address be obtained." ], &ip_strategies_usage(), + [ "usev6", "=s", "-usev6 which : how the should IPv6 address be obtained." ], + &ipv6_strategies_usage(), "", [ "ip", "=s", "-ip address : set the IP address to 'address'" ], + [ "ipv6", "=s", "-ipv6 address : set the IPv6 address to 'address'" ], "", [ "if", "=s", "-if interface : obtain IP address from 'interface'" ], [ "if-skip", "=s", "-if-skip pattern : skip any IP addresses before 'pattern' in the output of ifconfig {if}" ], "", [ "web", "=s", "-web provider|url : obtain IP address from provider's IP checking page" ], [ "web-skip", "=s", "-web-skip pattern : skip any IP addresses before 'pattern' on the web provider|url" ], + [ "webv6", "=s", "-webv6 provider|url : obtain IPv6 address from provider's IP checking page" ], + [ "webv6-skip", "=s", "-webv6-skip pattern : skip any IPv6 addresses before 'pattern' on the web provider|url" ], "", [ "fw", "=s", "-fw address|url : obtain IP address from firewall at 'address'" ], [ "fw-skip", "=s", "-fw-skip pattern : skip any IP addresses before 'pattern' on the firewall address|url" ], @@ -710,6 +743,8 @@ my @opt = ( "", [ "cmd", "=s", "-cmd program : obtain IP address from by calling {program}" ], [ "cmd-skip", "=s", "-cmd-skip pattern : skip any IP addresses before 'pattern' in the output of {cmd}" ], + [ "cmdv6", "=s", "-cmdv6 program : obtain IPv6 address from by calling {program}" ], + [ "cmdv6-skip", "=s", "-cmdv6-skip pattern : skip any IPv6 addresses before 'pattern' in the output of {cmd}" ], "", [ "login", "=s", "-login user : login as 'user'" ], [ "password", "=s", "-password secret : use password 'secret'" ], @@ -718,6 +753,7 @@ my @opt = ( [ "options", "=s", "-options opt,opt : optional per-service arguments (see below)" ], "", [ "ssl", "!", "-{no}ssl : do updates over encrypted SSL connection" ], + [ "curl", "!", "-{no}curl : use cURL for network connections (default nocurl)" ], [ "retry", "!", "-{no}retry : retry failed updates." ], [ "force", "!", "-{no}force : force an update even if the update may be unnecessary" ], [ "timeout", "=i", "-timeout max : wait at most 'max' seconds for the host to respond" ], @@ -731,7 +767,6 @@ my @opt = ( [ "debug", "!", "-{no}debug : print {no} debugging information" ], [ "verbose", "!", "-{no}verbose : print {no} verbose information" ], [ "quiet", "!", "-{no}quiet : print {no} messages for unnecessary updates" ], - [ "ipv6", "!", "-{no}ipv6 : use ipv6" ], [ "help", "", "-help : this message" ], [ "postscript", "", "-postscript : script to run after updating ddclient, has new IP as param" ], @@ -818,7 +853,11 @@ do { # usage("invalid argument '-use %s'; possible values are:\n\t%s", $opt{'use'}, join("\n\t,",sort keys %ip_strategies)) usage("invalid argument '-use %s'; possible values are:\n%s", $opt{'use'}, join("\n",ip_strategies_usage())) unless exists $ip_strategies{lc opt('use')}; - + if (defined($opt{'usev6'})) { + usage("invalid argument '-usev6 %s'; possible values are:\n%s", $opt{'usev6'}, join("\n",ipv6_strategies_usage())) + unless exists $ipv6_strategies{lc opt('usev6')}; + } + $daemon = $opt{'daemon'}; $daemon = 0 if opt('force'); @@ -879,9 +918,10 @@ sub runpostscript { sub update_nics { my %examined = (); my %iplist = (); + my %ipv6list = (); foreach my $s (sort keys %services) { - my (@hosts, %ips) = (); + my (@hosts, %ips, %ipsv6) = (); my $updateable = $services{$s}{'updateable'}; my $update = $services{$s}{'update'}; @@ -890,33 +930,54 @@ sub update_nics { $examined{$h} = 1; # we only do this once per 'use' and argument combination my $use = opt('use', $h); + my $usev6 = opt('usev6', $h); my $arg_ip = opt('ip', $h) || ''; + my $arg_ipv6 = opt('ipv6', $h) || ''; my $arg_fw = opt('fw', $h) || ''; my $arg_if = opt('if', $h) || ''; my $arg_web = opt('web', $h) || ''; + my $arg_webv6 = opt('webv6', $h) || ''; my $arg_cmd = opt('cmd', $h) || ''; + my $arg_cmdv6 = opt('cmdv6', $h) || ''; my $ip = ""; + my $ipv6 = ""; if (exists $iplist{$use}{$arg_ip}{$arg_fw}{$arg_if}{$arg_web}{$arg_cmd}) { $ip = $iplist{$use}{$arg_ip}{$arg_fw}{$arg_if}{$arg_web}{$arg_cmd}; } else { $ip = get_ip($use, $h); if (!defined $ip || !$ip) { - warning("unable to determine IP address") + warning("%s: unable to determine IPv4 address", $h) if !$daemon || opt('verbose'); - next; + } elsif ($ip !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) { + warning("%s: malformed IPv4 address (%s)", $h, $ip); + } else { + $iplist{$use}{$arg_ip}{$arg_fw}{$arg_if}{$arg_web}{$arg_cmd} = $ip; } - if ($ip !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) { - if( !ipv6_match($ip) ) { - warning("malformed IP address (%s)", $ip); - next; - } - } - $iplist{$use}{$arg_ip}{$arg_fw}{$arg_if}{$arg_web}{$arg_cmd} = $ip; + debug("IPv4 %s", define($ip, "")); } $config{$h}{'wantip'} = $ip; + if (defined($usev6)) { + if (exists $ipv6list{$usev6}{$arg_ipv6}{$arg_fw}{$arg_if}{$arg_webv6}{$arg_cmdv6}) { + $ipv6 = $ipv6list{$usev6}{$arg_ipv6}{$arg_fw}{$arg_if}{$arg_webv6}{$arg_cmdv6}; + } else { + $ipv6 = get_ipv6($usev6, $h); + if (!defined $ipv6 || !$ipv6) { + warning("%s: unable to determine IPv6 address", $h) + if !$daemon || opt('verbose'); + } elsif ($ipv6 !~ /^(((?=.*(::))(?!.*\3.+\3))\3?|([\dA-F]{1,4}(\3|:\b|$)|\2))(?4){5}((?4){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})\z/ai) { + # That little gem from http://home.deds.nl/~aeron/regex/ + warning("%s: malformed IPv6 address (%s)", $h, $ipv6); + } else { + $ipv6list{$usev6}{$arg_ipv6}{$arg_fw}{$arg_if}{$arg_webv6}{$arg_cmdv6} = $ipv6; + } + debug("IPv6 %s", define($ipv6, "")); + } + $config{$h}{'wantipv6'} = $ipv6; + } next if !nic_updateable($h, $updateable); push @hosts, $h; - $ips{$ip} = $h; + $ips{$ip} = $h if (defined($ip)); + $ipsv6{$ipv6} = $h if (defined($ipv6)); } if (@hosts) { $0 = sprintf("%s - updating %s", $program, join(',', @hosts)); @@ -969,7 +1030,7 @@ sub write_cache { ## merge the updated host entries into the cache. foreach my $h (keys %config) { if (! exists $cache{$h} || $config{$h}{'update'}) { - map {$cache{$h}{$_} = $config{$h}{$_} } @{$config{$h}{'cacheable'}}; + map { defined($config{$h}{$_}) ? ($cache{$h}{$_} = $config{$h}{$_}) : () } @{$config{$h}{'cacheable'}}; } else { map {$cache{$h}{$_} = $config{$h}{$_} } qw(atime wtime status); @@ -1229,6 +1290,10 @@ sub init_config { $opt{'use'} = 'if' if !define($opt{'use'}) && defined($opt{'if'}); $opt{'use'} = 'web' if !define($opt{'use'}) && defined($opt{'web'}); + ## infer the IPv6 strategy if possible + $opt{'usev6'} = 'ip' if !define($opt{'usev6'}) && defined($opt{'ipv6'}); + $opt{'usev6'} = 'web' if !define($opt{'usev6'}) && defined($opt{'webv6'}); + ## sanity check $opt{'max-interval'} = min(interval(opt('max-interval')), interval(default('max-interval'))); $opt{'min-interval'} = max(interval(opt('min-interval')), interval(default('min-interval'))); @@ -1431,8 +1496,7 @@ sub test_possible_ip { local $opt{'debug'} = 0; printf "use=ip, ip=%s address is %s\n", opt('ip'), define(get_ip('ip'), 'NOT FOUND') - if defined opt('ip'); - + if defined opt('ip'); { local $opt{'use'} = 'if'; foreach my $if (grep {/^[a-zA-Z]/} `ifconfig -a`) { @@ -1466,6 +1530,32 @@ sub test_possible_ip { local $opt{'use'} = 'cmd'; printf "use=cmd, cmd=%s address is %s\n", opt('cmd'), define(get_ip('cmd'), 'NOT FOUND'); } + + # Now for IPv6 + printf "use=ip, ipv6=%s address is %s\n", opt('ipv6'), define(get_ipv6('ip'), 'NOT FOUND') + if defined opt('ipv6'); + { + local $opt{'use'} = 'if'; + foreach my $if (grep {/^[a-zA-Z]/} `ifconfig -a`) { + $if =~ s/:?\s.*//is; + local $opt{'if'} = $if; + printf "use=if, ifv6=%s address is %s\n", opt('if'), define(get_ipv6('if'), 'NOT FOUND'); + } + } + { + local $opt{'use'} = 'web'; + foreach my $web (sort keys %builtinweb) { + local $opt{'webv6'} = $web; + printf "use=web, webv6=$web address is %s\n", define(get_ipv6('web'), 'NOT FOUND'); + } + printf "use=web, webv6=%s address is %s\n", opt('webv6'), define(get_ipv6('web'), 'NOT FOUND') + if ! exists $builtinweb{opt('webv6')}; + } + if (opt('cmd')) { + local $opt{'use'} = 'cmd'; + printf "use=cmd, cmdv6=%s address is %s\n", opt('cmd'), define(get_ipv6('cmd'), 'NOT FOUND'); + } + exit 0 unless opt('debug'); } ###################################################################### @@ -1839,6 +1929,10 @@ sub check_value { $value = lc $value; return undef if ! exists $ip_strategies{$value}; + } elsif ($type eq T_USEV6) { + $value = lc $value; + return undef if ! exists $ipv6_strategies{$value}; + } elsif ($type eq T_FILE) { return undef if $value eq ""; @@ -1855,9 +1949,11 @@ sub check_value { # return undef if $value =~ /:/; } elsif ($type eq T_IP) { - if( !ipv6_match($value) ) { - return undef if $value !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/; - } + return undef if $value !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/; + + } elsif ($type eq T_IPV6) { + # This little gem from http://home.deds.nl/~aeron/regex/ + return undef if $value !~ /^(((?=.*(::))(?!.*\3.+\3))\3?|([\dA-F]{1,4}(\3|:\b|$)|\2))(?4){5}((?4){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})\z/ai; } return $value; } @@ -1952,11 +2048,13 @@ sub geturl { my $url = shift || ''; my $login = shift || ''; my $password = shift || ''; + my $ipversion = shift || ''; my ($peer, $server, $port, $default_port, $use_ssl); my ($sd, $rq, $request, $reply); debug("proxy = $proxy"); debug("url = %s", $url); + debug("ip ver = %s", $ipversion); ## canonify proxy and url my $force_ssl; $force_ssl = 1 if ($url =~ /^https:/); @@ -1975,71 +2073,74 @@ sub geturl { if ( $force_ssl || ($globals{'ssl'} and (caller(1))[3] ne 'main::get_ip') ) { $use_ssl = 1; $default_port = 443; - load_ssl_support; } else { $use_ssl = 0; $default_port = 80; } - ## determine peer and port to use. - $peer = $proxy || $server; - $peer =~ s%/.*%%; - $port = $peer; - $port =~ s%^.*:%%; - $port = $default_port unless $port =~ /^\d+$/; - $peer =~ s%:.*$%%; + if (! opt('curl') ) { + # Access network using perl functions. + ## determine peer and port to use. + $peer = $proxy || $server; + $peer =~ s%/.*%%; + $port = $peer; + $port =~ s%^.*:%%; + $port = $default_port unless $port =~ /^\d+$/; + $peer =~ s%:.*$%%; - my $to = sprintf "%s%s", $server, $proxy ? " via proxy $peer:$port" : ""; - verbose("CONNECT:", "%s", $to); + my $to = sprintf "%s%s", $server, $proxy ? " via proxy $peer:$port" : ""; + verbose("CONNECT:", "%s", $to); - $request = "GET "; - $request .= "http://$server" if $proxy; - $request .= "/$url HTTP/1.0\n"; - $request .= "Host: $server\n"; + $request = "GET "; + $request .= "http://$server" if $proxy; + $request .= "/$url HTTP/1.0\n"; + $request .= "Host: $server\n"; - my $auth = encode_base64("${login}:${password}", ""); - $request .= "Authorization: Basic $auth\n" if $login || $password; - $request .= "User-Agent: ${program}/${version}\n"; - $request .= "Connection: close\n"; - $request .= "\n"; + my $auth = encode_base64("${login}:${password}", ""); + $request .= "Authorization: Basic $auth\n" if $login || $password; + $request .= "User-Agent: ${program}/${version}\n"; + $request .= "Connection: close\n"; + $request .= "\n"; - ## make sure newlines are for some pedantic proxy servers - ($rq = $request) =~ s/\n/\r\n/g; + ## make sure newlines are for some pedantic proxy servers + ($rq = $request) =~ s/\n/\r\n/g; - # local $^W = 0; - $0 = sprintf("%s - connecting to %s port %s", $program, $peer, $port); - if (! opt('exec')) { - debug("skipped network connection"); - verbose("SENDING:", "%s", $request); - } elsif ($use_ssl) { - $sd = IO::Socket::SSL->new( - PeerAddr => $peer, - PeerPort => $port, - Proto => 'tcp', - MultiHomed => 1, - Timeout => opt('timeout'), - ); + # local $^W = 0; + $0 = sprintf("%s - connecting to %s port %s", $program, $peer, $port); + if (! opt('exec')) { + debug("skipped network connection"); + verbose("SENDING:", "%s", $request); + } elsif ($ipversion eq '6') { + load_ipv6_support; + $sd = IO::Socket::INET6->new( + PeerAddr => $peer, + PeerPort => $port, + Proto => 'tcp', + MultiHomed => 1, + Timeout => opt('timeout'), + ); + defined $sd or warning("cannot connect to $peer:$port socket: $@"); + } elsif ($use_ssl) { + load_ssl_support; + $sd = IO::Socket::SSL->new( + PeerAddr => $peer, + PeerPort => $port, + Proto => 'tcp', + MultiHomed => 1, + Timeout => opt('timeout'), + ); defined $sd or warning("cannot connect to $peer:$port socket: $@ " . IO::Socket::SSL::errstr()); - } elsif ($globals{'ipv6'}) { - load_ipv6_support; - $sd = IO::Socket::INET6->new( - PeerAddr => $peer, - PeerPort => $port, - Proto => 'tcp', - MultiHomed => 1, - Timeout => opt('timeout'), - ); - defined $sd or warning("cannot connect to $peer:$port socket: $@"); - } else { - $sd = IO::Socket::INET->new( - PeerAddr => $peer, - PeerPort => $port, - Proto => 'tcp', - MultiHomed => 1, - Timeout => opt('timeout'), - ); - defined $sd or warning("cannot connect to $peer:$port socket: $@"); - } + + } else { + $sd = IO::Socket::INET->new( + PeerAddr => $peer, + PeerPort => $port, + Proto => 'tcp', + MultiHomed => 1, + Timeout => opt('timeout'), + ); + defined $sd or warning("cannot connect to $peer:$port socket: $@"); + } if (defined $sd) { ## send the request to the http server @@ -2075,6 +2176,37 @@ sub geturl { } $0 = sprintf("%s - closed %s port %s", $program, $peer, $port); + } else + { + # Access network using cURL. + my $curlopt = ''; + my $protocol = 'http'; + $curlopt = '--ipv4' if ($ipversion eq '4'); + $curlopt = '--ipv6' if ($ipversion eq '6'); + if ($use_ssl) { + $protocol = 'https'; + $curlopt .= ' --insecure'; + } + + if (! opt('exec')) { + debug("skipped network connection"); + verbose("SENDING:", "%s", '${protocol}://${server}/${url}'); + } else + { + $0 = sprintf("%s - curl sending to %s", $program, '${protocol}://${server}/${url}'); + my $timeout = opt('timeout'); + + $reply = `/usr/bin/curl -si0 --user "${login}:${password}" --user-agent "${program}/${version}" \\ + --connect-timeout $timeout --max-time $timeout $curlopt \\ + --url "${protocol}://${server}/${url}" 2>/dev/null`; + + if (! $reply) { + warning("curl cannot connect to ${protocol}://${server}/${url}"); + } + } + } + + ## during testing simulate reading the URL if (opt('test')) { my $filename = "$server/$url"; @@ -2168,7 +2300,8 @@ sub get_ip { $arg = $url; if ($url) { - $reply = geturl(opt('proxy', $h), $url) || ''; + # when using a web server to find public IPv4 address we should force use of IPv4 + $reply = geturl(opt('proxy', $h), $url, '', '', '4') || ''; } } elsif (($use eq 'cisco')) { @@ -2230,11 +2363,6 @@ sub get_ip { $ip = $1; $ip = un_zero_pad($ip); $ip = filter_local($ip) if opt('fw-banlocal', $h); - } elsif ( $ip = ipv6_match($reply) ) { - $ip = un_zero_pad($ip); - $ip = filter_local($ip) if opt('fw-banlocal', $h); - } else { - warning("found neither ipv4 nor ipv6 address"); } if (($use ne 'ip') && (define($ip,'') eq '0.0.0.0')) { $ip = undef; @@ -2245,31 +2373,69 @@ sub get_ip { } ###################################################################### -## ipv6_match determine ipv6 address from given string and return them +## get_ipv6 ###################################################################### -sub ipv6_match { - my $content = shift; - my $omits; - my $ip = ""; - my $linenumbers = 0; - - my @values = split('\n', $content); - foreach my $val (@values) { - next unless $val =~ /((:{0,2}[A-F0-9]{1,4}){0,7}:{1,2}[A-F0-9]{1,4})/ai; # invalid char - my $parsed = $1; - - # check for at least 7 colons - my $count_colon = () = $parsed =~ /:/g; - if ($count_colon != 7) { - # or one double colon - my $count_double_colon = () = $parsed =~ /::/g; - if ($count_double_colon != 1) { - next - } - } - return $parsed; +sub get_ipv6 { + my $usev6 = lc shift; + my $h = shift; + my ($ipv6, $arg, $reply, $url, $skip) = (undef, opt($usev6, $h), ''); + $arg = '' unless $arg; + + if ($usev6 eq 'ip') { + $ipv6 = opt('ipv6', $h); + $arg = 'ipv6'; + + } elsif ($usev6 eq 'if') { + $skip = opt('if-skip', $h) || ''; + $reply = `ip -6 addr list dev $arg | grep "scope.global" 2> /dev/null`; + if ($reply =~ /.*? ([0-9:][^\/]*)/i) { + $reply = $1; + } + else { + $reply = '' + } + } elsif ($usev6 eq 'cmd') { + $arg = opt('cmdv6', $h); + if ($arg) { + $skip = opt('cmdv6-skip', $h) || ''; + debug("GetIPv6 cmd: %s",$arg); + $reply = `$arg`; + $reply = '' if $?; + } + + } elsif ($usev6 eq 'web') { + $url = opt('webv6', $h) || ''; + $skip = opt('webv6-skip', $h) || ''; + + if (exists $builtinweb{$url}) { + $skip = $builtinweb{$url}->{'skip'} unless $skip; + $url = $builtinweb{$url}->{'url'}; + } + $arg = $url; + + if ($url) { + # when using a web server to find our IPv6 address we should force use of IPv6 + $reply = geturl(opt('proxy', $h), $url, '', '', '6') || ''; + } + } - return; + if (!defined $reply) { + $reply = ''; + } + if ($skip) { + $skip =~ s/ /\\s/is; + $reply =~ s/^.*?${skip}//is; + } + + # Extract IPv6 address from the text + $ipv6 = $& if ($reply =~ /(?i)(?")); + return $ipv6; } ###################################################################### @@ -2361,6 +2527,7 @@ sub nic_updateable { my $sub = shift; my $update = 0; my $ip = $config{$host}{'wantip'}; + my $ipv6 = $config{$host}{'wantipv6'}; if ($config{$host}{'login'} eq '') { warning("null login name specified for host %s.", $host); @@ -2410,13 +2577,13 @@ sub nic_updateable { } elsif (($cache{$host}{'status'} ne 'good') && !interval_expired($host, 'atime', 'min-error-interval')) { - warning("skipping update of %s from %s to %s.\nlast updated %s but last attempt on %s failed.\nWait at least %s between update attempts.", - $host, - ($cache{$host}{'ip'} ? $cache{$host}{'ip'} : ''), + warning("skipping update of %s from %s to %s.\nlast updated %s but last attempt on %s failed.\nWait at least %s between update attempts.", + $host, + ($cache{$host}{'ip'} ? $cache{$host}{'ip'} : ''), $ip, ($cache{$host}{'mtime'} ? prettytime($cache{$host}{'mtime'}) : ''), ($cache{$host}{'atime'} ? prettytime($cache{$host}{'atime'}) : ''), - prettyinterval($config{$host}{'min-error-interval'}) + prettyinterval($config{$host}{'min-error-interval'}) ) if opt('verbose') || !define($cache{$host}{'warned-min-error-interval'}, 0); @@ -2425,7 +2592,37 @@ sub nic_updateable { } else { $update = 1; } + } elsif ((!exists($cache{$host}{'ipv6'})) || + ("$cache{$host}{'ipv6'}" ne "$ipv6")) { + if (($cache{$host}{'status'} eq 'good') && + !interval_expired($host, 'mtime', 'min-interval')) { + warning("skipping update of %s from %s to %s.\nlast updated %s.\nWait at least %s between update attempts.", + $host, + ($cache{$host}{'ipv6'} ? $cache{$host}{'ipv6'} : ''), + $ipv6, + ($cache{$host}{'mtime'} ? prettytime($cache{$host}{'mtime'}) : ''), + prettyinterval($config{$host}{'min-interval'}) + ) + if opt('verbose') || !define($cache{$host}{'warned-min-interval'}, 0); + $cache{$host}{'warned-min-interval'} = $now; + + } elsif (($cache{$host}{'status'} ne 'good') && !interval_expired($host, 'atime', 'min-error-interval')) { + warning("skipping update of %s from %s to %s.\nlast updated %s but last attempt on %s failed.\nWait at least %s between update attempts.", + $host, + ($cache{$host}{'ipv6'} ? $cache{$host}{'ipv6'} : ''), + $ipv6, + ($cache{$host}{'mtime'} ? prettytime($cache{$host}{'mtime'}) : ''), + ($cache{$host}{'atime'} ? prettytime($cache{$host}{'atime'}) : ''), + prettyinterval($config{$host}{'min-error-interval'}) + ) + if opt('verbose') || !define($cache{$host}{'warned-min-error-interval'}, 0); + + $cache{$host}{'warned-min-error-interval'} = $now; + + } else { + $update = 1; + } } elsif (defined($sub) && &$sub($host)) { $update = 1; } elsif ((defined($cache{$host}{'static'}) && defined($config{$host}{'static'}) && @@ -3772,10 +3969,14 @@ EoEXAMPLE ## ###################################################################### sub nic_freedns_update { + + debug("\nnic_freedns_update -------------------"); + ## First get the list of updatable hosts my $url; - $url = "http://$config{$_[0]}{'server'}/api/?action=getdyndns&sha=".&sha1_hex("$config{$_[0]}{'login'}|$config{$_[0]}{'password'}"); + # note, use '&v=2' in $url in order to pull IPv6 AAAA records as well as IPv4 A records + $url = "http://$config{$_[0]}{'server'}/api/?action=getdyndns&v=2&sha=".&sha1_hex("$config{$_[0]}{'login'}|$config{$_[0]}{'password'}"); my $reply = geturl(opt('proxy'), $url); if (!defined($reply) || !$reply || !header_ok($_[0], $reply)) { failed("updating %s: Could not connect to %s for site list.", $_[0], $url); @@ -3783,9 +3984,22 @@ sub nic_freedns_update { } my @lines = split("\n", $reply); my %freedns_hosts; + my %freedns_hosts_ipv6; + + # We have retrieved list of URLs associated with users FreeDNS account. + # Store them into freedns_hosts separating IPv4 (A records) from IPv6 (AAAA records) grep { my @rec = split(/\|/, $_); - $freedns_hosts{$rec[0]} = \@rec if ($#rec > 0); + if ($#rec > 0) { + # This little gem from http://home.deds.nl/~aeron/regex/ + if ($rec[1] =~ /^(((?=.*(::))(?!.*\3.+\3))\3?|([\dA-F]{1,4}(\3|:\b|$)|\2))(?4){5}((?4){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})\z/ai) { + debug("Host: %s, IPv6: %s", $rec[0], $rec[1]); + $freedns_hosts_ipv6{$rec[0]} = \@rec; + } else { + debug("Host: %s, IPv4: %s", $rec[0], $rec[1]); + $freedns_hosts{$rec[0]} = \@rec; + } + } } @lines; if (!keys %freedns_hosts) { failed("Could not get freedns update URLs from %s", $config{$_[0]}{'server'}); @@ -3795,41 +4009,81 @@ sub nic_freedns_update { foreach my $h (@_) { if(!$h){ next }; my $ip = delete $config{$h}{'wantip'}; - info("setting IP address to %s for %s", $ip, $h); - verbose("UPDATE:","updating %s", $h); + my $ipv6 = delete $config{$h}{'wantipv6'}; - if($ip eq $freedns_hosts{$h}->[1]) { - $config{$h}{'ip'} = $ip; - $config{$h}{'mtime'} = $now; - $config{$h}{'status'} = 'good'; - success("update not necessary %s: good: IP address already set to %s", $h, $ip); - } else { - my $reply = geturl(opt('proxy'), $freedns_hosts{$h}->[2]); - if (!defined($reply) || !$reply) { - failed("updating %s: Could not connect to %s.", $h, $freedns_hosts{$h}->[2]); - last; - } - if(!header_ok($h, $reply)) { - $config{$h}{'status'} = 'failed'; - last; - } + info("%s: setting IP address(s) to %s / %s", $h, define($ip, ""), define($ipv6, "")); - if ($reply =~ /Updated.*$h.*to.*$ip/) { + if (defined($freedns_hosts{$h}) && defined($ip)) { + if($ip eq $freedns_hosts{$h}->[1]) { $config{$h}{'ip'} = $ip; $config{$h}{'mtime'} = $now; $config{$h}{'status'} = 'good'; - success("updating %s: good: IP address set to %s", $h, $ip); - } elsif ($reply =~ /Address (\d+\.\d+\.\d+\.\d+) has not changed/) { - $ip = $1; - $config{$h}{'mtime'} = $now; - $config{$h}{'status'} = 'good'; - $config{$h}{'ip'} = $ip; - success("updating %s: good: IP address %s has not changed", $h, $ip); + success("update not necessary %s: good: IPv4 address already set to %s", $h, $ip); } else { - $config{$h}{'status'} = 'failed'; - warning("SENT: %s", $freedns_hosts{$h}->[2]) unless opt('verbose'); - warning("REPLIED: %s", $reply); - failed("updating %s: Invalid reply.", $h); + debug("Update: %s", $freedns_hosts{$h}->[2]."&address=".$ip); + my $reply = geturl(opt('proxy'), $freedns_hosts{$h}->[2]."&address=".$ip); + if (!defined($reply) || !$reply) { + failed("updating %s: Could not connect to %s.", $h, $freedns_hosts{$h}->[2]); + last; + } + if(!header_ok($h, $reply)) { + $config{$h}{'status'} = 'failed'; + last; + } + + if($reply =~ /Updated.*$h.*to.*$ip/) { + $config{$h}{'ip'} = $ip; + $config{$h}{'mtime'} = $now; + $config{$h}{'status'} = 'good'; + success("updating %s: good: IP address set to %s", $h, $ip); + } else { + $config{$h}{'status'} = 'failed'; + warning("SENT: %s", $freedns_hosts{$h}->[2]) unless opt('verbose'); + warning("REPLIED: %s", $reply); + failed("updating %s: Invalid reply.", $h); + } + } + } else { + if (!$daemon || opt('verbose')) { + warning("%s: Cannot set IPv4 to %s No A record at FreeDNS", $h, $ip) if (!defined($freedns_hosts{$h}) && defined($ip)); + warning("%s: Cannot set IPv4, A record exists but no address provided", $h) if (defined($freedns_hosts{$h}) && !defined($ip)); + } + } + + if (defined($freedns_hosts_ipv6{$h}) && defined($ipv6)) { + if($ipv6 eq $freedns_hosts_ipv6{$h}->[1]) { + $config{$h}{'ipv6'} = $ipv6; + $config{$h}{'mtime'} = $now; + $config{$h}{'status'} = 'good'; + success("update not necessary %s: good: IPv6 address already set to %s", $h, $ipv6); + } else { + debug("Update: %s", $freedns_hosts_ipv6{$h}->[2]."&address=".$ipv6); + my $reply = geturl(opt('proxy'), $freedns_hosts_ipv6{$h}->[2]."&address=".$ipv6); + if (!defined($reply) || !$reply) { + failed("updating %s: Could not connect to %s.", $h, $freedns_hosts_ipv6{$h}->[2]); + last; + } + if(!header_ok($h, $reply)) { + $config{$h}{'status'} = 'failed'; + last; + } + + if($reply =~ /Updated.*$h.*to.*$ipv6/) { + $config{$h}{'ipv6'} = $ipv6; + $config{$h}{'mtime'} = $now; + $config{$h}{'status'} = 'good'; + success("updating %s: good: IPv6 address set to %s", $h, $ipv6); + } else { + $config{$h}{'status'} = 'failed'; + warning("SENT: %s", $freedns_hosts_ipv6{$h}->[2]) unless opt('verbose'); + warning("REPLIED: %s", $reply); + failed("updating %s: Invalid reply.", $h); + } + } + } else { + if (!$daemon || opt('verbose')) { + warning("%s: Cannot set IPv6 to %s No AAAA record at FreeDNS", $h, $ipv6) if (!defined($freedns_hosts_ipv6{$h}) && defined($ipv6)); + warning("%s: Cannot set IPv6, AAAA record exists but no address provided", $h) if (defined($freedns_hosts_ipv6{$h}) && !defined($ipv6)); } } } @@ -4133,48 +4387,87 @@ sub nic_nsupdate_update { my $server = $config{$h}{'server'}; my $zone = $config{$h}{'zone'}; my $ip = $config{$h}{'wantip'}; - my $recordtype = ''; - if (is_ipv6($ip)) { - $recordtype = 'AAAA'; - } else { - $recordtype = 'A'; - } + my $ipv6 = $config{$h}{'wantipv6'}; delete $config{$_}{'wantip'} foreach @hosts; + delete $config{$_}{'wantipv6'} foreach @hosts; - info("setting IP address to %s for %s", $ip, $hosts); - verbose("UPDATE:","updating %s", $hosts); +##TODO +## Requires testing with new IPv6 code +##TODO + if (defined($ip)) { + info("setting IP address to %s for %s", $ip, $hosts); + verbose("UPDATE:","updating %s", $hosts); - ## send separate requests for each zone with all hosts in that zone - my $instructions = < Date: Fri, 18 Mar 2016 13:00:44 -0400 Subject: [PATCH 2/9] Force use of cURL when IPv6 protocol requested. The Perl NET6 functions do not fail gracefully or fall back on IPv4 when running on a system that is not connected to IPv6 network. Result is that ddclient may crash if IPv6 requested on a system that doesn't have IPv6. --- ddclient | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/ddclient b/ddclient index 258eb8b..d59846b 100755 --- a/ddclient +++ b/ddclient @@ -2078,7 +2078,7 @@ sub geturl { $default_port = 80; } - if (! opt('curl') ) { + if ( (!opt('curl')) and ($ipversion ne '6') ) { # Access network using perl functions. ## determine peer and port to use. $peer = $proxy || $server; @@ -2110,16 +2110,6 @@ sub geturl { if (! opt('exec')) { debug("skipped network connection"); verbose("SENDING:", "%s", $request); - } elsif ($ipversion eq '6') { - load_ipv6_support; - $sd = IO::Socket::INET6->new( - PeerAddr => $peer, - PeerPort => $port, - Proto => 'tcp', - MultiHomed => 1, - Timeout => opt('timeout'), - ); - defined $sd or warning("cannot connect to $peer:$port socket: $@"); } elsif ($use_ssl) { load_ssl_support; $sd = IO::Socket::SSL->new( From 816216ee291da954f94c53be7ec16a49f613fdba Mon Sep 17 00:00:00 2001 From: David Kerr Date: Fri, 18 Mar 2016 13:07:26 -0400 Subject: [PATCH 3/9] use "HERE Document" method for calling cURL command. --- ddclient | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ddclient b/ddclient index d59846b..cd88594 100755 --- a/ddclient +++ b/ddclient @@ -2186,9 +2186,11 @@ sub geturl { $0 = sprintf("%s - curl sending to %s", $program, '${protocol}://${server}/${url}'); my $timeout = opt('timeout'); - $reply = `/usr/bin/curl -si0 --user "${login}:${password}" --user-agent "${program}/${version}" \\ + $reply = <<`USE_CURL`; + /usr/bin/curl -si0 --user '${login}:${password}' --user-agent '${program}/${version}' \\ --connect-timeout $timeout --max-time $timeout $curlopt \\ - --url "${protocol}://${server}/${url}" 2>/dev/null`; + --url '${protocol}://${server}/${url}' 2>/dev/null +USE_CURL if (! $reply) { warning("curl cannot connect to ${protocol}://${server}/${url}"); From 15ad4b54b50a9e4b2f8c013b179e87175ee32b8c Mon Sep 17 00:00:00 2001 From: David Kerr Date: Tue, 20 Sep 2016 22:14:47 -0400 Subject: [PATCH 4/9] Fixed bug that would cause IPv4 address to be updated even if it was unchanged. Caused by testing for IPv6 address change even though no IPv6 update was requested. Added capability to specify whether IPv4 or IPv6 should be updated or not with new values: use=no usev6=no --- ddclient | 252 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 130 insertions(+), 122 deletions(-) diff --git a/ddclient b/ddclient index 3e3bd8f..3ebcb17 100755 --- a/ddclient +++ b/ddclient @@ -292,6 +292,7 @@ my %builtinfw = ( }, ); my %ip_strategies = ( + 'no' => ": do not obtain an IPv4 address for this host", 'ip' => ": obtain IP from -ip {address}", 'web' => ": obtain IP from an IP discovery page on the web", 'fw' => ": obtain IP from the firewall specified by -fw {type|address}", @@ -307,6 +308,7 @@ sub ip_strategies_usage { my %ipv6_strategies = ( + 'no' => ": do not obtain an IPv6 address for this host", 'ip' => ": obtain IP from -ipv6 {address}", 'if' => ": obtain IP from the -if {interface}", 'cmd' => ": obtain IP from the -cmdv6 {external-command}", @@ -391,7 +393,7 @@ my %variables = ( 'host' => setv(T_STRING, 1, 1, 1, '', undef), 'use' => setv(T_USE, 0, 0, 1, 'ip', undef), - 'usev6' => setv(T_USE, 0, 0, 1, undef, undef), + 'usev6' => setv(T_USEV6, 0, 0, 1, undef, undef), 'if' => setv(T_IF, 0, 0, 1, 'ppp0', undef), 'if-skip' => setv(T_STRING,0, 0, 1, '', undef), 'web' => setv(T_STRING,0, 0, 1, 'dyndns', undef), @@ -947,7 +949,7 @@ sub update_nics { } else { $ip = get_ip($use, $h); if (!defined $ip || !$ip) { - warning("%s: unable to determine IPv4 address", $h) + warning("%s: unable to determine IPv4 address with strategy use=%s", $h, $use) if !$daemon || opt('verbose'); } elsif ($ip !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) { warning("%s: malformed IPv4 address (%s)", $h, $ip); @@ -963,7 +965,7 @@ sub update_nics { } else { $ipv6 = get_ipv6($usev6, $h); if (!defined $ipv6 || !$ipv6) { - warning("%s: unable to determine IPv6 address", $h) + warning("%s: unable to determine IPv6 address with strategy usev6=%s", $h, $usev6) if !$daemon || opt('verbose'); } elsif ($ipv6 !~ /^(((?=.*(::))(?!.*\3.+\3))\3?|([\dA-F]{1,4}(\3|:\b|$)|\2))(?4){5}((?4){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})\z/ai) { # That little gem from http://home.deds.nl/~aeron/regex/ @@ -2273,35 +2275,35 @@ sub get_ip { $arg = '' unless $arg; if ($use eq 'ip') { - $ip = opt('ip', $h); - $arg = 'ip'; + $ip = opt('ip', $h); + $arg = 'ip'; } elsif ($use eq 'if') { - $skip = opt('if-skip', $h) || ''; - $reply = `ifconfig $arg 2> /dev/null`; - $reply = `ip addr list dev $arg 2> /dev/null` if $?; - $reply = '' if $?; + $skip = opt('if-skip', $h) || ''; + $reply = `ifconfig $arg 2> /dev/null`; + $reply = `ip addr list dev $arg 2> /dev/null` if $?; + $reply = '' if $?; } elsif ($use eq 'cmd') { - if ($arg) { - $skip = opt('cmd-skip', $h) || ''; - $reply = `$arg`; - $reply = '' if $?; - } + if ($arg) { + $skip = opt('cmd-skip', $h) || ''; + $reply = `$arg`; + $reply = '' if $?; + } } elsif ($use eq 'web') { - $url = opt('web', $h) || ''; - $skip = opt('web-skip', $h) || ''; + $url = opt('web', $h) || ''; + $skip = opt('web-skip', $h) || ''; - if (exists $builtinweb{$url}) { - $skip = $builtinweb{$url}->{'skip'} unless $skip; - $url = $builtinweb{$url}->{'url'}; - } - $arg = $url; + if (exists $builtinweb{$url}) { + $skip = $builtinweb{$url}->{'skip'} unless $skip; + $url = $builtinweb{$url}->{'url'}; + } + $arg = $url; - if ($url) { - # when using a web server to find public IPv4 address we should force use of IPv4 - $reply = geturl(opt('proxy', $h), $url, undef, undef, undef, undef, undef, '4') || ''; + if ($url) { + # when using a web server to find public IPv4 address we should force use of IPv4 + $reply = geturl(opt('proxy', $h), $url, undef, undef, undef, undef, undef, '4') || ''; } } elsif (($use eq 'cisco')) { @@ -2338,6 +2340,9 @@ sub get_ip { $reply = geturl('', $url, opt('fw-login', $h), opt('fw-password', $h)) || ''; $arg = $url; + } elsif ($use eq 'no') { + $reply = '0.0.0.0'; + } else { $url = opt('fw', $h) || ''; $skip = opt('fw-skip', $h) || ''; @@ -2354,7 +2359,8 @@ sub get_ip { } if (!defined $reply) { $reply = ''; - } + } + if ($skip) { $skip =~ s/ /\\s/is; $reply =~ s/^.*?${skip}//is; @@ -2365,7 +2371,7 @@ sub get_ip { $ip = filter_local($ip) if opt('fw-banlocal', $h); } if (($use ne 'ip') && (define($ip,'') eq '0.0.0.0')) { - $ip = undef; + $ip = undef; } debug("get_ip: using %s, %s reports %s", $use, $arg, define($ip, "")); @@ -2389,38 +2395,41 @@ sub get_ipv6 { $skip = opt('if-skip', $h) || ''; $reply = `ip -6 addr list dev $arg | grep "scope.global" 2> /dev/null`; if ($reply =~ /.*? ([0-9:][^\/]*)/i) { - $reply = $1; + $reply = $1; } else { $reply = '' } } elsif ($usev6 eq 'cmd') { - $arg = opt('cmdv6', $h); - if ($arg) { - $skip = opt('cmdv6-skip', $h) || ''; - debug("GetIPv6 cmd: %s",$arg); - $reply = `$arg`; - $reply = '' if $?; - } + $arg = opt('cmdv6', $h); + if ($arg) { + $skip = opt('cmdv6-skip', $h) || ''; + debug("GetIPv6 cmd: %s",$arg); + $reply = `$arg`; + $reply = '' if $?; + } } elsif ($usev6 eq 'web') { - $url = opt('webv6', $h) || ''; - $skip = opt('webv6-skip', $h) || ''; + $url = opt('webv6', $h) || ''; + $skip = opt('webv6-skip', $h) || ''; - if (exists $builtinweb{$url}) { - $skip = $builtinweb{$url}->{'skip'} unless $skip; - $url = $builtinweb{$url}->{'url'}; + if (exists $builtinweb{$url}) { + $skip = $builtinweb{$url}->{'skip'} unless $skip; + $url = $builtinweb{$url}->{'url'}; + } + $arg = $url; + + if ($url) { + # when using a web server to find our IPv6 address we should force use of IPv6 + $reply = geturl(opt('proxy', $h), $url, undef, undef, undef, undef, undef, '6') || ''; + } + + } elsif ($usev6 eq 'no') { + $reply = ''; } - $arg = $url; - - if ($url) { - # when using a web server to find our IPv6 address we should force use of IPv6 - $reply = geturl(opt('proxy', $h), $url, undef, undef, undef, undef, undef, '6') || ''; - } - - } + if (!defined $reply) { - $reply = ''; + $reply = '::'; } if ($skip) { $skip =~ s/ /\\s/is; @@ -2530,101 +2539,99 @@ sub nic_updateable { my $ipv6 = $config{$host}{'wantipv6'}; if ($config{$host}{'login'} eq '') { - warning("null login name specified for host %s.", $host); + warning("null login name specified for host %s.", $host); } elsif ($config{$host}{'password'} eq '') { - warning("null password specified for host %s.", $host); + warning("null password specified for host %s.", $host); } elsif ($opt{'force'}) { - info("forcing update of %s.", $host); - $update = 1; + info("forcing update of %s.", $host); + $update = 1; } elsif (!exists($cache{$host})) { - info("forcing updating %s because no cached entry exists.", $host); - $update = 1; + info("forcing updating %s because no cached entry exists.", $host); + $update = 1; } elsif ($cache{$host}{'wtime'} && $cache{$host}{'wtime'} > $now) { - warning("cannot update %s from %s to %s until after %s.", - $host, - ($cache{$host}{'ip'} ? $cache{$host}{'ip'} : ''), $ip, - prettytime($cache{$host}{'wtime'}) - ); + warning("cannot update %s from %s to %s until after %s.", + $host, + ($cache{$host}{'ip'} ? $cache{$host}{'ip'} : ''), $ip, + prettytime($cache{$host}{'wtime'}) + ); } elsif ($cache{$host}{'mtime'} && interval_expired($host, 'mtime', 'max-interval')) { - warning("forcing update of %s from %s to %s; %s since last update on %s.", - $host, - ($cache{$host}{'ip'} ? $cache{$host}{'ip'} : ''), $ip, - prettyinterval($config{$host}{'max-interval'}), - prettytime($cache{$host}{'mtime'}) - ); - $update = 1; + warning("forcing update of %s from %s to %s; %s since last update on %s.", + $host, + ($cache{$host}{'ip'} ? $cache{$host}{'ip'} : ''), $ip, + prettyinterval($config{$host}{'max-interval'}), + prettytime($cache{$host}{'mtime'}) + ); + $update = 1; - } elsif ((!exists($cache{$host}{'ip'})) || - ("$cache{$host}{'ip'}" ne "$ip")) { + } elsif (defined($cache{$host}{'use'} && ($cache{$host}{'use'} ne 'no')) && + ((!exists($cache{$host}{'ip'})) || ("$cache{$host}{'ip'}" ne "$ip"))) { if (($cache{$host}{'status'} eq 'good') && !interval_expired($host, 'mtime', 'min-interval')) { - warning("skipping update of %s from %s to %s.\nlast updated %s.\nWait at least %s between update attempts.", - $host, - ($cache{$host}{'ip'} ? $cache{$host}{'ip'} : ''), - $ip, - ($cache{$host}{'mtime'} ? prettytime($cache{$host}{'mtime'}) : ''), - prettyinterval($config{$host}{'min-interval'}) - ) - if opt('verbose') || !define($cache{$host}{'warned-min-interval'}, 0); - - $cache{$host}{'warned-min-interval'} = $now; + warning("skipping update of %s from %s to %s.\nlast updated %s.\nWait at least %s between update attempts.", + $host, + ($cache{$host}{'ip'} ? $cache{$host}{'ip'} : ''), + $ip, + ($cache{$host}{'mtime'} ? prettytime($cache{$host}{'mtime'}) : ''), + prettyinterval($config{$host}{'min-interval'}) + ) + if opt('verbose') || !define($cache{$host}{'warned-min-interval'}, 0); + $cache{$host}{'warned-min-interval'} = $now; - } elsif (($cache{$host}{'status'} ne 'good') && !interval_expired($host, 'atime', 'min-error-interval')) { + } elsif (($cache{$host}{'status'} ne 'good') && !interval_expired($host, 'atime', 'min-error-interval')) { - warning("skipping update of %s from %s to %s.\nlast updated %s but last attempt on %s failed.\nWait at least %s between update attempts.", - $host, - ($cache{$host}{'ip'} ? $cache{$host}{'ip'} : ''), - $ip, - ($cache{$host}{'mtime'} ? prettytime($cache{$host}{'mtime'}) : ''), - ($cache{$host}{'atime'} ? prettytime($cache{$host}{'atime'}) : ''), - prettyinterval($config{$host}{'min-error-interval'}) - ) - if opt('verbose') || !define($cache{$host}{'warned-min-error-interval'}, 0); + warning("skipping update of %s from %s to %s.\nlast updated %s but last attempt on %s failed.\nWait at least %s between update attempts.", + $host, + ($cache{$host}{'ip'} ? $cache{$host}{'ip'} : ''), + $ip, + ($cache{$host}{'mtime'} ? prettytime($cache{$host}{'mtime'}) : ''), + ($cache{$host}{'atime'} ? prettytime($cache{$host}{'atime'}) : ''), + prettyinterval($config{$host}{'min-error-interval'}) + ) + if opt('verbose') || !define($cache{$host}{'warned-min-error-interval'}, 0); + $cache{$host}{'warned-min-error-interval'} = $now; - $cache{$host}{'warned-min-error-interval'} = $now; - - } else { - $update = 1; - } - } elsif ((!exists($cache{$host}{'ipv6'})) || - ("$cache{$host}{'ipv6'}" ne "$ipv6")) { + } else { + $update = 1; + } + + } elsif (defined($cache{$host}{'usev6'} && ($cache{$host}{'usev6'} ne 'no') ) && + ((!exists($cache{$host}{'ipv6'})) || ("$cache{$host}{'ipv6'}" ne "$ipv6"))) { if (($cache{$host}{'status'} eq 'good') && !interval_expired($host, 'mtime', 'min-interval')) { warning("skipping update of %s from %s to %s.\nlast updated %s.\nWait at least %s between update attempts.", - $host, - ($cache{$host}{'ipv6'} ? $cache{$host}{'ipv6'} : ''), - $ipv6, - ($cache{$host}{'mtime'} ? prettytime($cache{$host}{'mtime'}) : ''), - prettyinterval($config{$host}{'min-interval'}) + $host, + ($cache{$host}{'ipv6'} ? $cache{$host}{'ipv6'} : ''), + $ipv6, + ($cache{$host}{'mtime'} ? prettytime($cache{$host}{'mtime'}) : ''), + prettyinterval($config{$host}{'min-interval'}) ) if opt('verbose') || !define($cache{$host}{'warned-min-interval'}, 0); - - $cache{$host}{'warned-min-interval'} = $now; + $cache{$host}{'warned-min-interval'} = $now; } elsif (($cache{$host}{'status'} ne 'good') && !interval_expired($host, 'atime', 'min-error-interval')) { warning("skipping update of %s from %s to %s.\nlast updated %s but last attempt on %s failed.\nWait at least %s between update attempts.", - $host, - ($cache{$host}{'ipv6'} ? $cache{$host}{'ipv6'} : ''), - $ipv6, - ($cache{$host}{'mtime'} ? prettytime($cache{$host}{'mtime'}) : ''), - ($cache{$host}{'atime'} ? prettytime($cache{$host}{'atime'}) : ''), - prettyinterval($config{$host}{'min-error-interval'}) + $host, + ($cache{$host}{'ipv6'} ? $cache{$host}{'ipv6'} : ''), + $ipv6, + ($cache{$host}{'mtime'} ? prettytime($cache{$host}{'mtime'}) : ''), + ($cache{$host}{'atime'} ? prettytime($cache{$host}{'atime'}) : ''), + prettyinterval($config{$host}{'min-error-interval'}) ) if opt('verbose') || !define($cache{$host}{'warned-min-error-interval'}, 0); - - $cache{$host}{'warned-min-error-interval'} = $now; + $cache{$host}{'warned-min-error-interval'} = $now; } else { $update = 1; } + } elsif (defined($sub) && &$sub($host)) { - $update = 1; + $update = 1; } elsif ((defined($cache{$host}{'static'}) && defined($config{$host}{'static'}) && ($cache{$host}{'static'} ne $config{$host}{'static'})) || (defined($cache{$host}{'wildcard'}) && defined($config{$host}{'wildcard'}) && @@ -2633,28 +2640,29 @@ sub nic_updateable { ($cache{$host}{'mx'} ne $config{$host}{'mx'})) || (defined($cache{$host}{'backupmx'}) && defined($config{$host}{'backupmx'}) && ($cache{$host}{'backupmx'} ne $config{$host}{'backupmx'})) ) { - info("updating %s because host settings have been changed.", $host); - $update = 1; + info("updating %s because host settings have been changed.", $host); + $update = 1; } else { - success("%s: skipped: IP address was already set to %s.", $host, $ip) - if opt('verbose'); + success("%s: skipped: IP address was already set to %s.", $host, $ip) + if opt('verbose'); } $config{$host}{'status'} = define($cache{$host}{'status'},''); $config{$host}{'update'} = $update; if ($update) { - $config{$host}{'status'} = 'noconnect'; - $config{$host}{'atime'} = $now; - $config{$host}{'wtime'} = 0; - $config{$host}{'warned-min-interval'} = 0; - $config{$host}{'warned-min-error-interval'} = 0; + $config{$host}{'status'} = 'noconnect'; + $config{$host}{'atime'} = $now; + $config{$host}{'wtime'} = 0; + $config{$host}{'warned-min-interval'} = 0; + $config{$host}{'warned-min-error-interval'} = 0; - delete $cache{$host}{'warned-min-interval'}; - delete $cache{$host}{'warned-min-error-interval'}; + delete $cache{$host}{'warned-min-interval'}; + delete $cache{$host}{'warned-min-error-interval'}; } return $update; } + ###################################################################### ## header_ok ###################################################################### From e1a5c15d3707effd2546ee9a672fbb10da142112 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 25 Sep 2016 14:32:55 -0400 Subject: [PATCH 5/9] Fixed bugs in logic testing when to update IPv6 address --- ddclient | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ddclient b/ddclient index 3ebcb17..1231287 100755 --- a/ddclient +++ b/ddclient @@ -2568,7 +2568,7 @@ sub nic_updateable { ); $update = 1; - } elsif (defined($cache{$host}{'use'} && ($cache{$host}{'use'} ne 'no')) && + } elsif ((defined($config{$host}{'use'}) && ($config{$host}{'use'} ne 'no')) && ((!exists($cache{$host}{'ip'})) || ("$cache{$host}{'ip'}" ne "$ip"))) { if (($cache{$host}{'status'} eq 'good') && !interval_expired($host, 'mtime', 'min-interval')) { @@ -2600,7 +2600,7 @@ sub nic_updateable { $update = 1; } - } elsif (defined($cache{$host}{'usev6'} && ($cache{$host}{'usev6'} ne 'no') ) && + } elsif ((defined($config{$host}{'usev6'}) && ($config{$host}{'usev6'} ne 'no') ) && ((!exists($cache{$host}{'ipv6'})) || ("$cache{$host}{'ipv6'}" ne "$ipv6"))) { if (($cache{$host}{'status'} eq 'good') && !interval_expired($host, 'mtime', 'min-interval')) { @@ -2644,8 +2644,13 @@ sub nic_updateable { $update = 1; } else { - success("%s: skipped: IP address was already set to %s.", $host, $ip) - if opt('verbose'); + if opt('verbose') { + + if (defined($config{$host}{'use'}) && ($config{$host}{'use'} ne 'no')) + success("%s: skipped: IP address was already set to %s.", $host, $ip) + if (defined($config{$host}{'usev6'}) && ($config{$host}{'usev6'} ne 'no')) + success("%s: skipped: IP address was already set to %s.", $host, $ipv6) + } } $config{$host}{'status'} = define($cache{$host}{'status'},''); $config{$host}{'update'} = $update; From 374660efa4ed99e9f59d331fb2e4c3c279fb78fe Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 25 Sep 2016 14:52:06 -0400 Subject: [PATCH 6/9] Fix syntax errors testing for verbose option to print status messages. Woops. --- ddclient | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ddclient b/ddclient index 1231287..949595c 100755 --- a/ddclient +++ b/ddclient @@ -2644,12 +2644,13 @@ sub nic_updateable { $update = 1; } else { - if opt('verbose') { - - if (defined($config{$host}{'use'}) && ($config{$host}{'use'} ne 'no')) - success("%s: skipped: IP address was already set to %s.", $host, $ip) - if (defined($config{$host}{'usev6'}) && ($config{$host}{'usev6'} ne 'no')) - success("%s: skipped: IP address was already set to %s.", $host, $ipv6) + if (opt('verbose')) { + if (defined($config{$host}{'use'}) && ($config{$host}{'use'} ne 'no')) { + success("%s: skipped: IP address was already set to %s.", $host, $ip); + } + if (defined($config{$host}{'usev6'}) && ($config{$host}{'usev6'} ne 'no')) { + success("%s: skipped: IP address was already set to %s.", $host, $ipv6); + } } } $config{$host}{'status'} = define($cache{$host}{'status'},''); From 1449e21a7da02eb09a49efefce47547dad3156c2 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Wed, 15 Feb 2017 22:40:55 -0500 Subject: [PATCH 7/9] Improve IPv6 parsing and some error messages --- ddclient | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/ddclient b/ddclient index 524a00b..9267053 100755 --- a/ddclient +++ b/ddclient @@ -2406,11 +2406,10 @@ sub get_ipv6 { } elsif ($usev6 eq 'if') { $skip = opt('if-skip', $h) || ''; - $reply = `ip -6 addr list dev $arg | grep "scope.global" 2> /dev/null`; - if ($reply =~ /.*? ([0-9:][^\/]*)/i) { + $reply = `ip -6 -o addr show dev $arg scope global 2>/dev/null`; + if ($reply =~ /^.*? ([a-f0-9]{1,4}:[a-f0-9:]+:[a-f0-9]{1,4})\/.*/is) { $reply = $1; - } - else { + } else { $reply = '' } } elsif ($usev6 eq 'cmd') { @@ -2450,7 +2449,11 @@ sub get_ipv6 { } # Extract IPv6 address from the text - $ipv6 = $& if ($reply =~ /(?i)(? Date: Thu, 16 Feb 2017 10:22:53 -0500 Subject: [PATCH 8/9] fix whitespace diffs --- ddclient | 154 +++++++++++++++++++++++++++---------------------------- 1 file changed, 76 insertions(+), 78 deletions(-) diff --git a/ddclient b/ddclient index 9267053..836c0a7 100755 --- a/ddclient +++ b/ddclient @@ -409,8 +409,6 @@ my %variables = ( 'cmd-skip' => setv(T_STRING,0, 0, 1, '', undef), 'cmdv6' => setv(T_PROG, 0, 0, 1, '', undef), 'cmdv6-skip' => setv(T_STRING,0, 0, 1, '', undef), - - 'ip' => setv(T_IP, 0, 1, 0, undef, undef), 'ipv6' => setv(T_IPV6, 0, 1, 0, undef, undef), 'wtime' => setv(T_DELAY, 0, 1, 1, 0, interval('30s')), @@ -2288,35 +2286,35 @@ sub get_ip { $arg = '' unless $arg; if ($use eq 'ip') { - $ip = opt('ip', $h); - $arg = 'ip'; + $ip = opt('ip', $h); + $arg = 'ip'; } elsif ($use eq 'if') { - $skip = opt('if-skip', $h) || ''; - $reply = `ifconfig $arg 2> /dev/null`; - $reply = `ip addr list dev $arg 2> /dev/null` if $?; - $reply = '' if $?; + $skip = opt('if-skip', $h) || ''; + $reply = `ifconfig $arg 2> /dev/null`; + $reply = `ip addr list dev $arg 2> /dev/null` if $?; + $reply = '' if $?; } elsif ($use eq 'cmd') { - if ($arg) { - $skip = opt('cmd-skip', $h) || ''; - $reply = `$arg`; - $reply = '' if $?; - } + if ($arg) { + $skip = opt('cmd-skip', $h) || ''; + $reply = `$arg`; + $reply = '' if $?; + } } elsif ($use eq 'web') { - $url = opt('web', $h) || ''; - $skip = opt('web-skip', $h) || ''; + $url = opt('web', $h) || ''; + $skip = opt('web-skip', $h) || ''; - if (exists $builtinweb{$url}) { - $skip = $builtinweb{$url}->{'skip'} unless $skip; - $url = $builtinweb{$url}->{'url'}; - } - $arg = $url; + if (exists $builtinweb{$url}) { + $skip = $builtinweb{$url}->{'skip'} unless $skip; + $url = $builtinweb{$url}->{'url'}; + } + $arg = $url; - if ($url) { - # when using a web server to find public IPv4 address we should force use of IPv4 - $reply = geturl(opt('proxy', $h), $url, undef, undef, undef, undef, undef, '4') || ''; + if ($url) { + # when using a web server to find public IPv4 address we should force use of IPv4 + $reply = geturl(opt('proxy', $h), $url, undef, undef, undef, undef, undef, '4') || ''; } } elsif (($use eq 'cisco')) { @@ -2372,8 +2370,7 @@ sub get_ip { } if (!defined $reply) { $reply = ''; - } - + } if ($skip) { $skip =~ s/ /\\s/is; $reply =~ s/^.*?${skip}//is; @@ -2384,7 +2381,7 @@ sub get_ip { $ip = filter_local($ip) if opt('fw-banlocal', $h); } if (($use ne 'ip') && (define($ip,'') eq '0.0.0.0')) { - $ip = undef; + $ip = undef; } debug("get_ip: using %s, %s reports %s", $use, $arg, define($ip, "")); @@ -2555,66 +2552,68 @@ sub nic_updateable { my $ipv6 = $config{$host}{'wantipv6'}; if ($config{$host}{'login'} eq '') { - warning("null login name specified for host %s.", $host); + warning("null login name specified for host %s.", $host); } elsif ($config{$host}{'password'} eq '') { - warning("null password specified for host %s.", $host); + warning("null password specified for host %s.", $host); } elsif ($opt{'force'}) { - info("forcing update of %s.", $host); - $update = 1; + info("forcing update of %s.", $host); + $update = 1; } elsif (!exists($cache{$host})) { - info("forcing updating %s because no cached entry exists.", $host); - $update = 1; + info("forcing updating %s because no cached entry exists.", $host); + $update = 1; } elsif ($cache{$host}{'wtime'} && $cache{$host}{'wtime'} > $now) { - warning("cannot update %s from %s to %s until after %s.", - $host, - ($cache{$host}{'ip'} ? $cache{$host}{'ip'} : ''), $ip, - prettytime($cache{$host}{'wtime'}) - ); + warning("cannot update %s from %s to %s until after %s.", + $host, + ($cache{$host}{'ip'} ? $cache{$host}{'ip'} : ''), $ip, + prettytime($cache{$host}{'wtime'}) + ); } elsif ($cache{$host}{'mtime'} && interval_expired($host, 'mtime', 'max-interval')) { - warning("forcing update of %s from %s to %s; %s since last update on %s.", - $host, - ($cache{$host}{'ip'} ? $cache{$host}{'ip'} : ''), $ip, - prettyinterval($config{$host}{'max-interval'}), - prettytime($cache{$host}{'mtime'}) - ); - $update = 1; + warning("forcing update of %s from %s to %s; %s since last update on %s.", + $host, + ($cache{$host}{'ip'} ? $cache{$host}{'ip'} : ''), $ip, + prettyinterval($config{$host}{'max-interval'}), + prettytime($cache{$host}{'mtime'}) + ); + $update = 1; } elsif ((defined($config{$host}{'use'}) && ($config{$host}{'use'} ne 'no')) && ((!exists($cache{$host}{'ip'})) || ("$cache{$host}{'ip'}" ne "$ip"))) { if (($cache{$host}{'status'} eq 'good') && !interval_expired($host, 'mtime', 'min-interval')) { - warning("skipping update of %s from %s to %s.\nlast updated %s.\nWait at least %s between update attempts.", - $host, - ($cache{$host}{'ip'} ? $cache{$host}{'ip'} : ''), - $ip, - ($cache{$host}{'mtime'} ? prettytime($cache{$host}{'mtime'}) : ''), - prettyinterval($config{$host}{'min-interval'}) - ) - if opt('verbose') || !define($cache{$host}{'warned-min-interval'}, 0); - $cache{$host}{'warned-min-interval'} = $now; + warning("skipping update of %s from %s to %s.\nlast updated %s.\nWait at least %s between update attempts.", + $host, + ($cache{$host}{'ip'} ? $cache{$host}{'ip'} : ''), + $ip, + ($cache{$host}{'mtime'} ? prettytime($cache{$host}{'mtime'}) : ''), + prettyinterval($config{$host}{'min-interval'}) + ) + if opt('verbose') || !define($cache{$host}{'warned-min-interval'}, 0); + + $cache{$host}{'warned-min-interval'} = $now; - } elsif (($cache{$host}{'status'} ne 'good') && !interval_expired($host, 'atime', 'min-error-interval')) { + } elsif (($cache{$host}{'status'} ne 'good') && !interval_expired($host, 'atime', 'min-error-interval')) { - warning("skipping update of %s from %s to %s.\nlast updated %s but last attempt on %s failed.\nWait at least %s between update attempts.", - $host, - ($cache{$host}{'ip'} ? $cache{$host}{'ip'} : ''), - $ip, - ($cache{$host}{'mtime'} ? prettytime($cache{$host}{'mtime'}) : ''), - ($cache{$host}{'atime'} ? prettytime($cache{$host}{'atime'}) : ''), - prettyinterval($config{$host}{'min-error-interval'}) - ) - if opt('verbose') || !define($cache{$host}{'warned-min-error-interval'}, 0); - $cache{$host}{'warned-min-error-interval'} = $now; + warning("skipping update of %s from %s to %s.\nlast updated %s but last attempt on %s failed.\nWait at least %s between update attempts.", + $host, + ($cache{$host}{'ip'} ? $cache{$host}{'ip'} : ''), + $ip, + ($cache{$host}{'mtime'} ? prettytime($cache{$host}{'mtime'}) : ''), + ($cache{$host}{'atime'} ? prettytime($cache{$host}{'atime'}) : ''), + prettyinterval($config{$host}{'min-error-interval'}) + ) + if opt('verbose') || !define($cache{$host}{'warned-min-error-interval'}, 0); - } else { - $update = 1; - } + $cache{$host}{'warned-min-error-interval'} = $now; + + } else { + $update = 1; + } } elsif ((defined($config{$host}{'usev6'}) && ($config{$host}{'usev6'} ne 'no') ) && ((!exists($cache{$host}{'ipv6'})) || ("$cache{$host}{'ipv6'}" ne "$ipv6"))) { @@ -2647,7 +2646,7 @@ sub nic_updateable { } } elsif (defined($sub) && &$sub($host)) { - $update = 1; + $update = 1; } elsif ((defined($cache{$host}{'static'}) && defined($config{$host}{'static'}) && ($cache{$host}{'static'} ne $config{$host}{'static'})) || (defined($cache{$host}{'wildcard'}) && defined($config{$host}{'wildcard'}) && @@ -2656,8 +2655,8 @@ sub nic_updateable { ($cache{$host}{'mx'} ne $config{$host}{'mx'})) || (defined($cache{$host}{'backupmx'}) && defined($config{$host}{'backupmx'}) && ($cache{$host}{'backupmx'} ne $config{$host}{'backupmx'})) ) { - info("updating %s because host settings have been changed.", $host); - $update = 1; + info("updating %s because host settings have been changed.", $host); + $update = 1; } else { if (opt('verbose')) { @@ -2672,19 +2671,18 @@ sub nic_updateable { $config{$host}{'status'} = define($cache{$host}{'status'},''); $config{$host}{'update'} = $update; if ($update) { - $config{$host}{'status'} = 'noconnect'; - $config{$host}{'atime'} = $now; - $config{$host}{'wtime'} = 0; - $config{$host}{'warned-min-interval'} = 0; - $config{$host}{'warned-min-error-interval'} = 0; + $config{$host}{'status'} = 'noconnect'; + $config{$host}{'atime'} = $now; + $config{$host}{'wtime'} = 0; + $config{$host}{'warned-min-interval'} = 0; + $config{$host}{'warned-min-error-interval'} = 0; - delete $cache{$host}{'warned-min-interval'}; - delete $cache{$host}{'warned-min-error-interval'}; + delete $cache{$host}{'warned-min-interval'}; + delete $cache{$host}{'warned-min-error-interval'}; } return $update; } - ###################################################################### ## header_ok ###################################################################### From 364c908753cf7829722106d988834778c388b566 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sat, 25 Feb 2017 13:41:28 -0500 Subject: [PATCH 9/9] Pass IPv6 address to postscript application (if present) --- ddclient | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ddclient b/ddclient index 836c0a7..008e3a1 100755 --- a/ddclient +++ b/ddclient @@ -996,7 +996,7 @@ sub update_nics { if (@hosts) { $0 = sprintf("%s - updating %s", $program, join(',', @hosts)); &$update(@hosts); - runpostscript(join ' ', keys %ips); + runpostscript(join ' ', keys %ips, keys %ipsv6); } } foreach my $h (sort keys %config) {