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