ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent/lib/AnyEvent/DNS.pm
Revision: 1.22
Committed: Sat May 24 02:06:43 2008 UTC (16 years, 1 month ago) by root
Branch: MAIN
Changes since 1.21: +49 -30 lines
Log Message:
seems to work

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 root 1.22 The number of seconds (default: C<300>) that a query id cannot be re-used
726     after a timeout. If there as no time-out then query id's can be reused
727     immediately.
728 root 1.13
729 root 1.1 =back
730    
731     =cut
732    
733     sub new {
734     my ($class, %arg) = @_;
735    
736     socket my $fh, &Socket::AF_INET, &Socket::SOCK_DGRAM, 0
737     or Carp::croak "socket: $!";
738    
739     AnyEvent::Util::fh_nonblocking $fh, 1;
740    
741     my $self = bless {
742     server => [v127.0.0.1],
743     timeout => [2, 5, 5],
744     search => [],
745     ndots => 1,
746     max_outstanding => 10,
747 root 1.22 reuse => 300, # reuse id's after 5 minutes only, if possible
748 root 1.1 %arg,
749     fh => $fh,
750     reuse_q => [],
751     }, $class;
752    
753     # search should default to gethostname's domain
754     # but perl lacks a good posix module
755    
756     Scalar::Util::weaken (my $wself = $self);
757     $self->{rw} = AnyEvent->io (fh => $fh, poll => "r", cb => sub { $wself->_recv });
758    
759     $self->_compile;
760    
761     $self
762     }
763    
764     =item $resolver->parse_resolv_conv ($string)
765    
766     Parses the given string a sif it were a F<resolv.conf> file. The following
767 root 1.14 directives are supported (but not neecssarily implemented).
768 root 1.1
769     C<#>-style comments, C<nameserver>, C<domain>, C<search>, C<sortlist>,
770     C<options> (C<timeout>, C<attempts>, C<ndots>).
771    
772     Everything else is silently ignored.
773    
774     =cut
775    
776     sub parse_resolv_conf {
777     my ($self, $resolvconf) = @_;
778    
779     $self->{server} = [];
780     $self->{search} = [];
781    
782     my $attempts;
783    
784     for (split /\n/, $resolvconf) {
785     if (/^\s*#/) {
786     # comment
787     } elsif (/^\s*nameserver\s+(\S+)\s*$/i) {
788     my $ip = $1;
789     if (AnyEvent::Util::dotted_quad $ip) {
790     push @{ $self->{server} }, AnyEvent::Util::socket_inet_aton $ip;
791     } else {
792     warn "nameserver $ip invalid and ignored\n";
793     }
794     } elsif (/^\s*domain\s+(\S*)\s+$/i) {
795     $self->{search} = [$1];
796     } elsif (/^\s*search\s+(.*?)\s*$/i) {
797     $self->{search} = [split /\s+/, $1];
798     } elsif (/^\s*sortlist\s+(.*?)\s*$/i) {
799     # ignored, NYI
800     } elsif (/^\s*options\s+(.*?)\s*$/i) {
801     for (split /\s+/, $1) {
802     if (/^timeout:(\d+)$/) {
803     $self->{timeout} = [$1];
804     } elsif (/^attempts:(\d+)$/) {
805     $attempts = $1;
806     } elsif (/^ndots:(\d+)$/) {
807     $self->{ndots} = $1;
808     } else {
809     # debug, rotate, no-check-names, inet6
810     }
811     }
812     }
813     }
814    
815     $self->{timeout} = [($self->{timeout}[0]) x $attempts]
816     if $attempts;
817    
818     $self->_compile;
819     }
820    
821 root 1.14 =item $resolver->os_config
822 root 1.1
823 root 1.14 Tries so load and parse F</etc/resolv.conf> on portable opertaing systems. Tries various
824     egregious hacks on windows to force the dns servers and searchlist out of the config.
825 root 1.1
826     =cut
827    
828 root 1.14 sub os_config {
829 root 1.1 my ($self) = @_;
830    
831 root 1.14 if ($^O =~ /mswin32|cygwin/i) {
832     # yeah, it suxx... lets hope DNS is DNS in all locales
833    
834     if (open my $fh, "ipconfig /all |") {
835     delete $self->{server};
836     delete $self->{search};
837    
838     while (<$fh>) {
839     # first DNS.* is suffix list
840     if (/^\s*DNS/) {
841     while (/\s+([[:alnum:].\-]+)\s*$/) {
842     push @{ $self->{search} }, $1;
843     $_ = <$fh>;
844     }
845     last;
846     }
847     }
848    
849     while (<$fh>) {
850     # second DNS.* is server address list
851     if (/^\s*DNS/) {
852     while (/\s+(\d+\.\d+\.\d+\.\d+)\s*$/) {
853     my $ip = $1;
854     push @{ $self->{server} }, AnyEvent::Util::socket_inet_aton $ip
855     if AnyEvent::Util::dotted_quad $ip;
856     $_ = <$fh>;
857     }
858     last;
859     }
860     }
861    
862     $self->_compile;
863     }
864     } else {
865     # try resolv.conf everywhere
866 root 1.1
867 root 1.14 if (open my $fh, "</etc/resolv.conf") {
868     local $/;
869     $self->parse_resolv_conf (<$fh>);
870     }
871     }
872 root 1.1 }
873    
874     sub _compile {
875     my $self = shift;
876    
877     my @retry;
878    
879     for my $timeout (@{ $self->{timeout} }) {
880     for my $server (@{ $self->{server} }) {
881     push @retry, [$server, $timeout];
882     }
883     }
884    
885     $self->{retry} = \@retry;
886     }
887    
888 root 1.6 sub _feed {
889     my ($self, $res) = @_;
890    
891     $res = dns_unpack $res
892     or return;
893    
894     my $id = $self->{id}{$res->{id}};
895    
896     return unless ref $id;
897    
898     $NOW = time;
899     $id->[1]->($res);
900     }
901    
902 root 1.1 sub _recv {
903     my ($self) = @_;
904    
905 root 1.9 while (my $peer = recv $self->{fh}, my $res, 4096, 0) {
906 root 1.21 my ($port, $host) = AnyEvent::Socket::unpack_sockaddr ($peer);
907 root 1.1
908     return unless $port == 53 && grep $_ eq $host, @{ $self->{server} };
909    
910 root 1.6 $self->_feed ($res);
911 root 1.1 }
912     }
913    
914 root 1.22 sub _free_id {
915     my ($self, $id, $timeout) = @_;
916    
917     if ($timeout) {
918     # we need to block the id for a while
919     $self->{id}{$id} = 1;
920     push @{ $self->{reuse_q} }, [$NOW + $self->{reuse}, $id];
921     } else {
922     # we can quickly recycle the id
923     delete $self->{id}{$id};
924     }
925    
926     --$self->{outstanding};
927     $self->_scheduler;
928     }
929    
930     # execute a single request, involves sending it with timeouts to multiple servers
931 root 1.1 sub _exec {
932 root 1.22 my ($self, $req) = @_;
933    
934     my $retry; # of retries
935     my $do_retry;
936    
937     $do_retry = sub {
938     my $retry_cfg = $self->{retry}[$retry++]
939     or do {
940     # failure
941     $self->_free_id ($req->[2], $retry > 1);
942     undef $do_retry; return $req->[1]->();
943     };
944 root 1.1
945     my ($server, $timeout) = @$retry_cfg;
946    
947     $self->{id}{$req->[2]} = [AnyEvent->timer (after => $timeout, cb => sub {
948     $NOW = time;
949    
950     # timeout, try next
951 root 1.22 &$do_retry;
952 root 1.1 }), sub {
953     my ($res) = @_;
954    
955 root 1.6 if ($res->{tc}) {
956     # success, but truncated, so use tcp
957 root 1.17 AnyEvent::Socket::tcp_connect ((Socket::inet_ntoa $server), 53, sub {
958 root 1.6 my ($fh) = @_
959 root 1.22 or return &$do_retry;
960 root 1.6
961     my $handle = new AnyEvent::Handle
962     fh => $fh,
963     on_error => sub {
964     # failure, try next
965 root 1.22 &$do_retry;
966 root 1.6 };
967    
968     $handle->push_write (pack "n/a", $req->[0]);
969     $handle->push_read_chunk (2, sub {
970     $handle->unshift_read_chunk ((unpack "n", $_[1]), sub {
971     $self->_feed ($_[1]);
972     });
973     });
974     shutdown $fh, 1;
975    
976 root 1.17 }, sub { $timeout });
977 root 1.1
978 root 1.6 } else {
979     # success
980 root 1.22 $self->_free_id ($req->[2], $retry > 1);
981     undef $do_retry; return $req->[1]->($res);
982 root 1.6 }
983 root 1.1 }];
984    
985 root 1.21 send $self->{fh}, $req->[0], 0, AnyEvent::Socket::pack_sockaddr (53, $server);
986 root 1.22 };
987 root 1.1
988 root 1.22 &$do_retry;
989 root 1.1 }
990    
991     sub _scheduler {
992     my ($self) = @_;
993    
994     $NOW = time;
995    
996     # first clear id reuse queue
997     delete $self->{id}{ (shift @{ $self->{reuse_q} })->[1] }
998 root 1.13 while @{ $self->{reuse_q} } && $self->{reuse_q}[0][0] <= $NOW;
999 root 1.1
1000     while ($self->{outstanding} < $self->{max_outstanding}) {
1001 root 1.13
1002     if (@{ $self->{reuse_q} } >= 30000) {
1003 root 1.22 warn "reusing id's, waiting ",$self->{reuse_q}[0][0] - $NOW;#d#
1004 root 1.13 # 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.3 Note that this resolver is just a stub resolver: it requires a nameserver
1055     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