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