AnyEvent
view release on metacpan or search on metacpan
lib/AnyEvent/DNS.pm view on Meta::CPAN
=head1 NAME
AnyEvent::DNS - fully asynchronous DNS resolution
=head1 SYNOPSIS
use AnyEvent::DNS;
my $cv = AnyEvent->condvar;
AnyEvent::DNS::a "www.google.de", $cv;
# ... later
my @addrs = $cv->recv;
=head1 DESCRIPTION
This module offers both a number of DNS convenience functions as well
as a fully asynchronous and high-performance pure-perl stub resolver.
The stub resolver supports DNS over IPv4 and IPv6, UDP and TCP, optional
EDNS0 support for up to 4kiB datagrams and automatically falls back to
virtual circuit mode for large responses.
=head2 CONVENIENCE FUNCTIONS
=over 4
=cut
package AnyEvent::DNS;
use Carp ();
use Socket qw(AF_INET SOCK_DGRAM SOCK_STREAM);
use AnyEvent (); BEGIN { AnyEvent::common_sense }
use AnyEvent::Util qw(AF_INET6);
our $VERSION = $AnyEvent::VERSION;
our @DNS_FALLBACK; # some public dns servers as fallback
{
my $prep = sub {
$_ = $_->[rand @$_] for @_;
push @_, splice @_, rand $_, 1 for reverse 1..@_; # shuffle
$_ = pack "H*", $_ for @_;
\@_
};
my $ipv4 = $prep->(
["08080808", "08080404"], # 8.8.8.8, 8.8.4.4 - google public dns
["01010101", "01000001"], # 1.1.1.1, 1.0.0.1 - cloudflare public dns
["50505050", "50505151"], # 80.80.80.80, 80.80.81.81 - freenom.world
## ["d1f40003", "d1f30004"], # v209.244.0.3/4 - resolver1/2.level3.net - status unknown
## ["04020201", "04020203", "04020204", "04020205", "04020206"], # v4.2.2.1/3/4/5/6 - vnsc-pri.sys.gtei.net - effectively public
## ["cdd22ad2", "4044c8c8"], # 205.210.42.205, 64.68.200.200 - cache1/2.dnsresolvers.com - verified public
# ["8d010101"], # 141.1.1.1 - cable&wireless, now vodafone - status unknown
# 84.200.69.80 # dns.watch
# 84.200.70.40 # dns.watch
# 37.235.1.174 # freedns.zone
# 37.235.1.177 # freedns.zone
# 213.73.91.35 # dnscache.berlin.ccc.de
# 194.150.168.168 # dns.as250.net; Berlin/Frankfurt
# 85.214.20.141 # FoeBud (digitalcourage.de)
# 77.109.148.136 # privacyfoundation.ch
# 77.109.148.137 # privacyfoundation.ch
# 91.239.100.100 # anycast.censurfridns.dk
# 89.233.43.71 # ns1.censurfridns.dk
# 204.152.184.76 # f.6to4-servers.net, ISC, USA
);
my $ipv6 = $prep->(
["20014860486000000000000000008888", "20014860486000000000000000008844"], # 2001:4860:4860::8888/8844 - google ipv6
["26064700470000000000000000001111", "26064700470000000000000000001001"], # 2606:4700:4700::1111/1001 - cloudflare dns
);
undef $ipv4 unless $AnyEvent::PROTOCOL{ipv4};
undef $ipv6 unless $AnyEvent::PROTOCOL{ipv6};
lib/AnyEvent/DNS.pm view on Meta::CPAN
=back
=cut
sub new {
my ($class, %arg) = @_;
my $self = bless {
server => [],
timeout => [2, 5, 5],
search => [],
ndots => 1,
max_outstanding => 10,
reuse => 300,
%arg,
inhibit => 0,
reuse_q => [],
}, $class;
# search should default to gethostname's domain
# but perl lacks a good posix module
# try to create an ipv4 and an ipv6 socket
# only fail when we cannot create either
my $got_socket;
Scalar::Util::weaken (my $wself = $self);
if (socket my $fh4, AF_INET , Socket::SOCK_DGRAM(), 0) {
++$got_socket;
AnyEvent::fh_unblock $fh4;
$self->{fh4} = $fh4;
$self->{rw4} = AE::io $fh4, 0, sub {
if (my $peer = recv $fh4, my $pkt, MAX_PKT, 0) {
$wself->_recv ($pkt, $peer);
}
};
}
if (AF_INET6 && socket my $fh6, AF_INET6, Socket::SOCK_DGRAM(), 0) {
++$got_socket;
$self->{fh6} = $fh6;
AnyEvent::fh_unblock $fh6;
$self->{rw6} = AE::io $fh6, 0, sub {
if (my $peer = recv $fh6, my $pkt, MAX_PKT, 0) {
$wself->_recv ($pkt, $peer);
}
};
}
$got_socket
or Carp::croak "unable to create either an IPv4 or an IPv6 socket";
$self->_compile;
$self
}
# called to start asynchronous configuration
sub _config_begin {
++$_[0]{inhibit};
}
# called when done with async config
sub _config_done {
--$_[0]{inhibit};
$_[0]->_compile;
$_[0]->_scheduler;
}
=item $resolver->parse_resolv_conf ($string)
Parses the given string as if it were a F<resolv.conf> file. The following
directives are supported (but not necessarily implemented).
C<#>- and C<;>-style comments, C<nameserver>, C<domain>, C<search>, C<sortlist>,
C<options> (C<timeout>, C<attempts>, C<ndots>).
Everything else is silently ignored.
=cut
sub parse_resolv_conf {
my ($self, $resolvconf) = @_;
$self->{server} = [];
$self->{search} = [];
my $attempts;
for (split /\n/, $resolvconf) {
s/\s*[;#].*$//; # not quite legal, but many people insist
if (/^\s*nameserver\s+(\S+)\s*$/i) {
my $ip = $1;
if (my $ipn = AnyEvent::Socket::parse_address ($ip)) {
push @{ $self->{server} }, $ipn;
} else {
AE::log 5 => "nameserver $ip invalid and ignored, while parsing resolver config.";
}
} elsif (/^\s*domain\s+(\S*)\s*$/i) {
$self->{search} = [$1];
} elsif (/^\s*search\s+(.*?)\s*$/i) {
$self->{search} = [split /\s+/, $1];
} elsif (/^\s*sortlist\s+(.*?)\s*$/i) {
# ignored, NYI
} elsif (/^\s*options\s+(.*?)\s*$/i) {
for (split /\s+/, $1) {
if (/^timeout:(\d+)$/) {
$self->{timeout} = [$1];
} elsif (/^attempts:(\d+)$/) {
$attempts = $1;
} elsif (/^ndots:(\d+)$/) {
$self->{ndots} = $1;
} else {
# debug, rotate, no-check-names, inet6
}
}
} else {
( run in 0.672 second using v1.01-cache-2.11-cpan-39bf76dae61 )