1package App::Netdisco::Util::FastResolver;
2
3use strict;
4use warnings;
5use Dancer ':script';
6
7use AnyEvent::DNS;
8use App::Netdisco::Util::Permission 'check_acl_no';
9
10use base 'Exporter';
11our @EXPORT = ();
12our @EXPORT_OK = qw/hostnames_resolve_async/;
13our %EXPORT_TAGS = (all => \@EXPORT_OK);
14
15=head1 NAME
16
17App::Netdisco::Util::FastResolver
18
19=head1 DESCRIPTION
20
21A set of helper subroutines to support parts of the Netdisco application.
22
23There are no default exports, however the C<:all> tag will export all
24subroutines.
25
26=head1 EXPORT_OK
27
28=head2 hostnames_resolve_async( \@ips, \@timeouts? )
29
30This method uses a fully asynchronous and high-performance pure-perl stub
31resolver C<AnyEvent::DNS>.
32
33Given a reference to an array of hashes will resolve the C<IPv4> or C<IPv6>
34address in the C<ip>, C<alias>, or C<device> key of each hash into its
35hostname which will be inserted in the C<dns> key of the hash.
36
37Optionally provide a set of timeout values in seconds which is also the
38number of resolver attempts. The default is C<< [2,5,5] >>.
39
40Returns the supplied reference to an array of hashes with dns values for
41addresses which resolved.
42
43=cut
44
45sub hostnames_resolve_async {
46  my ($ips, $timeouts) = @_;
47  return [] unless $ips and ref [] eq ref $ips;
48  $timeouts ||= [2,5,5];
49
50  my $skip = setting('dns')->{'no'};
51  my $ETCHOSTS = setting('dns')->{'ETCHOSTS'};
52  AnyEvent::DNS::resolver->timeout(@$timeouts);
53
54  # Set up the condvar
55  my $done = AE::cv;
56  $done->begin( sub { shift->send } );
57
58  IP: foreach my $hash_ref (@$ips) {
59    my $ip = $hash_ref->{'ip'} || $hash_ref->{'alias'} || $hash_ref->{'device'};
60    next IP if check_acl_no($ip, $skip);
61
62    # check /etc/hosts file and short-circuit if found
63    foreach my $name (reverse sort keys %$ETCHOSTS) {
64        if ($ETCHOSTS->{$name}->[0]->[0] eq $ip) {
65            $hash_ref->{'dns'} = $name;
66            next IP;
67        }
68    }
69
70    $done->begin;
71    AnyEvent::DNS::reverse_lookup $ip,
72            sub { $hash_ref->{'dns'} = shift; $done->end; };
73  }
74
75  # Decrement the cv counter to cancel out the send declaration
76  $done->end;
77
78  # Wait for the resolver to perform all resolutions
79  $done->recv;
80
81  # Remove reference to resolver so that we close sockets
82  # and allow return to any instance defaults we have changed
83  undef $AnyEvent::DNS::RESOLVER if $AnyEvent::DNS::RESOLVER;
84
85  return $ips;
86}
87
881;
89