From 323a873b222db5d0f16ddf0fa964f9147cb7c3c1 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 4 Jun 2020 21:37:20 -0400 Subject: [PATCH 1/2] Change geturl to take a hash of parameters This makes the call sites more readable, and it will be easier to extend in the future (to add an option to force IPv4 or IPv6, for example). --- ddclient | 175 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 133 insertions(+), 42 deletions(-) diff --git a/ddclient b/ddclient index ddf1066..62ff478 100755 --- a/ddclient +++ b/ddclient @@ -1521,7 +1521,12 @@ sub test_possible_ip { sub test_geturl { my $url = shift; - my $reply = geturl(opt('proxy'), $url, opt('login'), opt('password')); + my $reply = geturl({ + proxy => opt('proxy'), + url => $url, + login => opt('login'), + password => opt('password'), + }); print "URL $url\n"; print defined($reply) ? $reply : "\n"; exit; @@ -1995,13 +2000,14 @@ EOM ## geturl ###################################################################### sub geturl { - my $proxy = shift || ''; - my $url = shift || ''; - my $login = shift || ''; - my $password = shift || ''; - my $headers = shift || ''; - my $method = shift || 'GET'; - my $data = shift || ''; + my ($params) = @_; + my $proxy = $params->{proxy} // ''; + my $url = $params->{url} // ''; + my $login = $params->{login} // ''; + my $password = $params->{password} // ''; + my $headers = $params->{headers} // ''; + my $method = $params->{method} // 'GET'; + my $data = $params->{data} // ''; my ($peer, $server, $port, $default_port, $use_ssl); my ($sd, $request, $reply); @@ -2229,7 +2235,7 @@ sub get_ip { $arg = $url; if ($url) { - $reply = geturl(opt('proxy', $h), $url) || ''; + $reply = geturl({ proxy => opt('proxy', $h), url => $url }) || ''; } } elsif (($use eq 'cisco')) { @@ -2245,9 +2251,13 @@ sub get_ip { # Protect special HTML characters (like '?') $queryif =~ s/([\?&= ])/sprintf("%%%02x", ord($1))/ge; - $url = "http://" . opt('fw', $h) . "/level/1/exec/show/ip/interface/brief/${queryif}/CR"; - $reply = geturl('', $url, opt('fw-login', $h), opt('fw-password', $h)) || ''; - $arg = $url; + $url = "http://" . opt('fw', $h) . "/level/1/exec/show/ip/interface/brief/${queryif}/CR"; + $reply = geturl({ + url => $url, + login => opt('fw-login', $h), + password => opt('fw-password', $h), + }) || ''; + $arg = $url; } elsif (($use eq 'cisco-asa')) { # Stuff added to support Cisco ASA ip https daemon @@ -2262,9 +2272,13 @@ sub get_ip { # Protect special HTML characters (like '?') $queryif =~ s/([\?&= ])/sprintf("%%%02x", ord($1))/ge; - $url = "https://" . opt('fw', $h) . "/exec/show%20interface%20${queryif}"; - $reply = geturl('', $url, opt('fw-login', $h), opt('fw-password', $h)) || ''; - $arg = $url; + $url = "https://" . opt('fw', $h) . "/exec/show%20interface%20${queryif}"; + $reply = geturl({ + url => $url, + login => opt('fw-login', $h), + password => opt('fw-password', $h), + }) || ''; + $arg = $url; } else { $url = opt('fw', $h) || ''; @@ -2277,7 +2291,11 @@ sub get_ip { $arg = $url; if ($url) { - $reply = geturl('', $url, opt('fw-login', $h), opt('fw-password', $h)) || ''; + $reply = geturl({ + url => $url, + login => opt('fw-login', $h), + password => opt('fw-password', $h), + }) || ''; } } if (!defined $reply) { @@ -2656,7 +2674,12 @@ sub nic_dyndns1_update { $url .= "&backmx=" . ynu($config{$h}{'backupmx'}, 'YES', 'NO'); } - my $reply = geturl(opt('proxy'), $url, $config{$h}{'login'}, $config{$h}{'password'}); + my $reply = geturl({ + proxy => opt('proxy'), + url => $url, + login => $config{$h}{'login'}, + password => $config{$h}{'password'}, + }); if (!defined($reply) || !$reply) { failed("updating %s: Could not connect to %s.", $h, $config{$h}{'server'}); next; @@ -2819,7 +2842,12 @@ sub nic_dyndns2_update { $url .= "&backmx=" . ynu($config{$h}{'backupmx'}, 'YES', 'NO'); } - my $reply = geturl(opt('proxy'), $url, $config{$h}{'login'}, $config{$h}{'password'}); + my $reply = geturl({ + proxy => opt('proxy'), + url => $url, + login => $config{$h}{'login'}, + password => $config{$h}{'password'}, + }); if (!defined($reply) || !$reply) { failed("updating %s: Could not connect to %s.", $hosts, $config{$h}{'server'}); last; @@ -2924,7 +2952,12 @@ sub nic_noip_update { $url .= "&myip="; $url .= $ip if $ip; - my $reply = geturl(opt('proxy'), $url, $config{$h}{'login'}, $config{$h}{'password'}); + my $reply = geturl({ + proxy => opt('proxy'), + url => $url, + login => $config{$h}{'login'}, + password => $config{$h}{'password'}, + }); if (!defined($reply) || !$reply) { failed("updating %s: Could not connect to %s.", $hosts, $config{$h}{'server'}); last; @@ -3059,7 +3092,12 @@ sub nic_dslreports1_update { $url .= "&myip="; $url .= $ip if $ip; - my $reply = geturl(opt('proxy'), $url, $config{$h}{'login'}, $config{$h}{'password'}); + my $reply = geturl({ + proxy => opt('proxy'), + url => $url, + login => $config{$h}{'login'}, + password => $config{$h}{'password'}, + }); if (!defined($reply) || !$reply) { failed("updating %s: Could not connect to %s.", $h, $config{$h}{'server'}); next; @@ -3134,7 +3172,12 @@ sub nic_hammernode1_update { $url .= "&ip="; $url .= $ip if $ip; - my $reply = geturl(opt('proxy'), $url, $config{$h}{'login'}, $config{$h}{'password'}); + my $reply = geturl({ + proxy => opt('proxy'), + url => $url, + login => $config{$h}{'login'}, + password => $config{$h}{'password'}, + }); if (!defined($reply) || !$reply) { failed("updating %s: Could not connect to %s.", $h, $config{$h}{'server'}); last; @@ -3224,7 +3267,12 @@ sub nic_zoneedit1_update { $url .= "&dnsto=$ip" if $ip; $url .= "&zone=$config{$h}{'zone'}" if defined $config{$h}{'zone'}; - my $reply = geturl(opt('proxy'), $url, $config{$h}{'login'}, $config{$h}{'password'}); + my $reply = geturl({ + proxy => opt('proxy'), + url => $url, + login => $config{$h}{'login'}, + password => $config{$h}{'password'}, + }); if (!defined($reply) || !$reply) { failed("updating %s: Could not connect to %s.", $hosts, $config{$h}{'server'}); last; @@ -3374,7 +3422,12 @@ sub nic_easydns_update { $url .= "&backmx=" . ynu($config{$h}{'backupmx'}, 'YES', 'NO'); } - my $reply = geturl(opt('proxy'), $url, $config{$h}{'login'}, $config{$h}{'password'}); + my $reply = geturl({ + proxy => opt('proxy'), + url => $url, + login => $config{$h}{'login'}, + password => $config{$h}{'password'}, + }); if (!defined($reply) || !$reply) { failed("updating %s: Could not connect to %s.", $hosts, $config{$h}{'server'}); last; @@ -3488,7 +3541,7 @@ sub nic_namecheap_update { $url .= "&ip="; $url .= $ip if $ip; - my $reply = geturl(opt('proxy'), $url); + my $reply = geturl({ proxy => opt('proxy'), url => $url }); if (!defined($reply) || !$reply) { failed("updating %s: Could not connect to %s.", $h, $config{$h}{'server'}); last; @@ -3619,7 +3672,13 @@ sub nic_nfsn_make_request { $header .= "Content-Type: application/x-www-form-urlencoded\n"; } - return geturl(opt('proxy'), $url, '', '', $header, $method, $body); + return geturl({ + proxy => opt('proxy'), + url => $url, + headers => $header, + method => $method, + data => $body, + }); } ###################################################################### @@ -3792,7 +3851,7 @@ sub nic_sitelutions_update { $url .= "&ip="; $url .= $ip if $ip; - my $reply = geturl(opt('proxy'), $url); + my $reply = geturl({ proxy => opt('proxy'), url => $url }); if (!defined($reply) || !$reply) { failed("updating %s: Could not connect to %s.", $h, $config{$h}{'server'}); last; @@ -3863,7 +3922,7 @@ sub nic_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'}"); - my $reply = geturl(opt('proxy'), $url); + my $reply = geturl({ proxy => opt('proxy'), url => $url }); if (!defined($reply) || !$reply || !header_ok($_[0], $reply)) { failed("updating %s: Could not connect to %s for site list.", $_[0], $url); return; @@ -3891,7 +3950,7 @@ sub nic_freedns_update { $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]); + my $reply = geturl({proxy => opt('proxy'), url => $freedns_hosts{$h}->[2] }); if (!defined($reply) || !$reply) { failed("updating %s: Could not connect to %s.", $h, $freedns_hosts{$h}->[2]); last; @@ -3968,7 +4027,12 @@ sub nic_changeip_update { $url .= "&ip="; $url .= $ip if $ip; - my $reply = geturl(opt('proxy'), $url, $config{$h}{'login'}, $config{$h}{'password'}); + my $reply = geturl({ + proxy => opt('proxy'), + url => $url, + login => $config{$h}{'login'}, + password => $config{$h}{'password'}, + }); if (!defined($reply) || !$reply) { failed("updating %s: Could not connect to %s.", $h, $config{$h}{'server'}); last; @@ -4044,7 +4108,7 @@ sub nic_dtdns_update { $url .= $config{$h}{'client'}; # Try to get URL - my $reply = geturl(opt('proxy'), $url); + my $reply = geturl({ proxy => opt('proxy'), url => $url }); # No response, declare as failed if (!defined($reply) || !$reply) { @@ -4129,7 +4193,12 @@ sub nic_googledomains_update { $url .= "&myip="; $url .= $ip if $ip; - my $reply = geturl(opt('proxy'), $url, $config{$host}{'login'}, $config{$host}{'password'}); + my $reply = geturl({ + proxy => opt('proxy'), + url => $url, + login => $config{$host}{'login'}, + password => $config{$host}{'password'}, + }); unless ($reply) { failed("updating %s: Could not connect to %s.", $host, $config{$host}{'server'}); last; @@ -4338,7 +4407,7 @@ sub nic_cloudflare_update { my $url = "https://$config{$key}{'server'}/zones?"; $url .= "name=".$config{$key}{'zone'}; - my $reply = geturl(opt('proxy'), $url, undef, undef, $headers); + my $reply = geturl({ proxy => opt('proxy'), url => $url, headers => $headers }); unless ($reply) { failed("updating %s: Could not connect to %s.", $domain, $config{$key}{'server'}); last; @@ -4369,7 +4438,7 @@ sub nic_cloudflare_update { $url .= "type=A&name=$domain"; } - $reply = geturl(opt('proxy'), $url, undef, undef, $headers); + $reply = geturl({ proxy => opt('proxy'), url => $url, headers => $headers }); unless ($reply) { failed("updating %s: Could not connect to %s.", $domain, $config{$key}{'server'}); last; @@ -4395,7 +4464,13 @@ sub nic_cloudflare_update { # Set domain $url = "https://$config{$key}{'server'}/zones/$zone_id/dns_records/$dns_rec_id"; my $data = "{\"content\":\"$ip\"}"; - $reply = geturl(opt('proxy'), $url, undef, undef, $headers, "PATCH", $data); + $reply = geturl({ + proxy => opt('proxy'), + url => $url, + headers => $headers, + method => "PATCH", + data => $data, + }); unless ($reply) { failed("updating %s: Could not connect to %s.", $domain, $config{$domain}{'server'}); last; @@ -4479,7 +4554,7 @@ sub nic_yandex_update { my $url = "https://$config{$host}{'server'}/api2/admin/dns/list?"; $url .= "domain="; $url .= $config{$key}{'login'}; - my $reply = geturl(opt('proxy'), $url, '', '', $headers); + my $reply = geturl({ proxy => opt('proxy'), url => $url, headers => $headers }); unless ($reply) { failed("updating %s: Could not connect to %s.", $host, $config{$key}{'server'}); last; @@ -4510,7 +4585,13 @@ sub nic_yandex_update { $data .= "&content="; $data .= $ip if $ip; - $reply = geturl(opt('proxy'), $url, '', '', $headers, 'POST', $data); + $reply = geturl({ + proxy => opt('proxy'), + url => $url, + headers => $headers, + method => 'POST', + data => $data, + }); unless ($reply) { failed("updating %s: Could not connect to %s.", $host, $config{$host}{'server'}); last; @@ -4588,7 +4669,7 @@ sub nic_duckdns_update { # Try to get URL - my $reply = geturl(opt('proxy'), $url); + my $reply = geturl({ proxy => opt('proxy'), url => $url }); # No response, declare as failed if (!defined($reply) || !$reply) { @@ -4659,7 +4740,7 @@ sub nic_freemyip_update { $url .= $h; # Try to get URL - my $reply = geturl(opt('proxy'), $url); + my $reply = geturl({ proxy => opt('proxy'), url => $url }); # No response, declare as failed if (!defined($reply) || !$reply) { @@ -4787,7 +4868,12 @@ sub nic_woima_update { $url .= "&backmx=" . ynu($config{$h}{'backupmx'}, 'YES', 'NO'); } - my $reply = geturl(opt('proxy'), $url, $config{$h}{'login'}, $config{$h}{'password'}); + my $reply = geturl({ + proxy => opt('proxy'), + url => $url, + login => $config{$h}{'login'}, + password => $config{$h}{'password'}, + }); if (!defined($reply) || !$reply) { failed("updating %s: Could not connect to %s.", $h, $config{$h}{'server'}); last; @@ -4903,7 +4989,7 @@ sub nic_dondominio_update { # Try to get URL - my $reply = geturl(opt('proxy'), $url); + my $reply = geturl({ proxy => opt('proxy'), url => $url }); # No response, declare as failed if (!defined($reply) || !$reply) { @@ -4989,7 +5075,7 @@ sub nic_dnsmadeeasy_update { $url .= "&id=$h"; # Try to get URL - my $reply = geturl(opt('proxy'), $url); + my $reply = geturl({ proxy => opt('proxy'), url => $url }); # No response, declare as failed if (!defined($reply) || !$reply) { @@ -5060,7 +5146,12 @@ sub nic_ovh_update { $url .= "&myip="; $url .= $ip if $ip; - my $reply = geturl(opt('proxy'), $url, "$config{$h}{'login'}", "$config{$h}{'password'}"); + my $reply = geturl({ + proxy => opt('proxy'), + url => $url, + login => $config{$h}{'login'}, + password => $config{$h}{'password'}, + }); if (!defined($reply) || !$reply) { failed("updating %s: Could not connect to %s.", $h, $config{$h}{'server'}); From 39bd6fce9e78a58763fdf16fb65c3274d739ee44 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 11 Jun 2020 13:23:25 -0400 Subject: [PATCH 2/2] New geturl param to force IPv4 or IPv6 This will be used by the upcoming `webv4` and `webv6` options to ensure that the checkip service returns the desired type of IP address. Addresses #172 --- ddclient | 84 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/ddclient b/ddclient index 62ff478..86a32f3 100755 --- a/ddclient +++ b/ddclient @@ -2005,6 +2005,7 @@ sub geturl { my $url = $params->{url} // ''; my $login = $params->{login} // ''; my $password = $params->{password} // ''; + my $ipversion = $params->{ipversion} // ''; my $headers = $params->{headers} // ''; my $method = $params->{method} // 'GET'; my $data = $params->{data} // ''; @@ -2027,7 +2028,6 @@ 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; @@ -2036,6 +2036,7 @@ sub geturl { debug("protocol = %s", $use_ssl ? "https" : "http"); debug("server = %s", $server); debug("url = %s", $url); + debug("ip ver = %s", $ipversion); ## determine peer and port to use. $peer = $proxy || $server; @@ -2045,9 +2046,6 @@ sub geturl { $port = $default_port unless $port =~ /^\d+$/; $peer =~ s%:.*$%%; - my $to = sprintf "%s%s", $server, $proxy ? " via proxy $peer:$port" : ""; - verbose("CONNECT:", "%s", $to); - $request = "$method "; if (!$use_ssl) { $request .= "http://$server" if $proxy; @@ -2073,57 +2071,61 @@ sub geturl { $request .= $data; $rq .= $data; - - $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'), - ); - defined $sd or warning("cannot connect to %s:%s socket: %s %s", $peer, $port, $@, IO::Socket::SSL::errstr()); - } elsif ($globals{'ipv6'}) { + my $socket_class = 'IO::Socket::INET'; + if ($use_ssl) { + # IO::Socket::SSL will load IPv6 support if available on the system. + load_ssl_support; + $socket_class = 'IO::Socket::SSL'; + } elsif ($globals{'ipv6'} || $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 %s:%s socket: %s", $peer, $port, $@); - } else { - $sd = IO::Socket::INET->new( - PeerAddr => $peer, - PeerPort => $port, - Proto => 'tcp', - MultiHomed => 1, - Timeout => opt('timeout'), - ); - defined $sd or warning("cannot connect to %s:%s socket: %s", $peer, $port, $@); + $socket_class = 'IO::Socket::INET6'; + } + my %socket_args = ( + PeerAddr => $peer, + PeerPort => $port, + Proto => 'tcp', + MultiHomed => 1, + Timeout => opt('timeout'), + ); + if ($ipversion eq '4') { + $socket_args{Domain} = PF_INET; + $socket_args{Family} = AF_INET; + } elsif ($ipversion eq '6') { + $socket_args{Domain} = PF_INET6; + $socket_args{Family} = AF_INET6; + } elsif ($ipversion ne '') { + fatal("geturl passed unsupported 'ipversion' value %s", $ipversion); } + my $ipv = $ipversion eq '' ? '' : sprintf(" (IPv%s)", $ipversion); + my $peer_port_ipv = sprintf("%s:%s%s", $peer, $port, $ipv); + my $to = sprintf("%s%s%s", $server, $proxy ? " via proxy $peer:$port" : "", $ipv); + verbose("CONNECT:", "%s", $to); + $0 = sprintf("%s - connecting to %s", $program, $peer_port_ipv); + if (opt('exec')) { + $sd = $socket_class->new(%socket_args); + defined($sd) or warning("cannot connect to %s socket: %s%s", $peer_port_ipv, $@, + $use_ssl ? ' ' . IO::Socket::SSL::errstr() : ''); + } else { + debug("skipped network connection"); + verbose("SENDING:", "%s", $request); + } if (defined $sd) { ## send the request to the http server verbose("CONNECTED: ", $use_ssl ? 'using SSL' : 'using HTTP'); verbose("SENDING:", "%s", $request); - $0 = sprintf("%s - sending to %s port %s", $program, $peer, $port); + $0 = sprintf("%s - sending to %s", $program, $peer_port_ipv); my $result = syswrite $sd, $rq; if ($result != length($rq)) { - warning("cannot send to %s:%s (%s).", $peer, $port, $!); + warning("cannot send to %s (%s).", $peer_port_ipv, $!); } else { - $0 = sprintf("%s - reading from %s port %s", $program, $peer, $port); + $0 = sprintf("%s - reading from %s", $program, $peer_port_ipv); eval { local $SIG{'ALRM'} = sub { die "timeout"; }; alarm(opt('timeout')) if opt('timeout') > 0; while ($_ = <$sd>) { - $0 = sprintf("%s - read from %s port %s", $program, $peer, $port); + $0 = sprintf("%s - read from %s", $program, $peer_port_ipv); verbose("RECEIVE:", "%s", define($_, "")); $reply .= $_ if defined $_; } @@ -2140,7 +2142,7 @@ sub geturl { $reply = '' if !defined $reply; } } - $0 = sprintf("%s - closed %s port %s", $program, $peer, $port); + $0 = sprintf("%s - closed %s", $program, $peer_port_ipv); ## during testing simulate reading the URL if (opt('test')) {