ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent/lib/AnyEvent/DNS.pm
Revision: 1.48
Committed: Thu May 29 22:02:19 2008 UTC (16 years, 1 month ago) by root
Branch: MAIN
Changes since 1.47: +96 -19 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 root 1.26 my $cv = AnyEvent->condvar;
10 root 1.30 AnyEvent::DNS::a "www.google.de", $cv;
11 root 1.26 # ... later
12     my @addrs = $cv->recv;
13    
14 root 1.1 =head1 DESCRIPTION
15    
16     This module offers both a number of DNS convenience functions as well
17     as a fully asynchronous and high-performance pure-perl stub resolver.
18    
19 root 1.46 The stub resolver supports DNS over IPv4 and IPv6, UDP and TCP, optional
20     EDNS0 support for up to 4kiB datagrams and automatically falls back to
21     virtual circuit mode for large responses.
22 root 1.12
23 root 1.1 =head2 CONVENIENCE FUNCTIONS
24    
25     =over 4
26    
27     =cut
28    
29     package AnyEvent::DNS;
30    
31 root 1.3 no warnings;
32 root 1.1 use strict;
33    
34 root 1.29 use Socket qw(AF_INET SOCK_DGRAM SOCK_STREAM);
35    
36 root 1.35 use AnyEvent ();
37 root 1.6 use AnyEvent::Handle ();
38 root 1.37 use AnyEvent::Util qw(AF_INET6);
39 root 1.1
40 root 1.39 our $VERSION = '1.0';
41    
42 root 1.32 our @DNS_FALLBACK = (v208.67.220.220, v208.67.222.222);
43    
44 root 1.4 =item AnyEvent::DNS::a $domain, $cb->(@addrs)
45    
46     Tries to resolve the given domain to IPv4 address(es).
47    
48 root 1.17 =item AnyEvent::DNS::aaaa $domain, $cb->(@addrs)
49    
50     Tries to resolve the given domain to IPv6 address(es).
51    
52 root 1.4 =item AnyEvent::DNS::mx $domain, $cb->(@hostnames)
53    
54     Tries to resolve the given domain into a sorted (lower preference value
55     first) list of domain names.
56    
57     =item AnyEvent::DNS::ns $domain, $cb->(@hostnames)
58    
59     Tries to resolve the given domain name into a list of name servers.
60    
61     =item AnyEvent::DNS::txt $domain, $cb->(@hostnames)
62    
63     Tries to resolve the given domain name into a list of text records.
64    
65     =item AnyEvent::DNS::srv $service, $proto, $domain, $cb->(@srv_rr)
66    
67     Tries to resolve the given service, protocol and domain name into a list
68     of service records.
69    
70 root 1.24 Each srv_rr is an array reference with the following contents:
71 root 1.4 C<[$priority, $weight, $transport, $target]>.
72    
73     They will be sorted with lowest priority, highest weight first (TODO:
74 root 1.24 should use the RFC algorithm to reorder same-priority records for weight).
75 root 1.4
76     Example:
77    
78     AnyEvent::DNS::srv "sip", "udp", "schmorp.de", sub { ...
79     # @_ = ( [10, 10, 5060, "sip1.schmorp.de" ] )
80    
81 root 1.48 =item AnyEvent::DNS::ptr $domain, $cb->(@hostnames)
82    
83     Tries to make a PTR lookup on the given domain. See C<reverse_lookup>
84     and C<reverse_verify> if you want to resolve an IP address to a hostname
85     instead.
86    
87     =item AnyEvent::DNS::any $domain, $cb->(@rrs)
88    
89     Tries to resolve the given domain and passes all resource records found to
90     the callback.
91    
92     =item AnyEvent::DNS::reverse_lookup $ipv4_or_6, $cb->(@hostnames)
93 root 1.4
94     Tries to reverse-resolve the given IPv4 or IPv6 address (in textual form)
95 root 1.48 into it's hostname(s). Handles V4MAPPED and V4COMPAT IPv6 addresses
96     transparently.
97    
98     =item AnyEvent::DNS::reverse_verify $ipv4_or_6, $cb->(@hostnames)
99    
100     The same as C<reverse_lookup>, but does forward-lookups to verify that
101     the resolved hostnames indeed point to the address, which makes spoofing
102     harder.
103    
104     If you want to resolve an address into a hostname, this is the preferred
105     method: The DNS records could still change, but at least this function
106     verified that the hostname, at one point in the past, pointed at the IP
107     address you originally resolved.
108 root 1.4
109     Example:
110    
111     AnyEvent::DNS::ptr "2001:500:2f::f", sub { print shift };
112     # => f.root-servers.net
113    
114     =cut
115    
116 root 1.38 sub MAX_PKT() { 4096 } # max packet size we advertise and accept
117    
118     sub DOMAIN_PORT() { 53 } # if this changes drop me a note
119    
120 root 1.4 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 root 1.48 my ($domain, $cb) = @_;
173    
174     resolver->resolve ($domain => "ptr", sub {
175     $cb->(map $_->[3], @_);
176     });
177     }
178    
179     sub any($$) {
180     my ($domain, $cb) = @_;
181    
182     resolver->resolve ($domain => "*", $cb);
183     }
184    
185     # convert textual ip address into reverse lookup form
186     sub _munge_ptr($) {
187     my $ipn = $_[0]
188     or return;
189    
190     my $ptr;
191    
192     my $af = AnyEvent::Socket::address_family ($ipn);
193 root 1.4
194 root 1.48 if ($af == AF_INET6) {
195     $ipn = substr $ipn, 0, 16; # anticipate future expansion
196 root 1.4
197 root 1.48 # handle v4mapped and v4compat
198     if ($ipn =~ s/^\x00{10}(?:\xff\xff|\x00\x00)//) {
199     $af = AF_INET;
200     } else {
201     $ptr = join ".", (reverse split //, unpack "H32", $ipn), "ip6.arpa.";
202     }
203     }
204 root 1.37
205     if ($af == AF_INET) {
206 root 1.48 $ptr = join ".", (reverse unpack "C4", $ipn), "in-addr.arpa.";
207 root 1.4 }
208    
209 root 1.48 $ptr
210     }
211    
212     sub reverse_lookup($$) {
213     my ($ip, $cb) = @_;
214    
215     $ip = _munge_ptr AnyEvent::Socket::parse_address ($ip)
216     or return $cb->();
217    
218 root 1.17 resolver->resolve ($ip => "ptr", sub {
219 root 1.4 $cb->(map $_->[3], @_);
220     });
221     }
222 root 1.1
223 root 1.48 sub reverse_verify($$) {
224     my ($ip, $cb) = @_;
225    
226     my $ipn = AnyEvent::Socket::parse_address ($ip)
227     or return $cb->();
228    
229     my $af = AnyEvent::Socket::address_family ($ipn);
230    
231     my @res;
232     my $cnt;
233    
234     my $ptr = _munge_ptr $ipn
235     or return $cb->();
236    
237     $ip = AnyEvent::Socket::format_address ($ipn); # normalise into the same form
238    
239     ptr $ptr, sub {
240     for my $name (@_) {
241     ++$cnt;
242    
243     # () around AF_INET to work around bug in 5.8
244     resolver->resolve ($name => ($af == (AF_INET) ? "a" : "aaaa"), sub {
245     for (@_) {
246     push @res, $name
247     if $_->[3] eq $ip;
248     }
249     $cb->(@res) unless --$cnt;
250     });
251     }
252 root 1.5
253 root 1.48 $cb->() unless $cnt;
254     };
255 root 1.5 }
256    
257 root 1.36 #################################################################################
258 root 1.18
259 root 1.15 =back
260    
261 root 1.13 =head2 LOW-LEVEL DNS EN-/DECODING FUNCTIONS
262 root 1.1
263     =over 4
264    
265 root 1.11 =item $AnyEvent::DNS::EDNS0
266    
267 root 1.13 This variable decides whether dns_pack automatically enables EDNS0
268 root 1.24 support. By default, this is disabled (C<0>), unless overridden by
269 root 1.40 C<$ENV{PERL_ANYEVENT_EDNS0}>, but when set to C<1>, AnyEvent::DNS will use
270 root 1.21 EDNS0 in all requests.
271 root 1.11
272 root 1.1 =cut
273    
274 root 1.21 our $EDNS0 = $ENV{PERL_ANYEVENT_EDNS0} * 1; # set to 1 to enable (partial) edns0
275 root 1.10
276 root 1.1 our %opcode_id = (
277     query => 0,
278     iquery => 1,
279     status => 2,
280 root 1.5 notify => 4,
281     update => 5,
282     map +($_ => $_), 3, 6..15
283 root 1.1 );
284    
285     our %opcode_str = reverse %opcode_id;
286    
287     our %rcode_id = (
288 root 1.5 noerror => 0,
289     formerr => 1,
290     servfail => 2,
291     nxdomain => 3,
292     notimp => 4,
293     refused => 5,
294     yxdomain => 6, # Name Exists when it should not [RFC 2136]
295     yxrrset => 7, # RR Set Exists when it should not [RFC 2136]
296     nxrrset => 8, # RR Set that should exist does not [RFC 2136]
297     notauth => 9, # Server Not Authoritative for zone [RFC 2136]
298     notzone => 10, # Name not contained in zone [RFC 2136]
299     # EDNS0 16 BADVERS Bad OPT Version [RFC 2671]
300     # EDNS0 16 BADSIG TSIG Signature Failure [RFC 2845]
301     # EDNS0 17 BADKEY Key not recognized [RFC 2845]
302     # EDNS0 18 BADTIME Signature out of time window [RFC 2845]
303     # EDNS0 19 BADMODE Bad TKEY Mode [RFC 2930]
304     # EDNS0 20 BADNAME Duplicate key name [RFC 2930]
305     # EDNS0 21 BADALG Algorithm not supported [RFC 2930]
306     map +($_ => $_), 11..15
307 root 1.1 );
308    
309     our %rcode_str = reverse %rcode_id;
310    
311     our %type_id = (
312     a => 1,
313     ns => 2,
314     md => 3,
315     mf => 4,
316     cname => 5,
317     soa => 6,
318     mb => 7,
319     mg => 8,
320     mr => 9,
321     null => 10,
322     wks => 11,
323     ptr => 12,
324     hinfo => 13,
325     minfo => 14,
326     mx => 15,
327     txt => 16,
328     aaaa => 28,
329     srv => 33,
330 root 1.47 naptr => 35, # rfc2915
331 root 1.5 opt => 41,
332     spf => 99,
333     tkey => 249,
334     tsig => 250,
335     ixfr => 251,
336 root 1.1 axfr => 252,
337     mailb => 253,
338     "*" => 255,
339     );
340    
341     our %type_str = reverse %type_id;
342    
343     our %class_id = (
344 root 1.5 in => 1,
345     ch => 3,
346     hs => 4,
347     none => 254,
348     "*" => 255,
349 root 1.1 );
350    
351     our %class_str = reverse %class_id;
352    
353 root 1.34 sub _enc_name($) {
354 root 1.29 pack "(C/a*)*", (split /\./, shift), ""
355 root 1.1 }
356    
357     sub _enc_qd() {
358 root 1.34 (_enc_name $_->[0]) . pack "nn",
359 root 1.1 ($_->[1] > 0 ? $_->[1] : $type_id {$_->[1]}),
360     ($_->[2] > 0 ? $_->[2] : $class_id{$_->[2] || "in"})
361     }
362    
363     sub _enc_rr() {
364     die "encoding of resource records is not supported";
365     }
366    
367     =item $pkt = AnyEvent::DNS::dns_pack $dns
368    
369     Packs a perl data structure into a DNS packet. Reading RFC1034 is strongly
370     recommended, then everything will be totally clear. Or maybe not.
371    
372     Resource records are not yet encodable.
373    
374     Examples:
375    
376     # very simple request, using lots of default values:
377     { rd => 1, qd => [ [ "host.domain", "a"] ] }
378    
379     # more complex example, showing how flags etc. are named:
380    
381     {
382     id => 10000,
383     op => "query",
384     rc => "nxdomain",
385    
386     # flags
387     qr => 1,
388     aa => 0,
389     tc => 0,
390     rd => 0,
391     ra => 0,
392 root 1.5 ad => 0,
393     cd => 0,
394 root 1.1
395     qd => [@rr], # query section
396     an => [@rr], # answer section
397     ns => [@rr], # authority section
398     ar => [@rr], # additional records section
399     }
400    
401     =cut
402    
403     sub dns_pack($) {
404     my ($req) = @_;
405    
406 root 1.7 pack "nn nnnn a* a* a* a* a*",
407 root 1.1 $req->{id},
408    
409     ! !$req->{qr} * 0x8000
410     + $opcode_id{$req->{op}} * 0x0800
411     + ! !$req->{aa} * 0x0400
412     + ! !$req->{tc} * 0x0200
413     + ! !$req->{rd} * 0x0100
414     + ! !$req->{ra} * 0x0080
415 root 1.5 + ! !$req->{ad} * 0x0020
416     + ! !$req->{cd} * 0x0010
417 root 1.1 + $rcode_id{$req->{rc}} * 0x0001,
418    
419     scalar @{ $req->{qd} || [] },
420     scalar @{ $req->{an} || [] },
421     scalar @{ $req->{ns} || [] },
422 root 1.44 $EDNS0 + scalar @{ $req->{ar} || [] }, # EDNS0 option included here
423 root 1.1
424     (join "", map _enc_qd, @{ $req->{qd} || [] }),
425     (join "", map _enc_rr, @{ $req->{an} || [] }),
426     (join "", map _enc_rr, @{ $req->{ns} || [] }),
427 root 1.7 (join "", map _enc_rr, @{ $req->{ar} || [] }),
428    
429 root 1.44 ($EDNS0 ? pack "C nnNn", 0, 41, MAX_PKT, 0, 0 : "") # EDNS0 option
430 root 1.1 }
431    
432     our $ofs;
433     our $pkt;
434    
435     # bitches
436 root 1.34 sub _dec_name {
437 root 1.1 my @res;
438     my $redir;
439     my $ptr = $ofs;
440     my $cnt;
441    
442     while () {
443     return undef if ++$cnt >= 256; # to avoid DoS attacks
444    
445     my $len = ord substr $pkt, $ptr++, 1;
446    
447 root 1.34 if ($len >= 0xc0) {
448 root 1.1 $ptr++;
449     $ofs = $ptr if $ptr > $ofs;
450     $ptr = (unpack "n", substr $pkt, $ptr - 2, 2) & 0x3fff;
451     } elsif ($len) {
452     push @res, substr $pkt, $ptr, $len;
453     $ptr += $len;
454     } else {
455     $ofs = $ptr if $ptr > $ofs;
456     return join ".", @res;
457     }
458     }
459     }
460    
461     sub _dec_qd {
462 root 1.34 my $qname = _dec_name;
463 root 1.1 my ($qt, $qc) = unpack "nn", substr $pkt, $ofs; $ofs += 4;
464     [$qname, $type_str{$qt} || $qt, $class_str{$qc} || $qc]
465     }
466    
467     our %dec_rr = (
468 root 1.29 1 => sub { join ".", unpack "C4", $_ }, # a
469 root 1.34 2 => sub { local $ofs = $ofs - length; _dec_name }, # ns
470     5 => sub { local $ofs = $ofs - length; _dec_name }, # cname
471 root 1.1 6 => sub {
472     local $ofs = $ofs - length;
473 root 1.34 my $mname = _dec_name;
474     my $rname = _dec_name;
475 root 1.1 ($mname, $rname, unpack "NNNNN", substr $pkt, $ofs)
476     }, # soa
477 root 1.29 11 => sub { ((join ".", unpack "C4", $_), unpack "C a*", substr $_, 4) }, # wks
478 root 1.34 12 => sub { local $ofs = $ofs - length; _dec_name }, # ptr
479 root 1.29 13 => sub { unpack "C/a* C/a*", $_ }, # hinfo
480 root 1.34 15 => sub { local $ofs = $ofs + 2 - length; ((unpack "n", $_), _dec_name) }, # mx
481 root 1.29 16 => sub { unpack "(C/a*)*", $_ }, # txt
482 root 1.36 28 => sub { AnyEvent::Socket::format_address ($_) }, # aaaa
483 root 1.34 33 => sub { local $ofs = $ofs + 6 - length; ((unpack "nnn", $_), _dec_name) }, # srv
484 root 1.47 35 => sub { # naptr
485     my ($order, $preference, $flags, $service, $regexp, $offset) = unpack "nn C/a* C/a* C/a* .", $_;
486     local $ofs = $ofs + $offset - length;
487     ($order, $preference, $flags, $service, $regexp, _dec_name)
488     },
489 root 1.29 99 => sub { unpack "(C/a*)*", $_ }, # spf
490 root 1.1 );
491    
492     sub _dec_rr {
493 root 1.34 my $name = _dec_name;
494 root 1.1
495     my ($rt, $rc, $ttl, $rdlen) = unpack "nn N n", substr $pkt, $ofs; $ofs += 10;
496     local $_ = substr $pkt, $ofs, $rdlen; $ofs += $rdlen;
497    
498     [
499 root 1.34 $name,
500 root 1.1 $type_str{$rt} || $rt,
501     $class_str{$rc} || $rc,
502     ($dec_rr{$rt} || sub { $_ })->(),
503     ]
504     }
505    
506     =item $dns = AnyEvent::DNS::dns_unpack $pkt
507    
508     Unpacks a DNS packet into a perl data structure.
509    
510     Examples:
511    
512 root 1.13 # an unsuccessful reply
513 root 1.1 {
514     'qd' => [
515     [ 'ruth.plan9.de.mach.uni-karlsruhe.de', '*', 'in' ]
516     ],
517     'rc' => 'nxdomain',
518     'ar' => [],
519     'ns' => [
520     [
521     'uni-karlsruhe.de',
522     'soa',
523     'in',
524     'netserv.rz.uni-karlsruhe.de',
525     'hostmaster.rz.uni-karlsruhe.de',
526 root 1.13 2008052201, 10800, 1800, 2592000, 86400
527 root 1.1 ]
528     ],
529     'tc' => '',
530     'ra' => 1,
531     'qr' => 1,
532     'id' => 45915,
533     'aa' => '',
534     'an' => [],
535     'rd' => 1,
536     'op' => 'query'
537     }
538    
539     # a successful reply
540    
541     {
542     'qd' => [ [ 'www.google.de', 'a', 'in' ] ],
543     'rc' => 0,
544     'ar' => [
545     [ 'a.l.google.com', 'a', 'in', '209.85.139.9' ],
546     [ 'b.l.google.com', 'a', 'in', '64.233.179.9' ],
547     [ 'c.l.google.com', 'a', 'in', '64.233.161.9' ],
548     ],
549     'ns' => [
550     [ 'l.google.com', 'ns', 'in', 'a.l.google.com' ],
551     [ 'l.google.com', 'ns', 'in', 'b.l.google.com' ],
552     ],
553     'tc' => '',
554     'ra' => 1,
555     'qr' => 1,
556     'id' => 64265,
557     'aa' => '',
558     'an' => [
559     [ 'www.google.de', 'cname', 'in', 'www.google.com' ],
560     [ 'www.google.com', 'cname', 'in', 'www.l.google.com' ],
561     [ 'www.l.google.com', 'a', 'in', '66.249.93.104' ],
562     [ 'www.l.google.com', 'a', 'in', '66.249.93.147' ],
563     ],
564     'rd' => 1,
565     'op' => 0
566     }
567    
568     =cut
569    
570     sub dns_unpack($) {
571     local $pkt = shift;
572     my ($id, $flags, $qd, $an, $ns, $ar)
573     = unpack "nn nnnn A*", $pkt;
574    
575     local $ofs = 6 * 2;
576    
577     {
578     id => $id,
579     qr => ! ! ($flags & 0x8000),
580     aa => ! ! ($flags & 0x0400),
581     tc => ! ! ($flags & 0x0200),
582     rd => ! ! ($flags & 0x0100),
583     ra => ! ! ($flags & 0x0080),
584 root 1.5 ad => ! ! ($flags & 0x0020),
585     cd => ! ! ($flags & 0x0010),
586 root 1.1 op => $opcode_str{($flags & 0x001e) >> 11},
587     rc => $rcode_str{($flags & 0x000f)},
588    
589     qd => [map _dec_qd, 1 .. $qd],
590     an => [map _dec_rr, 1 .. $an],
591     ns => [map _dec_rr, 1 .. $ns],
592     ar => [map _dec_rr, 1 .. $ar],
593     }
594     }
595    
596     #############################################################################
597    
598     =back
599    
600     =head2 THE AnyEvent::DNS RESOLVER CLASS
601    
602 root 1.13 This is the class which does the actual protocol work.
603 root 1.1
604     =over 4
605    
606     =cut
607    
608     use Carp ();
609     use Scalar::Util ();
610     use Socket ();
611    
612     our $NOW;
613    
614 root 1.2 =item AnyEvent::DNS::resolver
615    
616     This function creates and returns a resolver that is ready to use and
617     should mimic the default resolver for your system as good as possible.
618    
619     It only ever creates one resolver and returns this one on subsequent
620     calls.
621    
622     Unless you have special needs, prefer this function over creating your own
623     resolver object.
624    
625     =cut
626    
627     our $RESOLVER;
628    
629     sub resolver() {
630     $RESOLVER || do {
631     $RESOLVER = new AnyEvent::DNS;
632 root 1.14 $RESOLVER->os_config;
633 root 1.2 $RESOLVER
634     }
635     }
636    
637 root 1.1 =item $resolver = new AnyEvent::DNS key => value...
638    
639 root 1.6 Creates and returns a new resolver.
640 root 1.2
641     The following options are supported:
642 root 1.1
643     =over 4
644    
645     =item server => [...]
646    
647 root 1.41 A list of server addresses (default: C<v127.0.0.1>) in network format
648 root 1.42 (i.e. as returned by C<AnyEvent::Socket::parse_address> - both IPv4 and
649 root 1.41 IPv6 are supported).
650 root 1.1
651     =item timeout => [...]
652    
653     A list of timeouts to use (also determines the number of retries). To make
654     three retries with individual time-outs of 2, 5 and 5 seconds, use C<[2,
655     5, 5]>, which is also the default.
656    
657     =item search => [...]
658    
659     The default search list of suffixes to append to a domain name (default: none).
660    
661 root 1.2 =item ndots => $integer
662 root 1.1
663     The number of dots (default: C<1>) that a name must have so that the resolver
664     tries to resolve the name without any suffixes first.
665    
666 root 1.2 =item max_outstanding => $integer
667 root 1.1
668 root 1.43 Most name servers do not handle many parallel requests very well. This
669     option limits the number of outstanding requests to C<$integer>
670     (default: C<10>), that means if you request more than this many requests,
671     then the additional requests will be queued until some other requests have
672     been resolved.
673 root 1.1
674 root 1.13 =item reuse => $seconds
675    
676 root 1.22 The number of seconds (default: C<300>) that a query id cannot be re-used
677     after a timeout. If there as no time-out then query id's can be reused
678     immediately.
679 root 1.13
680 root 1.1 =back
681    
682     =cut
683    
684     sub new {
685     my ($class, %arg) = @_;
686    
687 root 1.38 # try to create a ipv4 and an ipv6 socket
688     # only fail when we cnanot create either
689 root 1.1
690 root 1.38 socket my $fh4, AF_INET , &Socket::SOCK_DGRAM, 0;
691     socket my $fh6, AF_INET6, &Socket::SOCK_DGRAM, 0;
692    
693     $fh4 || $fh6
694     or Carp::croak "unable to create either an IPv6 or an IPv4 socket";
695 root 1.1
696     my $self = bless {
697 root 1.32 server => [],
698 root 1.1 timeout => [2, 5, 5],
699     search => [],
700     ndots => 1,
701     max_outstanding => 10,
702 root 1.22 reuse => 300, # reuse id's after 5 minutes only, if possible
703 root 1.1 %arg,
704     reuse_q => [],
705     }, $class;
706    
707     # search should default to gethostname's domain
708     # but perl lacks a good posix module
709    
710     Scalar::Util::weaken (my $wself = $self);
711 root 1.38
712     if ($fh4) {
713     AnyEvent::Util::fh_nonblocking $fh4, 1;
714     $self->{fh4} = $fh4;
715     $self->{rw4} = AnyEvent->io (fh => $fh4, poll => "r", cb => sub {
716     if (my $peer = recv $fh4, my $pkt, MAX_PKT, 0) {
717     $wself->_recv ($pkt, $peer);
718     }
719     });
720     }
721    
722     if ($fh6) {
723     $self->{fh6} = $fh6;
724     AnyEvent::Util::fh_nonblocking $fh6, 1;
725     $self->{rw6} = AnyEvent->io (fh => $fh6, poll => "r", cb => sub {
726     if (my $peer = recv $fh6, my $pkt, MAX_PKT, 0) {
727     $wself->_recv ($pkt, $peer);
728     }
729     });
730     }
731 root 1.1
732     $self->_compile;
733    
734     $self
735     }
736    
737     =item $resolver->parse_resolv_conv ($string)
738    
739 root 1.24 Parses the given string as if it were a F<resolv.conf> file. The following
740     directives are supported (but not necessarily implemented).
741 root 1.1
742     C<#>-style comments, C<nameserver>, C<domain>, C<search>, C<sortlist>,
743     C<options> (C<timeout>, C<attempts>, C<ndots>).
744    
745     Everything else is silently ignored.
746    
747     =cut
748    
749     sub parse_resolv_conf {
750     my ($self, $resolvconf) = @_;
751    
752     $self->{server} = [];
753     $self->{search} = [];
754    
755     my $attempts;
756    
757     for (split /\n/, $resolvconf) {
758     if (/^\s*#/) {
759     # comment
760     } elsif (/^\s*nameserver\s+(\S+)\s*$/i) {
761     my $ip = $1;
762 root 1.36 if (my $ipn = AnyEvent::Socket::parse_address ($ip)) {
763 root 1.25 push @{ $self->{server} }, $ipn;
764 root 1.1 } else {
765     warn "nameserver $ip invalid and ignored\n";
766     }
767     } elsif (/^\s*domain\s+(\S*)\s+$/i) {
768     $self->{search} = [$1];
769     } elsif (/^\s*search\s+(.*?)\s*$/i) {
770     $self->{search} = [split /\s+/, $1];
771     } elsif (/^\s*sortlist\s+(.*?)\s*$/i) {
772     # ignored, NYI
773     } elsif (/^\s*options\s+(.*?)\s*$/i) {
774     for (split /\s+/, $1) {
775     if (/^timeout:(\d+)$/) {
776     $self->{timeout} = [$1];
777     } elsif (/^attempts:(\d+)$/) {
778     $attempts = $1;
779     } elsif (/^ndots:(\d+)$/) {
780     $self->{ndots} = $1;
781     } else {
782     # debug, rotate, no-check-names, inet6
783     }
784     }
785     }
786     }
787    
788     $self->{timeout} = [($self->{timeout}[0]) x $attempts]
789     if $attempts;
790    
791     $self->_compile;
792     }
793    
794 root 1.14 =item $resolver->os_config
795 root 1.1
796 root 1.24 Tries so load and parse F</etc/resolv.conf> on portable operating systems. Tries various
797     egregious hacks on windows to force the DNS servers and searchlist out of the system.
798 root 1.1
799     =cut
800    
801 root 1.14 sub os_config {
802 root 1.1 my ($self) = @_;
803    
804 root 1.32 $self->{server} = [];
805     $self->{search} = [];
806    
807 root 1.35 if (AnyEvent::WIN32 || $^O =~ /cygwin/i) {
808 root 1.32 no strict 'refs';
809    
810     # there are many options to find the current nameservers etc. on windows
811     # all of them don't work consistently:
812     # - the registry thing needs separate code on win32 native vs. cygwin
813     # - the registry layout differs between windows versions
814     # - calling windows api functions doesn't work on cygwin
815     # - ipconfig uses locale-specific messages
816    
817     # we use ipconfig parsing because, despite all it's brokenness,
818     # it seems most stable in practise.
819     # for good measure, we append a fallback nameserver to our list.
820 root 1.14
821     if (open my $fh, "ipconfig /all |") {
822 root 1.32 # parsing strategy: we go through the output and look for
823     # :-lines with DNS in them. everything in those is regarded as
824     # either a nameserver (if it parses as an ip address), or a suffix
825     # (all else).
826 root 1.14
827 root 1.32 my $dns;
828 root 1.14 while (<$fh>) {
829 root 1.32 if (s/^\s.*\bdns\b.*://i) {
830     $dns = 1;
831     } elsif (/^\S/ || /^\s[^:]{16,}: /) {
832     $dns = 0;
833     }
834     if ($dns && /^\s*(\S+)\s*$/) {
835     my $s = $1;
836     $s =~ s/%\d+(?!\S)//; # get rid of scope id
837 root 1.36 if (my $ipn = AnyEvent::Socket::parse_address ($s)) {
838 root 1.32 push @{ $self->{server} }, $ipn;
839     } else {
840     push @{ $self->{search} }, $s;
841 root 1.14 }
842     }
843     }
844    
845 root 1.32 # always add one fallback server
846     push @{ $self->{server} }, $DNS_FALLBACK[rand @DNS_FALLBACK];
847 root 1.14
848     $self->_compile;
849     }
850     } else {
851     # try resolv.conf everywhere
852 root 1.1
853 root 1.14 if (open my $fh, "</etc/resolv.conf") {
854     local $/;
855     $self->parse_resolv_conf (<$fh>);
856     }
857     }
858 root 1.1 }
859    
860     sub _compile {
861     my $self = shift;
862    
863 root 1.38 my %search; $self->{search} = [grep 0 < length, grep !$search{$_}++, @{ $self->{search} }];
864     my %server; $self->{server} = [grep 0 < length, grep !$server{$_}++, @{ $self->{server} }];
865 root 1.32
866     unless (@{ $self->{server} }) {
867     # use 127.0.0.1 by default, and one opendns nameserver as fallback
868     $self->{server} = [v127.0.0.1, $DNS_FALLBACK[rand @DNS_FALLBACK]];
869     }
870    
871 root 1.1 my @retry;
872    
873     for my $timeout (@{ $self->{timeout} }) {
874     for my $server (@{ $self->{server} }) {
875     push @retry, [$server, $timeout];
876     }
877     }
878    
879     $self->{retry} = \@retry;
880     }
881    
882 root 1.6 sub _feed {
883     my ($self, $res) = @_;
884    
885     $res = dns_unpack $res
886     or return;
887    
888     my $id = $self->{id}{$res->{id}};
889    
890     return unless ref $id;
891    
892     $NOW = time;
893     $id->[1]->($res);
894     }
895    
896 root 1.1 sub _recv {
897 root 1.38 my ($self, $pkt, $peer) = @_;
898 root 1.1
899 root 1.32 # we ignore errors (often one gets port unreachable, but there is
900     # no good way to take advantage of that.
901 root 1.1
902 root 1.38 my ($port, $host) = AnyEvent::Socket::unpack_sockaddr ($peer);
903    
904     return unless $port == 53 && grep $_ eq $host, @{ $self->{server} };
905 root 1.1
906 root 1.38 $self->_feed ($pkt);
907 root 1.1 }
908    
909 root 1.22 sub _free_id {
910     my ($self, $id, $timeout) = @_;
911    
912     if ($timeout) {
913     # we need to block the id for a while
914     $self->{id}{$id} = 1;
915     push @{ $self->{reuse_q} }, [$NOW + $self->{reuse}, $id];
916     } else {
917     # we can quickly recycle the id
918     delete $self->{id}{$id};
919     }
920    
921     --$self->{outstanding};
922     $self->_scheduler;
923     }
924    
925     # execute a single request, involves sending it with timeouts to multiple servers
926 root 1.1 sub _exec {
927 root 1.22 my ($self, $req) = @_;
928    
929     my $retry; # of retries
930     my $do_retry;
931    
932     $do_retry = sub {
933     my $retry_cfg = $self->{retry}[$retry++]
934     or do {
935     # failure
936     $self->_free_id ($req->[2], $retry > 1);
937     undef $do_retry; return $req->[1]->();
938     };
939 root 1.1
940     my ($server, $timeout) = @$retry_cfg;
941    
942     $self->{id}{$req->[2]} = [AnyEvent->timer (after => $timeout, cb => sub {
943     $NOW = time;
944    
945     # timeout, try next
946 root 1.22 &$do_retry;
947 root 1.1 }), sub {
948     my ($res) = @_;
949    
950 root 1.6 if ($res->{tc}) {
951     # success, but truncated, so use tcp
952 root 1.38 AnyEvent::Socket::tcp_connect (AnyEvent::Socket::format_address ($server), DOMAIN_PORT, sub {
953 root 1.6 my ($fh) = @_
954 root 1.22 or return &$do_retry;
955 root 1.6
956     my $handle = new AnyEvent::Handle
957     fh => $fh,
958     on_error => sub {
959     # failure, try next
960 root 1.22 &$do_retry;
961 root 1.6 };
962    
963     $handle->push_write (pack "n/a", $req->[0]);
964 root 1.27 $handle->push_read (chunk => 2, sub {
965     $handle->unshift_read (chunk => (unpack "n", $_[1]), sub {
966 root 1.6 $self->_feed ($_[1]);
967     });
968     });
969     shutdown $fh, 1;
970    
971 root 1.17 }, sub { $timeout });
972 root 1.1
973 root 1.6 } else {
974     # success
975 root 1.22 $self->_free_id ($req->[2], $retry > 1);
976     undef $do_retry; return $req->[1]->($res);
977 root 1.6 }
978 root 1.1 }];
979 root 1.38
980     my $sa = AnyEvent::Socket::pack_sockaddr (DOMAIN_PORT, $server);
981    
982 root 1.39 my $fh = AF_INET == Socket::sockaddr_family ($sa)
983 root 1.38 ? $self->{fh4} : $self->{fh6}
984     or return &$do_retry;
985 root 1.1
986 root 1.38 send $fh, $req->[0], 0, $sa;
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 root 1.44 Queries the DNS for the given domain name C<$qname> of type C<$qtype>.
1047    
1048     A C<$qtype> is either a numerical query type (e.g. C<1> for A recods) or
1049     a lowercase name (you have to look at the source to see which aliases are
1050     supported, but all types from RFC 1034, C<aaaa>, C<srv>, C<spf> and a few
1051     more are known to this module). A qtype of "*" is supported and means
1052     "any" record type.
1053 root 1.1
1054     The callback will be invoked with a list of matching result records or
1055     none on any error or if the name could not be found.
1056    
1057     CNAME chains (although illegal) are followed up to a length of 8.
1058    
1059 root 1.44 The callback will be invoked with an result code in string form (noerror,
1060     formerr, servfail, nxdomain, notimp, refused and so on), or numerical
1061     form if the result code is not supported. The remaining arguments are
1062     arraryefs of the form C<[$name, $type, $class, @data>], where C<$name> is
1063     the domain name, C<$type> a type string or number, C<$class> a class name
1064     and @data is resource-record-dependent data. For C<a> records, this will
1065     be the textual IPv4 addresses, for C<ns> or C<cname> records this will be
1066     a domain name, for C<txt> records these are all the strings and so on.
1067    
1068     All types mentioned in RFC 1034, C<aaaa>, C<srv> and C<spf> are
1069     decoded. All resource records not known to this module will just return
1070     the raw C<rdata> field as fourth entry.
1071    
1072 root 1.24 Note that this resolver is just a stub resolver: it requires a name server
1073 root 1.3 supporting recursive queries, will not do any recursive queries itself and
1074     is not secure when used against an untrusted name server.
1075    
1076 root 1.1 The following options are supported:
1077    
1078     =over 4
1079    
1080     =item search => [$suffix...]
1081    
1082     Use the given search list (which might be empty), by appending each one
1083     in turn to the C<$qname>. If this option is missing then the configured
1084     C<ndots> and C<search> define its value. If the C<$qname> ends in a dot,
1085 root 1.2 then the searchlist will be ignored.
1086 root 1.1
1087     =item accept => [$type...]
1088    
1089     Lists the acceptable result types: only result types in this set will be
1090     accepted and returned. The default includes the C<$qtype> and nothing
1091 root 1.44 else. If this list includes C<cname>, then CNAME-chains will not be
1092     followed (because you asked for the CNAME record).
1093 root 1.1
1094     =item class => "class"
1095    
1096     Specify the query class ("in" for internet, "ch" for chaosnet and "hs" for
1097 root 1.2 hesiod are the only ones making sense). The default is "in", of course.
1098 root 1.1
1099     =back
1100    
1101     Examples:
1102    
1103 root 1.46 # full example, you can paste this into perl:
1104 root 1.45 use Data::Dumper;
1105     use AnyEvent::DNS;
1106     AnyEvent::DNS::resolver->resolve (
1107     "google.com", "*", my $cv = AnyEvent->condvar);
1108     warn Dumper [$cv->recv];
1109    
1110     # shortened result:
1111     # [
1112     # [ 'google.com', 'soa', 'in', 'ns1.google.com', 'dns-admin.google.com',
1113     # 2008052701, 7200, 1800, 1209600, 300 ],
1114     # [
1115     # 'google.com', 'txt', 'in',
1116     # 'v=spf1 include:_netblocks.google.com ~all'
1117     # ],
1118     # [ 'google.com', 'a', 'in', '64.233.187.99' ],
1119     # [ 'google.com', 'mx', 'in', 10, 'smtp2.google.com' ],
1120     # [ 'google.com', 'ns', 'in', 'ns2.google.com' ],
1121     # ]
1122    
1123     # resolve a records:
1124     $res->resolve ("ruth.plan9.de", "a", sub { warn Dumper [@_] });
1125    
1126     # result:
1127     # [
1128     # [ 'ruth.schmorp.de', 'a', 'in', '129.13.162.95' ]
1129     # ]
1130 root 1.1
1131 root 1.45 # resolve any records, but return only a and aaaa records:
1132 root 1.1 $res->resolve ("test1.laendle", "*",
1133     accept => ["a", "aaaa"],
1134     sub {
1135     warn Dumper [@_];
1136     }
1137     );
1138    
1139 root 1.45 # result:
1140     # [
1141     # [ 'test1.laendle', 'a', 'in', '10.0.0.255' ],
1142     # [ 'test1.laendle', 'aaaa', 'in', '3ffe:1900:4545:0002:0240:0000:0000:f7e1' ]
1143     # ]
1144 root 1.1
1145     =cut
1146    
1147     sub resolve($%) {
1148     my $cb = pop;
1149     my ($self, $qname, $qtype, %opt) = @_;
1150    
1151     my @search = $qname =~ s/\.$//
1152     ? ""
1153     : $opt{search}
1154     ? @{ $opt{search} }
1155     : ($qname =~ y/.//) >= $self->{ndots}
1156     ? ("", @{ $self->{search} })
1157     : (@{ $self->{search} }, "");
1158    
1159     my $class = $opt{class} || "in";
1160    
1161     my %atype = $opt{accept}
1162     ? map +($_ => 1), @{ $opt{accept} }
1163     : ($qtype => 1);
1164    
1165     # advance in searchlist
1166 root 1.22 my ($do_search, $do_req);
1167    
1168     $do_search = sub {
1169 root 1.1 @search
1170 root 1.22 or (undef $do_search), (undef $do_req), return $cb->();
1171 root 1.1
1172 root 1.4 (my $name = lc "$qname." . shift @search) =~ s/\.$//;
1173 root 1.1 my $depth = 2;
1174    
1175     # advance in cname-chain
1176 root 1.22 $do_req = sub {
1177 root 1.1 $self->request ({
1178     rd => 1,
1179     qd => [[$name, $qtype, $class]],
1180     }, sub {
1181     my ($res) = @_
1182     or return $do_search->();
1183    
1184     my $cname;
1185    
1186     while () {
1187 root 1.2 # results found?
1188 root 1.4 my @rr = grep $name eq lc $_->[0] && ($atype{"*"} || $atype{$_->[1]}), @{ $res->{an} };
1189 root 1.1
1190 root 1.22 (undef $do_search), (undef $do_req), return $cb->(@rr)
1191 root 1.1 if @rr;
1192    
1193     # see if there is a cname we can follow
1194 root 1.4 my @rr = grep $name eq lc $_->[0] && $_->[1] eq "cname", @{ $res->{an} };
1195 root 1.1
1196     if (@rr) {
1197     $depth--
1198     or return $do_search->(); # cname chain too long
1199    
1200     $cname = 1;
1201     $name = $rr[0][3];
1202    
1203     } elsif ($cname) {
1204     # follow the cname
1205     return $do_req->();
1206    
1207     } else {
1208 root 1.2 # no, not found anything
1209 root 1.1 return $do_search->();
1210     }
1211     }
1212     });
1213     };
1214    
1215     $do_req->();
1216     };
1217    
1218     $do_search->();
1219     }
1220    
1221 root 1.17 use AnyEvent::Socket (); # circular dependency, so do not import anything and do it at the end
1222    
1223 root 1.1 1;
1224    
1225     =back
1226    
1227     =head1 AUTHOR
1228    
1229     Marc Lehmann <schmorp@schmorp.de>
1230     http://home.schmorp.de/
1231    
1232     =cut
1233