Merge pull request #742 from rhansen/strategy-dedup

Improve deduplication of redundant `use*` queries
This commit is contained in:
Richard Hansen 2024-09-06 18:58:09 -04:00 committed by GitHub
commit 1c0ba9a126
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 443 additions and 243 deletions

View file

@ -279,11 +279,11 @@ our %builtinweb = (
); );
sub query_cisco { sub query_cisco {
my ($h, $asa, $v4) = @_; my ($asa, $v4, %p) = @_;
warning("'--if' is deprecated; use '--ifv4' instead") warning("'--if' is deprecated; use '--ifv4' instead")
if ($v4 && !defined(opt('ifv4', $h)) && defined(opt('if', $h))); if ($v4 && !defined($p{'ifv4'}) && defined($p{'if'}));
my $if = ($v4 ? opt('ifv4', $h) : undef) // opt('if', $h); my $if = ($v4 ? $p{'ifv4'} : undef) // $p{'if'};
my $fw = ($v4 ? opt('fwv4', $h) : undef) // opt('fw', $h); my $fw = ($v4 ? $p{'fwv4'} : undef) // $p{'fw'};
# Convert slashes to protected value "\/" # Convert slashes to protected value "\/"
$if =~ s%\/%\\\/%g; $if =~ s%\/%\\\/%g;
# Protect special HTML characters (like '?') # Protect special HTML characters (like '?')
@ -292,10 +292,10 @@ sub query_cisco {
url => ($asa) url => ($asa)
? "https://$fw/exec/show%20interface%20$if" ? "https://$fw/exec/show%20interface%20$if"
: "http://$fw/level/1/exec/show/ip/interface/brief/$if/CR", : "http://$fw/level/1/exec/show/ip/interface/brief/$if/CR",
login => opt('fw-login', $h), login => $p{'fw-login'},
password => opt('fw-password', $h), password => $p{'fw-password'},
ignore_ssl_option => 1, ignore_ssl_option => 1,
ssl_validate => opt('fw-ssl-validate', $h), ssl_validate => $p{'fw-ssl-validate'},
); );
return undef if !header_ok($reply, \&warning); return undef if !header_ok($reply, \&warning);
$reply =~ s/^.*?\n\n//s; $reply =~ s/^.*?\n\n//s;
@ -345,14 +345,18 @@ our %builtinfw = (
}, },
'cisco' => { 'cisco' => {
'name' => 'Cisco FW', 'name' => 'Cisco FW',
'query' => sub { return query_cisco($_[0], 0, 0); }, 'query' => sub { return query_cisco(0, 0, @_); },
'queryv4' => sub { return query_cisco($_[0], 0, 1); }, 'inputs' => ['fw', 'if', 'fw-login', 'fw-password', 'fw-ssl-validate'],
'queryv4' => sub { return query_cisco(0, 1, @_); },
'inputsv4' => ['fwv4', 'fw', 'ifv4', 'if', 'fw-login', 'fw-password', 'fw-ssl-validate'],
'help' => sub { return " at the host given by --fw$_[0]=<host> and interface given by --if$_[0]=<interface>"; }, 'help' => sub { return " at the host given by --fw$_[0]=<host> and interface given by --if$_[0]=<interface>"; },
}, },
'cisco-asa' => { 'cisco-asa' => {
'name' => 'Cisco ASA', 'name' => 'Cisco ASA',
'query' => sub { return query_cisco($_[0], 1, 0); }, 'query' => sub { return query_cisco(1, 0, @_); },
'queryv4' => sub { return query_cisco($_[0], 1, 1); }, 'inputs' => ['fw', 'if', 'fw-login', 'fw-password', 'fw-ssl-validate'],
'queryv4' => sub { return query_cisco(1, 1, @_); },
'inputsv4' => ['fwv4', 'fw', 'ifv4', 'if', 'fw-login', 'fw-password', 'fw-ssl-validate'],
'help' => sub { return " at the host given by --fw$_[0]=<host> and interface given by --if$_[0]=<interface>"; }, 'help' => sub { return " at the host given by --fw$_[0]=<host> and interface given by --if$_[0]=<interface>"; },
}, },
'dlink-524' => { 'dlink-524' => {
@ -552,66 +556,109 @@ our %builtinfw = (
}, },
); );
my %ip_strategies = ( sub builtinfw_strategy {
'disabled' => ": do not use a deprecated method to obtain an IP address for this host", my ($n) = @_;
'no' => ": deprecated, see '--use=disabled'", my $fw = $builtinfw{$n};
'ip' => ": deprecated, see '--usev4=ipv4' and '--usev6=ipv6'", return ($n => {
'web' => ": deprecated, see '--usev4=webv4' and '--usev6=webv6'", help => ": deprecated, see '--usev4=$n'" .
'fw' => ": deprecated, see '--usev4=fwv4' and '--usev6=fwv6'", (defined($fw->{queryv6}) ? " and '--usev6=$n'" : ''),
'if' => ": deprecated, see '--usev4=ifv4' and '--usev6=ifv6'", inputs => $fw->{inputs} // ['fw', 'fw-skip', 'fw-login', 'fw-password', 'fw-ssl-validate'],
'cmd' => ": deprecated, see '--usev4=cmdv4' and '--usev6=cmdv6'", });
map({ }
my $fw = $builtinfw{$_};
$_ => ": deprecated, see '--usev4=$_'" . our %ip_strategies = (
(defined($fw->{queryv6}) ? " and '--usev6=$_'" : ''); 'disabled' => {help => ": do not use a deprecated method to obtain an IP address for this host",
} keys(%builtinfw)), inputs => []},
'no' => {help => ": deprecated, see '--use=disabled'",
inputs => []},
'ip' => {help => ": deprecated, see '--usev4=ipv4' and '--usev6=ipv6'",
inputs => ['ip']},
'web' => {help => ": deprecated, see '--usev4=webv4' and '--usev6=webv6'",
inputs => ['web', 'web-skip', 'proxy', 'web-ssl-validate']},
'fw' => {help => ": deprecated, see '--usev4=fwv4' and '--usev6=fwv6'",
inputs => ['fw', 'fw-skip', 'fw-login', 'fw-password', 'fw-ssl-validate']},
'if' => {help => ": deprecated, see '--usev4=ifv4' and '--usev6=ifv6'",
inputs => ['if']},
'cmd' => {help => ": deprecated, see '--usev4=cmdv4' and '--usev6=cmdv6'",
inputs => ['cmd', 'cmd-skip']},
map(builtinfw_strategy($_), keys(%builtinfw)),
); );
sub ip_strategies_usage { sub ip_strategies_usage {
return map({ sprintf(" --use=%-22s %s.", $_, $ip_strategies{$_}) } return map({ sprintf(" --use=%-22s %s.", $_, $ip_strategies{$_}{help}); }
'disabled', 'no', 'ip', 'web', 'if', 'cmd', 'fw', sort(keys(%builtinfw))); 'disabled', 'no', 'ip', 'web', 'if', 'cmd', 'fw', sort(keys(%builtinfw)));
} }
my %ipv4_strategies = ( sub builtinfwv4_strategy {
'disabled' => ": do not obtain an IPv4 address for this host (except possibly via the deprecated '--use' option, if it is enabled)", my ($n) = @_;
'ipv4' => ": obtain IPv4 from the address given by --ipv4=<address>", my $fw = $builtinfw{$n};
'webv4' => ": obtain IPv4 from an IP discovery page on the web", return ($n => {
'ifv4' => ": obtain IPv4 from the interface given by --ifv4=<interface>", help => defined($fw->{queryv4})
'cmdv4' => ": obtain IPv4 from the command given by --cmdv4=<command>",
'fwv4' => ": obtain IPv4 from the URL given by --fwv4=<URL>",
map({
my $fw = $builtinfw{$_};
$_ => defined($fw->{queryv4})
? ": obtain IPv4 from $fw->{name}@{[($fw->{help} // sub {})->('v4') // '']}" ? ": obtain IPv4 from $fw->{name}@{[($fw->{help} // sub {})->('v4') // '']}"
: ": obtain IPv4 from $fw->{name} at the host or URL given by --fwv4=<host|URL>"; : ": obtain IPv4 from $fw->{name} at the host or URL given by --fwv4=<host|URL>",
} keys(%builtinfw)), inputs => $fw->{inputsv4} // ['fwv4', 'fw', 'fwv4-skip', 'fw-skip', 'fw-login',
'fw-password', 'fw-ssl-validate'],
});
}
our %ipv4_strategies = (
'disabled' => {help => ": do not obtain an IPv4 address for this host (except possibly via the deprecated '--use' option, if it is enabled)",
inputs => []},
'ipv4' => {help => ": obtain IPv4 from the address given by --ipv4=<address>",
inputs => ['ipv4']},
'webv4' => {help => ": obtain IPv4 from an IP discovery page on the web",
inputs => ['webv4', 'webv4-skip', 'proxy', 'web-ssl-validate']},
'ifv4' => {help => ": obtain IPv4 from the interface given by --ifv4=<interface>",
inputs => ['ifv4']},
'cmdv4' => {help => ": obtain IPv4 from the command given by --cmdv4=<command>",
inputs => ['cmdv4', 'cmd-skip']},
'fwv4' => {help => ": obtain IPv4 from the URL given by --fwv4=<URL>",
inputs => ['fwv4', 'fw', 'fwv4-skip', 'fw-skip', 'fw-login', 'fw-password', 'fw-ssl-validate']},
map(builtinfwv4_strategy($_), keys(%builtinfw)),
); );
sub ipv4_strategies_usage { sub ipv4_strategies_usage {
return map({ sprintf(" --usev4=%-22s %s.", $_, $ipv4_strategies{$_}) } return map({ sprintf(" --usev4=%-22s %s.", $_, $ipv4_strategies{$_}{help}) }
'disabled', 'ipv4', 'webv4', 'ifv4', 'cmdv4', 'fwv4', sort(keys(%builtinfw))); 'disabled', 'ipv4', 'webv4', 'ifv4', 'cmdv4', 'fwv4', sort(keys(%builtinfw)));
} }
my %ipv6_strategies = ( sub builtinfwv6_strategy {
'disabled' => ": do not obtain an IPv6 address for this host (except possibly via the deprecated '--use' option, if it is enabled)", my ($n) = @_;
'no' => ": deprecated, use '--usev6=disabled'", my $fw = $builtinfw{$n};
'ipv6' => ": obtain IPv6 from the address given by --ipv6=<address>", return defined($fw->{queryv6})
'ip' => ": deprecated, use '--usev6=ipv6'", ? ($n => {
'webv6' => ": obtain IPv6 from an IP discovery page on the web", help => ": obtain IPv6 from $fw->{name}@{[($fw->{help} // sub {})->('v6') // '']}",
'web' => ": deprecated, use '--usev6=webv6'", inputs => $fw->{inputsv6} // ['fwv6', 'fwv6-skip'],
'ifv6' => ": obtain IPv6 from the interface given by --ifv6=<interface>", })
'if' => ": deprecated, use '--usev6=ifv6'",
'cmdv6' => ": obtain IPv6 from the command given by --cmdv6=<command>",
'cmd' => ": deprecated, use '--usev6=cmdv6'",
'fwv6' => ": obtain IPv6 from the URL given by --fwv6=<URL>",
map({
my $fw = $builtinfw{$_};
defined($fw->{queryv6})
? ($_ => ": obtain IPv6 from $fw->{name}@{[($fw->{help} // sub {})->('v6') // '']}")
: (); : ();
} keys(%builtinfw)), }
our %ipv6_strategies = (
'disabled' => {help => ": do not obtain an IPv6 address for this host (except possibly via the deprecated '--use' option, if it is enabled)",
inputs => []},
'no' => {help => ": deprecated, use '--usev6=disabled'",
inputs => []},
'ipv6' => {help => ": obtain IPv6 from the address given by --ipv6=<address>",
inputs => ['ipv6', 'ip']},
'ip' => {help => ": deprecated, use '--usev6=ipv6'",
inputs => ['ipv6', 'ip']},
'webv6' => {help => ": obtain IPv6 from an IP discovery page on the web",
inputs => ['webv6', 'web', 'webv6-skip', 'web-skip', 'proxy', 'web-ssl-validate']},
'web' => {help => ": deprecated, use '--usev6=webv6'",
inputs => ['webv6', 'web', 'webv6-skip', 'web-skip', 'proxy', 'web-ssl-validate']},
'ifv6' => {help => ": obtain IPv6 from the interface given by --ifv6=<interface>",
inputs => ['ifv6', 'if']},
'if' => {help => ": deprecated, use '--usev6=ifv6'",
inputs => ['ifv6', 'if']},
'cmdv6' => {help => ": obtain IPv6 from the command given by --cmdv6=<command>",
inputs => ['cmdv6', 'cmd', 'cmd-skip']},
'cmd' => {help => ": deprecated, use '--usev6=cmdv6'",
inputs => ['cmdv6', 'cmd', 'cmd-skip']},
'fwv6' => {help => ": obtain IPv6 from the URL given by --fwv6=<URL>",
inputs => ['fwv6', 'fwv6-skip']},
map(builtinfwv6_strategy($_), keys(%builtinfw)),
); );
sub ipv6_strategies_usage { sub ipv6_strategies_usage {
return map({ sprintf(" --usev6=%-22s %s.", $_, $ipv6_strategies{$_}) } return map({ sprintf(" --usev6=%-22s %s.", $_, $ipv6_strategies{$_}{help}) }
'disabled', 'no', 'ipv6', 'ip', 'webv6', 'web', 'ifv6', 'if', 'cmdv6', 'cmd', 'disabled', 'no', 'ipv6', 'ip', 'webv6', 'web', 'ifv6', 'if', 'cmdv6', 'cmd',
'fwv6', sort(map({exists($ipv6_strategies{$_}) ? ($_) : ()} keys(%builtinfw)))); 'fwv6', sort(map({exists($ipv6_strategies{$_}) ? ($_) : ()} keys(%builtinfw))));
} }
@ -1486,9 +1533,9 @@ sub runpostscript {
###################################################################### ######################################################################
sub update_nics { sub update_nics {
my %examined = (); my %examined = ();
my %iplist = (); my %use_results;
my %ipv4list = (); my %usev4_results;
my %ipv6list = (); my %usev6_results;
for my $p (sort keys %protocols) { for my $p (sort keys %protocols) {
my (@hosts, %ipsv4, %ipsv6) = (); my (@hosts, %ipsv4, %ipsv6) = ();
@ -1500,74 +1547,44 @@ sub update_nics {
my $use = opt('use', $h); my $use = opt('use', $h);
my $usev4 = opt('usev4', $h); my $usev4 = opt('usev4', $h);
my $usev6 = opt('usev6', $h); my $usev6 = opt('usev6', $h);
my $arg_ip = opt('ip', $h) // '';
my $arg_ipv4 = opt('ipv4', $h) // '';
my $arg_ipv6 = opt('ipv6', $h) // '';
my $arg_fw = opt('fw', $h) // '';
my $arg_fwv4 = opt('fwv4', $h) // '';
my $arg_fwv6 = opt('fwv6', $h) // '';
my $arg_if = opt('if', $h) // '';
my $arg_ifv4 = opt('ifv4', $h) // '';
my $arg_ifv6 = opt('ifv6', $h) // '';
my $arg_web = opt('web', $h) // '';
my $arg_webv4 = opt('webv4', $h) // '';
my $arg_webv6 = opt('webv6', $h) // '';
my $arg_cmd = opt('cmd', $h) // '';
my $arg_cmdv4 = opt('cmdv4', $h) // '';
my $arg_cmdv6 = opt('cmdv6', $h) // '';
my $ip = undef; my $ip = undef;
my $ipv4 = undef; my $ipv4 = undef;
my $ipv6 = undef; my $ipv6 = undef;
if ($use ne 'disabled') { if ($use ne 'disabled') {
if (exists $iplist{$use}{$arg_ip}{$arg_fw}{$arg_if}{$arg_web}{$arg_cmd}) { my %inputs = strategy_inputs('use', $h);
# If we have already done a get_ip() for this, don't do it again. my $sig = repr(\%inputs, Indent => 0);
$ip = $iplist{$use}{$arg_ip}{$arg_fw}{$arg_if}{$arg_web}{$arg_cmd}; $use_results{$sig} //= get_ip(%inputs);
} else { if (!is_ipv4($use_results{$sig}) && !is_ipv6($use_results{$sig})) {
# Else need to find the IP address...
$ip = get_ip($use, $h);
if (is_ipv4($ip) || is_ipv6($ip)) {
# And if it is valid, remember it...
$iplist{$use}{$arg_ip}{$arg_fw}{$arg_if}{$arg_web}{$arg_cmd} = $ip;
} else {
warning("unable to determine IP address with strategy '--use=$use'") warning("unable to determine IP address with strategy '--use=$use'")
if !$daemon || opt('verbose'); if !$daemon || opt('verbose');
delete $use_results{$sig};
} }
} $ip = $use_results{$sig};
} }
if ($usev4 ne 'disabled') { if ($usev4 ne 'disabled') {
if (exists $ipv4list{$usev4}{$arg_ipv4}{$arg_fwv4}{$arg_ifv4}{$arg_webv4}{$arg_cmdv4}) { my %inputs = strategy_inputs('usev4', $h);
# If we have already done a get_ipv4() for this, don't do it again. my $sig = repr(\%inputs, Indent => 0);
$ipv4 = $ipv4list{$usev4}{$arg_ipv4}{$arg_fwv4}{$arg_ifv4}{$arg_webv4}{$arg_cmdv4}; $usev4_results{$sig} //= get_ipv4(%inputs);
} else { if (!is_ipv4($usev4_results{$sig})) {
# Else need to find the IPv4 address...
$ipv4 = get_ipv4($usev4, $h);
if (is_ipv4($ipv4)) {
# And if it is valid, remember it...
$ipv4list{$usev4}{$arg_ipv4}{$arg_fwv4}{$arg_ifv4}{$arg_webv4}{$arg_cmdv4} = $ipv4;
} else {
warning("unable to determine IPv4 address with strategy '--usev4=$usev4'") warning("unable to determine IPv4 address with strategy '--usev4=$usev4'")
if !$daemon || opt('verbose'); if !$daemon || opt('verbose');
delete $usev4_results{$sig};
} }
} $ipv4 = $usev4_results{$sig};
} }
if ($usev6 ne 'disabled') { if ($usev6 ne 'disabled') {
if (exists $ipv6list{$usev6}{$arg_ipv6}{$arg_fwv6}{$arg_ifv6}{$arg_webv6}{$arg_cmdv6}) { my %inputs = strategy_inputs('usev6', $h);
# If we have already done a get_ipv6() for this, don't do it again. my $sig = repr(\%inputs, Indent => 0);
$ipv6 = $ipv6list{$usev6}{$arg_ipv6}{$arg_fwv6}{$arg_ifv6}{$arg_webv6}{$arg_cmdv6}; $usev6_results{$sig} //= get_ipv6(%inputs);
} else { if (!is_ipv6($usev6_results{$sig})) {
# Else need to find the IPv6 address...
$ipv6 = get_ipv6($usev6, $h);
if (is_ipv6($ipv6)) {
# And if it is valid, remember it...
$ipv6list{$usev6}{$arg_ipv6}{$arg_fwv6}{$arg_ifv6}{$arg_webv6}{$arg_cmdv6} = $ipv6;
} else {
warning("unable to determine IPv6 address with strategy '--usev6=$usev6'") warning("unable to determine IPv6 address with strategy '--usev6=$usev6'")
if !$daemon || opt('verbose'); if !$daemon || opt('verbose');
delete $usev6_results{$sig};
} }
} $ipv6 = $usev6_results{$sig};
} }
$ip //= $ipv4 // $ipv6; $ip //= $ipv4 // $ipv6;
@ -2143,7 +2160,8 @@ sub test_possible_ip {
printf "----- Test_possible_ip with 'get_ip' -----\n"; printf "----- Test_possible_ip with 'get_ip' -----\n";
if (defined(opt('ip'))) { if (defined(opt('ip'))) {
local $opt{'use'} = 'ip'; local $opt{'use'} = 'ip';
printf "use=ip, ip=%s address is %s\n", opt('ip'), get_ip('ip') // 'NOT FOUND'; printf("use=ip, ip=%s address is %s\n",
opt('ip'), get_ip(strategy_inputs('use')) // 'NOT FOUND');
} }
{ {
local $opt{'use'} = 'if'; local $opt{'use'} = 'if';
@ -2157,18 +2175,21 @@ sub test_possible_ip {
warning("failed to get list of interfaces") if !@ifs; warning("failed to get list of interfaces") if !@ifs;
for my $if (@ifs) { for my $if (@ifs) {
local $opt{'if'} = $if; local $opt{'if'} = $if;
printf "use=if, if=%s address is %s\n", opt('if'), get_ip('if') // 'NOT FOUND'; printf("use=if, if=%s address is %s\n",
opt('if'), get_ip(strategy_inputs('use')) // 'NOT FOUND');
} }
} }
if (opt('fw')) { if (opt('fw')) {
if (opt('fw') !~ m%/%) { if (opt('fw') !~ m%/%) {
for my $fw (sort keys %builtinfw) { for my $fw (sort keys %builtinfw) {
local $opt{'use'} = $fw; local $opt{'use'} = $fw;
printf "use=%s address is %s\n", $fw, get_ip($fw) // 'NOT FOUND'; printf("use=%s address is %s\n",
$fw, get_ip(strategy_inputs('use')) // 'NOT FOUND');
} }
} }
local $opt{'use'} = 'fw'; local $opt{'use'} = 'fw';
printf "use=fw, fw=%s address is %s\n", opt('fw'), get_ip('fw') // 'NOT FOUND' printf("use=fw, fw=%s address is %s\n",
opt('fw'), get_ip(strategy_inputs('use')) // 'NOT FOUND')
if !exists $builtinfw{opt('fw')}; if !exists $builtinfw{opt('fw')};
} }
@ -2176,21 +2197,25 @@ sub test_possible_ip {
local $opt{'use'} = 'web'; local $opt{'use'} = 'web';
for my $web (sort keys %builtinweb) { for my $web (sort keys %builtinweb) {
local $opt{'web'} = $web; local $opt{'web'} = $web;
printf "use=web, web=%s address is %s\n", $web, get_ip('web') // 'NOT FOUND'; printf("use=web, web=%s address is %s\n",
$web, get_ip(strategy_inputs('use')) // 'NOT FOUND');
} }
printf "use=web, web=%s address is %s\n", opt('web'), get_ip('web') // 'NOT FOUND' printf("use=web, web=%s address is %s\n",
opt('web'), get_ip(strategy_inputs('use')) // 'NOT FOUND')
if !exists $builtinweb{opt('web')}; if !exists $builtinweb{opt('web')};
} }
if (opt('cmd')) { if (opt('cmd')) {
local $opt{'use'} = 'cmd'; local $opt{'use'} = 'cmd';
printf "use=cmd, cmd=%s address is %s\n", opt('cmd'), get_ip('cmd') // 'NOT FOUND'; printf("use=cmd, cmd=%s address is %s\n",
opt('cmd'), get_ip(strategy_inputs('use')) // 'NOT FOUND');
} }
# Now force IPv4 # Now force IPv4
printf "----- Test_possible_ip with 'get_ipv4' ------\n"; printf "----- Test_possible_ip with 'get_ipv4' ------\n";
if (defined(opt('ipv4'))) { if (defined(opt('ipv4'))) {
local $opt{'usev4'} = 'ipv4'; local $opt{'usev4'} = 'ipv4';
printf "usev4=ipv4, ipv4=%s address is %s\n", opt('ipv4'), get_ipv4('ipv4') // 'NOT FOUND'; printf("usev4=ipv4, ipv4=%s address is %s\n",
opt('ipv4'), get_ipv4(strategy_inputs('usev4')) // 'NOT FOUND');
} }
{ {
local $opt{'usev4'} = 'ifv4'; local $opt{'usev4'} = 'ifv4';
@ -2204,29 +2229,34 @@ sub test_possible_ip {
warning("failed to get list of interfaces") if !@ifs; warning("failed to get list of interfaces") if !@ifs;
for my $if (@ifs) { for my $if (@ifs) {
local $opt{'ifv4'} = $if; local $opt{'ifv4'} = $if;
printf "usev4=ifv4, ifv4=%s address is %s\n", opt('ifv4'), get_ipv4('ifv4') // 'NOT FOUND'; printf("usev4=ifv4, ifv4=%s address is %s\n",
opt('ifv4'), get_ipv4(strategy_inputs('usev4')) // 'NOT FOUND');
} }
} }
{ {
local $opt{'usev4'} = 'webv4'; local $opt{'usev4'} = 'webv4';
for my $web (sort keys %builtinweb) { for my $web (sort keys %builtinweb) {
local $opt{'webv4'} = $web; local $opt{'webv4'} = $web;
printf "usev4=webv4, webv4=$web address is %s\n", get_ipv4('webv4') // 'NOT FOUND' printf("usev4=webv4, webv4=%s address is %s\n",
$web, get_ipv4(strategy_inputs('usev4')) // 'NOT FOUND')
if ($web !~ "6") ## Don't bother if web site only supports IPv6; if ($web !~ "6") ## Don't bother if web site only supports IPv6;
} }
printf "usev4=webv4, webv4=%s address is %s\n", opt('webv4'), get_ipv4('webv4') // 'NOT FOUND' printf("usev4=webv4, webv4=%s address is %s\n",
opt('webv4'), get_ipv4(strategy_inputs('usev4')) // 'NOT FOUND')
if ! exists $builtinweb{opt('webv4')}; if ! exists $builtinweb{opt('webv4')};
} }
if (opt('cmdv4')) { if (opt('cmdv4')) {
local $opt{'usev4'} = 'cmdv4'; local $opt{'usev4'} = 'cmdv4';
printf "usev4=cmdv4, cmdv4=%s address is %s\n", opt('cmdv4'), get_ipv4('cmdv4') // 'NOT FOUND'; printf("usev4=cmdv4, cmdv4=%s address is %s\n",
opt('cmdv4'), get_ipv4(strategy_inputs('usev4')) // 'NOT FOUND');
} }
# Now force IPv6 # Now force IPv6
printf "----- Test_possible_ip with 'get_ipv6' -----\n"; printf "----- Test_possible_ip with 'get_ipv6' -----\n";
if (defined(opt('ipv6'))) { if (defined(opt('ipv6'))) {
local $opt{'usev6'} = 'ipv6'; local $opt{'usev6'} = 'ipv6';
printf "usev6=ipv6, ipv6=%s address is %s\n", opt('ipv6'), get_ipv6('ipv6') // 'NOT FOUND'; printf("usev6=ipv6, ipv6=%s address is %s\n",
opt('ipv6'), get_ipv6(strategy_inputs('usev6')) // 'NOT FOUND');
} }
{ {
local $opt{'usev6'} = 'ifv6'; local $opt{'usev6'} = 'ifv6';
@ -2240,22 +2270,26 @@ sub test_possible_ip {
warning("failed to get list of interfaces") if !@ifs; warning("failed to get list of interfaces") if !@ifs;
for my $if (@ifs) { for my $if (@ifs) {
local $opt{'ifv6'} = $if; local $opt{'ifv6'} = $if;
printf "usev6=ifv6, ifv6=%s address is %s\n", opt('ifv6'), get_ipv6('ifv6') // 'NOT FOUND'; printf("usev6=ifv6, ifv6=%s address is %s\n",
opt('ifv6'), get_ipv6(strategy_inputs('usev6')) // 'NOT FOUND');
} }
} }
{ {
local $opt{'usev6'} = 'webv6'; local $opt{'usev6'} = 'webv6';
for my $web (sort keys %builtinweb) { for my $web (sort keys %builtinweb) {
local $opt{'webv6'} = $web; local $opt{'webv6'} = $web;
printf "usev6=webv6, webv6=$web address is %s\n", get_ipv6('webv6') // 'NOT FOUND' printf("usev6=webv6, webv6=%s address is %s\n",
$web, get_ipv6(strategy_inputs('usev6')) // 'NOT FOUND')
if ($web !~ "4"); ## Don't bother if web site only supports IPv4 if ($web !~ "4"); ## Don't bother if web site only supports IPv4
} }
printf "usev6=webv6, webv6=%s address is %s\n", opt('webv6'), get_ipv6('webv6') // 'NOT FOUND' printf("usev6=webv6, webv6=%s address is %s\n",
opt('webv6'), get_ipv6(strategy_inputs('usev6')) // 'NOT FOUND')
if ! exists $builtinweb{opt('webv6')}; if ! exists $builtinweb{opt('webv6')};
} }
if (opt('cmdv6')) { if (opt('cmdv6')) {
local $opt{'usev6'} = 'cmdv6'; local $opt{'usev6'} = 'cmdv6';
printf "usev6=cmdv6, cmdv6=%s address is %s\n", opt('cmdv6'), get_ipv6('cmdv6') // 'NOT FOUND'; printf("usev6=cmdv6, cmdv6=%s address is %s\n",
opt('cmdv6'), get_ipv6(strategy_inputs('usev6')) // 'NOT FOUND');
} }
exit 0 unless opt('debug'); exit 0 unless opt('debug');
@ -2847,35 +2881,51 @@ sub geturl {
return $reply; return $reply;
} }
# Collects and returns all configuration data that get_ip* needs to determine the IP address. This
# makes it possible to avoid redundant queries by comparing the configuration data for different
# hosts.
sub strategy_inputs {
my ($whichuse, $h) = @_;
my $use = opt($whichuse, $h);
my $strategies
= $whichuse eq 'use' ? \%ip_strategies
: $whichuse eq 'usev4' ? \%ipv4_strategies
: $whichuse eq 'usev6' ? \%ipv6_strategies
: undef;
my $s = $strategies->{$use};
my @v = @{$s->{inputs} // []};
return map({ $_ => opt($_, $h); } $whichuse, @v);
}
###################################################################### ######################################################################
## get_ip ## get_ip
###################################################################### ######################################################################
sub get_ip { sub get_ip {
my ($use, $h) = @_; my %p = @_;
my ($ip, $reply, $url, $skip) = (undef, ''); my ($ip, $reply, $url, $skip) = (undef, '');
my $argvar = $use; my $argvar = $p{'use'};
# Note that --use=firewallname uses --fw=arg, not --firewallname=arg. # Note that --use=firewallname uses --fw=arg, not --firewallname=arg.
$argvar = 'fw' if $builtinfw{$use}; $argvar = 'fw' if $builtinfw{$p{'use'}};
my $arg = opt($argvar, $h); my $arg = $p{$argvar};
local $_l = pushlogctx("use=$use $argvar=" . ($arg // '<undefined>')); local $_l = pushlogctx("use=$p{'use'} $argvar=" . ($arg // '<undefined>'));
if ($use eq 'ip') { if ($p{'use'} eq 'ip') {
$ip = opt('ip', $h); $ip = $arg;
if (!is_ipv4($ip) && !is_ipv6($ip)) { if (!is_ipv4($ip) && !is_ipv6($ip)) {
warning('not a valid IPv4 or IPv6 address'); warning('not a valid IPv4 or IPv6 address');
$ip = undef; $ip = undef;
} }
} elsif ($use eq 'if') { } elsif ($p{'use'} eq 'if') {
$ip = get_ip_from_interface($arg); $ip = get_ip_from_interface($arg);
} elsif ($use eq 'cmd') { } elsif ($p{'use'} eq 'cmd') {
if ($arg) { if ($arg) {
$skip = opt('cmd-skip', $h); $skip = $p{'cmd-skip'};
$reply = `$arg`; $reply = `$arg`;
$reply = '' if $?; $reply = '' if $?;
} }
} elsif ($use eq 'web') { } elsif ($p{'use'} eq 'web') {
$url = opt('web', $h) // ''; $url = $arg;
$skip = opt('web-skip', $h); $skip = $p{'web-skip'};
if (my $biw = $builtinweb{$url}) { if (my $biw = $builtinweb{$url}) {
warning("'$arg' is deprecated: $biw->{deprecated}") if $biw->{deprecated}; warning("'$arg' is deprecated: $biw->{deprecated}") if $biw->{deprecated};
$skip //= $biw->{skip}; $skip //= $biw->{skip};
@ -2883,9 +2933,9 @@ sub get_ip {
} }
if ($url) { if ($url) {
$reply = geturl( $reply = geturl(
proxy => opt('proxy', $h), proxy => opt('proxy'),
url => $url, url => $url,
ssl_validate => opt('web-ssl-validate', $h), ssl_validate => $p{'web-ssl-validate'},
); );
if (header_ok($reply, \&warning)) { if (header_ok($reply, \&warning)) {
$reply =~ s/^.*?\n\n//s; $reply =~ s/^.*?\n\n//s;
@ -2893,17 +2943,17 @@ sub get_ip {
$reply = undef; $reply = undef;
} }
} }
} elsif ($use eq 'disabled') { } elsif ($p{'use'} eq 'disabled') {
## This is a no-op... Do not get an IP address for this host/service ## This is a no-op... Do not get an IP address for this host/service
$reply = ''; $reply = '';
} elsif ($use eq 'fw' || defined(my $fw = $builtinfw{$use})) { } elsif ($p{'use'} eq 'fw' || defined(my $fw = $builtinfw{$p{'use'}})) {
$url = $arg; $url = $arg;
$skip = opt('fw-skip', $h); $skip = $p{'fw-skip'};
if ($fw) { if ($fw) {
$skip //= $fw->{'skip'}; $skip //= $fw->{'skip'};
if (defined(my $query = $fw->{'query'})) { if (defined(my $query = $fw->{'query'})) {
$url = undef; $url = undef;
$reply = $query->($h); $reply = $query->(%p);
} else { } else {
$url = "http://$url$fw->{'url'}" unless $url =~ /\//; $url = "http://$url$fw->{'url'}" unless $url =~ /\//;
} }
@ -2911,10 +2961,10 @@ sub get_ip {
if ($url) { if ($url) {
$reply = geturl( $reply = geturl(
url => $url, url => $url,
login => opt('fw-login', $h), login => $p{'fw-login'},
password => opt('fw-password', $h), password => $p{'fw-password'},
ignore_ssl_option => 1, ignore_ssl_option => 1,
ssl_validate => opt('fw-ssl-validate', $h), ssl_validate => $p{'fw-ssl-validate'},
); );
if (header_ok($reply, \&warning)) { if (header_ok($reply, \&warning)) {
$reply =~ s/^.*?\n\n//s; $reply =~ s/^.*?\n\n//s;
@ -2923,7 +2973,7 @@ sub get_ip {
} }
} }
} else { } else {
warning("ignoring unsupported '--use' strategy: $use"); warning("ignoring unsupported '--use' strategy: $p{'use'}");
} }
if (!defined $reply) { if (!defined $reply) {
$reply = ''; $reply = '';
@ -2933,7 +2983,7 @@ sub get_ip {
$reply =~ s/^.*?${skip}//is; $reply =~ s/^.*?${skip}//is;
} }
$ip //= extract_ipv4($reply) // extract_ipv6($reply); $ip //= extract_ipv4($reply) // extract_ipv6($reply);
if ($use ne 'ip' && ($ip // '') eq '0.0.0.0') { if ($p{'use'} ne 'ip' && ($ip // '') eq '0.0.0.0') {
$ip = undef; $ip = undef;
} }
warning('did not find an IPv4 or IPv6 address') if !defined($ip); warning('did not find an IPv4 or IPv6 address') if !defined($ip);
@ -3246,40 +3296,41 @@ sub get_ip_from_interface {
## get_ipv4 ## get_ipv4
###################################################################### ######################################################################
sub get_ipv4 { sub get_ipv4 {
my ($usev4, $h) = @_; my %p = @_;
my $ipv4 = undef; ## Found IPv4 address my $ipv4 = undef; ## Found IPv4 address
my $reply = ''; ## Text returned from various methods my $reply = ''; ## Text returned from various methods
my $url = ''; ## URL of website or firewall my $url = ''; ## URL of website or firewall
my $skip = undef; ## Regex of pattern to skip before looking for IP my $skip = undef; ## Regex of pattern to skip before looking for IP
my $argvar = $usev4; my $argvar = $p{'usev4'};
# Note that --usev4=firewallname uses --fwv4=arg, not --firewallname=arg. # Note that --usev4=firewallname uses --fwv4=arg, not --firewallname=arg.
$argvar = (defined(opt('fwv4', $h)) || !defined(opt('fw', $h))) ? 'fwv4' : 'fw' $argvar = (defined($p{'fwv4'}) || !defined($p{'fw'})) ? 'fwv4' : 'fw'
if $builtinfw{$usev4}; if $builtinfw{$p{'usev4'}};
my $arg = opt($argvar, $h); my $arg = $p{$argvar};
local $_l = pushlogctx("usev4=$usev4 $argvar=" . ($arg // '<undefined>')); local $_l = pushlogctx("usev4=$p{'usev4'} $argvar=" . ($arg // '<undefined>'));
if ($usev4 eq 'ipv4') { if ($p{'usev4'} eq 'ipv4') {
## Static IPv4 address is provided in "ipv4=<address>" ## Static IPv4 address is provided in "ipv4=<address>"
$ipv4 = $arg; $ipv4 = $arg;
if (!is_ipv4($ipv4)) { if (!is_ipv4($ipv4)) {
warning('not a valid IPv4 address'); warning('not a valid IPv4 address');
$ipv4 = undef; $ipv4 = undef;
} }
} elsif ($usev4 eq 'ifv4') { } elsif ($p{'usev4'} eq 'ifv4') {
## Obtain IPv4 address from interface mamed in "ifv4=<if>" ## Obtain IPv4 address from interface mamed in "ifv4=<if>"
$ipv4 = get_ip_from_interface($arg, 4); $ipv4 = get_ip_from_interface($arg, 4);
} elsif ($usev4 eq 'cmdv4') { } elsif ($p{'usev4'} eq 'cmdv4') {
## Obtain IPv4 address by executing the command in "cmdv4=<command>" ## Obtain IPv4 address by executing the command in "cmdv4=<command>"
warning("'--cmd-skip' ignored") if (opt('verbose') && opt('cmd-skip', $h)); warning("'--cmd-skip' ignored for '--usev4=$p{'usev4'}'")
if (opt('verbose') && $p{'cmd-skip'});
if ($arg) { if ($arg) {
my $sys_cmd = quotemeta($arg); my $sys_cmd = quotemeta($arg);
$reply = qx{$sys_cmd}; $reply = qx{$sys_cmd};
$reply = '' if $?; $reply = '' if $?;
} }
} elsif ($usev4 eq 'webv4') { } elsif ($p{'usev4'} eq 'webv4') {
## Obtain IPv4 address by accessing website at url in "webv4=<url>" ## Obtain IPv4 address by accessing website at url in "webv4=<url>"
$url = $arg; $url = $arg;
$skip = opt('webv4-skip', $h); $skip = $p{'webv4-skip'};
if (my $biw = $builtinweb{$url}) { if (my $biw = $builtinweb{$url}) {
warning("'$arg' is deprecated: $biw->{deprecated}") if $biw->{deprecated}; warning("'$arg' is deprecated: $biw->{deprecated}") if $biw->{deprecated};
$skip //= $biw->{skip}; $skip //= $biw->{skip};
@ -3287,10 +3338,10 @@ sub get_ipv4 {
} }
if ($url) { if ($url) {
$reply = geturl( $reply = geturl(
proxy => opt('proxy', $h), proxy => opt('proxy'),
url => $url, url => $url,
ipversion => 4, # when using a URL to find IPv4 address we should force use of IPv4 ipversion => 4, # when using a URL to find IPv4 address we should force use of IPv4
ssl_validate => opt('web-ssl-validate', $h), ssl_validate => $p{'web-ssl-validate'},
); );
if (header_ok($reply, \&warning)) { if (header_ok($reply, \&warning)) {
$reply =~ s/^.*?\n\n//s; $reply =~ s/^.*?\n\n//s;
@ -3298,21 +3349,21 @@ sub get_ipv4 {
$reply = undef; $reply = undef;
} }
} }
} elsif ($usev4 eq 'disabled') { } elsif ($p{'usev4'} eq 'disabled') {
## This is a no-op... Do not get an IPv4 address for this host/service ## This is a no-op... Do not get an IPv4 address for this host/service
$reply = ''; $reply = '';
} elsif ($usev4 eq 'fwv4' || defined(my $fw = $builtinfw{$usev4})) { } elsif ($p{'usev4'} eq 'fwv4' || defined(my $fw = $builtinfw{$p{'usev4'}})) {
warning("'--fw' is deprecated; use '--fwv4' instead") warning("'--fw' is deprecated; use '--fwv4' instead")
if (!defined(opt('fwv4', $h)) && defined(opt('fw', $h))); if (!defined($p{'fwv4'}) && defined($p{'fw'}));
warning("'--fw-skip' is deprecated; use '--fwv4-skip' instead") warning("'--fw-skip' is deprecated; use '--fwv4-skip' instead")
if (!defined(opt('fwv4-skip', $h)) && defined(opt('fw-skip', $h))); if (!defined($p{'fwv4-skip'}) && defined($p{'fw-skip'}));
$url = $arg; $url = $arg;
$skip = opt('fwv4-skip', $h) // opt('fw-skip', $h); $skip = $p{'fwv4-skip'} // $p{'fw-skip'};
if ($fw) { if ($fw) {
$skip //= $fw->{'skip'}; $skip //= $fw->{'skip'};
if (defined(my $query = $fw->{'queryv4'})) { if (defined(my $query = $fw->{'queryv4'})) {
$url = undef; $url = undef;
$reply = $query->($h); $reply = $query->(%p);
} else { } else {
$url = "http://$url$fw->{'url'}" unless $url =~ /\//; $url = "http://$url$fw->{'url'}" unless $url =~ /\//;
} }
@ -3320,11 +3371,11 @@ sub get_ipv4 {
if ($url) { if ($url) {
$reply = geturl( $reply = geturl(
url => $url, url => $url,
login => opt('fw-login', $h), login => $p{'fw-login'},
password => opt('fw-password', $h), password => $p{'fw-password'},
ipversion => 4, # when using a URL to find IPv4 address we should force use of IPv4 ipversion => 4, # when using a URL to find IPv4 address we should force use of IPv4
ignore_ssl_option => 1, ignore_ssl_option => 1,
ssl_validate => opt('fw-ssl-validate', $h), ssl_validate => $p{'fw-ssl-validate'},
); );
if (header_ok($reply, \&warning)) { if (header_ok($reply, \&warning)) {
$reply =~ s/^.*?\n\n//s; $reply =~ s/^.*?\n\n//s;
@ -3333,7 +3384,7 @@ sub get_ipv4 {
} }
} }
} else { } else {
warning("ignoring unsupported '--usev4' strategy: $usev4"); warning("ignoring unsupported '--usev4' strategy: $p{'usev4'}");
} }
## Set to loopback address if no text set yet ## Set to loopback address if no text set yet
@ -3345,7 +3396,7 @@ sub get_ipv4 {
## If $ipv4 not set yet look for IPv4 address in the $reply text ## If $ipv4 not set yet look for IPv4 address in the $reply text
$ipv4 //= extract_ipv4($reply); $ipv4 //= extract_ipv4($reply);
## Return undef for loopback address unless statically assigned by "ipv4=0.0.0.0" ## Return undef for loopback address unless statically assigned by "ipv4=0.0.0.0"
$ipv4 = undef if (($usev4 ne 'ipv4') && (($ipv4 // '') eq '0.0.0.0')); $ipv4 = undef if (($p{'usev4'} ne 'ipv4') && (($ipv4 // '') eq '0.0.0.0'));
warning('did not find an IPv4 address') if !defined($ipv4); warning('did not find an IPv4 address') if !defined($ipv4);
debug("found IPv4 address: $ipv4") if $ipv4; debug("found IPv4 address: $ipv4") if $ipv4;
return $ipv4; return $ipv4;
@ -3355,46 +3406,46 @@ sub get_ipv4 {
## get_ipv6 ## get_ipv6
###################################################################### ######################################################################
sub get_ipv6 { sub get_ipv6 {
my ($usev6, $h) = @_; my %p = @_;
my $ipv6 = undef; ## Found IPv6 address my $ipv6 = undef; ## Found IPv6 address
my $reply = ''; ## Text returned from various methods my $reply = ''; ## Text returned from various methods
my $url = ''; ## URL of website or firewall my $url = ''; ## URL of website or firewall
my $skip = undef; ## Regex of pattern to skip before looking for IP my $skip = undef; ## Regex of pattern to skip before looking for IP
my $argvar = $usev6; my $argvar = $p{'usev6'};
if (grep($usev6 eq $_, qw(ip if cmd web))) { if (grep($p{'usev6'} eq $_, qw(ip if cmd web))) {
my $new = $usev6 . 'v6'; my $new = $p{'usev6'} . 'v6';
warning("'--usev6=$usev6' is deprecated; use '--usev6=$new'"); warning("'--usev6=$p{'usev6'}' is deprecated; use '--usev6=$new'");
$argvar = $new if defined(opt($new, $h)); $argvar = $new if defined($p{$new});
} }
# Note that --usev6=firewallname uses --fwv6=arg, not --firewallname=arg. # Note that --usev6=firewallname uses --fwv6=arg, not --firewallname=arg.
$argvar = 'fwv6' if $builtinfw{$usev6}; $argvar = 'fwv6' if $builtinfw{$p{'usev6'}};
my $arg = opt($argvar, $h); my $arg = $p{$argvar};
local $_l = pushlogctx("usev6=$usev6 $argvar=" . ($arg // '<undefined>')); local $_l = pushlogctx("usev6=$p{'usev6'} $argvar=" . ($arg // '<undefined>'));
if ($usev6 eq 'ipv6' || $usev6 eq 'ip') { if ($p{'usev6'} eq 'ipv6' || $p{'usev6'} eq 'ip') {
## Static IPv6 address is provided in "ipv6=<address>" ## Static IPv6 address is provided in "ipv6=<address>"
$ipv6 = $arg; $ipv6 = $arg;
if (!is_ipv6($ipv6)) { if (!is_ipv6($ipv6)) {
warning('not a valid IPv6 address'); warning('not a valid IPv6 address');
$ipv6 = undef; $ipv6 = undef;
} }
} elsif ($usev6 eq 'ifv6' || $usev6 eq 'if') { } elsif ($p{'usev6'} eq 'ifv6' || $p{'usev6'} eq 'if') {
## Obtain IPv6 address from interface mamed in "ifv6=<if>" ## Obtain IPv6 address from interface mamed in "ifv6=<if>"
$ipv6 = get_ip_from_interface($arg, 6); $ipv6 = get_ip_from_interface($arg, 6);
} elsif ($usev6 eq 'cmdv6' || $usev6 eq 'cmd') { } elsif ($p{'usev6'} eq 'cmdv6' || $p{'usev6'} eq 'cmd') {
## Obtain IPv6 address by executing the command in "cmdv6=<command>" ## Obtain IPv6 address by executing the command in "cmdv6=<command>"
warning("'--cmd-skip' ignored") if (opt('verbose') && opt('cmd-skip', $h)); warning("'--cmd-skip' ignored") if opt('verbose') && p{'cmd-skip'};
if ($arg) { if ($arg) {
my $sys_cmd = quotemeta($arg); my $sys_cmd = quotemeta($arg);
$reply = qx{$sys_cmd}; $reply = qx{$sys_cmd};
$reply = '' if $?; $reply = '' if $?;
} }
} elsif ($usev6 eq 'webv6' || $usev6 eq 'web') { } elsif ($p{'usev6'} eq 'webv6' || $p{'usev6'} eq 'web') {
## Obtain IPv6 address by accessing website at url in "webv6=<url>" ## Obtain IPv6 address by accessing website at url in "webv6=<url>"
warning("'--web-skip' ignored; use '--webv6-skip' instead") warning("'--web-skip' ignored; use '--webv6-skip' instead")
if (!defined(opt('webv6-skip', $h)) && defined(opt('web-skip', $h))); if (!defined($p{'webv6-skip'}) && defined($p{'web-skip'}));
$url = $arg; $url = $arg;
$skip = opt('webv6-skip', $h); $skip = $p{'webv6-skip'};
if (my $biw = $builtinweb{$url}) { if (my $biw = $builtinweb{$url}) {
warning("'--webv6=$url' is deprecated! $biw->{deprecated}") if $biw->{deprecated}; warning("'--webv6=$url' is deprecated! $biw->{deprecated}") if $biw->{deprecated};
$skip //= $biw->{skip}; $skip //= $biw->{skip};
@ -3405,7 +3456,7 @@ sub get_ipv6 {
proxy => opt('proxy'), proxy => opt('proxy'),
url => $url, url => $url,
ipversion => 6, # when using a URL to find IPv6 address we should force use of IPv6 ipversion => 6, # when using a URL to find IPv6 address we should force use of IPv6
ssl_validate => opt('web-ssl-validate', $h), ssl_validate => $p{'web-ssl-validate'},
); );
if (header_ok($reply, \&warning)) { if (header_ok($reply, \&warning)) {
$reply =~ s/^.*?\n\n//s; $reply =~ s/^.*?\n\n//s;
@ -3413,18 +3464,18 @@ sub get_ipv6 {
$reply = undef; $reply = undef;
} }
} }
} elsif ($usev6 eq 'disabled') { } elsif ($p{'usev6'} eq 'disabled') {
$reply = ''; $reply = '';
} elsif ($usev6 eq 'fwv6' || defined(my $fw = $builtinfw{$usev6})) { } elsif ($p{'usev6'} eq 'fwv6' || defined(my $fw = $builtinfw{$p{'usev6'}})) {
$skip = opt('fwv6-skip', $h) // $fw->{'skip'}; $skip = $p{'fwv6-skip'} // $fw->{'skip'};
if ($fw && defined(my $query = $fw->{'queryv6'})) { if ($fw && defined(my $query = $fw->{'queryv6'})) {
$skip //= $fw->{'skip'}; $skip //= $fw->{'skip'};
$reply = $query->($h); $reply = $query->(%p);
} else { } else {
warning("not implemented (does nothing)"); warning("not implemented (does nothing)");
} }
} else { } else {
warning("ignoring unsupported '--usev6' strategy: $usev6"); warning("ignoring unsupported '--usev6' strategy: $p{'usev6'}");
} }
## Set to loopback address if no text set yet ## Set to loopback address if no text set yet
@ -3436,7 +3487,7 @@ sub get_ipv6 {
## If $ipv6 not set yet look for IPv6 address in the $reply text ## If $ipv6 not set yet look for IPv6 address in the $reply text
$ipv6 //= extract_ipv6($reply); $ipv6 //= extract_ipv6($reply);
## Return undef for loopback address unless statically assigned by "ipv6=::" ## Return undef for loopback address unless statically assigned by "ipv6=::"
$ipv6 = undef if (($usev6 ne 'ipv6') && ($usev6 ne 'ip') && (($ipv6 // '') eq '::')); $ipv6 = undef if (($p{'usev6'} ne 'ipv6') && ($p{'usev6'} ne 'ip') && (($ipv6 // '') eq '::'));
warning('did not find an IPv6 address') if !defined($ipv6); warning('did not find an IPv6 address') if !defined($ipv6);
debug("found IPv6 address: $ipv6") if $ipv6; debug("found IPv6 address: $ipv6") if $ipv6;
return $ipv6; return $ipv6;

View file

@ -2,26 +2,26 @@ use Test::More;
BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } } BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } }
BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); } BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); }
my $got_host; sub setbuiltinfw {
my $builtinfw = 't/builtinfw_query.pl'; my ($fw) = @_;
$ddclient::builtinfw{$builtinfw} = { no warnings 'once';
name => 'dummy device for testing', $ddclient::builtinfw{$fw->{name}} = $fw;
query => sub { %ddclient::ip_strategies = ddclient::builtinfw_strategy($fw->{name});
($got_host) = @_; %ddclient::ipv4_strategies = ddclient::builtinfwv4_strategy($fw->{name});
return '192.0.2.1 skip1 192.0.2.2 skip2 192.0.2.3'; %ddclient::ipv6_strategies = ddclient::builtinfwv6_strategy($fw->{name});
}, }
queryv4 => sub {
($got_host) = @_;
return '192.0.2.4 skip1 192.0.2.5 skip3 192.0.2.6';
},
queryv6 => sub {
($got_host) = @_;
return '2001:db8::1 skip1 2001:db8::2 skip4 2001:db8::3';
},
};
%ddclient::builtinfw if 0; # suppress spurious warning "Name used only once: possible typo"
my @test_cases = ( my @gotcalls;
my $skip_test_fw = 't/builtinfw_query.pl skip test';
setbuiltinfw({
name => $skip_test_fw,
query => sub { return '192.0.2.1 skip1 192.0.2.2 skip2 192.0.2.3'; },
queryv4 => sub { return '192.0.2.4 skip1 192.0.2.5 skip3 192.0.2.6'; },
queryv6 => sub { return '2001:db8::1 skip1 2001:db8::2 skip4 2001:db8::3'; },
});
my @skip_test_cases = (
{ {
desc => 'query', desc => 'query',
getip => \&ddclient::get_ip, getip => \&ddclient::get_ip,
@ -61,20 +61,109 @@ my @test_cases = (
}, },
); );
for my $tc (@test_cases) { for my $tc (@skip_test_cases) {
subtest $tc->{desc} => sub {
my $h = "t/builtinfw_query.pl $tc->{desc}"; my $h = "t/builtinfw_query.pl $tc->{desc}";
$ddclient::config{$h} = { $ddclient::config{$h} = {
$tc->{useopt} => $builtinfw, $tc->{useopt} => $skip_test_fw,
'fw-skip' => 'skip1', 'fw-skip' => 'skip1',
%{$tc->{cfgxtra}}, %{$tc->{cfgxtra}},
}; };
%ddclient::config if 0; # suppress spurious warning "Name used only once: possible typo" my $got = $tc->{getip}(ddclient::strategy_inputs($tc->{useopt}, $h));
undef($got_host); is($got, $tc->{want}, $tc->{desc});
my $got = $tc->{getip}($builtinfw, $h); }
is($got_host, $h, "host is passed through");
is($got, $tc->{want}, "returned IP matches"); my $default_inputs_fw = 't/builtinfw_query.pl default inputs';
setbuiltinfw({
name => $default_inputs_fw,
query => sub { my %p = @_; push(@gotcalls, \%p); return '192.0.2.1'; },
queryv4 => sub { my %p = @_; push(@gotcalls, \%p); return '192.0.2.2'; },
queryv6 => sub { my %p = @_; push(@gotcalls, \%p); return '2001:db8::1'; },
});
my @default_inputs_test_cases = (
{
desc => 'use with default inputs',
getip => \&ddclient::get_ip,
useopt => 'use',
want => {use => $default_inputs_fw, fw => 'server', 'fw-skip' => 'skip',
'fw-login' => 'login', 'fw-password' => 'password', 'fw-ssl-validate' => 1},
},
{
desc => 'usev4 with default inputs',
getip => \&ddclient::get_ipv4,
useopt => 'usev4',
want => {usev4 => $default_inputs_fw, fwv4 => 'serverv4', fw => 'server',
'fwv4-skip' => 'skipv4', 'fw-skip' => 'skip', 'fw-login' => 'login',
'fw-password' => 'password', 'fw-ssl-validate' => 1},
},
{
desc => 'usev6 with default inputs',
getip => \&ddclient::get_ipv6,
useopt => 'usev6',
want => {usev6 => $default_inputs_fw, fwv6 => 'serverv6', 'fwv6-skip' => 'skipv6'},
},
);
for my $tc (@default_inputs_test_cases) {
my $h = "t/builtinfw_query.pl $tc->{desc}";
$ddclient::config{$h} = {
$tc->{useopt} => $default_inputs_fw,
'fw' => 'server',
'fwv4' => 'serverv4',
'fwv6' => 'serverv6',
'fw-login' => 'login',
'fw-password' => 'password',
'fw-ssl-validate' => 1,
'fw-skip' => 'skip',
'fwv4-skip' => 'skipv4',
'fwv6-skip' => 'skipv6',
}; };
@gotcalls = ();
$tc->{getip}(ddclient::strategy_inputs($tc->{useopt}, $h));
is_deeply(\@gotcalls, [$tc->{want}], $tc->{desc});
}
my $custom_inputs_fw = 't/builtinfw_query.pl custom inputs';
setbuiltinfw({
name => $custom_inputs_fw,
query => sub { my %p = @_; push(@gotcalls, \%p); return '192.0.2.1'; },
inputs => ['if'],
queryv4 => sub { my %p = @_; push(@gotcalls, \%p); return '192.0.2.2'; },
inputsv4 => ['ifv4'],
queryv6 => sub { my %p = @_; push(@gotcalls, \%p); return '2001:db8::1'; },
inputsv6 => ['ifv6'],
});
my @custom_inputs_test_cases = (
{
desc => 'use with custom inputs',
getip => \&ddclient::get_ip,
useopt => 'use',
want => {use => $custom_inputs_fw, if => 'eth0'},
},
{
desc => 'usev4 with custom inputs',
getip => \&ddclient::get_ipv4,
useopt => 'usev4',
want => {usev4 => $custom_inputs_fw, ifv4 => 'eth4'},
},
{
desc => 'usev6 with custom inputs',
getip => \&ddclient::get_ipv6,
useopt => 'usev6',
want => {usev6 => $custom_inputs_fw, ifv6 => 'eth6'},
},
);
for my $tc (@custom_inputs_test_cases) {
my $h = "t/builtinfw_query.pl $tc->{desc}";
$ddclient::config{$h} = {
$tc->{useopt} => $custom_inputs_fw,
'if' => 'eth0',
'ifv4' => 'eth4',
'ifv6' => 'eth6',
};
@gotcalls = ();
$tc->{getip}(ddclient::strategy_inputs($tc->{useopt}, $h));
is_deeply(\@gotcalls, [$tc->{want}], $tc->{desc});
} }
done_testing(); done_testing();

View file

@ -22,6 +22,11 @@ $ddclient::builtinweb{$builtinwebv6} = {'url' => httpd('6')->endpoint(), 'skip'
if httpd('6'); if httpd('6');
$ddclient::builtinfw{$builtinfw} = {name => 'test', skip => 'skip'}; $ddclient::builtinfw{$builtinfw} = {name => 'test', skip => 'skip'};
%ddclient::builtinfw if 0; # suppress spurious warning "Name used only once: possible typo" %ddclient::builtinfw if 0; # suppress spurious warning "Name used only once: possible typo"
%ddclient::ip_strategies = (%ddclient::ip_strategies, ddclient::builtinfw_strategy($builtinfw));
%ddclient::ipv4_strategies =
(%ddclient::ipv4_strategies, ddclient::builtinfwv4_strategy($builtinfw));
%ddclient::ipv6_strategies =
(%ddclient::ipv6_strategies, ddclient::builtinfwv6_strategy($builtinfw));
sub run_test_case { sub run_test_case {
my %tc = @_; my %tc = @_;
@ -31,9 +36,12 @@ sub run_test_case {
my $h = 't/skip.pl'; my $h = 't/skip.pl';
$ddclient::config{$h} = $tc{cfg}; $ddclient::config{$h} = $tc{cfg};
%ddclient::config if 0; # suppress spurious warning "Name used only once: possible typo" %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_ip(ddclient::strategy_inputs('use', $h)), $tc{want}, $tc{desc})
is(ddclient::get_ipv4($tc{cfg}{usev4}, $h), $tc{want}, $tc{desc}) if ($tc{cfg}{usev4}); if ($tc{cfg}{use});
is(ddclient::get_ipv6($tc{cfg}{usev6}, $h), $tc{want}, $tc{desc}) if ($tc{cfg}{usev6}); is(ddclient::get_ipv4(ddclient::strategy_inputs('usev4', $h)), $tc{want}, $tc{desc})
if ($tc{cfg}{usev4});
is(ddclient::get_ipv6(ddclient::strategy_inputs('usev6', $h)), $tc{want}, $tc{desc})
if ($tc{cfg}{usev6});
} }
} }

View file

@ -75,9 +75,9 @@ for my $tc (@test_cases) {
skip("HTTP::Daemon too old for IPv6 support", 1) if $tc->{ipv6} && !$httpd_ipv6_supported; skip("HTTP::Daemon too old for IPv6 support", 1) if $tc->{ipv6} && !$httpd_ipv6_supported;
$ddclient::config{$h} = $tc->{cfg}; $ddclient::config{$h} = $tc->{cfg};
%ddclient::config if 0; # suppress spurious warning "Name used only once: possible typo" %ddclient::config if 0; # suppress spurious warning "Name used only once: possible typo"
is(ddclient::get_ipv4($tc->{cfg}{usev4}, $h), $tc->{want}, $tc->{desc}) is(ddclient::get_ipv4(ddclient::strategy_inputs('usev4', $h)), $tc->{want}, $tc->{desc})
if ($tc->{cfg}{usev4}); if ($tc->{cfg}{usev4});
is(ddclient::get_ipv6($tc->{cfg}{usev6}, $h), $tc->{want}, $tc->{desc}) is(ddclient::get_ipv6(ddclient::strategy_inputs('usev6', $h)), $tc->{want}, $tc->{desc})
if ($tc->{cfg}{usev6}); if ($tc->{cfg}{usev6});
} }
} }

View file

@ -272,6 +272,58 @@ my @test_cases = (
%$_, %$_,
}; };
} {cfg => {use => 'web'}}, {cfg => {usev4 => 'webv4'}}), } {cfg => {use => 'web'}}, {cfg => {usev4 => 'webv4'}}),
map({
my %cfg = %{delete($_->{cfg})};
my $desc = join(' ', map("$_=$cfg{$_}", sort(keys(%cfg))));
{
desc => "deduplicates identical IP discovery, $desc",
cfg => {
hosta => {protocol => 'legacy', %cfg},
hostb => {protocol => 'legacy', %cfg},
},
want_reqs_webv4 => 1,
want_updates => [['hosta', 'hostb']],
want_recap_changes => {
hosta => {
'atime' => $ddclient::now,
'ipv4' => '192.0.2.1',
'mtime' => $ddclient::now,
'status-ipv4' => 'good',
},
hostb => {
'atime' => $ddclient::now,
'ipv4' => '192.0.2.1',
'mtime' => $ddclient::now,
'status-ipv4' => 'good',
},
},
%$_,
};
} {cfg => {use => 'web'}}, {cfg => {usev4 => 'webv4'}}),
{
desc => "deduplicates identical IP discovery, usev6=webv6",
ipv6 => 1,
cfg => {
hosta => {protocol => 'legacy', usev6 => 'webv6'},
hostb => {protocol => 'legacy', usev6 => 'webv6'},
},
want_reqs_webv6 => 1,
want_updates => [['hosta', 'hostb']],
want_recap_changes => {
hosta => {
'atime' => $ddclient::now,
'ipv6' => '2001:db8::1',
'mtime' => $ddclient::now,
'status-ipv6' => 'good',
},
hostb => {
'atime' => $ddclient::now,
'ipv6' => '2001:db8::1',
'mtime' => $ddclient::now,
'status-ipv6' => 'good',
},
},
},
); );
for my $tc (@test_cases) { for my $tc (@test_cases) {

View file

@ -87,11 +87,11 @@ for my $tc (@test_cases) {
SKIP: { SKIP: {
skip("IPv6 not supported on this system", 1) if $tc->{ipv6} && !$ipv6_supported; 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} && !$httpd_ipv6_supported; skip("HTTP::Daemon too old for IPv6 support", 1) if $tc->{ipv6} && !$httpd_ipv6_supported;
is(ddclient::get_ip($tc->{cfg}{use}, $h), $tc->{want}, $tc->{desc}) is(ddclient::get_ip(ddclient::strategy_inputs('use', $h)), $tc->{want}, $tc->{desc})
if $tc->{cfg}{use}; if $tc->{cfg}{use};
is(ddclient::get_ipv4($tc->{cfg}{usev4}, $h), $tc->{want}, $tc->{desc}) is(ddclient::get_ipv4(ddclient::strategy_inputs('usev4', $h)), $tc->{want}, $tc->{desc})
if $tc->{cfg}{usev4}; if $tc->{cfg}{usev4};
is(ddclient::get_ipv6($tc->{cfg}{usev6}, $h), $tc->{want}, $tc->{desc}) is(ddclient::get_ipv6(ddclient::strategy_inputs('usev6', $h)), $tc->{want}, $tc->{desc})
if $tc->{cfg}{usev6}; if $tc->{cfg}{usev6};
} }
} }