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