ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent/lib/AnyEvent/DNS.pm
Revision: 1.17
Committed: Fri May 23 20:21:50 2008 UTC (16 years, 1 month ago) by root
Branch: MAIN
Changes since 1.16: +25 -17 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 =head1 DESCRIPTION
10
11 This module offers both a number of DNS convenience functions as well
12 as a fully asynchronous and high-performance pure-perl stub resolver.
13
14 The stub resolver supports DNS over UDP, optional EDNS0 support for up to
15 4kiB datagrams and automatically falls back to virtual circuit mode for
16 large responses.
17
18 =head2 CONVENIENCE FUNCTIONS
19
20 =over 4
21
22 =cut
23
24 package AnyEvent::DNS;
25
26 no warnings;
27 use strict;
28
29 use AnyEvent::Handle ();
30
31 =item AnyEvent::DNS::addr $node, $service, $family, $type, $cb->(@addrs)
32
33 NOT YET IMPLEMENTED
34
35 Tries to resolve the given nodename and service name into sockaddr
36 structures usable to connect to this node and service in a
37 protocol-independent way. It works similarly to the getaddrinfo posix
38 function.
39
40 Example:
41
42 AnyEvent::DNS::addr "google.com", "http", AF_UNSPEC, SOCK_STREAM, sub { ... };
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 arrayref 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 resolver;
99
100 sub a($$) {
101 my ($domain, $cb) = @_;
102
103 resolver->resolve ($domain => "a", sub {
104 $cb->(map $_->[3], @_);
105 });
106 }
107
108 sub aaaa($$) {
109 my ($domain, $cb) = @_;
110
111 resolver->resolve ($domain => "aaaa", sub {
112 $cb->(map $_->[3], @_);
113 });
114 }
115
116 sub mx($$) {
117 my ($domain, $cb) = @_;
118
119 resolver->resolve ($domain => "mx", sub {
120 $cb->(map $_->[4], sort { $a->[3] <=> $b->[3] } @_);
121 });
122 }
123
124 sub ns($$) {
125 my ($domain, $cb) = @_;
126
127 resolver->resolve ($domain => "ns", sub {
128 $cb->(map $_->[3], @_);
129 });
130 }
131
132 sub txt($$) {
133 my ($domain, $cb) = @_;
134
135 resolver->resolve ($domain => "txt", sub {
136 $cb->(map $_->[3], @_);
137 });
138 }
139
140 sub srv($$$$) {
141 my ($service, $proto, $domain, $cb) = @_;
142
143 # todo, ask for any and check glue records
144 resolver->resolve ("_$service._$proto.$domain" => "srv", sub {
145 $cb->(map [@$_[3,4,5,6]], sort { $a->[3] <=> $b->[3] || $b->[4] <=> $a->[4] } @_);
146 });
147 }
148
149 sub ptr($$) {
150 my ($ip, $cb) = @_;
151
152 $ip = AnyEvent::Socket::parse_ip ($ip)
153 or return $cb->();
154
155 if (4 == length $ip) {
156 $ip = join ".", (reverse split /\./, $ip), "in-addr.arpa.";
157 } else {
158 $ip = join ".", (reverse split //, unpack "H*", $ip), "ip6.arpa.";
159 }
160
161 resolver->resolve ($ip => "ptr", sub {
162 $cb->(map $_->[3], @_);
163 });
164 }
165
166 sub any($$) {
167 my ($domain, $cb) = @_;
168
169 resolver->resolve ($domain => "*", $cb);
170 }
171
172 =back
173
174 =head2 LOW-LEVEL DNS EN-/DECODING FUNCTIONS
175
176 =over 4
177
178 =item $AnyEvent::DNS::EDNS0
179
180 This variable decides whether dns_pack automatically enables EDNS0
181 support. By default, this is disabled (C<0>), but when set to C<1>,
182 AnyEvent::DNS will use EDNS0 in all requests.
183
184 =cut
185
186 our $EDNS0 = 0; # set to 1 to enable (partial) edns0
187
188 our %opcode_id = (
189 query => 0,
190 iquery => 1,
191 status => 2,
192 notify => 4,
193 update => 5,
194 map +($_ => $_), 3, 6..15
195 );
196
197 our %opcode_str = reverse %opcode_id;
198
199 our %rcode_id = (
200 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 );
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 opt => 41,
243 spf => 99,
244 tkey => 249,
245 tsig => 250,
246 ixfr => 251,
247 axfr => 252,
248 mailb => 253,
249 "*" => 255,
250 );
251
252 our %type_str = reverse %type_id;
253
254 our %class_id = (
255 in => 1,
256 ch => 3,
257 hs => 4,
258 none => 254,
259 "*" => 255,
260 );
261
262 our %class_str = reverse %class_id;
263
264 # names MUST have a trailing dot
265 sub _enc_qname($) {
266 pack "(C/a)*", (split /\./, shift), ""
267 }
268
269 sub _enc_qd() {
270 (_enc_qname $_->[0]) . pack "nn",
271 ($_->[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 ad => 0,
305 cd => 0,
306
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 pack "nn nnnn a* a* a* a* a*",
319 $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 + ! !$req->{ad} * 0x0020
328 + ! !$req->{cd} * 0x0010
329 + $rcode_id{$req->{rc}} * 0x0001,
330
331 scalar @{ $req->{qd} || [] },
332 scalar @{ $req->{an} || [] },
333 scalar @{ $req->{ns} || [] },
334 $EDNS0 + scalar @{ $req->{ar} || [] }, # include EDNS0 option here
335
336 (join "", map _enc_qd, @{ $req->{qd} || [] }),
337 (join "", map _enc_rr, @{ $req->{an} || [] }),
338 (join "", map _enc_rr, @{ $req->{ns} || [] }),
339 (join "", map _enc_rr, @{ $req->{ar} || [] }),
340
341 ($EDNS0 ? pack "C nnNn", 0, 41, 4096, 0, 0 : "") # EDNS0, 4kiB udp payload size
342 }
343
344 our $ofs;
345 our $pkt;
346
347 # bitches
348 sub _dec_qname {
349 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 if ($len & 0xc0) {
360 $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 my $qname = _dec_qname;
375 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 1 => sub { join ".", unpack "C4" }, # a
381 2 => sub { local $ofs = $ofs - length; _dec_qname }, # ns
382 5 => sub { local $ofs = $ofs - length; _dec_qname }, # cname
383 6 => sub {
384 local $ofs = $ofs - length;
385 my $mname = _dec_qname;
386 my $rname = _dec_qname;
387 ($mname, $rname, unpack "NNNNN", substr $pkt, $ofs)
388 }, # soa
389 11 => sub { ((join ".", unpack "C4"), unpack "C a*", substr $_, 4) }, # wks
390 12 => sub { local $ofs = $ofs - length; _dec_qname }, # ptr
391 13 => sub { unpack "C/a C/a", $_ }, # hinfo
392 15 => sub { local $ofs = $ofs + 2 - length; ((unpack "n", $_), _dec_qname) }, # mx
393 16 => sub { unpack "(C/a)*", $_ }, # txt
394 28 => sub { AnyEvent::Socket::format_ip ($_) }, # aaaa
395 33 => sub { local $ofs = $ofs + 6 - length; ((unpack "nnn", $_), _dec_qname) }, # srv
396 99 => sub { unpack "(C/a)*", $_ }, # spf
397 );
398
399 sub _dec_rr {
400 my $qname = _dec_qname;
401
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 $qname,
407 $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 # an unsuccessful reply
420 {
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 2008052201, 10800, 1800, 2592000, 86400
434 ]
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 ad => ! ! ($flags & 0x0020),
492 cd => ! ! ($flags & 0x0010),
493 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 This is the class which does the actual protocol work.
510
511 =over 4
512
513 =cut
514
515 use Carp ();
516 use Scalar::Util ();
517 use Socket ();
518
519 our $NOW;
520
521 =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 $RESOLVER->os_config;
540 $RESOLVER
541 }
542 }
543
544 =item $resolver = new AnyEvent::DNS key => value...
545
546 Creates and returns a new resolver.
547
548 The following options are supported:
549
550 =over 4
551
552 =item server => [...]
553
554 A list of server addressses (default: C<v127.0.0.1>) in network format (4
555 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 =item ndots => $integer
568
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 =item max_outstanding => $integer
573
574 Most name servers do not handle many parallel requests very well. This option
575 limits the numbe rof outstanding requests to C<$n> (default: C<10>), that means
576 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 =item reuse => $seconds
580
581 The number of seconds (default: C<60>) that a query id cannot be re-used
582 after a request. Since AnyEvent::DNS will only allocate up to 30000 ID's
583 at the same time, the long-term maximum number of requests per second is
584 C<30000 / $seconds> (and thus C<500> requests/s by default).
585
586 =back
587
588 =cut
589
590 sub new {
591 my ($class, %arg) = @_;
592
593 socket my $fh, &Socket::AF_INET, &Socket::SOCK_DGRAM, 0
594 or Carp::croak "socket: $!";
595
596 AnyEvent::Util::fh_nonblocking $fh, 1;
597
598 my $self = bless {
599 server => [v127.0.0.1],
600 timeout => [2, 5, 5],
601 search => [],
602 ndots => 1,
603 max_outstanding => 10,
604 reuse => 60, # reuse id's after 5 minutes only, if possible
605 %arg,
606 fh => $fh,
607 reuse_q => [],
608 }, $class;
609
610 # search should default to gethostname's domain
611 # but perl lacks a good posix module
612
613 Scalar::Util::weaken (my $wself = $self);
614 $self->{rw} = AnyEvent->io (fh => $fh, poll => "r", cb => sub { $wself->_recv });
615
616 $self->_compile;
617
618 $self
619 }
620
621 =item $resolver->parse_resolv_conv ($string)
622
623 Parses the given string a sif it were a F<resolv.conf> file. The following
624 directives are supported (but not neecssarily implemented).
625
626 C<#>-style comments, C<nameserver>, C<domain>, C<search>, C<sortlist>,
627 C<options> (C<timeout>, C<attempts>, C<ndots>).
628
629 Everything else is silently ignored.
630
631 =cut
632
633 sub parse_resolv_conf {
634 my ($self, $resolvconf) = @_;
635
636 $self->{server} = [];
637 $self->{search} = [];
638
639 my $attempts;
640
641 for (split /\n/, $resolvconf) {
642 if (/^\s*#/) {
643 # comment
644 } elsif (/^\s*nameserver\s+(\S+)\s*$/i) {
645 my $ip = $1;
646 if (AnyEvent::Util::dotted_quad $ip) {
647 push @{ $self->{server} }, AnyEvent::Util::socket_inet_aton $ip;
648 } else {
649 warn "nameserver $ip invalid and ignored\n";
650 }
651 } elsif (/^\s*domain\s+(\S*)\s+$/i) {
652 $self->{search} = [$1];
653 } elsif (/^\s*search\s+(.*?)\s*$/i) {
654 $self->{search} = [split /\s+/, $1];
655 } elsif (/^\s*sortlist\s+(.*?)\s*$/i) {
656 # ignored, NYI
657 } elsif (/^\s*options\s+(.*?)\s*$/i) {
658 for (split /\s+/, $1) {
659 if (/^timeout:(\d+)$/) {
660 $self->{timeout} = [$1];
661 } elsif (/^attempts:(\d+)$/) {
662 $attempts = $1;
663 } elsif (/^ndots:(\d+)$/) {
664 $self->{ndots} = $1;
665 } else {
666 # debug, rotate, no-check-names, inet6
667 }
668 }
669 }
670 }
671
672 $self->{timeout} = [($self->{timeout}[0]) x $attempts]
673 if $attempts;
674
675 $self->_compile;
676 }
677
678 =item $resolver->os_config
679
680 Tries so load and parse F</etc/resolv.conf> on portable opertaing systems. Tries various
681 egregious hacks on windows to force the dns servers and searchlist out of the config.
682
683 =cut
684
685 sub os_config {
686 my ($self) = @_;
687
688 if ($^O =~ /mswin32|cygwin/i) {
689 # yeah, it suxx... lets hope DNS is DNS in all locales
690
691 if (open my $fh, "ipconfig /all |") {
692 delete $self->{server};
693 delete $self->{search};
694
695 while (<$fh>) {
696 # first DNS.* is suffix list
697 if (/^\s*DNS/) {
698 while (/\s+([[:alnum:].\-]+)\s*$/) {
699 push @{ $self->{search} }, $1;
700 $_ = <$fh>;
701 }
702 last;
703 }
704 }
705
706 while (<$fh>) {
707 # second DNS.* is server address list
708 if (/^\s*DNS/) {
709 while (/\s+(\d+\.\d+\.\d+\.\d+)\s*$/) {
710 my $ip = $1;
711 push @{ $self->{server} }, AnyEvent::Util::socket_inet_aton $ip
712 if AnyEvent::Util::dotted_quad $ip;
713 $_ = <$fh>;
714 }
715 last;
716 }
717 }
718
719 $self->_compile;
720 }
721 } else {
722 # try resolv.conf everywhere
723
724 if (open my $fh, "</etc/resolv.conf") {
725 local $/;
726 $self->parse_resolv_conf (<$fh>);
727 }
728 }
729 }
730
731 sub _compile {
732 my $self = shift;
733
734 my @retry;
735
736 for my $timeout (@{ $self->{timeout} }) {
737 for my $server (@{ $self->{server} }) {
738 push @retry, [$server, $timeout];
739 }
740 }
741
742 $self->{retry} = \@retry;
743 }
744
745 sub _feed {
746 my ($self, $res) = @_;
747
748 $res = dns_unpack $res
749 or return;
750
751 my $id = $self->{id}{$res->{id}};
752
753 return unless ref $id;
754
755 $NOW = time;
756 $id->[1]->($res);
757 }
758
759 sub _recv {
760 my ($self) = @_;
761
762 while (my $peer = recv $self->{fh}, my $res, 4096, 0) {
763 my ($port, $host) = Socket::unpack_sockaddr_in $peer;
764
765 return unless $port == 53 && grep $_ eq $host, @{ $self->{server} };
766
767 $self->_feed ($res);
768 }
769 }
770
771 sub _exec {
772 my ($self, $req, $retry) = @_;
773
774 if (my $retry_cfg = $self->{retry}[$retry]) {
775 my ($server, $timeout) = @$retry_cfg;
776
777 $self->{id}{$req->[2]} = [AnyEvent->timer (after => $timeout, cb => sub {
778 $NOW = time;
779
780 # timeout, try next
781 $self->_exec ($req, $retry + 1);
782 }), sub {
783 my ($res) = @_;
784
785 if ($res->{tc}) {
786 # success, but truncated, so use tcp
787 AnyEvent::Socket::tcp_connect ((Socket::inet_ntoa $server), 53, sub {
788 my ($fh) = @_
789 or return $self->_exec ($req, $retry + 1);
790
791 my $handle = new AnyEvent::Handle
792 fh => $fh,
793 on_error => sub {
794 # failure, try next
795 $self->_exec ($req, $retry + 1);
796 };
797
798 $handle->push_write (pack "n/a", $req->[0]);
799 $handle->push_read_chunk (2, sub {
800 $handle->unshift_read_chunk ((unpack "n", $_[1]), sub {
801 $self->_feed ($_[1]);
802 });
803 });
804 shutdown $fh, 1;
805
806 }, sub { $timeout });
807
808 } else {
809 # success
810 $self->{id}{$req->[2]} = 1;
811 push @{ $self->{reuse_q} }, [$NOW + $self->{reuse}, $req->[2]];
812 --$self->{outstanding};
813 $self->_scheduler;
814
815 $req->[1]->($res);
816 }
817 }];
818
819 send $self->{fh}, $req->[0], 0, Socket::pack_sockaddr_in 53, $server;
820 } else {
821 # failure
822 $self->{id}{$req->[2]} = 1;
823 push @{ $self->{reuse_q} }, [$NOW + $self->{reuse}, $req->[2]];
824 --$self->{outstanding};
825 $self->_scheduler;
826
827 $req->[1]->();
828 }
829 }
830
831 sub _scheduler {
832 my ($self) = @_;
833
834 $NOW = time;
835
836 # first clear id reuse queue
837 delete $self->{id}{ (shift @{ $self->{reuse_q} })->[1] }
838 while @{ $self->{reuse_q} } && $self->{reuse_q}[0][0] <= $NOW;
839
840 while ($self->{outstanding} < $self->{max_outstanding}) {
841
842 if (@{ $self->{reuse_q} } >= 30000) {
843 # we ran out of ID's, wait a bit
844 $self->{reuse_to} ||= AnyEvent->timer (after => $self->{reuse_q}[0][0] - $NOW, cb => sub {
845 delete $self->{reuse_to};
846 $self->_scheduler;
847 });
848 last;
849 }
850
851 my $req = shift @{ $self->{queue} }
852 or last;
853
854 while () {
855 $req->[2] = int rand 65536;
856 last unless exists $self->{id}{$req->[2]};
857 }
858
859 $self->{id}{$req->[2]} = 1;
860 substr $req->[0], 0, 2, pack "n", $req->[2];
861
862 ++$self->{outstanding};
863 $self->_exec ($req, 0);
864 }
865 }
866
867 =item $resolver->request ($req, $cb->($res))
868
869 Sends a single request (a hash-ref formated as specified for
870 C<dns_pack>) to the configured nameservers including
871 retries. Calls the callback with the decoded response packet if a reply
872 was received, or no arguments on timeout.
873
874 =cut
875
876 sub request($$) {
877 my ($self, $req, $cb) = @_;
878
879 push @{ $self->{queue} }, [dns_pack $req, $cb];
880 $self->_scheduler;
881 }
882
883 =item $resolver->resolve ($qname, $qtype, %options, $cb->($rcode, @rr))
884
885 Queries the DNS for the given domain name C<$qname> of type C<$qtype> (a
886 qtype of "*" is supported and means "any").
887
888 The callback will be invoked with a list of matching result records or
889 none on any error or if the name could not be found.
890
891 CNAME chains (although illegal) are followed up to a length of 8.
892
893 Note that this resolver is just a stub resolver: it requires a nameserver
894 supporting recursive queries, will not do any recursive queries itself and
895 is not secure when used against an untrusted name server.
896
897 The following options are supported:
898
899 =over 4
900
901 =item search => [$suffix...]
902
903 Use the given search list (which might be empty), by appending each one
904 in turn to the C<$qname>. If this option is missing then the configured
905 C<ndots> and C<search> define its value. If the C<$qname> ends in a dot,
906 then the searchlist will be ignored.
907
908 =item accept => [$type...]
909
910 Lists the acceptable result types: only result types in this set will be
911 accepted and returned. The default includes the C<$qtype> and nothing
912 else.
913
914 =item class => "class"
915
916 Specify the query class ("in" for internet, "ch" for chaosnet and "hs" for
917 hesiod are the only ones making sense). The default is "in", of course.
918
919 =back
920
921 Examples:
922
923 $res->resolve ("ruth.plan9.de", "a", sub {
924 warn Dumper [@_];
925 });
926
927 [
928 [
929 'ruth.schmorp.de',
930 'a',
931 'in',
932 '129.13.162.95'
933 ]
934 ]
935
936 $res->resolve ("test1.laendle", "*",
937 accept => ["a", "aaaa"],
938 sub {
939 warn Dumper [@_];
940 }
941 );
942
943 [
944 [
945 'test1.laendle',
946 'a',
947 'in',
948 '10.0.0.255'
949 ],
950 [
951 'test1.laendle',
952 'aaaa',
953 'in',
954 '3ffe:1900:4545:0002:0240:0000:0000:f7e1'
955 ]
956 ]
957
958 =cut
959
960 sub resolve($%) {
961 my $cb = pop;
962 my ($self, $qname, $qtype, %opt) = @_;
963
964 my @search = $qname =~ s/\.$//
965 ? ""
966 : $opt{search}
967 ? @{ $opt{search} }
968 : ($qname =~ y/.//) >= $self->{ndots}
969 ? ("", @{ $self->{search} })
970 : (@{ $self->{search} }, "");
971
972 my $class = $opt{class} || "in";
973
974 my %atype = $opt{accept}
975 ? map +($_ => 1), @{ $opt{accept} }
976 : ($qtype => 1);
977
978 # advance in searchlist
979 my $do_search; $do_search = sub {
980 @search
981 or return $cb->();
982
983 (my $name = lc "$qname." . shift @search) =~ s/\.$//;
984 my $depth = 2;
985
986 # advance in cname-chain
987 my $do_req; $do_req = sub {
988 $self->request ({
989 rd => 1,
990 qd => [[$name, $qtype, $class]],
991 }, sub {
992 my ($res) = @_
993 or return $do_search->();
994
995 my $cname;
996
997 while () {
998 # results found?
999 my @rr = grep $name eq lc $_->[0] && ($atype{"*"} || $atype{$_->[1]}), @{ $res->{an} };
1000
1001 return $cb->(@rr)
1002 if @rr;
1003
1004 # see if there is a cname we can follow
1005 my @rr = grep $name eq lc $_->[0] && $_->[1] eq "cname", @{ $res->{an} };
1006
1007 if (@rr) {
1008 $depth--
1009 or return $do_search->(); # cname chain too long
1010
1011 $cname = 1;
1012 $name = $rr[0][3];
1013
1014 } elsif ($cname) {
1015 # follow the cname
1016 return $do_req->();
1017
1018 } else {
1019 # no, not found anything
1020 return $do_search->();
1021 }
1022 }
1023 });
1024 };
1025
1026 $do_req->();
1027 };
1028
1029 $do_search->();
1030 }
1031
1032 use AnyEvent::Socket (); # circular dependency, so do not import anything and do it at the end
1033
1034 1;
1035
1036 =back
1037
1038 =head1 AUTHOR
1039
1040 Marc Lehmann <schmorp@schmorp.de>
1041 http://home.schmorp.de/
1042
1043 =cut
1044