ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent/lib/AnyEvent/DNS.pm
Revision: 1.40
Committed: Thu May 29 06:15:24 2008 UTC (16 years, 1 month ago) by root
Branch: MAIN
Changes since 1.39: +1 -1 lines
Log Message:
*** empty log message ***

File Contents

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