ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent/lib/AnyEvent/DNS.pm
Revision: 1.24
Committed: Sat May 24 02:50:45 2008 UTC (16 years, 1 month ago) by root
Branch: MAIN
Changes since 1.23: +14 -13 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 root 1.24 database, then you can specify the service in the format C<name=port>
44 root 1.18 (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 root 1.24 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 root 1.24 Each srv_rr is an array reference with the following contents:
93 root 1.4 C<[$priority, $weight, $transport, $target]>.
94    
95     They will be sorted with lowest priority, highest weight first (TODO:
96 root 1.24 should use the RFC algorithm to reorder same-priority records for weight).
97 root 1.4
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 root 1.24 @target = (["127.0.0.1", $port], ["::1", $port]);
289     &$resolve;
290 root 1.21 } elsif (defined $service && !AnyEvent::Socket::parse_ip ($node)) {
291 root 1.18 srv $service, $proto, $node, sub {
292     my (@srv) = @_;
293    
294     # no srv records, continue traditionally
295     @srv
296     or return &$resolve;
297    
298     # only srv record has "." => abort
299     $srv[0][2] ne "." || $#srv
300     or return $cb->();
301    
302     # use srv records then
303 root 1.21 @target = map ["$_->[3].", $_->[2]],
304 root 1.18 grep $_->[3] ne ".",
305     @srv;
306    
307     &$resolve;
308     };
309     } else {
310     &$resolve;
311     }
312     }
313    
314     #############################################################################
315    
316 root 1.15 =back
317    
318 root 1.13 =head2 LOW-LEVEL DNS EN-/DECODING FUNCTIONS
319 root 1.1
320     =over 4
321    
322 root 1.11 =item $AnyEvent::DNS::EDNS0
323    
324 root 1.13 This variable decides whether dns_pack automatically enables EDNS0
325 root 1.24 support. By default, this is disabled (C<0>), unless overridden by
326 root 1.21 C<$ENV{PERL_ANYEVENT_EDNS0>), but when set to C<1>, AnyEvent::DNS will use
327     EDNS0 in all requests.
328 root 1.11
329 root 1.1 =cut
330    
331 root 1.21 our $EDNS0 = $ENV{PERL_ANYEVENT_EDNS0} * 1; # set to 1 to enable (partial) edns0
332 root 1.10
333 root 1.1 our %opcode_id = (
334     query => 0,
335     iquery => 1,
336     status => 2,
337 root 1.5 notify => 4,
338     update => 5,
339     map +($_ => $_), 3, 6..15
340 root 1.1 );
341    
342     our %opcode_str = reverse %opcode_id;
343    
344     our %rcode_id = (
345 root 1.5 noerror => 0,
346     formerr => 1,
347     servfail => 2,
348     nxdomain => 3,
349     notimp => 4,
350     refused => 5,
351     yxdomain => 6, # Name Exists when it should not [RFC 2136]
352     yxrrset => 7, # RR Set Exists when it should not [RFC 2136]
353     nxrrset => 8, # RR Set that should exist does not [RFC 2136]
354     notauth => 9, # Server Not Authoritative for zone [RFC 2136]
355     notzone => 10, # Name not contained in zone [RFC 2136]
356     # EDNS0 16 BADVERS Bad OPT Version [RFC 2671]
357     # EDNS0 16 BADSIG TSIG Signature Failure [RFC 2845]
358     # EDNS0 17 BADKEY Key not recognized [RFC 2845]
359     # EDNS0 18 BADTIME Signature out of time window [RFC 2845]
360     # EDNS0 19 BADMODE Bad TKEY Mode [RFC 2930]
361     # EDNS0 20 BADNAME Duplicate key name [RFC 2930]
362     # EDNS0 21 BADALG Algorithm not supported [RFC 2930]
363     map +($_ => $_), 11..15
364 root 1.1 );
365    
366     our %rcode_str = reverse %rcode_id;
367    
368     our %type_id = (
369     a => 1,
370     ns => 2,
371     md => 3,
372     mf => 4,
373     cname => 5,
374     soa => 6,
375     mb => 7,
376     mg => 8,
377     mr => 9,
378     null => 10,
379     wks => 11,
380     ptr => 12,
381     hinfo => 13,
382     minfo => 14,
383     mx => 15,
384     txt => 16,
385     aaaa => 28,
386     srv => 33,
387 root 1.5 opt => 41,
388     spf => 99,
389     tkey => 249,
390     tsig => 250,
391     ixfr => 251,
392 root 1.1 axfr => 252,
393     mailb => 253,
394     "*" => 255,
395     );
396    
397     our %type_str = reverse %type_id;
398    
399     our %class_id = (
400 root 1.5 in => 1,
401     ch => 3,
402     hs => 4,
403     none => 254,
404     "*" => 255,
405 root 1.1 );
406    
407     our %class_str = reverse %class_id;
408    
409     # names MUST have a trailing dot
410     sub _enc_qname($) {
411     pack "(C/a)*", (split /\./, shift), ""
412     }
413    
414     sub _enc_qd() {
415     (_enc_qname $_->[0]) . pack "nn",
416     ($_->[1] > 0 ? $_->[1] : $type_id {$_->[1]}),
417     ($_->[2] > 0 ? $_->[2] : $class_id{$_->[2] || "in"})
418     }
419    
420     sub _enc_rr() {
421     die "encoding of resource records is not supported";
422     }
423    
424     =item $pkt = AnyEvent::DNS::dns_pack $dns
425    
426     Packs a perl data structure into a DNS packet. Reading RFC1034 is strongly
427     recommended, then everything will be totally clear. Or maybe not.
428    
429     Resource records are not yet encodable.
430    
431     Examples:
432    
433     # very simple request, using lots of default values:
434     { rd => 1, qd => [ [ "host.domain", "a"] ] }
435    
436     # more complex example, showing how flags etc. are named:
437    
438     {
439     id => 10000,
440     op => "query",
441     rc => "nxdomain",
442    
443     # flags
444     qr => 1,
445     aa => 0,
446     tc => 0,
447     rd => 0,
448     ra => 0,
449 root 1.5 ad => 0,
450     cd => 0,
451 root 1.1
452     qd => [@rr], # query section
453     an => [@rr], # answer section
454     ns => [@rr], # authority section
455     ar => [@rr], # additional records section
456     }
457    
458     =cut
459    
460     sub dns_pack($) {
461     my ($req) = @_;
462    
463 root 1.7 pack "nn nnnn a* a* a* a* a*",
464 root 1.1 $req->{id},
465    
466     ! !$req->{qr} * 0x8000
467     + $opcode_id{$req->{op}} * 0x0800
468     + ! !$req->{aa} * 0x0400
469     + ! !$req->{tc} * 0x0200
470     + ! !$req->{rd} * 0x0100
471     + ! !$req->{ra} * 0x0080
472 root 1.5 + ! !$req->{ad} * 0x0020
473     + ! !$req->{cd} * 0x0010
474 root 1.1 + $rcode_id{$req->{rc}} * 0x0001,
475    
476     scalar @{ $req->{qd} || [] },
477     scalar @{ $req->{an} || [] },
478     scalar @{ $req->{ns} || [] },
479 root 1.11 $EDNS0 + scalar @{ $req->{ar} || [] }, # include EDNS0 option here
480 root 1.1
481     (join "", map _enc_qd, @{ $req->{qd} || [] }),
482     (join "", map _enc_rr, @{ $req->{an} || [] }),
483     (join "", map _enc_rr, @{ $req->{ns} || [] }),
484 root 1.7 (join "", map _enc_rr, @{ $req->{ar} || [] }),
485    
486 root 1.11 ($EDNS0 ? pack "C nnNn", 0, 41, 4096, 0, 0 : "") # EDNS0, 4kiB udp payload size
487 root 1.1 }
488    
489     our $ofs;
490     our $pkt;
491    
492     # bitches
493     sub _dec_qname {
494     my @res;
495     my $redir;
496     my $ptr = $ofs;
497     my $cnt;
498    
499     while () {
500     return undef if ++$cnt >= 256; # to avoid DoS attacks
501    
502     my $len = ord substr $pkt, $ptr++, 1;
503    
504     if ($len & 0xc0) {
505     $ptr++;
506     $ofs = $ptr if $ptr > $ofs;
507     $ptr = (unpack "n", substr $pkt, $ptr - 2, 2) & 0x3fff;
508     } elsif ($len) {
509     push @res, substr $pkt, $ptr, $len;
510     $ptr += $len;
511     } else {
512     $ofs = $ptr if $ptr > $ofs;
513     return join ".", @res;
514     }
515     }
516     }
517    
518     sub _dec_qd {
519     my $qname = _dec_qname;
520     my ($qt, $qc) = unpack "nn", substr $pkt, $ofs; $ofs += 4;
521     [$qname, $type_str{$qt} || $qt, $class_str{$qc} || $qc]
522     }
523    
524     our %dec_rr = (
525 root 1.17 1 => sub { join ".", unpack "C4" }, # a
526 root 1.1 2 => sub { local $ofs = $ofs - length; _dec_qname }, # ns
527     5 => sub { local $ofs = $ofs - length; _dec_qname }, # cname
528     6 => sub {
529     local $ofs = $ofs - length;
530     my $mname = _dec_qname;
531     my $rname = _dec_qname;
532     ($mname, $rname, unpack "NNNNN", substr $pkt, $ofs)
533     }, # soa
534 root 1.17 11 => sub { ((join ".", unpack "C4"), unpack "C a*", substr $_, 4) }, # wks
535 root 1.1 12 => sub { local $ofs = $ofs - length; _dec_qname }, # ptr
536 root 1.5 13 => sub { unpack "C/a C/a", $_ }, # hinfo
537 root 1.1 15 => sub { local $ofs = $ofs + 2 - length; ((unpack "n", $_), _dec_qname) }, # mx
538 root 1.5 16 => sub { unpack "(C/a)*", $_ }, # txt
539 root 1.17 28 => sub { AnyEvent::Socket::format_ip ($_) }, # aaaa
540 root 1.1 33 => sub { local $ofs = $ofs + 6 - length; ((unpack "nnn", $_), _dec_qname) }, # srv
541 root 1.5 99 => sub { unpack "(C/a)*", $_ }, # spf
542 root 1.1 );
543    
544     sub _dec_rr {
545     my $qname = _dec_qname;
546    
547     my ($rt, $rc, $ttl, $rdlen) = unpack "nn N n", substr $pkt, $ofs; $ofs += 10;
548     local $_ = substr $pkt, $ofs, $rdlen; $ofs += $rdlen;
549    
550     [
551     $qname,
552     $type_str{$rt} || $rt,
553     $class_str{$rc} || $rc,
554     ($dec_rr{$rt} || sub { $_ })->(),
555     ]
556     }
557    
558     =item $dns = AnyEvent::DNS::dns_unpack $pkt
559    
560     Unpacks a DNS packet into a perl data structure.
561    
562     Examples:
563    
564 root 1.13 # an unsuccessful reply
565 root 1.1 {
566     'qd' => [
567     [ 'ruth.plan9.de.mach.uni-karlsruhe.de', '*', 'in' ]
568     ],
569     'rc' => 'nxdomain',
570     'ar' => [],
571     'ns' => [
572     [
573     'uni-karlsruhe.de',
574     'soa',
575     'in',
576     'netserv.rz.uni-karlsruhe.de',
577     'hostmaster.rz.uni-karlsruhe.de',
578 root 1.13 2008052201, 10800, 1800, 2592000, 86400
579 root 1.1 ]
580     ],
581     'tc' => '',
582     'ra' => 1,
583     'qr' => 1,
584     'id' => 45915,
585     'aa' => '',
586     'an' => [],
587     'rd' => 1,
588     'op' => 'query'
589     }
590    
591     # a successful reply
592    
593     {
594     'qd' => [ [ 'www.google.de', 'a', 'in' ] ],
595     'rc' => 0,
596     'ar' => [
597     [ 'a.l.google.com', 'a', 'in', '209.85.139.9' ],
598     [ 'b.l.google.com', 'a', 'in', '64.233.179.9' ],
599     [ 'c.l.google.com', 'a', 'in', '64.233.161.9' ],
600     ],
601     'ns' => [
602     [ 'l.google.com', 'ns', 'in', 'a.l.google.com' ],
603     [ 'l.google.com', 'ns', 'in', 'b.l.google.com' ],
604     ],
605     'tc' => '',
606     'ra' => 1,
607     'qr' => 1,
608     'id' => 64265,
609     'aa' => '',
610     'an' => [
611     [ 'www.google.de', 'cname', 'in', 'www.google.com' ],
612     [ 'www.google.com', 'cname', 'in', 'www.l.google.com' ],
613     [ 'www.l.google.com', 'a', 'in', '66.249.93.104' ],
614     [ 'www.l.google.com', 'a', 'in', '66.249.93.147' ],
615     ],
616     'rd' => 1,
617     'op' => 0
618     }
619    
620     =cut
621    
622     sub dns_unpack($) {
623     local $pkt = shift;
624     my ($id, $flags, $qd, $an, $ns, $ar)
625     = unpack "nn nnnn A*", $pkt;
626    
627     local $ofs = 6 * 2;
628    
629     {
630     id => $id,
631     qr => ! ! ($flags & 0x8000),
632     aa => ! ! ($flags & 0x0400),
633     tc => ! ! ($flags & 0x0200),
634     rd => ! ! ($flags & 0x0100),
635     ra => ! ! ($flags & 0x0080),
636 root 1.5 ad => ! ! ($flags & 0x0020),
637     cd => ! ! ($flags & 0x0010),
638 root 1.1 op => $opcode_str{($flags & 0x001e) >> 11},
639     rc => $rcode_str{($flags & 0x000f)},
640    
641     qd => [map _dec_qd, 1 .. $qd],
642     an => [map _dec_rr, 1 .. $an],
643     ns => [map _dec_rr, 1 .. $ns],
644     ar => [map _dec_rr, 1 .. $ar],
645     }
646     }
647    
648     #############################################################################
649    
650     =back
651    
652     =head2 THE AnyEvent::DNS RESOLVER CLASS
653    
654 root 1.13 This is the class which does the actual protocol work.
655 root 1.1
656     =over 4
657    
658     =cut
659    
660     use Carp ();
661     use Scalar::Util ();
662     use Socket ();
663    
664     our $NOW;
665    
666 root 1.2 =item AnyEvent::DNS::resolver
667    
668     This function creates and returns a resolver that is ready to use and
669     should mimic the default resolver for your system as good as possible.
670    
671     It only ever creates one resolver and returns this one on subsequent
672     calls.
673    
674     Unless you have special needs, prefer this function over creating your own
675     resolver object.
676    
677     =cut
678    
679     our $RESOLVER;
680    
681     sub resolver() {
682     $RESOLVER || do {
683     $RESOLVER = new AnyEvent::DNS;
684 root 1.14 $RESOLVER->os_config;
685 root 1.2 $RESOLVER
686     }
687     }
688    
689 root 1.1 =item $resolver = new AnyEvent::DNS key => value...
690    
691 root 1.6 Creates and returns a new resolver.
692 root 1.2
693     The following options are supported:
694 root 1.1
695     =over 4
696    
697     =item server => [...]
698    
699 root 1.24 A list of server addresses (default: C<v127.0.0.1>) in network format (4
700 root 1.1 octets for IPv4, 16 octets for IPv6 - not yet supported).
701    
702     =item timeout => [...]
703    
704     A list of timeouts to use (also determines the number of retries). To make
705     three retries with individual time-outs of 2, 5 and 5 seconds, use C<[2,
706     5, 5]>, which is also the default.
707    
708     =item search => [...]
709    
710     The default search list of suffixes to append to a domain name (default: none).
711    
712 root 1.2 =item ndots => $integer
713 root 1.1
714     The number of dots (default: C<1>) that a name must have so that the resolver
715     tries to resolve the name without any suffixes first.
716    
717 root 1.2 =item max_outstanding => $integer
718 root 1.1
719     Most name servers do not handle many parallel requests very well. This option
720 root 1.24 limits the number of outstanding requests to C<$n> (default: C<10>), that means
721 root 1.1 if you request more than this many requests, then the additional requests will be queued
722     until some other requests have been resolved.
723    
724 root 1.13 =item reuse => $seconds
725    
726 root 1.22 The number of seconds (default: C<300>) that a query id cannot be re-used
727     after a timeout. If there as no time-out then query id's can be reused
728     immediately.
729 root 1.13
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.22 reuse => 300, # 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 root 1.24 Parses the given string as if it were a F<resolv.conf> file. The following
768     directives are supported (but not necessarily 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.24 Tries so load and parse F</etc/resolv.conf> on portable operating systems. Tries various
825     egregious hacks on windows to force the DNS servers and searchlist out of the system.
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 root 1.22 sub _free_id {
916     my ($self, $id, $timeout) = @_;
917    
918     if ($timeout) {
919     # we need to block the id for a while
920     $self->{id}{$id} = 1;
921     push @{ $self->{reuse_q} }, [$NOW + $self->{reuse}, $id];
922     } else {
923     # we can quickly recycle the id
924     delete $self->{id}{$id};
925     }
926    
927     --$self->{outstanding};
928     $self->_scheduler;
929     }
930    
931     # execute a single request, involves sending it with timeouts to multiple servers
932 root 1.1 sub _exec {
933 root 1.22 my ($self, $req) = @_;
934    
935     my $retry; # of retries
936     my $do_retry;
937    
938     $do_retry = sub {
939     my $retry_cfg = $self->{retry}[$retry++]
940     or do {
941     # failure
942     $self->_free_id ($req->[2], $retry > 1);
943     undef $do_retry; return $req->[1]->();
944     };
945 root 1.1
946     my ($server, $timeout) = @$retry_cfg;
947    
948     $self->{id}{$req->[2]} = [AnyEvent->timer (after => $timeout, cb => sub {
949     $NOW = time;
950    
951     # timeout, try next
952 root 1.22 &$do_retry;
953 root 1.1 }), sub {
954     my ($res) = @_;
955    
956 root 1.6 if ($res->{tc}) {
957     # success, but truncated, so use tcp
958 root 1.17 AnyEvent::Socket::tcp_connect ((Socket::inet_ntoa $server), 53, sub {
959 root 1.6 my ($fh) = @_
960 root 1.22 or return &$do_retry;
961 root 1.6
962     my $handle = new AnyEvent::Handle
963     fh => $fh,
964     on_error => sub {
965     # failure, try next
966 root 1.22 &$do_retry;
967 root 1.6 };
968    
969     $handle->push_write (pack "n/a", $req->[0]);
970     $handle->push_read_chunk (2, sub {
971     $handle->unshift_read_chunk ((unpack "n", $_[1]), sub {
972     $self->_feed ($_[1]);
973     });
974     });
975     shutdown $fh, 1;
976    
977 root 1.17 }, sub { $timeout });
978 root 1.1
979 root 1.6 } else {
980     # success
981 root 1.22 $self->_free_id ($req->[2], $retry > 1);
982     undef $do_retry; return $req->[1]->($res);
983 root 1.6 }
984 root 1.1 }];
985    
986 root 1.21 send $self->{fh}, $req->[0], 0, AnyEvent::Socket::pack_sockaddr (53, $server);
987 root 1.22 };
988 root 1.1
989 root 1.22 &$do_retry;
990 root 1.1 }
991    
992     sub _scheduler {
993     my ($self) = @_;
994    
995     $NOW = time;
996    
997     # first clear id reuse queue
998     delete $self->{id}{ (shift @{ $self->{reuse_q} })->[1] }
999 root 1.13 while @{ $self->{reuse_q} } && $self->{reuse_q}[0][0] <= $NOW;
1000 root 1.1
1001     while ($self->{outstanding} < $self->{max_outstanding}) {
1002 root 1.13
1003     if (@{ $self->{reuse_q} } >= 30000) {
1004     # we ran out of ID's, wait a bit
1005     $self->{reuse_to} ||= AnyEvent->timer (after => $self->{reuse_q}[0][0] - $NOW, cb => sub {
1006     delete $self->{reuse_to};
1007     $self->_scheduler;
1008     });
1009     last;
1010     }
1011    
1012 root 1.1 my $req = shift @{ $self->{queue} }
1013     or last;
1014    
1015     while () {
1016     $req->[2] = int rand 65536;
1017     last unless exists $self->{id}{$req->[2]};
1018     }
1019    
1020 root 1.22 ++$self->{outstanding};
1021 root 1.1 $self->{id}{$req->[2]} = 1;
1022     substr $req->[0], 0, 2, pack "n", $req->[2];
1023    
1024 root 1.22 $self->_exec ($req);
1025 root 1.1 }
1026     }
1027    
1028     =item $resolver->request ($req, $cb->($res))
1029    
1030     Sends a single request (a hash-ref formated as specified for
1031 root 1.3 C<dns_pack>) to the configured nameservers including
1032 root 1.1 retries. Calls the callback with the decoded response packet if a reply
1033     was received, or no arguments on timeout.
1034    
1035     =cut
1036    
1037     sub request($$) {
1038     my ($self, $req, $cb) = @_;
1039    
1040 root 1.3 push @{ $self->{queue} }, [dns_pack $req, $cb];
1041 root 1.1 $self->_scheduler;
1042     }
1043    
1044     =item $resolver->resolve ($qname, $qtype, %options, $cb->($rcode, @rr))
1045    
1046     Queries the DNS for the given domain name C<$qname> of type C<$qtype> (a
1047     qtype of "*" is supported and means "any").
1048    
1049     The callback will be invoked with a list of matching result records or
1050     none on any error or if the name could not be found.
1051    
1052     CNAME chains (although illegal) are followed up to a length of 8.
1053    
1054 root 1.24 Note that this resolver is just a stub resolver: it requires a name server
1055 root 1.3 supporting recursive queries, will not do any recursive queries itself and
1056     is not secure when used against an untrusted name server.
1057    
1058 root 1.1 The following options are supported:
1059    
1060     =over 4
1061    
1062     =item search => [$suffix...]
1063    
1064     Use the given search list (which might be empty), by appending each one
1065     in turn to the C<$qname>. If this option is missing then the configured
1066     C<ndots> and C<search> define its value. If the C<$qname> ends in a dot,
1067 root 1.2 then the searchlist will be ignored.
1068 root 1.1
1069     =item accept => [$type...]
1070    
1071     Lists the acceptable result types: only result types in this set will be
1072     accepted and returned. The default includes the C<$qtype> and nothing
1073     else.
1074    
1075     =item class => "class"
1076    
1077     Specify the query class ("in" for internet, "ch" for chaosnet and "hs" for
1078 root 1.2 hesiod are the only ones making sense). The default is "in", of course.
1079 root 1.1
1080     =back
1081    
1082     Examples:
1083    
1084     $res->resolve ("ruth.plan9.de", "a", sub {
1085     warn Dumper [@_];
1086     });
1087    
1088     [
1089     [
1090     'ruth.schmorp.de',
1091     'a',
1092     'in',
1093     '129.13.162.95'
1094     ]
1095     ]
1096    
1097     $res->resolve ("test1.laendle", "*",
1098     accept => ["a", "aaaa"],
1099     sub {
1100     warn Dumper [@_];
1101     }
1102     );
1103    
1104     [
1105     [
1106     'test1.laendle',
1107     'a',
1108     'in',
1109     '10.0.0.255'
1110     ],
1111     [
1112     'test1.laendle',
1113     'aaaa',
1114     'in',
1115     '3ffe:1900:4545:0002:0240:0000:0000:f7e1'
1116     ]
1117     ]
1118    
1119     =cut
1120    
1121     sub resolve($%) {
1122     my $cb = pop;
1123     my ($self, $qname, $qtype, %opt) = @_;
1124    
1125     my @search = $qname =~ s/\.$//
1126     ? ""
1127     : $opt{search}
1128     ? @{ $opt{search} }
1129     : ($qname =~ y/.//) >= $self->{ndots}
1130     ? ("", @{ $self->{search} })
1131     : (@{ $self->{search} }, "");
1132    
1133     my $class = $opt{class} || "in";
1134    
1135     my %atype = $opt{accept}
1136     ? map +($_ => 1), @{ $opt{accept} }
1137     : ($qtype => 1);
1138    
1139     # advance in searchlist
1140 root 1.22 my ($do_search, $do_req);
1141    
1142     $do_search = sub {
1143 root 1.1 @search
1144 root 1.22 or (undef $do_search), (undef $do_req), return $cb->();
1145 root 1.1
1146 root 1.4 (my $name = lc "$qname." . shift @search) =~ s/\.$//;
1147 root 1.1 my $depth = 2;
1148    
1149     # advance in cname-chain
1150 root 1.22 $do_req = sub {
1151 root 1.1 $self->request ({
1152     rd => 1,
1153     qd => [[$name, $qtype, $class]],
1154     }, sub {
1155     my ($res) = @_
1156     or return $do_search->();
1157    
1158     my $cname;
1159    
1160     while () {
1161 root 1.2 # results found?
1162 root 1.4 my @rr = grep $name eq lc $_->[0] && ($atype{"*"} || $atype{$_->[1]}), @{ $res->{an} };
1163 root 1.1
1164 root 1.22 (undef $do_search), (undef $do_req), return $cb->(@rr)
1165 root 1.1 if @rr;
1166    
1167     # see if there is a cname we can follow
1168 root 1.4 my @rr = grep $name eq lc $_->[0] && $_->[1] eq "cname", @{ $res->{an} };
1169 root 1.1
1170     if (@rr) {
1171     $depth--
1172     or return $do_search->(); # cname chain too long
1173    
1174     $cname = 1;
1175     $name = $rr[0][3];
1176    
1177     } elsif ($cname) {
1178     # follow the cname
1179     return $do_req->();
1180    
1181     } else {
1182 root 1.2 # no, not found anything
1183 root 1.1 return $do_search->();
1184     }
1185     }
1186     });
1187     };
1188    
1189     $do_req->();
1190     };
1191    
1192     $do_search->();
1193     }
1194    
1195 root 1.17 use AnyEvent::Socket (); # circular dependency, so do not import anything and do it at the end
1196    
1197 root 1.1 1;
1198    
1199     =back
1200    
1201     =head1 AUTHOR
1202    
1203     Marc Lehmann <schmorp@schmorp.de>
1204     http://home.schmorp.de/
1205    
1206     =cut
1207