#320 DNS subroutines are redefined

This commit is contained in:
Oliver Gorwits
2017-06-24 11:13:47 +01:00
parent 74fa338373
commit 7dddf743e5
9 changed files with 122 additions and 84 deletions

View File

@@ -1,3 +1,9 @@
2.036001 - TESTING
[BUG FIXES]
* #320 DNS subroutines are redefined
2.036001 - 2017-06-22
[BUG FIXES]

View File

@@ -129,6 +129,7 @@ lib/App/Netdisco/Util/Backend.pm
lib/App/Netdisco/Util/Device.pm
lib/App/Netdisco/Util/DNS.pm
lib/App/Netdisco/Util/ExpandParams.pm
lib/App/Netdisco/Util/FastResolver.pm
lib/App/Netdisco/Util/Graph.pm
lib/App/Netdisco/Util/Node.pm
lib/App/Netdisco/Util/NodeMonitor.pm

View File

@@ -42,7 +42,7 @@ BEGIN {
use App::Netdisco;
use App::Netdisco::Core::Arpnip 'store_arp';
use App::Netdisco::Util::Node 'check_mac';
use App::Netdisco::Util::DNS 'hostnames_resolve_async';
use App::Netdisco::Util::FastResolver 'hostnames_resolve_async';
use Dancer ':script';
use Data::Printer;

View File

@@ -66,6 +66,22 @@ if (exists setting('workers')->{interactives}
setting('dns')->{hosts_file} ||= '/etc/hosts';
setting('dns')->{no} ||= ['fe80::/64','169.254.0.0/16'];
# load /etc/hosts
setting('dns')->{'ETCHOSTS'} = {};
{
# AE::DNS::EtcHosts only works for A/AAAA/SRV, but we want PTR.
# this loads+parses /etc/hosts file using AE. dirty hack.
use AnyEvent::Socket 'format_address';
use AnyEvent::DNS::EtcHosts;
AnyEvent::DNS::EtcHosts::_load_hosts_unless(sub{},AE::cv);
no AnyEvent::DNS::EtcHosts; # unimport
setting('dns')->{'ETCHOSTS'}->{$_} =
[ map { [ $_ ? (format_address $_->[0]) : '' ] }
@{ $AnyEvent::DNS::EtcHosts::HOSTS{ $_ } } ]
for keys %AnyEvent::DNS::EtcHosts::HOSTS;
}
# support unordered dictionary as if it were a single item list
if (ref {} eq ref setting('device_identity')) {
config->{'device_identity'} = [ setting('device_identity') ];

View File

@@ -4,7 +4,7 @@ use Dancer qw/:syntax :script/;
use Dancer::Plugin::DBIC 'schema';
use App::Netdisco::Util::Node 'check_mac';
use App::Netdisco::Util::DNS ':all';
use App::Netdisco::Util::FastResolver 'hostnames_resolve_async';
use NetAddr::IP::Lite ':lower';
use Time::HiRes 'gettimeofday';
use NetAddr::MAC ();

View File

@@ -6,6 +6,7 @@ use Dancer::Plugin::DBIC 'schema';
use App::Netdisco::Util::Device
qw/get_device match_devicetype is_discoverable/;
use App::Netdisco::Util::Permission 'check_acl_only';
use App::Netdisco::Util::FastResolver 'hostnames_resolve_async';
use App::Netdisco::Util::DNS ':all';
use App::Netdisco::JobQueue qw/jq_queued jq_insert/;
use NetAddr::IP::Lite ':lower';

View File

@@ -5,30 +5,13 @@ use warnings;
use Dancer ':script';
use Net::DNS;
use AnyEvent::DNS;
use NetAddr::IP::Lite ':lower';
use App::Netdisco::Util::Permission;
use base 'Exporter';
our @EXPORT = ();
our @EXPORT_OK = qw/
hostname_from_ip hostnames_resolve_async ipv4_from_hostname
/;
our @EXPORT_OK = qw/hostname_from_ip ipv4_from_hostname/;
our %EXPORT_TAGS = (all => \@EXPORT_OK);
# AE::DNS::EtcHosts only works for A/AAAA/SRV, but we want PTR.
# this loads+parses /etc/hosts file using AE. dirty hack.
use AnyEvent::Socket 'format_address';
use AnyEvent::DNS::EtcHosts;
AnyEvent::DNS::EtcHosts::_load_hosts_unless(sub{},AE::cv);
no AnyEvent::DNS::EtcHosts; # unimport
our %HOSTS = ();
$HOSTS{$_} = [ map { [ $_ ? (format_address $_->[0]) : '' ] }
@{$AnyEvent::DNS::EtcHosts::HOSTS{$_}} ]
for keys %AnyEvent::DNS::EtcHosts::HOSTS;
=head1 NAME
App::Netdisco::Util::DNS
@@ -53,10 +36,11 @@ Returns C<undef> if no PTR record exists for the IP.
sub hostname_from_ip {
my $ip = shift;
return unless $ip;
my $ETCHOSTS = setting('dns')->{'ETCHOSTS'};
# check /etc/hosts file and short-circuit if found
foreach my $name (reverse sort keys %HOSTS) {
if ($HOSTS{$name}->[0]->[0] eq $ip) {
foreach my $name (reverse sort keys %$ETCHOSTS) {
if ($ETCHOSTS->{$name}->[0]->[0] eq $ip) {
return $name;
}
}
@@ -85,10 +69,11 @@ Returns C<undef> if no A record exists for the name.
sub ipv4_from_hostname {
my $name = shift;
return unless $name;
my $ETCHOSTS = setting('dns')->{'ETCHOSTS'};
# check /etc/hosts file and short-circuit if found
if (exists $HOSTS{$name} and $HOSTS{$name}->[0]->[0]) {
my $ip = NetAddr::IP::Lite->new($HOSTS{$name}->[0]->[0]);
if (exists $ETCHOSTS->{$name} and $ETCHOSTS->{$name}->[0]->[0]) {
my $ip = NetAddr::IP::Lite->new($ETCHOSTS->{$name}->[0]->[0]);
return $ip->addr if $ip and $ip->bits == 32;
}
@@ -105,63 +90,4 @@ sub ipv4_from_hostname {
return undef;
}
=head2 hostnames_resolve_async( \@ips, \@timeouts? )
This method uses a fully asynchronous and high-performance pure-perl stub
resolver C<AnyEvent::DNS>.
Given a reference to an array of hashes will resolve the C<IPv4> or C<IPv6>
address in the C<ip>, C<alias>, or C<device> key of each hash into its
hostname which will be inserted in the C<dns> key of the hash.
Optionally provide a set of timeout values in seconds which is also the
number of resolver attempts. The default is C<< [2,5,5] >>.
Returns the supplied reference to an array of hashes with dns values for
addresses which resolved.
=cut
sub hostnames_resolve_async {
my ($ips, $timeouts) = @_;
return [] unless $ips and ref [] eq ref $ips;
$timeouts ||= [2,5,5];
my $skip = setting('dns')->{'no'};
AnyEvent::DNS::resolver->timeout(@$timeouts);
# Set up the condvar
my $done = AE::cv;
$done->begin( sub { shift->send } );
IP: foreach my $hash_ref (@$ips) {
my $ip = $hash_ref->{'ip'} || $hash_ref->{'alias'} || $hash_ref->{'device'};
next IP if App::Netdisco::Util::Permission::check_acl_no($ip, $skip);
# check /etc/hosts file and short-circuit if found
foreach my $name (reverse sort keys %HOSTS) {
if ($HOSTS{$name}->[0]->[0] eq $ip) {
$hash_ref->{'dns'} = $name;
next IP;
}
}
$done->begin;
AnyEvent::DNS::reverse_lookup $ip,
sub { $hash_ref->{'dns'} = shift; $done->end; };
}
# Decrement the cv counter to cancel out the send declaration
$done->end;
# Wait for the resolver to perform all resolutions
$done->recv;
# Remove reference to resolver so that we close sockets
# and allow return to any instance defaults we have changed
undef $AnyEvent::DNS::RESOLVER if $AnyEvent::DNS::RESOLVER;
return $ips;
}
1;

View File

@@ -0,0 +1,88 @@
package App::Netdisco::Util::FastResolver;
use strict;
use warnings;
use Dancer ':script';
use AnyEvent::DNS;
use App::Netdisco::Util::Permission 'check_acl_no';
use base 'Exporter';
our @EXPORT = ();
our @EXPORT_OK = qw/hostnames_resolve_async/;
our %EXPORT_TAGS = (all => \@EXPORT_OK);
=head1 NAME
App::Netdisco::Util::FastResolver
=head1 DESCRIPTION
A set of helper subroutines to support parts of the Netdisco application.
There are no default exports, however the C<:all> tag will export all
subroutines.
=head1 EXPORT_OK
=head2 hostnames_resolve_async( \@ips, \@timeouts? )
This method uses a fully asynchronous and high-performance pure-perl stub
resolver C<AnyEvent::DNS>.
Given a reference to an array of hashes will resolve the C<IPv4> or C<IPv6>
address in the C<ip>, C<alias>, or C<device> key of each hash into its
hostname which will be inserted in the C<dns> key of the hash.
Optionally provide a set of timeout values in seconds which is also the
number of resolver attempts. The default is C<< [2,5,5] >>.
Returns the supplied reference to an array of hashes with dns values for
addresses which resolved.
=cut
sub hostnames_resolve_async {
my ($ips, $timeouts) = @_;
return [] unless $ips and ref [] eq ref $ips;
$timeouts ||= [2,5,5];
my $skip = setting('dns')->{'no'};
my $ETCHOSTS = setting('dns')->{'ETCHOSTS'};
AnyEvent::DNS::resolver->timeout(@$timeouts);
# Set up the condvar
my $done = AE::cv;
$done->begin( sub { shift->send } );
IP: foreach my $hash_ref (@$ips) {
my $ip = $hash_ref->{'ip'} || $hash_ref->{'alias'} || $hash_ref->{'device'};
next IP if check_acl_no($ip, $skip);
# check /etc/hosts file and short-circuit if found
foreach my $name (reverse sort keys %$ETCHOSTS) {
if ($ETCHOSTS->{$name}->[0]->[0] eq $ip) {
$hash_ref->{'dns'} = $name;
next IP;
}
}
$done->begin;
AnyEvent::DNS::reverse_lookup $ip,
sub { $hash_ref->{'dns'} = shift; $done->end; };
}
# Decrement the cv counter to cancel out the send declaration
$done->end;
# Wait for the resolver to perform all resolutions
$done->recv;
# Remove reference to resolver so that we close sockets
# and allow return to any instance defaults we have changed
undef $AnyEvent::DNS::RESOLVER if $AnyEvent::DNS::RESOLVER;
return $ips;
}
1;

View File

@@ -6,7 +6,7 @@ use Dancer::Plugin::DBIC;
use Dancer::Plugin::Auth::Extensible;
use App::Netdisco::Web::Plugin;
use App::Netdisco::Util::DNS 'hostnames_resolve_async';
use App::Netdisco::Util::FastResolver 'hostnames_resolve_async';
register_admin_task({
tag => 'timedoutdevices',