ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent/lib/AnyEvent/DNS.pm
Revision: 1.16
Committed: Fri May 23 17:47:06 2008 UTC (16 years, 1 month ago) by root
Branch: MAIN
Changes since 1.15: +2 -2 lines
Log Message:
*** empty log message ***

File Contents

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