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