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