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