| 1 |
=head1 NAME |
| 2 |
|
| 3 |
AnyEvent::DNS - fully asynchronous DNS resolution |
| 4 |
|
| 5 |
=head1 SYNOPSIS |
| 6 |
|
| 7 |
use AnyEvent::DNS; |
| 8 |
|
| 9 |
my $cv = AnyEvent->condvar; |
| 10 |
AnyEvent::DNS::a "www.google.de", $cv; |
| 11 |
# ... later |
| 12 |
my @addrs = $cv->recv; |
| 13 |
|
| 14 |
=head1 DESCRIPTION |
| 15 |
|
| 16 |
This module offers both a number of DNS convenience functions as well |
| 17 |
as a fully asynchronous and high-performance pure-perl stub resolver. |
| 18 |
|
| 19 |
The stub resolver supports DNS over IPv4 and IPv6, UDP and TCP, optional |
| 20 |
EDNS0 support for up to 4kiB datagrams and automatically falls back to |
| 21 |
virtual circuit mode for large responses. |
| 22 |
|
| 23 |
=head2 CONVENIENCE FUNCTIONS |
| 24 |
|
| 25 |
=over 4 |
| 26 |
|
| 27 |
=cut |
| 28 |
|
| 29 |
package AnyEvent::DNS; |
| 30 |
|
| 31 |
use Carp (); |
| 32 |
use Socket qw(AF_INET SOCK_DGRAM SOCK_STREAM); |
| 33 |
|
| 34 |
use AnyEvent (); BEGIN { AnyEvent::common_sense } |
| 35 |
use AnyEvent::Util qw(AF_INET6); |
| 36 |
|
| 37 |
our $VERSION = $AnyEvent::VERSION; |
| 38 |
our @DNS_FALLBACK; # some public dns servers as fallback |
| 39 |
|
| 40 |
{ |
| 41 |
my $prep = sub { |
| 42 |
$_ = $_->[rand @$_] for @_; |
| 43 |
push @_, splice @_, rand $_, 1 for reverse 1..@_; # shuffle |
| 44 |
$_ = pack "H*", $_ for @_; |
| 45 |
\@_ |
| 46 |
}; |
| 47 |
|
| 48 |
my $ipv4 = $prep->( |
| 49 |
["08080808", "08080404"], # 8.8.8.8, 8.8.4.4 - google public dns |
| 50 |
["01010101", "01000001"], # 1.1.1.1, 1.0.0.1 - cloudflare public dns |
| 51 |
["50505050", "50505151"], # 80.80.80.80, 80.80.81.81 - freenom.world |
| 52 |
## ["d1f40003", "d1f30004"], # v209.244.0.3/4 - resolver1/2.level3.net - status unknown |
| 53 |
## ["04020201", "04020203", "04020204", "04020205", "04020206"], # v4.2.2.1/3/4/5/6 - vnsc-pri.sys.gtei.net - effectively public |
| 54 |
## ["cdd22ad2", "4044c8c8"], # 205.210.42.205, 64.68.200.200 - cache1/2.dnsresolvers.com - verified public |
| 55 |
# ["8d010101"], # 141.1.1.1 - cable&wireless, now vodafone - status unknown |
| 56 |
# 84.200.69.80 # dns.watch |
| 57 |
# 84.200.70.40 # dns.watch |
| 58 |
# 37.235.1.174 # freedns.zone |
| 59 |
# 37.235.1.177 # freedns.zone |
| 60 |
# 213.73.91.35 # dnscache.berlin.ccc.de |
| 61 |
# 194.150.168.168 # dns.as250.net; Berlin/Frankfurt |
| 62 |
# 85.214.20.141 # FoeBud (digitalcourage.de) |
| 63 |
# 77.109.148.136 # privacyfoundation.ch |
| 64 |
# 77.109.148.137 # privacyfoundation.ch |
| 65 |
# 91.239.100.100 # anycast.censurfridns.dk |
| 66 |
# 89.233.43.71 # ns1.censurfridns.dk |
| 67 |
# 204.152.184.76 # f.6to4-servers.net, ISC, USA |
| 68 |
); |
| 69 |
|
| 70 |
my $ipv6 = $prep->( |
| 71 |
["20014860486000000000000000008888", "20014860486000000000000000008844"], # 2001:4860:4860::8888/8844 - google ipv6 |
| 72 |
["26064700470000000000000000001111", "26064700470000000000000000001001"], # 2606:4700:4700::1111/1001 - cloudflare dns |
| 73 |
); |
| 74 |
|
| 75 |
undef $ipv4 unless $AnyEvent::PROTOCOL{ipv4}; |
| 76 |
undef $ipv6 unless $AnyEvent::PROTOCOL{ipv6}; |
| 77 |
|
| 78 |
($ipv6, $ipv4) = ($ipv4, $ipv6) |
| 79 |
if $AnyEvent::PROTOCOL{ipv6} > $AnyEvent::PROTOCOL{ipv4}; |
| 80 |
|
| 81 |
@DNS_FALLBACK = (@$ipv4, @$ipv6); |
| 82 |
} |
| 83 |
|
| 84 |
=item AnyEvent::DNS::a $domain, $cb->(@addrs) |
| 85 |
|
| 86 |
Tries to resolve the given domain to IPv4 address(es). |
| 87 |
|
| 88 |
=item AnyEvent::DNS::aaaa $domain, $cb->(@addrs) |
| 89 |
|
| 90 |
Tries to resolve the given domain to IPv6 address(es). |
| 91 |
|
| 92 |
=item AnyEvent::DNS::mx $domain, $cb->(@hostnames) |
| 93 |
|
| 94 |
Tries to resolve the given domain into a sorted (lower preference value |
| 95 |
first) list of domain names. |
| 96 |
|
| 97 |
=item AnyEvent::DNS::ns $domain, $cb->(@hostnames) |
| 98 |
|
| 99 |
Tries to resolve the given domain name into a list of name servers. |
| 100 |
|
| 101 |
=item AnyEvent::DNS::txt $domain, $cb->(@hostnames) |
| 102 |
|
| 103 |
Tries to resolve the given domain name into a list of text records. Only |
| 104 |
the first text string per record will be returned. If you want all |
| 105 |
strings, you need to call the resolver manually: |
| 106 |
|
| 107 |
resolver->resolve ($domain => "txt", sub { |
| 108 |
for my $record (@_) { |
| 109 |
my (undef, undef, undef, @txt) = @$record; |
| 110 |
# strings now in @txt |
| 111 |
} |
| 112 |
}); |
| 113 |
|
| 114 |
=item AnyEvent::DNS::srv $service, $proto, $domain, $cb->(@srv_rr) |
| 115 |
|
| 116 |
Tries to resolve the given service, protocol and domain name into a list |
| 117 |
of service records. |
| 118 |
|
| 119 |
Each C<$srv_rr> is an array reference with the following contents: |
| 120 |
C<[$priority, $weight, $transport, $target]>. |
| 121 |
|
| 122 |
They will be sorted with lowest priority first, then randomly |
| 123 |
distributed by weight as per RFC 2782. |
| 124 |
|
| 125 |
Example: |
| 126 |
|
| 127 |
AnyEvent::DNS::srv "sip", "udp", "schmorp.de", sub { ... |
| 128 |
# @_ = ( [10, 10, 5060, "sip1.schmorp.de" ] ) |
| 129 |
|
| 130 |
=item AnyEvent::DNS::any $domain, $cb->(@rrs) |
| 131 |
|
| 132 |
Tries to resolve the given domain and passes all resource records found |
| 133 |
to the callback. Note that this uses a DNS C<ANY> query, which, as of RFC |
| 134 |
8482, are officially deprecated. |
| 135 |
|
| 136 |
=item AnyEvent::DNS::ptr $domain, $cb->(@hostnames) |
| 137 |
|
| 138 |
Tries to make a PTR lookup on the given domain. See C<reverse_lookup> |
| 139 |
and C<reverse_verify> if you want to resolve an IP address to a hostname |
| 140 |
instead. |
| 141 |
|
| 142 |
=item AnyEvent::DNS::reverse_lookup $ipv4_or_6, $cb->(@hostnames) |
| 143 |
|
| 144 |
Tries to reverse-resolve the given IPv4 or IPv6 address (in textual form) |
| 145 |
into its hostname(s). Handles V4MAPPED and V4COMPAT IPv6 addresses |
| 146 |
transparently. |
| 147 |
|
| 148 |
=item AnyEvent::DNS::reverse_verify $ipv4_or_6, $cb->(@hostnames) |
| 149 |
|
| 150 |
The same as C<reverse_lookup>, but does forward-lookups to verify that |
| 151 |
the resolved hostnames indeed point to the address, which makes spoofing |
| 152 |
harder. |
| 153 |
|
| 154 |
If you want to resolve an address into a hostname, this is the preferred |
| 155 |
method: The DNS records could still change, but at least this function |
| 156 |
verified that the hostname, at one point in the past, pointed at the IP |
| 157 |
address you originally resolved. |
| 158 |
|
| 159 |
Example: |
| 160 |
|
| 161 |
AnyEvent::DNS::reverse_verify "2001:500:2f::f", sub { print shift }; |
| 162 |
# => f.root-servers.net |
| 163 |
|
| 164 |
=cut |
| 165 |
|
| 166 |
sub MAX_PKT() { 4096 } # max packet size we advertise and accept |
| 167 |
|
| 168 |
sub DOMAIN_PORT() { 53 } # if this changes drop me a note |
| 169 |
|
| 170 |
sub resolver (); |
| 171 |
|
| 172 |
sub a($$) { |
| 173 |
my ($domain, $cb) = @_; |
| 174 |
|
| 175 |
resolver->resolve ($domain => "a", sub { |
| 176 |
$cb->(map $_->[4], @_); |
| 177 |
}); |
| 178 |
} |
| 179 |
|
| 180 |
sub aaaa($$) { |
| 181 |
my ($domain, $cb) = @_; |
| 182 |
|
| 183 |
resolver->resolve ($domain => "aaaa", sub { |
| 184 |
$cb->(map $_->[4], @_); |
| 185 |
}); |
| 186 |
} |
| 187 |
|
| 188 |
sub mx($$) { |
| 189 |
my ($domain, $cb) = @_; |
| 190 |
|
| 191 |
resolver->resolve ($domain => "mx", sub { |
| 192 |
$cb->(map $_->[5], sort { $a->[4] <=> $b->[4] } @_); |
| 193 |
}); |
| 194 |
} |
| 195 |
|
| 196 |
sub ns($$) { |
| 197 |
my ($domain, $cb) = @_; |
| 198 |
|
| 199 |
resolver->resolve ($domain => "ns", sub { |
| 200 |
$cb->(map $_->[4], @_); |
| 201 |
}); |
| 202 |
} |
| 203 |
|
| 204 |
sub txt($$) { |
| 205 |
my ($domain, $cb) = @_; |
| 206 |
|
| 207 |
resolver->resolve ($domain => "txt", sub { |
| 208 |
$cb->(map $_->[4], @_); |
| 209 |
}); |
| 210 |
} |
| 211 |
|
| 212 |
sub srv($$$$) { |
| 213 |
my ($service, $proto, $domain, $cb) = @_; |
| 214 |
|
| 215 |
# todo, ask for any and check glue records |
| 216 |
resolver->resolve ("_$service._$proto.$domain" => "srv", sub { |
| 217 |
my @res; |
| 218 |
|
| 219 |
# classify by priority |
| 220 |
my %pri; |
| 221 |
push @{ $pri{$_->[4]} }, [ @$_[4,5,6,7] ] |
| 222 |
for @_; |
| 223 |
|
| 224 |
# order by priority |
| 225 |
for my $pri (sort { $a <=> $b } keys %pri) { |
| 226 |
# order by weight |
| 227 |
my @rr = sort { $a->[1] <=> $b->[1] } @{ delete $pri{$pri} }; |
| 228 |
|
| 229 |
my $sum; $sum += $_->[1] for @rr; |
| 230 |
|
| 231 |
while (@rr) { |
| 232 |
my $w = int rand $sum + 1; |
| 233 |
for (0 .. $#rr) { |
| 234 |
if (($w -= $rr[$_][1]) <= 0) { |
| 235 |
$sum -= $rr[$_][1]; |
| 236 |
push @res, splice @rr, $_, 1, (); |
| 237 |
last; |
| 238 |
} |
| 239 |
} |
| 240 |
} |
| 241 |
} |
| 242 |
|
| 243 |
$cb->(@res); |
| 244 |
}); |
| 245 |
} |
| 246 |
|
| 247 |
sub ptr($$) { |
| 248 |
my ($domain, $cb) = @_; |
| 249 |
|
| 250 |
resolver->resolve ($domain => "ptr", sub { |
| 251 |
$cb->(map $_->[4], @_); |
| 252 |
}); |
| 253 |
} |
| 254 |
|
| 255 |
sub any($$) { |
| 256 |
my ($domain, $cb) = @_; |
| 257 |
|
| 258 |
resolver->resolve ($domain => "*", $cb); |
| 259 |
} |
| 260 |
|
| 261 |
# convert textual ip address into reverse lookup form |
| 262 |
sub _munge_ptr($) { |
| 263 |
my $ipn = $_[0] |
| 264 |
or return; |
| 265 |
|
| 266 |
my $ptr; |
| 267 |
|
| 268 |
my $af = AnyEvent::Socket::address_family ($ipn); |
| 269 |
|
| 270 |
if ($af == AF_INET6) { |
| 271 |
$ipn = substr $ipn, 0, 16; # anticipate future expansion |
| 272 |
|
| 273 |
# handle v4mapped and v4compat |
| 274 |
if ($ipn =~ s/^\x00{10}(?:\xff\xff|\x00\x00)//) { |
| 275 |
$af = AF_INET; |
| 276 |
} else { |
| 277 |
$ptr = join ".", (reverse split //, unpack "H32", $ipn), "ip6.arpa."; |
| 278 |
} |
| 279 |
} |
| 280 |
|
| 281 |
if ($af == AF_INET) { |
| 282 |
$ptr = join ".", (reverse unpack "C4", $ipn), "in-addr.arpa."; |
| 283 |
} |
| 284 |
|
| 285 |
$ptr |
| 286 |
} |
| 287 |
|
| 288 |
sub reverse_lookup($$) { |
| 289 |
my ($ip, $cb) = @_; |
| 290 |
|
| 291 |
$ip = _munge_ptr AnyEvent::Socket::parse_address ($ip) |
| 292 |
or return $cb->(); |
| 293 |
|
| 294 |
resolver->resolve ($ip => "ptr", sub { |
| 295 |
$cb->(map $_->[4], @_); |
| 296 |
}); |
| 297 |
} |
| 298 |
|
| 299 |
sub reverse_verify($$) { |
| 300 |
my ($ip, $cb) = @_; |
| 301 |
|
| 302 |
my $ipn = AnyEvent::Socket::parse_address ($ip) |
| 303 |
or return $cb->(); |
| 304 |
|
| 305 |
my $af = AnyEvent::Socket::address_family ($ipn); |
| 306 |
|
| 307 |
my @res; |
| 308 |
my $cnt; |
| 309 |
|
| 310 |
my $ptr = _munge_ptr $ipn |
| 311 |
or return $cb->(); |
| 312 |
|
| 313 |
$ip = AnyEvent::Socket::format_address ($ipn); # normalise into the same form |
| 314 |
|
| 315 |
ptr $ptr, sub { |
| 316 |
for my $name (@_) { |
| 317 |
++$cnt; |
| 318 |
|
| 319 |
# () around AF_INET to work around bug in 5.8 |
| 320 |
resolver->resolve ("$name." => ($af == (AF_INET) ? "a" : "aaaa"), sub { |
| 321 |
for (@_) { |
| 322 |
push @res, $name |
| 323 |
if $_->[4] eq $ip; |
| 324 |
} |
| 325 |
$cb->(@res) unless --$cnt; |
| 326 |
}); |
| 327 |
} |
| 328 |
|
| 329 |
$cb->() unless $cnt; |
| 330 |
}; |
| 331 |
} |
| 332 |
|
| 333 |
################################################################################# |
| 334 |
|
| 335 |
=back |
| 336 |
|
| 337 |
=head2 LOW-LEVEL DNS EN-/DECODING FUNCTIONS |
| 338 |
|
| 339 |
=over 4 |
| 340 |
|
| 341 |
=item $AnyEvent::DNS::EDNS0 |
| 342 |
|
| 343 |
This variable decides whether dns_pack automatically enables EDNS0 |
| 344 |
support. By default, this is disabled (C<0>), unless overridden by |
| 345 |
C<$ENV{PERL_ANYEVENT_EDNS0}>, but when set to C<1>, AnyEvent::DNS will use |
| 346 |
EDNS0 in all requests. |
| 347 |
|
| 348 |
=cut |
| 349 |
|
| 350 |
our $EDNS0 = $ENV{PERL_ANYEVENT_EDNS0}*1; # set to 1 to enable (partial) edns0 |
| 351 |
|
| 352 |
our %opcode_id = ( |
| 353 |
query => 0, |
| 354 |
iquery => 1, |
| 355 |
status => 2, |
| 356 |
notify => 4, |
| 357 |
update => 5, |
| 358 |
map +($_ => $_), 3, 6..15 |
| 359 |
); |
| 360 |
|
| 361 |
our %opcode_str = reverse %opcode_id; |
| 362 |
|
| 363 |
our %rcode_id = ( |
| 364 |
noerror => 0, |
| 365 |
formerr => 1, |
| 366 |
servfail => 2, |
| 367 |
nxdomain => 3, |
| 368 |
notimp => 4, |
| 369 |
refused => 5, |
| 370 |
yxdomain => 6, # Name Exists when it should not [RFC 2136] |
| 371 |
yxrrset => 7, # RR Set Exists when it should not [RFC 2136] |
| 372 |
nxrrset => 8, # RR Set that should exist does not [RFC 2136] |
| 373 |
notauth => 9, # Server Not Authoritative for zone [RFC 2136] |
| 374 |
notzone => 10, # Name not contained in zone [RFC 2136] |
| 375 |
# EDNS0 16 BADVERS Bad OPT Version [RFC 2671] |
| 376 |
# EDNS0 16 BADSIG TSIG Signature Failure [RFC 2845] |
| 377 |
# EDNS0 17 BADKEY Key not recognized [RFC 2845] |
| 378 |
# EDNS0 18 BADTIME Signature out of time window [RFC 2845] |
| 379 |
# EDNS0 19 BADMODE Bad TKEY Mode [RFC 2930] |
| 380 |
# EDNS0 20 BADNAME Duplicate key name [RFC 2930] |
| 381 |
# EDNS0 21 BADALG Algorithm not supported [RFC 2930] |
| 382 |
map +($_ => $_), 11..15 |
| 383 |
); |
| 384 |
|
| 385 |
our %rcode_str = reverse %rcode_id; |
| 386 |
|
| 387 |
our %type_id = ( |
| 388 |
a => 1, |
| 389 |
ns => 2, |
| 390 |
md => 3, |
| 391 |
mf => 4, |
| 392 |
cname => 5, |
| 393 |
soa => 6, |
| 394 |
mb => 7, |
| 395 |
mg => 8, |
| 396 |
mr => 9, |
| 397 |
null => 10, |
| 398 |
wks => 11, |
| 399 |
ptr => 12, |
| 400 |
hinfo => 13, |
| 401 |
minfo => 14, |
| 402 |
mx => 15, |
| 403 |
txt => 16, |
| 404 |
sig => 24, |
| 405 |
key => 25, |
| 406 |
gpos => 27, # rfc1712 |
| 407 |
aaaa => 28, |
| 408 |
loc => 29, # rfc1876 |
| 409 |
srv => 33, |
| 410 |
naptr => 35, # rfc2915 |
| 411 |
dname => 39, # rfc2672 |
| 412 |
opt => 41, |
| 413 |
ds => 43, # rfc4034 |
| 414 |
sshfp => 44, # rfc4255 |
| 415 |
rrsig => 46, # rfc4034 |
| 416 |
nsec => 47, # rfc4034 |
| 417 |
dnskey=> 48, # rfc4034 |
| 418 |
smimea=> 53, # rfc8162 |
| 419 |
cds => 59, # rfc7344 |
| 420 |
cdnskey=> 60, # rfc7344 |
| 421 |
openpgpkey=> 61, # rfc7926 |
| 422 |
csync => 62, # rfc7929 |
| 423 |
spf => 99, |
| 424 |
tkey => 249, |
| 425 |
tsig => 250, |
| 426 |
ixfr => 251, |
| 427 |
axfr => 252, |
| 428 |
mailb => 253, |
| 429 |
"*" => 255, |
| 430 |
uri => 256, |
| 431 |
caa => 257, # rfc6844 |
| 432 |
); |
| 433 |
|
| 434 |
our %type_str = reverse %type_id; |
| 435 |
|
| 436 |
our %class_id = ( |
| 437 |
in => 1, |
| 438 |
ch => 3, |
| 439 |
hs => 4, |
| 440 |
none => 254, |
| 441 |
"*" => 255, |
| 442 |
); |
| 443 |
|
| 444 |
our %class_str = reverse %class_id; |
| 445 |
|
| 446 |
sub _enc_name($) { |
| 447 |
pack "(C/a*)*", (split /\./, shift), "" |
| 448 |
} |
| 449 |
|
| 450 |
if ($] < 5.008) { |
| 451 |
# special slower 5.6 version |
| 452 |
*_enc_name = sub ($) { |
| 453 |
join "", map +(pack "C/a*", $_), (split /\./, shift), "" |
| 454 |
}; |
| 455 |
} |
| 456 |
|
| 457 |
sub _enc_qd() { |
| 458 |
(_enc_name $_->[0]) . pack "nn", |
| 459 |
($_->[1] > 0 ? $_->[1] : $type_id {$_->[1]}), |
| 460 |
($_->[3] > 0 ? $_->[2] : $class_id{$_->[2] || "in"}) |
| 461 |
} |
| 462 |
|
| 463 |
sub _enc_rr() { |
| 464 |
die "encoding of resource records is not supported"; |
| 465 |
} |
| 466 |
|
| 467 |
=item $pkt = AnyEvent::DNS::dns_pack $dns |
| 468 |
|
| 469 |
Packs a perl data structure into a DNS packet. Reading RFC 1035 is strongly |
| 470 |
recommended, then everything will be totally clear. Or maybe not. |
| 471 |
|
| 472 |
Resource records are not yet encodable. |
| 473 |
|
| 474 |
Examples: |
| 475 |
|
| 476 |
# very simple request, using lots of default values: |
| 477 |
{ rd => 1, qd => [ [ "host.domain", "a"] ] } |
| 478 |
|
| 479 |
# more complex example, showing how flags etc. are named: |
| 480 |
|
| 481 |
{ |
| 482 |
id => 10000, |
| 483 |
op => "query", |
| 484 |
rc => "nxdomain", |
| 485 |
|
| 486 |
# flags |
| 487 |
qr => 1, |
| 488 |
aa => 0, |
| 489 |
tc => 0, |
| 490 |
rd => 0, |
| 491 |
ra => 0, |
| 492 |
ad => 0, |
| 493 |
cd => 0, |
| 494 |
|
| 495 |
qd => [@rr], # query section |
| 496 |
an => [@rr], # answer section |
| 497 |
ns => [@rr], # authority section |
| 498 |
ar => [@rr], # additional records section |
| 499 |
} |
| 500 |
|
| 501 |
=cut |
| 502 |
|
| 503 |
sub dns_pack($) { |
| 504 |
my ($req) = @_; |
| 505 |
|
| 506 |
pack "nn nnnn a* a* a* a* a*", |
| 507 |
$req->{id}, |
| 508 |
|
| 509 |
! !$req->{qr} * 0x8000 |
| 510 |
+ $opcode_id{$req->{op}} * 0x0800 |
| 511 |
+ ! !$req->{aa} * 0x0400 |
| 512 |
+ ! !$req->{tc} * 0x0200 |
| 513 |
+ ! !$req->{rd} * 0x0100 |
| 514 |
+ ! !$req->{ra} * 0x0080 |
| 515 |
+ ! !$req->{ad} * 0x0020 |
| 516 |
+ ! !$req->{cd} * 0x0010 |
| 517 |
+ $rcode_id{$req->{rc}} * 0x0001, |
| 518 |
|
| 519 |
scalar @{ $req->{qd} || [] }, |
| 520 |
scalar @{ $req->{an} || [] }, |
| 521 |
scalar @{ $req->{ns} || [] }, |
| 522 |
$EDNS0 + scalar @{ $req->{ar} || [] }, # EDNS0 option included here |
| 523 |
|
| 524 |
(join "", map _enc_qd, @{ $req->{qd} || [] }), |
| 525 |
(join "", map _enc_rr, @{ $req->{an} || [] }), |
| 526 |
(join "", map _enc_rr, @{ $req->{ns} || [] }), |
| 527 |
(join "", map _enc_rr, @{ $req->{ar} || [] }), |
| 528 |
|
| 529 |
($EDNS0 ? pack "C nnNn", 0, 41, MAX_PKT, 0, 0 : "") # EDNS0 option |
| 530 |
} |
| 531 |
|
| 532 |
our $ofs; |
| 533 |
our $pkt; |
| 534 |
|
| 535 |
# bitches |
| 536 |
sub _dec_name { |
| 537 |
my @res; |
| 538 |
my $redir; |
| 539 |
my $ptr = $ofs; |
| 540 |
my $cnt; |
| 541 |
|
| 542 |
while () { |
| 543 |
return undef if ++$cnt >= 256; # to avoid DoS attacks |
| 544 |
|
| 545 |
my $len = ord substr $pkt, $ptr++, 1; |
| 546 |
|
| 547 |
if ($len >= 0xc0) { |
| 548 |
$ptr++; |
| 549 |
$ofs = $ptr if $ptr > $ofs; |
| 550 |
$ptr = (unpack "n", substr $pkt, $ptr - 2, 2) & 0x3fff; |
| 551 |
} elsif ($len) { |
| 552 |
push @res, substr $pkt, $ptr, $len; |
| 553 |
$ptr += $len; |
| 554 |
} else { |
| 555 |
$ofs = $ptr if $ptr > $ofs; |
| 556 |
return join ".", @res; |
| 557 |
} |
| 558 |
} |
| 559 |
} |
| 560 |
|
| 561 |
sub _dec_qd { |
| 562 |
my $qname = _dec_name; |
| 563 |
my ($qt, $qc) = unpack "nn", substr $pkt, $ofs; $ofs += 4; |
| 564 |
[$qname, $type_str{$qt} || $qt, $class_str{$qc} || $qc] |
| 565 |
} |
| 566 |
|
| 567 |
our %dec_rr = ( |
| 568 |
1 => sub { join ".", unpack "C4", $_ }, # a |
| 569 |
2 => sub { local $ofs = $ofs - length; _dec_name }, # ns |
| 570 |
5 => sub { local $ofs = $ofs - length; _dec_name }, # cname |
| 571 |
6 => sub { |
| 572 |
local $ofs = $ofs - length; |
| 573 |
my $mname = _dec_name; |
| 574 |
my $rname = _dec_name; |
| 575 |
($mname, $rname, unpack "NNNNN", substr $pkt, $ofs) |
| 576 |
}, # soa |
| 577 |
11 => sub { ((join ".", unpack "C4", $_), unpack "C a*", substr $_, 4) }, # wks |
| 578 |
12 => sub { local $ofs = $ofs - length; _dec_name }, # ptr |
| 579 |
13 => sub { unpack "C/a* C/a*", $_ }, # hinfo |
| 580 |
15 => sub { local $ofs = $ofs + 2 - length; ((unpack "n", $_), _dec_name) }, # mx |
| 581 |
16 => sub { unpack "(C/a*)*", $_ }, # txt |
| 582 |
28 => sub { AnyEvent::Socket::format_ipv6 ($_) }, # aaaa |
| 583 |
33 => sub { local $ofs = $ofs + 6 - length; ((unpack "nnn", $_), _dec_name) }, # srv |
| 584 |
35 => sub { # naptr |
| 585 |
# requires perl 5.10, sorry |
| 586 |
my ($order, $preference, $flags, $service, $regexp, $offset) = unpack "nn C/a* C/a* C/a* .", $_; |
| 587 |
local $ofs = $ofs + $offset - length; |
| 588 |
($order, $preference, $flags, $service, $regexp, _dec_name) |
| 589 |
}, |
| 590 |
39 => sub { local $ofs = $ofs - length; _dec_name }, # dname |
| 591 |
99 => sub { unpack "(C/a*)*", $_ }, # spf |
| 592 |
257 => sub { unpack "CC/a*a*", $_ }, # caa |
| 593 |
); |
| 594 |
|
| 595 |
sub _dec_rr { |
| 596 |
my $name = _dec_name; |
| 597 |
|
| 598 |
my ($rt, $rc, $ttl, $rdlen) = unpack "nn N n", substr $pkt, $ofs; $ofs += 10; |
| 599 |
local $_ = substr $pkt, $ofs, $rdlen; $ofs += $rdlen; |
| 600 |
|
| 601 |
[ |
| 602 |
$name, |
| 603 |
$type_str{$rt} || $rt, |
| 604 |
$class_str{$rc} || $rc, |
| 605 |
$ttl, |
| 606 |
($dec_rr{$rt} || sub { $_ })->(), |
| 607 |
] |
| 608 |
} |
| 609 |
|
| 610 |
=item $dns = AnyEvent::DNS::dns_unpack $pkt |
| 611 |
|
| 612 |
Unpacks a DNS packet into a perl data structure. |
| 613 |
|
| 614 |
Examples: |
| 615 |
|
| 616 |
# an unsuccessful reply |
| 617 |
{ |
| 618 |
'qd' => [ |
| 619 |
[ 'ruth.plan9.de.mach.uni-karlsruhe.de', '*', 'in' ] |
| 620 |
], |
| 621 |
'rc' => 'nxdomain', |
| 622 |
'ar' => [], |
| 623 |
'ns' => [ |
| 624 |
[ |
| 625 |
'uni-karlsruhe.de', |
| 626 |
'soa', |
| 627 |
'in', |
| 628 |
600, |
| 629 |
'netserv.rz.uni-karlsruhe.de', |
| 630 |
'hostmaster.rz.uni-karlsruhe.de', |
| 631 |
2008052201, 10800, 1800, 2592000, 86400 |
| 632 |
] |
| 633 |
], |
| 634 |
'tc' => '', |
| 635 |
'ra' => 1, |
| 636 |
'qr' => 1, |
| 637 |
'id' => 45915, |
| 638 |
'aa' => '', |
| 639 |
'an' => [], |
| 640 |
'rd' => 1, |
| 641 |
'op' => 'query', |
| 642 |
'__' => '<original dns packet>', |
| 643 |
} |
| 644 |
|
| 645 |
# a successful reply |
| 646 |
|
| 647 |
{ |
| 648 |
'qd' => [ [ 'www.google.de', 'a', 'in' ] ], |
| 649 |
'rc' => 0, |
| 650 |
'ar' => [ |
| 651 |
[ 'a.l.google.com', 'a', 'in', 3600, '209.85.139.9' ], |
| 652 |
[ 'b.l.google.com', 'a', 'in', 3600, '64.233.179.9' ], |
| 653 |
[ 'c.l.google.com', 'a', 'in', 3600, '64.233.161.9' ], |
| 654 |
], |
| 655 |
'ns' => [ |
| 656 |
[ 'l.google.com', 'ns', 'in', 3600, 'a.l.google.com' ], |
| 657 |
[ 'l.google.com', 'ns', 'in', 3600, 'b.l.google.com' ], |
| 658 |
], |
| 659 |
'tc' => '', |
| 660 |
'ra' => 1, |
| 661 |
'qr' => 1, |
| 662 |
'id' => 64265, |
| 663 |
'aa' => '', |
| 664 |
'an' => [ |
| 665 |
[ 'www.google.de', 'cname', 'in', 3600, 'www.google.com' ], |
| 666 |
[ 'www.google.com', 'cname', 'in', 3600, 'www.l.google.com' ], |
| 667 |
[ 'www.l.google.com', 'a', 'in', 3600, '66.249.93.104' ], |
| 668 |
[ 'www.l.google.com', 'a', 'in', 3600, '66.249.93.147' ], |
| 669 |
], |
| 670 |
'rd' => 1, |
| 671 |
'op' => 0, |
| 672 |
'__' => '<original dns packet>', |
| 673 |
} |
| 674 |
|
| 675 |
=cut |
| 676 |
|
| 677 |
sub dns_unpack($) { |
| 678 |
local $pkt = shift; |
| 679 |
my ($id, $flags, $qd, $an, $ns, $ar) |
| 680 |
= unpack "nn nnnn A*", $pkt; |
| 681 |
|
| 682 |
local $ofs = 6 * 2; |
| 683 |
|
| 684 |
{ |
| 685 |
__ => $pkt, |
| 686 |
id => $id, |
| 687 |
qr => ! ! ($flags & 0x8000), |
| 688 |
aa => ! ! ($flags & 0x0400), |
| 689 |
tc => ! ! ($flags & 0x0200), |
| 690 |
rd => ! ! ($flags & 0x0100), |
| 691 |
ra => ! ! ($flags & 0x0080), |
| 692 |
ad => ! ! ($flags & 0x0020), |
| 693 |
cd => ! ! ($flags & 0x0010), |
| 694 |
op => $opcode_str{($flags & 0x001e) >> 11}, |
| 695 |
rc => $rcode_str{($flags & 0x000f)}, |
| 696 |
|
| 697 |
qd => [map _dec_qd, 1 .. $qd], |
| 698 |
an => [map _dec_rr, 1 .. $an], |
| 699 |
ns => [map _dec_rr, 1 .. $ns], |
| 700 |
ar => [map _dec_rr, 1 .. $ar], |
| 701 |
} |
| 702 |
} |
| 703 |
|
| 704 |
############################################################################# |
| 705 |
|
| 706 |
=back |
| 707 |
|
| 708 |
=head3 Extending DNS Encoder and Decoder |
| 709 |
|
| 710 |
This section describes an I<experimental> method to extend the DNS encoder |
| 711 |
and decoder with new opcode, rcode, class and type strings, as well as |
| 712 |
resource record decoders. |
| 713 |
|
| 714 |
Since this is experimental, it can change, as anything can change, but |
| 715 |
this interface is expe ctedc to be relatively stable and was stable during |
| 716 |
the whole existance of C<AnyEvent::DNS> so far. |
| 717 |
|
| 718 |
Note that, since changing the decoder or encoder might break existing |
| 719 |
code, you should either be sure to control for this, or only temporarily |
| 720 |
change these values, e.g. like so: |
| 721 |
|
| 722 |
my $decoded = do { |
| 723 |
local $AnyEvent::DNS::opcode_str{7} = "yxrrset"; |
| 724 |
AnyEvent::DNS::dns_unpack $mypkt |
| 725 |
}; |
| 726 |
|
| 727 |
=over 4 |
| 728 |
|
| 729 |
=item %AnyEvent::DNS::opcode_id, %AnyEvent::DNS::opcode_str |
| 730 |
|
| 731 |
Two hashes that map lowercase opcode strings to numerical id's (For the |
| 732 |
encoder), or vice versa (for the decoder). Example: add a new opcode |
| 733 |
string C<notzone>. |
| 734 |
|
| 735 |
$AnyEvent::DNS::opcode_id{notzone} = 10; |
| 736 |
$AnyEvent::DNS::opcode_str{10} = 'notzone'; |
| 737 |
|
| 738 |
=item %AnyEvent::DNS::rcode_id, %AnyEvent::DNS::rcode_str |
| 739 |
|
| 740 |
Same as above, for for rcode values. |
| 741 |
|
| 742 |
=item %AnyEvent::DNS::class_id, %AnyEvent::DNS::class_str |
| 743 |
|
| 744 |
Same as above, but for resource record class names/values. |
| 745 |
|
| 746 |
=item %AnyEvent::DNS::type_id, %AnyEvent::DNS::type_str |
| 747 |
|
| 748 |
Same as above, but for resource record type names/values. |
| 749 |
|
| 750 |
=item %AnyEvent::DNS::dec_rr |
| 751 |
|
| 752 |
This hash maps resource record type values to code references. When |
| 753 |
decoding, they are called with C<$_> set to the undecoded data portion and |
| 754 |
C<$ofs> being the current byte offset. of the record. You should have a |
| 755 |
look at the existing implementations to understand how it works in detail, |
| 756 |
but here are two examples: |
| 757 |
|
| 758 |
Decode an A record. A records are simply four bytes with one byte per |
| 759 |
address component, so the decoder simply unpacks them and joins them with |
| 760 |
dots in between: |
| 761 |
|
| 762 |
$AnyEvent::DNS::dec_rr{1} = sub { join ".", unpack "C4", $_ }; |
| 763 |
|
| 764 |
Decode a CNAME record, which contains a potentially compressed domain |
| 765 |
name. |
| 766 |
|
| 767 |
package AnyEvent::DNS; # for %dec_rr, $ofsd and &_dec_name |
| 768 |
$dec_rr{5} = sub { local $ofs = $ofs - length; _dec_name }; |
| 769 |
|
| 770 |
=back |
| 771 |
|
| 772 |
=head2 THE AnyEvent::DNS RESOLVER CLASS |
| 773 |
|
| 774 |
This is the class which does the actual protocol work. |
| 775 |
|
| 776 |
=over 4 |
| 777 |
|
| 778 |
=cut |
| 779 |
|
| 780 |
use Carp (); |
| 781 |
use Scalar::Util (); |
| 782 |
use Socket (); |
| 783 |
|
| 784 |
our $NOW; |
| 785 |
|
| 786 |
=item AnyEvent::DNS::resolver |
| 787 |
|
| 788 |
This function creates and returns a resolver that is ready to use and |
| 789 |
should mimic the default resolver for your system as good as possible. It |
| 790 |
is used by AnyEvent itself as well. |
| 791 |
|
| 792 |
It only ever creates one resolver and returns this one on subsequent calls |
| 793 |
- see C<$AnyEvent::DNS::RESOLVER>, below, for details. |
| 794 |
|
| 795 |
Unless you have special needs, prefer this function over creating your own |
| 796 |
resolver object. |
| 797 |
|
| 798 |
The resolver is created with the following parameters: |
| 799 |
|
| 800 |
untaint enabled |
| 801 |
max_outstanding $ENV{PERL_ANYEVENT_MAX_OUTSTANDING_DNS} (default 10) |
| 802 |
|
| 803 |
C<os_config> will be used for OS-specific configuration, unless |
| 804 |
C<$ENV{PERL_ANYEVENT_RESOLV_CONF}> is specified, in which case that file |
| 805 |
gets parsed. |
| 806 |
|
| 807 |
=item $AnyEvent::DNS::RESOLVER |
| 808 |
|
| 809 |
This variable stores the default resolver returned by |
| 810 |
C<AnyEvent::DNS::resolver>, or C<undef> when the default resolver hasn't |
| 811 |
been instantiated yet. |
| 812 |
|
| 813 |
One can provide a custom resolver (e.g. one with caching functionality) |
| 814 |
by storing it in this variable, causing all subsequent resolves done via |
| 815 |
C<AnyEvent::DNS::resolver> to be done via the custom one. |
| 816 |
|
| 817 |
=cut |
| 818 |
|
| 819 |
our $RESOLVER; |
| 820 |
|
| 821 |
sub resolver() { |
| 822 |
$RESOLVER || do { |
| 823 |
$RESOLVER = new AnyEvent::DNS |
| 824 |
untaint => 1, |
| 825 |
max_outstanding => $ENV{PERL_ANYEVENT_MAX_OUTSTANDING_DNS}*1 || 10, |
| 826 |
; |
| 827 |
|
| 828 |
$ENV{PERL_ANYEVENT_RESOLV_CONF} |
| 829 |
? $RESOLVER->_load_resolv_conf_file ($ENV{PERL_ANYEVENT_RESOLV_CONF}) |
| 830 |
: $RESOLVER->os_config; |
| 831 |
|
| 832 |
$RESOLVER |
| 833 |
} |
| 834 |
} |
| 835 |
|
| 836 |
=item $resolver = new AnyEvent::DNS key => value... |
| 837 |
|
| 838 |
Creates and returns a new resolver. |
| 839 |
|
| 840 |
The following options are supported: |
| 841 |
|
| 842 |
=over 4 |
| 843 |
|
| 844 |
=item server => [...] |
| 845 |
|
| 846 |
A list of server addresses (default: C<v127.0.0.1> or C<::1>) in network |
| 847 |
format (i.e. as returned by C<AnyEvent::Socket::parse_address> - both IPv4 |
| 848 |
and IPv6 are supported). |
| 849 |
|
| 850 |
=item timeout => [...] |
| 851 |
|
| 852 |
A list of timeouts to use (also determines the number of retries). To make |
| 853 |
three retries with individual time-outs of 2, 5 and 5 seconds, use C<[2, |
| 854 |
5, 5]>, which is also the default. |
| 855 |
|
| 856 |
=item search => [...] |
| 857 |
|
| 858 |
The default search list of suffixes to append to a domain name (default: none). |
| 859 |
|
| 860 |
=item ndots => $integer |
| 861 |
|
| 862 |
The number of dots (default: C<1>) that a name must have so that the resolver |
| 863 |
tries to resolve the name without any suffixes first. |
| 864 |
|
| 865 |
=item max_outstanding => $integer |
| 866 |
|
| 867 |
Most name servers do not handle many parallel requests very well. This |
| 868 |
option limits the number of outstanding requests to C<$integer> |
| 869 |
(default: C<10>), that means if you request more than this many requests, |
| 870 |
then the additional requests will be queued until some other requests have |
| 871 |
been resolved. |
| 872 |
|
| 873 |
=item reuse => $seconds |
| 874 |
|
| 875 |
The number of seconds (default: C<300>) that a query id cannot be re-used |
| 876 |
after a timeout. If there was no time-out then query ids can be reused |
| 877 |
immediately. |
| 878 |
|
| 879 |
=item untaint => $boolean |
| 880 |
|
| 881 |
When true, then the resolver will automatically untaint results, and might |
| 882 |
also ignore certain environment variables. |
| 883 |
|
| 884 |
=back |
| 885 |
|
| 886 |
=cut |
| 887 |
|
| 888 |
sub new { |
| 889 |
my ($class, %arg) = @_; |
| 890 |
|
| 891 |
my $self = bless { |
| 892 |
server => [], |
| 893 |
timeout => [2, 5, 5], |
| 894 |
search => [], |
| 895 |
ndots => 1, |
| 896 |
max_outstanding => 10, |
| 897 |
reuse => 300, |
| 898 |
%arg, |
| 899 |
inhibit => 0, |
| 900 |
reuse_q => [], |
| 901 |
}, $class; |
| 902 |
|
| 903 |
# search should default to gethostname's domain |
| 904 |
# but perl lacks a good posix module |
| 905 |
|
| 906 |
# try to create an ipv4 and an ipv6 socket |
| 907 |
# only fail when we cannot create either |
| 908 |
my $got_socket; |
| 909 |
|
| 910 |
Scalar::Util::weaken (my $wself = $self); |
| 911 |
|
| 912 |
if (socket my $fh4, AF_INET , Socket::SOCK_DGRAM(), 0) { |
| 913 |
++$got_socket; |
| 914 |
|
| 915 |
AnyEvent::fh_unblock $fh4; |
| 916 |
$self->{fh4} = $fh4; |
| 917 |
$self->{rw4} = AE::io $fh4, 0, sub { |
| 918 |
if (my $peer = recv $fh4, my $pkt, MAX_PKT, 0) { |
| 919 |
$wself->_recv ($pkt, $peer); |
| 920 |
} |
| 921 |
}; |
| 922 |
} |
| 923 |
|
| 924 |
if (AF_INET6 && socket my $fh6, AF_INET6, Socket::SOCK_DGRAM(), 0) { |
| 925 |
++$got_socket; |
| 926 |
|
| 927 |
$self->{fh6} = $fh6; |
| 928 |
AnyEvent::fh_unblock $fh6; |
| 929 |
$self->{rw6} = AE::io $fh6, 0, sub { |
| 930 |
if (my $peer = recv $fh6, my $pkt, MAX_PKT, 0) { |
| 931 |
$wself->_recv ($pkt, $peer); |
| 932 |
} |
| 933 |
}; |
| 934 |
} |
| 935 |
|
| 936 |
$got_socket |
| 937 |
or Carp::croak "unable to create either an IPv4 or an IPv6 socket"; |
| 938 |
|
| 939 |
$self->_compile; |
| 940 |
|
| 941 |
$self |
| 942 |
} |
| 943 |
|
| 944 |
# called to start asynchronous configuration |
| 945 |
sub _config_begin { |
| 946 |
++$_[0]{inhibit}; |
| 947 |
} |
| 948 |
|
| 949 |
# called when done with async config |
| 950 |
sub _config_done { |
| 951 |
--$_[0]{inhibit}; |
| 952 |
$_[0]->_compile; |
| 953 |
$_[0]->_scheduler; |
| 954 |
} |
| 955 |
|
| 956 |
=item $resolver->parse_resolv_conf ($string) |
| 957 |
|
| 958 |
Parses the given string as if it were a F<resolv.conf> file. The following |
| 959 |
directives are supported (but not necessarily implemented). |
| 960 |
|
| 961 |
C<#>- and C<;>-style comments, C<nameserver>, C<domain>, C<search>, C<sortlist>, |
| 962 |
C<options> (C<timeout>, C<attempts>, C<ndots>). |
| 963 |
|
| 964 |
Everything else is silently ignored. |
| 965 |
|
| 966 |
=cut |
| 967 |
|
| 968 |
sub parse_resolv_conf { |
| 969 |
my ($self, $resolvconf) = @_; |
| 970 |
|
| 971 |
$self->{server} = []; |
| 972 |
$self->{search} = []; |
| 973 |
|
| 974 |
my $attempts; |
| 975 |
|
| 976 |
for (split /\n/, $resolvconf) { |
| 977 |
s/\s*[;#].*$//; # not quite legal, but many people insist |
| 978 |
|
| 979 |
if (/^\s*nameserver\s+(\S+)\s*$/i) { |
| 980 |
my $ip = $1; |
| 981 |
if (my $ipn = AnyEvent::Socket::parse_address ($ip)) { |
| 982 |
push @{ $self->{server} }, $ipn; |
| 983 |
} else { |
| 984 |
AE::log 5 => "nameserver $ip invalid and ignored, while parsing resolver config."; |
| 985 |
} |
| 986 |
} elsif (/^\s*domain\s+(\S*)\s*$/i) { |
| 987 |
$self->{search} = [$1]; |
| 988 |
} elsif (/^\s*search\s+(.*?)\s*$/i) { |
| 989 |
$self->{search} = [split /\s+/, $1]; |
| 990 |
} elsif (/^\s*sortlist\s+(.*?)\s*$/i) { |
| 991 |
# ignored, NYI |
| 992 |
} elsif (/^\s*options\s+(.*?)\s*$/i) { |
| 993 |
for (split /\s+/, $1) { |
| 994 |
if (/^timeout:(\d+)$/) { |
| 995 |
$self->{timeout} = [$1]; |
| 996 |
} elsif (/^attempts:(\d+)$/) { |
| 997 |
$attempts = $1; |
| 998 |
} elsif (/^ndots:(\d+)$/) { |
| 999 |
$self->{ndots} = $1; |
| 1000 |
} else { |
| 1001 |
# debug, rotate, no-check-names, inet6 |
| 1002 |
} |
| 1003 |
} |
| 1004 |
} else { |
| 1005 |
# silently skip stuff we don't understand |
| 1006 |
} |
| 1007 |
} |
| 1008 |
|
| 1009 |
$self->{timeout} = [($self->{timeout}[0]) x $attempts] |
| 1010 |
if $attempts; |
| 1011 |
|
| 1012 |
$self->_compile; |
| 1013 |
} |
| 1014 |
|
| 1015 |
sub _load_resolv_conf_file { |
| 1016 |
my ($self, $resolv_conf) = @_; |
| 1017 |
|
| 1018 |
$self->_config_begin; |
| 1019 |
|
| 1020 |
require AnyEvent::IO; |
| 1021 |
AnyEvent::IO::aio_load ($resolv_conf, sub { |
| 1022 |
if (my ($contents) = @_) { |
| 1023 |
$self->parse_resolv_conf ($contents); |
| 1024 |
} else { |
| 1025 |
AE::log 4 => "$resolv_conf: $!"; |
| 1026 |
} |
| 1027 |
|
| 1028 |
$self->_config_done; |
| 1029 |
}); |
| 1030 |
} |
| 1031 |
|
| 1032 |
=item $resolver->os_config |
| 1033 |
|
| 1034 |
Tries so load and parse F</etc/resolv.conf> on portable operating |
| 1035 |
systems. Tries various egregious hacks on windows to force the DNS servers |
| 1036 |
and searchlist out of the system. |
| 1037 |
|
| 1038 |
This method must be called at most once before trying to resolve anything. |
| 1039 |
|
| 1040 |
=cut |
| 1041 |
|
| 1042 |
sub os_config { |
| 1043 |
my ($self) = @_; |
| 1044 |
|
| 1045 |
$self->_config_begin; |
| 1046 |
|
| 1047 |
$self->{server} = []; |
| 1048 |
$self->{search} = []; |
| 1049 |
|
| 1050 |
if ((AnyEvent::WIN32 || $^O =~ /cygwin/i)) { |
| 1051 |
# TODO: this blocks the program, but should not, but I |
| 1052 |
# am too lazy to implement and test it. need to boot windows. ugh. |
| 1053 |
|
| 1054 |
#no strict 'refs'; |
| 1055 |
|
| 1056 |
# there are many options to find the current nameservers etc. on windows |
| 1057 |
# all of them don't work consistently: |
| 1058 |
# - the registry thing needs separate code on win32 native vs. cygwin |
| 1059 |
# - the registry layout differs between windows versions |
| 1060 |
# - calling windows api functions doesn't work on cygwin |
| 1061 |
# - ipconfig uses locale-specific messages |
| 1062 |
|
| 1063 |
# we use Net::DNS::Resolver first, and if it fails, will fall back to |
| 1064 |
# ipconfig parsing. |
| 1065 |
unless (eval { |
| 1066 |
# Net::DNS::Resolver uses a LOT of ram (~10mb), but what can we do :/ |
| 1067 |
# (this seems mostly to be due to Win32::API). |
| 1068 |
require Net::DNS::Resolver; |
| 1069 |
my $r = Net::DNS::Resolver->new; |
| 1070 |
|
| 1071 |
$r->nameservers |
| 1072 |
or die; |
| 1073 |
|
| 1074 |
for my $s ($r->nameservers) { |
| 1075 |
if (my $ipn = AnyEvent::Socket::parse_address ($s)) { |
| 1076 |
push @{ $self->{server} }, $ipn; |
| 1077 |
} |
| 1078 |
} |
| 1079 |
$self->{search} = [$r->searchlist]; |
| 1080 |
|
| 1081 |
1 |
| 1082 |
}) { |
| 1083 |
# we use ipconfig parsing because, despite all its brokenness, |
| 1084 |
# it seems quite stable in practise. |
| 1085 |
# unfortunately it wants a console window. |
| 1086 |
# for good measure, we append a fallback nameserver to our list. |
| 1087 |
|
| 1088 |
if (open my $fh, "ipconfig /all |") { |
| 1089 |
# parsing strategy: we go through the output and look for |
| 1090 |
# :-lines with DNS in them. everything in those is regarded as |
| 1091 |
# either a nameserver (if it parses as an ip address), or a suffix |
| 1092 |
# (all else). |
| 1093 |
|
| 1094 |
my $dns; |
| 1095 |
local $_; |
| 1096 |
while (<$fh>) { |
| 1097 |
if (s/^\s.*\bdns\b.*://i) { |
| 1098 |
$dns = 1; |
| 1099 |
} elsif (/^\S/ || /^\s[^:]{16,}: /) { |
| 1100 |
$dns = 0; |
| 1101 |
} |
| 1102 |
if ($dns && /^\s*(\S+)\s*$/) { |
| 1103 |
my $s = $1; |
| 1104 |
$s =~ s/%\d+(?!\S)//; # get rid of ipv6 scope id |
| 1105 |
if (my $ipn = AnyEvent::Socket::parse_address ($s)) { |
| 1106 |
push @{ $self->{server} }, $ipn; |
| 1107 |
} else { |
| 1108 |
push @{ $self->{search} }, $s; |
| 1109 |
} |
| 1110 |
} |
| 1111 |
} |
| 1112 |
} |
| 1113 |
} |
| 1114 |
|
| 1115 |
# always add the fallback servers on windows |
| 1116 |
push @{ $self->{server} }, @DNS_FALLBACK; |
| 1117 |
|
| 1118 |
$self->_config_done; |
| 1119 |
} else { |
| 1120 |
# try /etc/resolv.conf everywhere else |
| 1121 |
|
| 1122 |
require AnyEvent::IO; |
| 1123 |
AnyEvent::IO::aio_stat ("/etc/resolv.conf", sub { |
| 1124 |
$self->_load_resolv_conf_file ("/etc/resolv.conf") |
| 1125 |
if @_; |
| 1126 |
$self->_config_done; |
| 1127 |
}); |
| 1128 |
} |
| 1129 |
} |
| 1130 |
|
| 1131 |
=item $resolver->timeout ($timeout, ...) |
| 1132 |
|
| 1133 |
Sets the timeout values. See the C<timeout> constructor argument (and |
| 1134 |
note that this method expects the timeout values themselves, not an |
| 1135 |
array-reference). |
| 1136 |
|
| 1137 |
=cut |
| 1138 |
|
| 1139 |
sub timeout { |
| 1140 |
my ($self, @timeout) = @_; |
| 1141 |
|
| 1142 |
$self->{timeout} = \@timeout; |
| 1143 |
$self->_compile; |
| 1144 |
} |
| 1145 |
|
| 1146 |
=item $resolver->max_outstanding ($nrequests) |
| 1147 |
|
| 1148 |
Sets the maximum number of outstanding requests to C<$nrequests>. See the |
| 1149 |
C<max_outstanding> constructor argument. |
| 1150 |
|
| 1151 |
=cut |
| 1152 |
|
| 1153 |
sub max_outstanding { |
| 1154 |
my ($self, $max) = @_; |
| 1155 |
|
| 1156 |
$self->{max_outstanding} = $max; |
| 1157 |
$self->_compile; |
| 1158 |
} |
| 1159 |
|
| 1160 |
sub _compile { |
| 1161 |
my $self = shift; |
| 1162 |
|
| 1163 |
my %search; $self->{search} = [grep 0 < length, grep !$search{$_}++, @{ $self->{search} }]; |
| 1164 |
my %server; $self->{server} = [grep 0 < length, grep !$server{$_}++, @{ $self->{server} }]; |
| 1165 |
|
| 1166 |
unless (@{ $self->{server} }) { |
| 1167 |
# use 127.0.0.1/::1 by default, add public nameservers as fallback |
| 1168 |
my $default = $AnyEvent::PROTOCOL{ipv6} > $AnyEvent::PROTOCOL{ipv4} |
| 1169 |
? "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1" : "\x7f\x00\x00\x01"; |
| 1170 |
$self->{server} = [$default, @DNS_FALLBACK]; |
| 1171 |
} |
| 1172 |
|
| 1173 |
my @retry; |
| 1174 |
|
| 1175 |
for my $timeout (@{ $self->{timeout} }) { |
| 1176 |
for my $server (@{ $self->{server} }) { |
| 1177 |
push @retry, [$server, $timeout]; |
| 1178 |
} |
| 1179 |
} |
| 1180 |
|
| 1181 |
$self->{retry} = \@retry; |
| 1182 |
} |
| 1183 |
|
| 1184 |
sub _feed { |
| 1185 |
my ($self, $res) = @_; |
| 1186 |
|
| 1187 |
($res) = $res =~ /^(.*)$/s |
| 1188 |
if AnyEvent::TAINT && $self->{untaint}; |
| 1189 |
|
| 1190 |
$res = dns_unpack $res |
| 1191 |
or return; |
| 1192 |
|
| 1193 |
my $id = $self->{id}{$res->{id}}; |
| 1194 |
|
| 1195 |
return unless ref $id; |
| 1196 |
|
| 1197 |
$NOW = time; |
| 1198 |
$id->[1]->($res); |
| 1199 |
} |
| 1200 |
|
| 1201 |
sub _recv { |
| 1202 |
my ($self, $pkt, $peer) = @_; |
| 1203 |
|
| 1204 |
# we ignore errors (often one gets port unreachable, but there is |
| 1205 |
# no good way to take advantage of that. |
| 1206 |
|
| 1207 |
my ($port, $host) = AnyEvent::Socket::unpack_sockaddr ($peer); |
| 1208 |
|
| 1209 |
return unless $port == DOMAIN_PORT && grep $_ eq $host, @{ $self->{server} }; |
| 1210 |
|
| 1211 |
$self->_feed ($pkt); |
| 1212 |
} |
| 1213 |
|
| 1214 |
sub _free_id { |
| 1215 |
my ($self, $id, $timeout) = @_; |
| 1216 |
|
| 1217 |
if ($timeout) { |
| 1218 |
# we need to block the id for a while |
| 1219 |
$self->{id}{$id} = 1; |
| 1220 |
push @{ $self->{reuse_q} }, [$NOW + $self->{reuse}, $id]; |
| 1221 |
} else { |
| 1222 |
# we can quickly recycle the id |
| 1223 |
delete $self->{id}{$id}; |
| 1224 |
} |
| 1225 |
|
| 1226 |
--$self->{outstanding}; |
| 1227 |
$self->_scheduler; |
| 1228 |
} |
| 1229 |
|
| 1230 |
# execute a single request, involves sending it with timeouts to multiple servers |
| 1231 |
sub _exec { |
| 1232 |
my ($self, $req) = @_; |
| 1233 |
|
| 1234 |
my $retry; # of retries |
| 1235 |
my $do_retry; |
| 1236 |
|
| 1237 |
$do_retry = sub { |
| 1238 |
my $retry_cfg = $self->{retry}[$retry++] |
| 1239 |
or do { |
| 1240 |
# failure |
| 1241 |
$self->_free_id ($req->[2], $retry > 1); |
| 1242 |
undef $do_retry; return $req->[1]->(); |
| 1243 |
}; |
| 1244 |
|
| 1245 |
my ($server, $timeout) = @$retry_cfg; |
| 1246 |
|
| 1247 |
$self->{id}{$req->[2]} = [(AE::timer $timeout, 0, sub { |
| 1248 |
$NOW = time; |
| 1249 |
|
| 1250 |
# timeout, try next |
| 1251 |
&$do_retry if $do_retry; |
| 1252 |
}), sub { |
| 1253 |
my ($res) = @_; |
| 1254 |
|
| 1255 |
if ($res->{tc}) { |
| 1256 |
# success, but truncated, so use tcp |
| 1257 |
AnyEvent::Socket::tcp_connect (AnyEvent::Socket::format_address ($server), DOMAIN_PORT, sub { |
| 1258 |
return unless $do_retry; # some other request could have invalidated us already |
| 1259 |
|
| 1260 |
my ($fh) = @_ |
| 1261 |
or return &$do_retry; |
| 1262 |
|
| 1263 |
require AnyEvent::Handle; |
| 1264 |
|
| 1265 |
my $handle; $handle = new AnyEvent::Handle |
| 1266 |
fh => $fh, |
| 1267 |
timeout => $timeout, |
| 1268 |
on_error => sub { |
| 1269 |
undef $handle; |
| 1270 |
return unless $do_retry; # some other request could have invalidated us already |
| 1271 |
# failure, try next |
| 1272 |
&$do_retry; |
| 1273 |
}; |
| 1274 |
|
| 1275 |
$handle->push_write (pack "n/a*", $req->[0]); |
| 1276 |
$handle->push_read (chunk => 2, sub { |
| 1277 |
$handle->unshift_read (chunk => (unpack "n", $_[1]), sub { |
| 1278 |
undef $handle; |
| 1279 |
$self->_feed ($_[1]); |
| 1280 |
}); |
| 1281 |
}); |
| 1282 |
|
| 1283 |
}, sub { $timeout }); |
| 1284 |
|
| 1285 |
} else { |
| 1286 |
# success |
| 1287 |
$self->_free_id ($req->[2], $retry > 1); |
| 1288 |
undef $do_retry; return $req->[1]->($res); |
| 1289 |
} |
| 1290 |
}]; |
| 1291 |
|
| 1292 |
my $sa = AnyEvent::Socket::pack_sockaddr (DOMAIN_PORT, $server); |
| 1293 |
|
| 1294 |
my $fh = AF_INET == AnyEvent::Socket::sockaddr_family ($sa) |
| 1295 |
? $self->{fh4} : $self->{fh6} |
| 1296 |
or return &$do_retry; |
| 1297 |
|
| 1298 |
send $fh, $req->[0], 0, $sa; |
| 1299 |
}; |
| 1300 |
|
| 1301 |
&$do_retry; |
| 1302 |
} |
| 1303 |
|
| 1304 |
sub _scheduler { |
| 1305 |
my ($self) = @_; |
| 1306 |
|
| 1307 |
return if $self->{inhibit}; |
| 1308 |
|
| 1309 |
#no strict 'refs'; |
| 1310 |
|
| 1311 |
$NOW = time; |
| 1312 |
|
| 1313 |
# first clear id reuse queue |
| 1314 |
delete $self->{id}{ (shift @{ $self->{reuse_q} })->[1] } |
| 1315 |
while @{ $self->{reuse_q} } && $self->{reuse_q}[0][0] <= $NOW; |
| 1316 |
|
| 1317 |
while ($self->{outstanding} < $self->{max_outstanding}) { |
| 1318 |
|
| 1319 |
if (@{ $self->{reuse_q} } >= 30000) { |
| 1320 |
# we ran out of ID's, wait a bit |
| 1321 |
$self->{reuse_to} ||= AE::timer $self->{reuse_q}[0][0] - $NOW, 0, sub { |
| 1322 |
delete $self->{reuse_to}; |
| 1323 |
$self->_scheduler; |
| 1324 |
}; |
| 1325 |
last; |
| 1326 |
} |
| 1327 |
|
| 1328 |
if (my $req = shift @{ $self->{queue} }) { |
| 1329 |
# found a request in the queue, execute it |
| 1330 |
while () { |
| 1331 |
$req->[2] = int rand 65536; |
| 1332 |
last unless exists $self->{id}{$req->[2]}; |
| 1333 |
} |
| 1334 |
|
| 1335 |
++$self->{outstanding}; |
| 1336 |
$self->{id}{$req->[2]} = 1; |
| 1337 |
substr $req->[0], 0, 2, pack "n", $req->[2]; |
| 1338 |
|
| 1339 |
$self->_exec ($req); |
| 1340 |
|
| 1341 |
} elsif (my $cb = shift @{ $self->{wait} }) { |
| 1342 |
# found a wait_for_slot callback |
| 1343 |
$cb->($self); |
| 1344 |
|
| 1345 |
} else { |
| 1346 |
# nothing to do, just exit |
| 1347 |
last; |
| 1348 |
} |
| 1349 |
} |
| 1350 |
} |
| 1351 |
|
| 1352 |
=item $resolver->request ($req, $cb->($res)) |
| 1353 |
|
| 1354 |
This is the main low-level workhorse for sending DNS requests. |
| 1355 |
|
| 1356 |
This function sends a single request (a hash-ref formated as specified |
| 1357 |
for C<dns_pack>) to the configured nameservers in turn until it gets a |
| 1358 |
response. It handles timeouts, retries and automatically falls back to |
| 1359 |
virtual circuit mode (TCP) when it receives a truncated reply. It does not |
| 1360 |
handle anything else, such as the domain searchlist or relative names - |
| 1361 |
use C<< ->resolve >> for that. |
| 1362 |
|
| 1363 |
Calls the callback with the decoded response packet if a reply was |
| 1364 |
received, or no arguments in case none of the servers answered. |
| 1365 |
|
| 1366 |
=cut |
| 1367 |
|
| 1368 |
sub request($$) { |
| 1369 |
my ($self, $req, $cb) = @_; |
| 1370 |
|
| 1371 |
# _enc_name barfs on names that are too long, which is often outside |
| 1372 |
# program control, so check for too long names here. |
| 1373 |
for (@{ $req->{qd} }) { |
| 1374 |
return AE::postpone sub { $cb->(undef) } |
| 1375 |
if 255 < length $_->[0]; |
| 1376 |
} |
| 1377 |
|
| 1378 |
push @{ $self->{queue} }, [dns_pack $req, $cb]; |
| 1379 |
$self->_scheduler; |
| 1380 |
} |
| 1381 |
|
| 1382 |
=item $resolver->resolve ($qname, $qtype, %options, $cb->(@rr)) |
| 1383 |
|
| 1384 |
Queries the DNS for the given domain name C<$qname> of type C<$qtype>. |
| 1385 |
|
| 1386 |
A C<$qtype> is either a numerical query type (e.g. C<1> for A records) or |
| 1387 |
a lowercase name (you have to look at the source to see which aliases are |
| 1388 |
supported, but all types from RFC 1035, C<aaaa>, C<srv>, C<spf> and a few |
| 1389 |
more are known to this module). A C<$qtype> of "*" is supported and means |
| 1390 |
"any" record type. |
| 1391 |
|
| 1392 |
The callback will be invoked with a list of matching result records or |
| 1393 |
none on any error or if the name could not be found. |
| 1394 |
|
| 1395 |
CNAME chains (although illegal) are followed up to a length of 10. |
| 1396 |
|
| 1397 |
The callback will be invoked with arraryefs of the form C<[$name, |
| 1398 |
$type, $class, $ttl, @data>], where C<$name> is the domain name, |
| 1399 |
C<$type> a type string or number, C<$class> a class name, C<$ttl> is the |
| 1400 |
remaining time-to-live and C<@data> is resource-record-dependent data, in |
| 1401 |
seconds. For C<a> records, this will be the textual IPv4 addresses, for |
| 1402 |
C<ns> or C<cname> records this will be a domain name, for C<txt> records |
| 1403 |
these are all the strings and so on. |
| 1404 |
|
| 1405 |
All types mentioned in RFC 1035, C<aaaa>, C<srv>, C<naptr> and C<spf> are |
| 1406 |
decoded. All resource records not known to this module will have the raw |
| 1407 |
C<rdata> field as fifth array element. |
| 1408 |
|
| 1409 |
Note that this resolver is just a stub resolver: it requires a name server |
| 1410 |
supporting recursive queries, will not do any recursive queries itself and |
| 1411 |
is not secure when used against an untrusted name server. |
| 1412 |
|
| 1413 |
The following options are supported: |
| 1414 |
|
| 1415 |
=over 4 |
| 1416 |
|
| 1417 |
=item search => [$suffix...] |
| 1418 |
|
| 1419 |
Use the given search list (which might be empty), by appending each one |
| 1420 |
in turn to the C<$qname>. If this option is missing then the configured |
| 1421 |
C<ndots> and C<search> values define its value (depending on C<ndots>, the |
| 1422 |
empty suffix will be prepended or appended to that C<search> value). If |
| 1423 |
the C<$qname> ends in a dot, then the searchlist will be ignored. |
| 1424 |
|
| 1425 |
=item accept => [$type...] |
| 1426 |
|
| 1427 |
Lists the acceptable result types: only result types in this set will be |
| 1428 |
accepted and returned. The default includes the C<$qtype> and nothing |
| 1429 |
else. If this list includes C<cname>, then CNAME-chains will not be |
| 1430 |
followed (because you asked for the CNAME record). |
| 1431 |
|
| 1432 |
=item class => "class" |
| 1433 |
|
| 1434 |
Specify the query class ("in" for internet, "ch" for chaosnet and "hs" for |
| 1435 |
hesiod are the only ones making sense). The default is "in", of course. |
| 1436 |
|
| 1437 |
=back |
| 1438 |
|
| 1439 |
Examples: |
| 1440 |
|
| 1441 |
# full example, you can paste this into perl: |
| 1442 |
use Data::Dumper; |
| 1443 |
use AnyEvent::DNS; |
| 1444 |
AnyEvent::DNS::resolver->resolve ( |
| 1445 |
"google.com", "*", my $cv = AnyEvent->condvar); |
| 1446 |
warn Dumper [$cv->recv]; |
| 1447 |
|
| 1448 |
# shortened result: |
| 1449 |
# [ |
| 1450 |
# [ 'google.com', 'soa', 'in', 3600, 'ns1.google.com', 'dns-admin.google.com', |
| 1451 |
# 2008052701, 7200, 1800, 1209600, 300 ], |
| 1452 |
# [ |
| 1453 |
# 'google.com', 'txt', 'in', 3600, |
| 1454 |
# 'v=spf1 include:_netblocks.google.com ~all' |
| 1455 |
# ], |
| 1456 |
# [ 'google.com', 'a', 'in', 3600, '64.233.187.99' ], |
| 1457 |
# [ 'google.com', 'mx', 'in', 3600, 10, 'smtp2.google.com' ], |
| 1458 |
# [ 'google.com', 'ns', 'in', 3600, 'ns2.google.com' ], |
| 1459 |
# ] |
| 1460 |
|
| 1461 |
# resolve a records: |
| 1462 |
$res->resolve ("ruth.plan9.de", "a", sub { warn Dumper [@_] }); |
| 1463 |
|
| 1464 |
# result: |
| 1465 |
# [ |
| 1466 |
# [ 'ruth.schmorp.de', 'a', 'in', 86400, '129.13.162.95' ] |
| 1467 |
# ] |
| 1468 |
|
| 1469 |
# resolve any records, but return only a and aaaa records: |
| 1470 |
$res->resolve ("test1.laendle", "*", |
| 1471 |
accept => ["a", "aaaa"], |
| 1472 |
sub { |
| 1473 |
warn Dumper [@_]; |
| 1474 |
} |
| 1475 |
); |
| 1476 |
|
| 1477 |
# result: |
| 1478 |
# [ |
| 1479 |
# [ 'test1.laendle', 'a', 'in', 86400, '10.0.0.255' ], |
| 1480 |
# [ 'test1.laendle', 'aaaa', 'in', 60, '3ffe:1900:4545:0002:0240:0000:0000:f7e1' ] |
| 1481 |
# ] |
| 1482 |
|
| 1483 |
=cut |
| 1484 |
|
| 1485 |
sub resolve($%) { |
| 1486 |
my $cb = pop; |
| 1487 |
my ($self, $qname, $qtype, %opt) = @_; |
| 1488 |
|
| 1489 |
$self->wait_for_slot (sub { |
| 1490 |
my $self = shift; |
| 1491 |
|
| 1492 |
my @search = $qname =~ s/\.$// |
| 1493 |
? "" |
| 1494 |
: $opt{search} |
| 1495 |
? @{ $opt{search} } |
| 1496 |
: ($qname =~ y/.//) >= $self->{ndots} |
| 1497 |
? ("", @{ $self->{search} }) |
| 1498 |
: (@{ $self->{search} }, ""); |
| 1499 |
|
| 1500 |
my $class = $opt{class} || "in"; |
| 1501 |
|
| 1502 |
my %atype = $opt{accept} |
| 1503 |
? map +($_ => 1), @{ $opt{accept} } |
| 1504 |
: ($qtype => 1); |
| 1505 |
|
| 1506 |
# advance in searchlist |
| 1507 |
my ($do_search, $do_req); |
| 1508 |
|
| 1509 |
$do_search = sub { |
| 1510 |
@search |
| 1511 |
or (undef $do_search), (undef $do_req), return $cb->(); |
| 1512 |
|
| 1513 |
(my $name = lc "$qname." . shift @search) =~ s/\.$//; |
| 1514 |
my $depth = 10; |
| 1515 |
|
| 1516 |
# advance in cname-chain |
| 1517 |
$do_req = sub { |
| 1518 |
$self->request ({ |
| 1519 |
rd => 1, |
| 1520 |
qd => [[$name, $qtype, $class]], |
| 1521 |
}, sub { |
| 1522 |
my ($res) = @_ |
| 1523 |
or return $do_search->(); |
| 1524 |
|
| 1525 |
my $cname; |
| 1526 |
|
| 1527 |
while () { |
| 1528 |
# results found? |
| 1529 |
my @rr = grep $name eq lc $_->[0] && ($atype{"*"} || $atype{$_->[1]}), @{ $res->{an} }; |
| 1530 |
|
| 1531 |
(undef $do_search), (undef $do_req), return $cb->(@rr) |
| 1532 |
if @rr; |
| 1533 |
|
| 1534 |
# see if there is a cname we can follow |
| 1535 |
my @rr = grep $name eq lc $_->[0] && $_->[1] eq "cname", @{ $res->{an} }; |
| 1536 |
|
| 1537 |
if (@rr) { |
| 1538 |
$depth-- |
| 1539 |
or return $do_search->(); # cname chain too long |
| 1540 |
|
| 1541 |
$cname = 1; |
| 1542 |
$name = lc $rr[0][4]; |
| 1543 |
|
| 1544 |
} elsif ($cname) { |
| 1545 |
# follow the cname |
| 1546 |
return $do_req->(); |
| 1547 |
|
| 1548 |
} else { |
| 1549 |
# no, not found anything |
| 1550 |
return $do_search->(); |
| 1551 |
} |
| 1552 |
} |
| 1553 |
}); |
| 1554 |
}; |
| 1555 |
|
| 1556 |
$do_req->(); |
| 1557 |
}; |
| 1558 |
|
| 1559 |
$do_search->(); |
| 1560 |
}); |
| 1561 |
} |
| 1562 |
|
| 1563 |
=item $resolver->wait_for_slot ($cb->($resolver)) |
| 1564 |
|
| 1565 |
Wait until a free request slot is available and call the callback with the |
| 1566 |
resolver object. |
| 1567 |
|
| 1568 |
A request slot is used each time a request is actually sent to the |
| 1569 |
nameservers: There are never more than C<max_outstanding> of them. |
| 1570 |
|
| 1571 |
Although you can submit more requests (they will simply be queued until |
| 1572 |
a request slot becomes available), sometimes, usually for rate-limiting |
| 1573 |
purposes, it is useful to instead wait for a slot before generating the |
| 1574 |
request (or simply to know when the request load is low enough so one can |
| 1575 |
submit requests again). |
| 1576 |
|
| 1577 |
This is what this method does: The callback will be called when submitting |
| 1578 |
a DNS request will not result in that request being queued. The callback |
| 1579 |
may or may not generate any requests in response. |
| 1580 |
|
| 1581 |
Note that the callback will only be invoked when the request queue is |
| 1582 |
empty, so this does not play well if somebody else keeps the request queue |
| 1583 |
full at all times. |
| 1584 |
|
| 1585 |
=cut |
| 1586 |
|
| 1587 |
sub wait_for_slot { |
| 1588 |
my ($self, $cb) = @_; |
| 1589 |
|
| 1590 |
push @{ $self->{wait} }, $cb; |
| 1591 |
$self->_scheduler; |
| 1592 |
} |
| 1593 |
|
| 1594 |
use AnyEvent::Socket (); # circular dependency, so do not import anything and do it at the end |
| 1595 |
|
| 1596 |
=back |
| 1597 |
|
| 1598 |
=head1 AUTHOR |
| 1599 |
|
| 1600 |
Marc Lehmann <schmorp@schmorp.de> |
| 1601 |
http://anyevent.schmorp.de |
| 1602 |
|
| 1603 |
=cut |
| 1604 |
|
| 1605 |
1 |