ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent/lib/AnyEvent/DNS.pm
Revision: 1.37
Committed: Thu May 29 00:30:15 2008 UTC (16 years, 1 month ago) by root
Branch: MAIN
Changes since 1.36: +7 -2 lines
Log Message:
*** empty log message ***

File Contents

# Content
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 UDP, optional EDNS0 support for up to
20 4kiB datagrams and automatically falls back to virtual circuit mode for
21 large responses.
22
23 =head2 CONVENIENCE FUNCTIONS
24
25 =over 4
26
27 =cut
28
29 package AnyEvent::DNS;
30
31 no warnings;
32 use strict;
33
34 use Socket qw(AF_INET SOCK_DGRAM SOCK_STREAM);
35
36 use AnyEvent ();
37 use AnyEvent::Handle ();
38 use AnyEvent::Util qw(AF_INET6);
39
40 our @DNS_FALLBACK = (v208.67.220.220, v208.67.222.222);
41
42 =item AnyEvent::DNS::a $domain, $cb->(@addrs)
43
44 Tries to resolve the given domain to IPv4 address(es).
45
46 =item AnyEvent::DNS::aaaa $domain, $cb->(@addrs)
47
48 Tries to resolve the given domain to IPv6 address(es).
49
50 =item AnyEvent::DNS::mx $domain, $cb->(@hostnames)
51
52 Tries to resolve the given domain into a sorted (lower preference value
53 first) list of domain names.
54
55 =item AnyEvent::DNS::ns $domain, $cb->(@hostnames)
56
57 Tries to resolve the given domain name into a list of name servers.
58
59 =item AnyEvent::DNS::txt $domain, $cb->(@hostnames)
60
61 Tries to resolve the given domain name into a list of text records.
62
63 =item AnyEvent::DNS::srv $service, $proto, $domain, $cb->(@srv_rr)
64
65 Tries to resolve the given service, protocol and domain name into a list
66 of service records.
67
68 Each srv_rr is an array reference with the following contents:
69 C<[$priority, $weight, $transport, $target]>.
70
71 They will be sorted with lowest priority, highest weight first (TODO:
72 should use the RFC algorithm to reorder same-priority records for weight).
73
74 Example:
75
76 AnyEvent::DNS::srv "sip", "udp", "schmorp.de", sub { ...
77 # @_ = ( [10, 10, 5060, "sip1.schmorp.de" ] )
78
79 =item AnyEvent::DNS::ptr $ipv4_or_6, $cb->(@hostnames)
80
81 Tries to reverse-resolve the given IPv4 or IPv6 address (in textual form)
82 into it's hostname(s).
83
84 Example:
85
86 AnyEvent::DNS::ptr "2001:500:2f::f", sub { print shift };
87 # => f.root-servers.net
88
89 =item AnyEvent::DNS::any $domain, $cb->(@rrs)
90
91 Tries to resolve the given domain and passes all resource records found to
92 the callback.
93
94 =cut
95
96 sub resolver;
97
98 sub a($$) {
99 my ($domain, $cb) = @_;
100
101 resolver->resolve ($domain => "a", sub {
102 $cb->(map $_->[3], @_);
103 });
104 }
105
106 sub aaaa($$) {
107 my ($domain, $cb) = @_;
108
109 resolver->resolve ($domain => "aaaa", sub {
110 $cb->(map $_->[3], @_);
111 });
112 }
113
114 sub mx($$) {
115 my ($domain, $cb) = @_;
116
117 resolver->resolve ($domain => "mx", sub {
118 $cb->(map $_->[4], sort { $a->[3] <=> $b->[3] } @_);
119 });
120 }
121
122 sub ns($$) {
123 my ($domain, $cb) = @_;
124
125 resolver->resolve ($domain => "ns", sub {
126 $cb->(map $_->[3], @_);
127 });
128 }
129
130 sub txt($$) {
131 my ($domain, $cb) = @_;
132
133 resolver->resolve ($domain => "txt", sub {
134 $cb->(map $_->[3], @_);
135 });
136 }
137
138 sub srv($$$$) {
139 my ($service, $proto, $domain, $cb) = @_;
140
141 # todo, ask for any and check glue records
142 resolver->resolve ("_$service._$proto.$domain" => "srv", sub {
143 $cb->(map [@$_[3,4,5,6]], sort { $a->[3] <=> $b->[3] || $b->[4] <=> $a->[4] } @_);
144 });
145 }
146
147 sub ptr($$) {
148 my ($ip, $cb) = @_;
149
150 $ip = AnyEvent::Socket::parse_address ($ip)
151 or return $cb->();
152
153 my $af = AnyEvent::Socket::address_family ($ip);
154
155 if ($af == AF_INET) {
156 $ip = join ".", (reverse split /\./, $ip), "in-addr.arpa.";
157 } elsif ($af == AF_INET6) {
158 $ip = join ".", (reverse split //, unpack "H*", $ip), "ip6.arpa.";
159 } else {
160 return $cb->();
161 }
162
163 resolver->resolve ($ip => "ptr", sub {
164 $cb->(map $_->[3], @_);
165 });
166 }
167
168 sub any($$) {
169 my ($domain, $cb) = @_;
170
171 resolver->resolve ($domain => "*", $cb);
172 }
173
174 #################################################################################
175
176 =back
177
178 =head2 LOW-LEVEL DNS EN-/DECODING FUNCTIONS
179
180 =over 4
181
182 =item $AnyEvent::DNS::EDNS0
183
184 This variable decides whether dns_pack automatically enables EDNS0
185 support. By default, this is disabled (C<0>), unless overridden by
186 C<$ENV{PERL_ANYEVENT_EDNS0>), but when set to C<1>, AnyEvent::DNS will use
187 EDNS0 in all requests.
188
189 =cut
190
191 our $EDNS0 = $ENV{PERL_ANYEVENT_EDNS0} * 1; # set to 1 to enable (partial) edns0
192
193 our %opcode_id = (
194 query => 0,
195 iquery => 1,
196 status => 2,
197 notify => 4,
198 update => 5,
199 map +($_ => $_), 3, 6..15
200 );
201
202 our %opcode_str = reverse %opcode_id;
203
204 our %rcode_id = (
205 noerror => 0,
206 formerr => 1,
207 servfail => 2,
208 nxdomain => 3,
209 notimp => 4,
210 refused => 5,
211 yxdomain => 6, # Name Exists when it should not [RFC 2136]
212 yxrrset => 7, # RR Set Exists when it should not [RFC 2136]
213 nxrrset => 8, # RR Set that should exist does not [RFC 2136]
214 notauth => 9, # Server Not Authoritative for zone [RFC 2136]
215 notzone => 10, # Name not contained in zone [RFC 2136]
216 # EDNS0 16 BADVERS Bad OPT Version [RFC 2671]
217 # EDNS0 16 BADSIG TSIG Signature Failure [RFC 2845]
218 # EDNS0 17 BADKEY Key not recognized [RFC 2845]
219 # EDNS0 18 BADTIME Signature out of time window [RFC 2845]
220 # EDNS0 19 BADMODE Bad TKEY Mode [RFC 2930]
221 # EDNS0 20 BADNAME Duplicate key name [RFC 2930]
222 # EDNS0 21 BADALG Algorithm not supported [RFC 2930]
223 map +($_ => $_), 11..15
224 );
225
226 our %rcode_str = reverse %rcode_id;
227
228 our %type_id = (
229 a => 1,
230 ns => 2,
231 md => 3,
232 mf => 4,
233 cname => 5,
234 soa => 6,
235 mb => 7,
236 mg => 8,
237 mr => 9,
238 null => 10,
239 wks => 11,
240 ptr => 12,
241 hinfo => 13,
242 minfo => 14,
243 mx => 15,
244 txt => 16,
245 aaaa => 28,
246 srv => 33,
247 opt => 41,
248 spf => 99,
249 tkey => 249,
250 tsig => 250,
251 ixfr => 251,
252 axfr => 252,
253 mailb => 253,
254 "*" => 255,
255 );
256
257 our %type_str = reverse %type_id;
258
259 our %class_id = (
260 in => 1,
261 ch => 3,
262 hs => 4,
263 none => 254,
264 "*" => 255,
265 );
266
267 our %class_str = reverse %class_id;
268
269 # names MUST have a trailing dot
270 sub _enc_name($) {
271 pack "(C/a*)*", (split /\./, shift), ""
272 }
273
274 sub _enc_qd() {
275 (_enc_name $_->[0]) . pack "nn",
276 ($_->[1] > 0 ? $_->[1] : $type_id {$_->[1]}),
277 ($_->[2] > 0 ? $_->[2] : $class_id{$_->[2] || "in"})
278 }
279
280 sub _enc_rr() {
281 die "encoding of resource records is not supported";
282 }
283
284 =item $pkt = AnyEvent::DNS::dns_pack $dns
285
286 Packs a perl data structure into a DNS packet. Reading RFC1034 is strongly
287 recommended, then everything will be totally clear. Or maybe not.
288
289 Resource records are not yet encodable.
290
291 Examples:
292
293 # very simple request, using lots of default values:
294 { rd => 1, qd => [ [ "host.domain", "a"] ] }
295
296 # more complex example, showing how flags etc. are named:
297
298 {
299 id => 10000,
300 op => "query",
301 rc => "nxdomain",
302
303 # flags
304 qr => 1,
305 aa => 0,
306 tc => 0,
307 rd => 0,
308 ra => 0,
309 ad => 0,
310 cd => 0,
311
312 qd => [@rr], # query section
313 an => [@rr], # answer section
314 ns => [@rr], # authority section
315 ar => [@rr], # additional records section
316 }
317
318 =cut
319
320 sub dns_pack($) {
321 my ($req) = @_;
322
323 pack "nn nnnn a* a* a* a* a*",
324 $req->{id},
325
326 ! !$req->{qr} * 0x8000
327 + $opcode_id{$req->{op}} * 0x0800
328 + ! !$req->{aa} * 0x0400
329 + ! !$req->{tc} * 0x0200
330 + ! !$req->{rd} * 0x0100
331 + ! !$req->{ra} * 0x0080
332 + ! !$req->{ad} * 0x0020
333 + ! !$req->{cd} * 0x0010
334 + $rcode_id{$req->{rc}} * 0x0001,
335
336 scalar @{ $req->{qd} || [] },
337 scalar @{ $req->{an} || [] },
338 scalar @{ $req->{ns} || [] },
339 $EDNS0 + scalar @{ $req->{ar} || [] }, # include EDNS0 option here
340
341 (join "", map _enc_qd, @{ $req->{qd} || [] }),
342 (join "", map _enc_rr, @{ $req->{an} || [] }),
343 (join "", map _enc_rr, @{ $req->{ns} || [] }),
344 (join "", map _enc_rr, @{ $req->{ar} || [] }),
345
346 ($EDNS0 ? pack "C nnNn", 0, 41, 4096, 0, 0 : "") # EDNS0, 4kiB udp payload size
347 }
348
349 our $ofs;
350 our $pkt;
351
352 # bitches
353 sub _dec_name {
354 my @res;
355 my $redir;
356 my $ptr = $ofs;
357 my $cnt;
358
359 while () {
360 return undef if ++$cnt >= 256; # to avoid DoS attacks
361
362 my $len = ord substr $pkt, $ptr++, 1;
363
364 if ($len >= 0xc0) {
365 $ptr++;
366 $ofs = $ptr if $ptr > $ofs;
367 $ptr = (unpack "n", substr $pkt, $ptr - 2, 2) & 0x3fff;
368 } elsif ($len) {
369 push @res, substr $pkt, $ptr, $len;
370 $ptr += $len;
371 } else {
372 $ofs = $ptr if $ptr > $ofs;
373 return join ".", @res;
374 }
375 }
376 }
377
378 sub _dec_qd {
379 my $qname = _dec_name;
380 my ($qt, $qc) = unpack "nn", substr $pkt, $ofs; $ofs += 4;
381 [$qname, $type_str{$qt} || $qt, $class_str{$qc} || $qc]
382 }
383
384 our %dec_rr = (
385 1 => sub { join ".", unpack "C4", $_ }, # a
386 2 => sub { local $ofs = $ofs - length; _dec_name }, # ns
387 5 => sub { local $ofs = $ofs - length; _dec_name }, # cname
388 6 => sub {
389 local $ofs = $ofs - length;
390 my $mname = _dec_name;
391 my $rname = _dec_name;
392 ($mname, $rname, unpack "NNNNN", substr $pkt, $ofs)
393 }, # soa
394 11 => sub { ((join ".", unpack "C4", $_), unpack "C a*", substr $_, 4) }, # wks
395 12 => sub { local $ofs = $ofs - length; _dec_name }, # ptr
396 13 => sub { unpack "C/a* C/a*", $_ }, # hinfo
397 15 => sub { local $ofs = $ofs + 2 - length; ((unpack "n", $_), _dec_name) }, # mx
398 16 => sub { unpack "(C/a*)*", $_ }, # txt
399 28 => sub { AnyEvent::Socket::format_address ($_) }, # aaaa
400 33 => sub { local $ofs = $ofs + 6 - length; ((unpack "nnn", $_), _dec_name) }, # srv
401 99 => sub { unpack "(C/a*)*", $_ }, # spf
402 );
403
404 sub _dec_rr {
405 my $name = _dec_name;
406
407 my ($rt, $rc, $ttl, $rdlen) = unpack "nn N n", substr $pkt, $ofs; $ofs += 10;
408 local $_ = substr $pkt, $ofs, $rdlen; $ofs += $rdlen;
409
410 [
411 $name,
412 $type_str{$rt} || $rt,
413 $class_str{$rc} || $rc,
414 ($dec_rr{$rt} || sub { $_ })->(),
415 ]
416 }
417
418 =item $dns = AnyEvent::DNS::dns_unpack $pkt
419
420 Unpacks a DNS packet into a perl data structure.
421
422 Examples:
423
424 # an unsuccessful reply
425 {
426 'qd' => [
427 [ 'ruth.plan9.de.mach.uni-karlsruhe.de', '*', 'in' ]
428 ],
429 'rc' => 'nxdomain',
430 'ar' => [],
431 'ns' => [
432 [
433 'uni-karlsruhe.de',
434 'soa',
435 'in',
436 'netserv.rz.uni-karlsruhe.de',
437 'hostmaster.rz.uni-karlsruhe.de',
438 2008052201, 10800, 1800, 2592000, 86400
439 ]
440 ],
441 'tc' => '',
442 'ra' => 1,
443 'qr' => 1,
444 'id' => 45915,
445 'aa' => '',
446 'an' => [],
447 'rd' => 1,
448 'op' => 'query'
449 }
450
451 # a successful reply
452
453 {
454 'qd' => [ [ 'www.google.de', 'a', 'in' ] ],
455 'rc' => 0,
456 'ar' => [
457 [ 'a.l.google.com', 'a', 'in', '209.85.139.9' ],
458 [ 'b.l.google.com', 'a', 'in', '64.233.179.9' ],
459 [ 'c.l.google.com', 'a', 'in', '64.233.161.9' ],
460 ],
461 'ns' => [
462 [ 'l.google.com', 'ns', 'in', 'a.l.google.com' ],
463 [ 'l.google.com', 'ns', 'in', 'b.l.google.com' ],
464 ],
465 'tc' => '',
466 'ra' => 1,
467 'qr' => 1,
468 'id' => 64265,
469 'aa' => '',
470 'an' => [
471 [ 'www.google.de', 'cname', 'in', 'www.google.com' ],
472 [ 'www.google.com', 'cname', 'in', 'www.l.google.com' ],
473 [ 'www.l.google.com', 'a', 'in', '66.249.93.104' ],
474 [ 'www.l.google.com', 'a', 'in', '66.249.93.147' ],
475 ],
476 'rd' => 1,
477 'op' => 0
478 }
479
480 =cut
481
482 sub dns_unpack($) {
483 local $pkt = shift;
484 my ($id, $flags, $qd, $an, $ns, $ar)
485 = unpack "nn nnnn A*", $pkt;
486
487 local $ofs = 6 * 2;
488
489 {
490 id => $id,
491 qr => ! ! ($flags & 0x8000),
492 aa => ! ! ($flags & 0x0400),
493 tc => ! ! ($flags & 0x0200),
494 rd => ! ! ($flags & 0x0100),
495 ra => ! ! ($flags & 0x0080),
496 ad => ! ! ($flags & 0x0020),
497 cd => ! ! ($flags & 0x0010),
498 op => $opcode_str{($flags & 0x001e) >> 11},
499 rc => $rcode_str{($flags & 0x000f)},
500
501 qd => [map _dec_qd, 1 .. $qd],
502 an => [map _dec_rr, 1 .. $an],
503 ns => [map _dec_rr, 1 .. $ns],
504 ar => [map _dec_rr, 1 .. $ar],
505 }
506 }
507
508 #############################################################################
509
510 =back
511
512 =head2 THE AnyEvent::DNS RESOLVER CLASS
513
514 This is the class which does the actual protocol work.
515
516 =over 4
517
518 =cut
519
520 use Carp ();
521 use Scalar::Util ();
522 use Socket ();
523
524 our $NOW;
525
526 =item AnyEvent::DNS::resolver
527
528 This function creates and returns a resolver that is ready to use and
529 should mimic the default resolver for your system as good as possible.
530
531 It only ever creates one resolver and returns this one on subsequent
532 calls.
533
534 Unless you have special needs, prefer this function over creating your own
535 resolver object.
536
537 =cut
538
539 our $RESOLVER;
540
541 sub resolver() {
542 $RESOLVER || do {
543 $RESOLVER = new AnyEvent::DNS;
544 $RESOLVER->os_config;
545 $RESOLVER
546 }
547 }
548
549 =item $resolver = new AnyEvent::DNS key => value...
550
551 Creates and returns a new resolver.
552
553 The following options are supported:
554
555 =over 4
556
557 =item server => [...]
558
559 A list of server addresses (default: C<v127.0.0.1>) in network format (4
560 octets for IPv4, 16 octets for IPv6 - not yet supported).
561
562 =item timeout => [...]
563
564 A list of timeouts to use (also determines the number of retries). To make
565 three retries with individual time-outs of 2, 5 and 5 seconds, use C<[2,
566 5, 5]>, which is also the default.
567
568 =item search => [...]
569
570 The default search list of suffixes to append to a domain name (default: none).
571
572 =item ndots => $integer
573
574 The number of dots (default: C<1>) that a name must have so that the resolver
575 tries to resolve the name without any suffixes first.
576
577 =item max_outstanding => $integer
578
579 Most name servers do not handle many parallel requests very well. This option
580 limits the number of outstanding requests to C<$n> (default: C<10>), that means
581 if you request more than this many requests, then the additional requests will be queued
582 until some other requests have been resolved.
583
584 =item reuse => $seconds
585
586 The number of seconds (default: C<300>) that a query id cannot be re-used
587 after a timeout. If there as no time-out then query id's can be reused
588 immediately.
589
590 =back
591
592 =cut
593
594 sub new {
595 my ($class, %arg) = @_;
596
597 socket my $fh, AF_INET, &Socket::SOCK_DGRAM, 0
598 or Carp::croak "socket: $!";
599
600 AnyEvent::Util::fh_nonblocking $fh, 1;
601
602 my $self = bless {
603 server => [],
604 timeout => [2, 5, 5],
605 search => [],
606 ndots => 1,
607 max_outstanding => 10,
608 reuse => 300, # reuse id's after 5 minutes only, if possible
609 %arg,
610 fh => $fh,
611 reuse_q => [],
612 }, $class;
613
614 # search should default to gethostname's domain
615 # but perl lacks a good posix module
616
617 Scalar::Util::weaken (my $wself = $self);
618 $self->{rw} = AnyEvent->io (fh => $fh, poll => "r", cb => sub { $wself->_recv });
619
620 $self->_compile;
621
622 $self
623 }
624
625 =item $resolver->parse_resolv_conv ($string)
626
627 Parses the given string as if it were a F<resolv.conf> file. The following
628 directives are supported (but not necessarily implemented).
629
630 C<#>-style comments, C<nameserver>, C<domain>, C<search>, C<sortlist>,
631 C<options> (C<timeout>, C<attempts>, C<ndots>).
632
633 Everything else is silently ignored.
634
635 =cut
636
637 sub parse_resolv_conf {
638 my ($self, $resolvconf) = @_;
639
640 $self->{server} = [];
641 $self->{search} = [];
642
643 my $attempts;
644
645 for (split /\n/, $resolvconf) {
646 if (/^\s*#/) {
647 # comment
648 } elsif (/^\s*nameserver\s+(\S+)\s*$/i) {
649 my $ip = $1;
650 if (my $ipn = AnyEvent::Socket::parse_address ($ip)) {
651 push @{ $self->{server} }, $ipn;
652 } else {
653 warn "nameserver $ip invalid and ignored\n";
654 }
655 } elsif (/^\s*domain\s+(\S*)\s+$/i) {
656 $self->{search} = [$1];
657 } elsif (/^\s*search\s+(.*?)\s*$/i) {
658 $self->{search} = [split /\s+/, $1];
659 } elsif (/^\s*sortlist\s+(.*?)\s*$/i) {
660 # ignored, NYI
661 } elsif (/^\s*options\s+(.*?)\s*$/i) {
662 for (split /\s+/, $1) {
663 if (/^timeout:(\d+)$/) {
664 $self->{timeout} = [$1];
665 } elsif (/^attempts:(\d+)$/) {
666 $attempts = $1;
667 } elsif (/^ndots:(\d+)$/) {
668 $self->{ndots} = $1;
669 } else {
670 # debug, rotate, no-check-names, inet6
671 }
672 }
673 }
674 }
675
676 $self->{timeout} = [($self->{timeout}[0]) x $attempts]
677 if $attempts;
678
679 $self->_compile;
680 }
681
682 =item $resolver->os_config
683
684 Tries so load and parse F</etc/resolv.conf> on portable operating systems. Tries various
685 egregious hacks on windows to force the DNS servers and searchlist out of the system.
686
687 =cut
688
689 sub os_config {
690 my ($self) = @_;
691
692 $self->{server} = [];
693 $self->{search} = [];
694
695 if (AnyEvent::WIN32 || $^O =~ /cygwin/i) {
696 no strict 'refs';
697
698 # there are many options to find the current nameservers etc. on windows
699 # all of them don't work consistently:
700 # - the registry thing needs separate code on win32 native vs. cygwin
701 # - the registry layout differs between windows versions
702 # - calling windows api functions doesn't work on cygwin
703 # - ipconfig uses locale-specific messages
704
705 # we use ipconfig parsing because, despite all it's brokenness,
706 # it seems most stable in practise.
707 # for good measure, we append a fallback nameserver to our list.
708
709 if (open my $fh, "ipconfig /all |") {
710 # parsing strategy: we go through the output and look for
711 # :-lines with DNS in them. everything in those is regarded as
712 # either a nameserver (if it parses as an ip address), or a suffix
713 # (all else).
714
715 my $dns;
716 while (<$fh>) {
717 if (s/^\s.*\bdns\b.*://i) {
718 $dns = 1;
719 } elsif (/^\S/ || /^\s[^:]{16,}: /) {
720 $dns = 0;
721 }
722 if ($dns && /^\s*(\S+)\s*$/) {
723 my $s = $1;
724 $s =~ s/%\d+(?!\S)//; # get rid of scope id
725 if (my $ipn = AnyEvent::Socket::parse_address ($s)) {
726 push @{ $self->{server} }, $ipn;
727 } else {
728 push @{ $self->{search} }, $s;
729 }
730 }
731 }
732
733 # always add one fallback server
734 push @{ $self->{server} }, $DNS_FALLBACK[rand @DNS_FALLBACK];
735
736 $self->_compile;
737 }
738 } else {
739 # try resolv.conf everywhere
740
741 if (open my $fh, "</etc/resolv.conf") {
742 local $/;
743 $self->parse_resolv_conf (<$fh>);
744 }
745 }
746 }
747
748 sub _compile {
749 my $self = shift;
750
751 # we currently throw away all ipv6 nameservers, we do not yet support those
752
753 my %search; $self->{search} = [grep 0 < length, grep !$search{$_}++, @{ $self->{search} }];
754 my %server; $self->{server} = [grep 4 == length, grep !$server{$_}++, @{ $self->{server} }];
755
756 unless (@{ $self->{server} }) {
757 # use 127.0.0.1 by default, and one opendns nameserver as fallback
758 $self->{server} = [v127.0.0.1, $DNS_FALLBACK[rand @DNS_FALLBACK]];
759 }
760
761 my @retry;
762
763 for my $timeout (@{ $self->{timeout} }) {
764 for my $server (@{ $self->{server} }) {
765 push @retry, [$server, $timeout];
766 }
767 }
768
769 $self->{retry} = \@retry;
770 }
771
772 sub _feed {
773 my ($self, $res) = @_;
774
775 $res = dns_unpack $res
776 or return;
777
778 my $id = $self->{id}{$res->{id}};
779
780 return unless ref $id;
781
782 $NOW = time;
783 $id->[1]->($res);
784 }
785
786 sub _recv {
787 my ($self) = @_;
788
789 # we ignore errors (often one gets port unreachable, but there is
790 # no good way to take advantage of that.
791 while (my $peer = recv $self->{fh}, my $res, 4096, 0) {
792 my ($port, $host) = AnyEvent::Socket::unpack_sockaddr ($peer);
793
794 return unless $port == 53 && grep $_ eq $host, @{ $self->{server} };
795
796 $self->_feed ($res);
797 }
798 }
799
800 sub _free_id {
801 my ($self, $id, $timeout) = @_;
802
803 if ($timeout) {
804 # we need to block the id for a while
805 $self->{id}{$id} = 1;
806 push @{ $self->{reuse_q} }, [$NOW + $self->{reuse}, $id];
807 } else {
808 # we can quickly recycle the id
809 delete $self->{id}{$id};
810 }
811
812 --$self->{outstanding};
813 $self->_scheduler;
814 }
815
816 # execute a single request, involves sending it with timeouts to multiple servers
817 sub _exec {
818 my ($self, $req) = @_;
819
820 my $retry; # of retries
821 my $do_retry;
822
823 $do_retry = sub {
824 my $retry_cfg = $self->{retry}[$retry++]
825 or do {
826 # failure
827 $self->_free_id ($req->[2], $retry > 1);
828 undef $do_retry; return $req->[1]->();
829 };
830
831 my ($server, $timeout) = @$retry_cfg;
832
833 $self->{id}{$req->[2]} = [AnyEvent->timer (after => $timeout, cb => sub {
834 $NOW = time;
835
836 # timeout, try next
837 &$do_retry;
838 }), sub {
839 my ($res) = @_;
840
841 if ($res->{tc}) {
842 # success, but truncated, so use tcp
843 AnyEvent::Socket::tcp_connect ((Socket::inet_ntoa $server), 53, sub {
844 my ($fh) = @_
845 or return &$do_retry;
846
847 my $handle = new AnyEvent::Handle
848 fh => $fh,
849 on_error => sub {
850 # failure, try next
851 &$do_retry;
852 };
853
854 $handle->push_write (pack "n/a", $req->[0]);
855 $handle->push_read (chunk => 2, sub {
856 $handle->unshift_read (chunk => (unpack "n", $_[1]), sub {
857 $self->_feed ($_[1]);
858 });
859 });
860 shutdown $fh, 1;
861
862 }, sub { $timeout });
863
864 } else {
865 # success
866 $self->_free_id ($req->[2], $retry > 1);
867 undef $do_retry; return $req->[1]->($res);
868 }
869 }];
870
871 send $self->{fh}, $req->[0], 0, AnyEvent::Socket::pack_sockaddr (53, $server);
872 };
873
874 &$do_retry;
875 }
876
877 sub _scheduler {
878 my ($self) = @_;
879
880 $NOW = time;
881
882 # first clear id reuse queue
883 delete $self->{id}{ (shift @{ $self->{reuse_q} })->[1] }
884 while @{ $self->{reuse_q} } && $self->{reuse_q}[0][0] <= $NOW;
885
886 while ($self->{outstanding} < $self->{max_outstanding}) {
887
888 if (@{ $self->{reuse_q} } >= 30000) {
889 # we ran out of ID's, wait a bit
890 $self->{reuse_to} ||= AnyEvent->timer (after => $self->{reuse_q}[0][0] - $NOW, cb => sub {
891 delete $self->{reuse_to};
892 $self->_scheduler;
893 });
894 last;
895 }
896
897 my $req = shift @{ $self->{queue} }
898 or last;
899
900 while () {
901 $req->[2] = int rand 65536;
902 last unless exists $self->{id}{$req->[2]};
903 }
904
905 ++$self->{outstanding};
906 $self->{id}{$req->[2]} = 1;
907 substr $req->[0], 0, 2, pack "n", $req->[2];
908
909 $self->_exec ($req);
910 }
911 }
912
913 =item $resolver->request ($req, $cb->($res))
914
915 Sends a single request (a hash-ref formated as specified for
916 C<dns_pack>) to the configured nameservers including
917 retries. Calls the callback with the decoded response packet if a reply
918 was received, or no arguments on timeout.
919
920 =cut
921
922 sub request($$) {
923 my ($self, $req, $cb) = @_;
924
925 push @{ $self->{queue} }, [dns_pack $req, $cb];
926 $self->_scheduler;
927 }
928
929 =item $resolver->resolve ($qname, $qtype, %options, $cb->($rcode, @rr))
930
931 Queries the DNS for the given domain name C<$qname> of type C<$qtype> (a
932 qtype of "*" is supported and means "any").
933
934 The callback will be invoked with a list of matching result records or
935 none on any error or if the name could not be found.
936
937 CNAME chains (although illegal) are followed up to a length of 8.
938
939 Note that this resolver is just a stub resolver: it requires a name server
940 supporting recursive queries, will not do any recursive queries itself and
941 is not secure when used against an untrusted name server.
942
943 The following options are supported:
944
945 =over 4
946
947 =item search => [$suffix...]
948
949 Use the given search list (which might be empty), by appending each one
950 in turn to the C<$qname>. If this option is missing then the configured
951 C<ndots> and C<search> define its value. If the C<$qname> ends in a dot,
952 then the searchlist will be ignored.
953
954 =item accept => [$type...]
955
956 Lists the acceptable result types: only result types in this set will be
957 accepted and returned. The default includes the C<$qtype> and nothing
958 else.
959
960 =item class => "class"
961
962 Specify the query class ("in" for internet, "ch" for chaosnet and "hs" for
963 hesiod are the only ones making sense). The default is "in", of course.
964
965 =back
966
967 Examples:
968
969 $res->resolve ("ruth.plan9.de", "a", sub {
970 warn Dumper [@_];
971 });
972
973 [
974 [
975 'ruth.schmorp.de',
976 'a',
977 'in',
978 '129.13.162.95'
979 ]
980 ]
981
982 $res->resolve ("test1.laendle", "*",
983 accept => ["a", "aaaa"],
984 sub {
985 warn Dumper [@_];
986 }
987 );
988
989 [
990 [
991 'test1.laendle',
992 'a',
993 'in',
994 '10.0.0.255'
995 ],
996 [
997 'test1.laendle',
998 'aaaa',
999 'in',
1000 '3ffe:1900:4545:0002:0240:0000:0000:f7e1'
1001 ]
1002 ]
1003
1004 =cut
1005
1006 sub resolve($%) {
1007 my $cb = pop;
1008 my ($self, $qname, $qtype, %opt) = @_;
1009
1010 my @search = $qname =~ s/\.$//
1011 ? ""
1012 : $opt{search}
1013 ? @{ $opt{search} }
1014 : ($qname =~ y/.//) >= $self->{ndots}
1015 ? ("", @{ $self->{search} })
1016 : (@{ $self->{search} }, "");
1017
1018 my $class = $opt{class} || "in";
1019
1020 my %atype = $opt{accept}
1021 ? map +($_ => 1), @{ $opt{accept} }
1022 : ($qtype => 1);
1023
1024 # advance in searchlist
1025 my ($do_search, $do_req);
1026
1027 $do_search = sub {
1028 @search
1029 or (undef $do_search), (undef $do_req), return $cb->();
1030
1031 (my $name = lc "$qname." . shift @search) =~ s/\.$//;
1032 my $depth = 2;
1033
1034 # advance in cname-chain
1035 $do_req = sub {
1036 $self->request ({
1037 rd => 1,
1038 qd => [[$name, $qtype, $class]],
1039 }, sub {
1040 my ($res) = @_
1041 or return $do_search->();
1042
1043 my $cname;
1044
1045 while () {
1046 # results found?
1047 my @rr = grep $name eq lc $_->[0] && ($atype{"*"} || $atype{$_->[1]}), @{ $res->{an} };
1048
1049 (undef $do_search), (undef $do_req), return $cb->(@rr)
1050 if @rr;
1051
1052 # see if there is a cname we can follow
1053 my @rr = grep $name eq lc $_->[0] && $_->[1] eq "cname", @{ $res->{an} };
1054
1055 if (@rr) {
1056 $depth--
1057 or return $do_search->(); # cname chain too long
1058
1059 $cname = 1;
1060 $name = $rr[0][3];
1061
1062 } elsif ($cname) {
1063 # follow the cname
1064 return $do_req->();
1065
1066 } else {
1067 # no, not found anything
1068 return $do_search->();
1069 }
1070 }
1071 });
1072 };
1073
1074 $do_req->();
1075 };
1076
1077 $do_search->();
1078 }
1079
1080 use AnyEvent::Socket (); # circular dependency, so do not import anything and do it at the end
1081
1082 1;
1083
1084 =back
1085
1086 =head1 AUTHOR
1087
1088 Marc Lehmann <schmorp@schmorp.de>
1089 http://home.schmorp.de/
1090
1091 =cut
1092