ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent/lib/AnyEvent/DNS.pm
Revision: 1.47
Committed: Thu May 29 17:51:33 2008 UTC (16 years, 1 month ago) by root
Branch: MAIN
Changes since 1.46: +6 -0 lines
Log Message:
naptr

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 IPv4 and IPv6, UDP and TCP, optional
20 EDNS0 support for up to 4kiB datagrams and automatically falls back to
21 virtual circuit mode for 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 naptr => 35, # rfc2915
254 opt => 41,
255 spf => 99,
256 tkey => 249,
257 tsig => 250,
258 ixfr => 251,
259 axfr => 252,
260 mailb => 253,
261 "*" => 255,
262 );
263
264 our %type_str = reverse %type_id;
265
266 our %class_id = (
267 in => 1,
268 ch => 3,
269 hs => 4,
270 none => 254,
271 "*" => 255,
272 );
273
274 our %class_str = reverse %class_id;
275
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} || [] }, # EDNS0 option included 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 option
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 35 => sub { # naptr
408 my ($order, $preference, $flags, $service, $regexp, $offset) = unpack "nn C/a* C/a* C/a* .", $_;
409 local $ofs = $ofs + $offset - length;
410 ($order, $preference, $flags, $service, $regexp, _dec_name)
411 },
412 99 => sub { unpack "(C/a*)*", $_ }, # spf
413 );
414
415 sub _dec_rr {
416 my $name = _dec_name;
417
418 my ($rt, $rc, $ttl, $rdlen) = unpack "nn N n", substr $pkt, $ofs; $ofs += 10;
419 local $_ = substr $pkt, $ofs, $rdlen; $ofs += $rdlen;
420
421 [
422 $name,
423 $type_str{$rt} || $rt,
424 $class_str{$rc} || $rc,
425 ($dec_rr{$rt} || sub { $_ })->(),
426 ]
427 }
428
429 =item $dns = AnyEvent::DNS::dns_unpack $pkt
430
431 Unpacks a DNS packet into a perl data structure.
432
433 Examples:
434
435 # an unsuccessful reply
436 {
437 'qd' => [
438 [ 'ruth.plan9.de.mach.uni-karlsruhe.de', '*', 'in' ]
439 ],
440 'rc' => 'nxdomain',
441 'ar' => [],
442 'ns' => [
443 [
444 'uni-karlsruhe.de',
445 'soa',
446 'in',
447 'netserv.rz.uni-karlsruhe.de',
448 'hostmaster.rz.uni-karlsruhe.de',
449 2008052201, 10800, 1800, 2592000, 86400
450 ]
451 ],
452 'tc' => '',
453 'ra' => 1,
454 'qr' => 1,
455 'id' => 45915,
456 'aa' => '',
457 'an' => [],
458 'rd' => 1,
459 'op' => 'query'
460 }
461
462 # a successful reply
463
464 {
465 'qd' => [ [ 'www.google.de', 'a', 'in' ] ],
466 'rc' => 0,
467 'ar' => [
468 [ 'a.l.google.com', 'a', 'in', '209.85.139.9' ],
469 [ 'b.l.google.com', 'a', 'in', '64.233.179.9' ],
470 [ 'c.l.google.com', 'a', 'in', '64.233.161.9' ],
471 ],
472 'ns' => [
473 [ 'l.google.com', 'ns', 'in', 'a.l.google.com' ],
474 [ 'l.google.com', 'ns', 'in', 'b.l.google.com' ],
475 ],
476 'tc' => '',
477 'ra' => 1,
478 'qr' => 1,
479 'id' => 64265,
480 'aa' => '',
481 'an' => [
482 [ 'www.google.de', 'cname', 'in', 'www.google.com' ],
483 [ 'www.google.com', 'cname', 'in', 'www.l.google.com' ],
484 [ 'www.l.google.com', 'a', 'in', '66.249.93.104' ],
485 [ 'www.l.google.com', 'a', 'in', '66.249.93.147' ],
486 ],
487 'rd' => 1,
488 'op' => 0
489 }
490
491 =cut
492
493 sub dns_unpack($) {
494 local $pkt = shift;
495 my ($id, $flags, $qd, $an, $ns, $ar)
496 = unpack "nn nnnn A*", $pkt;
497
498 local $ofs = 6 * 2;
499
500 {
501 id => $id,
502 qr => ! ! ($flags & 0x8000),
503 aa => ! ! ($flags & 0x0400),
504 tc => ! ! ($flags & 0x0200),
505 rd => ! ! ($flags & 0x0100),
506 ra => ! ! ($flags & 0x0080),
507 ad => ! ! ($flags & 0x0020),
508 cd => ! ! ($flags & 0x0010),
509 op => $opcode_str{($flags & 0x001e) >> 11},
510 rc => $rcode_str{($flags & 0x000f)},
511
512 qd => [map _dec_qd, 1 .. $qd],
513 an => [map _dec_rr, 1 .. $an],
514 ns => [map _dec_rr, 1 .. $ns],
515 ar => [map _dec_rr, 1 .. $ar],
516 }
517 }
518
519 #############################################################################
520
521 =back
522
523 =head2 THE AnyEvent::DNS RESOLVER CLASS
524
525 This is the class which does the actual protocol work.
526
527 =over 4
528
529 =cut
530
531 use Carp ();
532 use Scalar::Util ();
533 use Socket ();
534
535 our $NOW;
536
537 =item AnyEvent::DNS::resolver
538
539 This function creates and returns a resolver that is ready to use and
540 should mimic the default resolver for your system as good as possible.
541
542 It only ever creates one resolver and returns this one on subsequent
543 calls.
544
545 Unless you have special needs, prefer this function over creating your own
546 resolver object.
547
548 =cut
549
550 our $RESOLVER;
551
552 sub resolver() {
553 $RESOLVER || do {
554 $RESOLVER = new AnyEvent::DNS;
555 $RESOLVER->os_config;
556 $RESOLVER
557 }
558 }
559
560 =item $resolver = new AnyEvent::DNS key => value...
561
562 Creates and returns a new resolver.
563
564 The following options are supported:
565
566 =over 4
567
568 =item server => [...]
569
570 A list of server addresses (default: C<v127.0.0.1>) in network format
571 (i.e. as returned by C<AnyEvent::Socket::parse_address> - both IPv4 and
572 IPv6 are supported).
573
574 =item timeout => [...]
575
576 A list of timeouts to use (also determines the number of retries). To make
577 three retries with individual time-outs of 2, 5 and 5 seconds, use C<[2,
578 5, 5]>, which is also the default.
579
580 =item search => [...]
581
582 The default search list of suffixes to append to a domain name (default: none).
583
584 =item ndots => $integer
585
586 The number of dots (default: C<1>) that a name must have so that the resolver
587 tries to resolve the name without any suffixes first.
588
589 =item max_outstanding => $integer
590
591 Most name servers do not handle many parallel requests very well. This
592 option limits the number of outstanding requests to C<$integer>
593 (default: C<10>), that means if you request more than this many requests,
594 then the additional requests will be queued until some other requests have
595 been resolved.
596
597 =item reuse => $seconds
598
599 The number of seconds (default: C<300>) that a query id cannot be re-used
600 after a timeout. If there as no time-out then query id's can be reused
601 immediately.
602
603 =back
604
605 =cut
606
607 sub new {
608 my ($class, %arg) = @_;
609
610 # try to create a ipv4 and an ipv6 socket
611 # only fail when we cnanot create either
612
613 socket my $fh4, AF_INET , &Socket::SOCK_DGRAM, 0;
614 socket my $fh6, AF_INET6, &Socket::SOCK_DGRAM, 0;
615
616 $fh4 || $fh6
617 or Carp::croak "unable to create either an IPv6 or an IPv4 socket";
618
619 my $self = bless {
620 server => [],
621 timeout => [2, 5, 5],
622 search => [],
623 ndots => 1,
624 max_outstanding => 10,
625 reuse => 300, # reuse id's after 5 minutes only, if possible
626 %arg,
627 reuse_q => [],
628 }, $class;
629
630 # search should default to gethostname's domain
631 # but perl lacks a good posix module
632
633 Scalar::Util::weaken (my $wself = $self);
634
635 if ($fh4) {
636 AnyEvent::Util::fh_nonblocking $fh4, 1;
637 $self->{fh4} = $fh4;
638 $self->{rw4} = AnyEvent->io (fh => $fh4, poll => "r", cb => sub {
639 if (my $peer = recv $fh4, my $pkt, MAX_PKT, 0) {
640 $wself->_recv ($pkt, $peer);
641 }
642 });
643 }
644
645 if ($fh6) {
646 $self->{fh6} = $fh6;
647 AnyEvent::Util::fh_nonblocking $fh6, 1;
648 $self->{rw6} = AnyEvent->io (fh => $fh6, poll => "r", cb => sub {
649 if (my $peer = recv $fh6, my $pkt, MAX_PKT, 0) {
650 $wself->_recv ($pkt, $peer);
651 }
652 });
653 }
654
655 $self->_compile;
656
657 $self
658 }
659
660 =item $resolver->parse_resolv_conv ($string)
661
662 Parses the given string as if it were a F<resolv.conf> file. The following
663 directives are supported (but not necessarily implemented).
664
665 C<#>-style comments, C<nameserver>, C<domain>, C<search>, C<sortlist>,
666 C<options> (C<timeout>, C<attempts>, C<ndots>).
667
668 Everything else is silently ignored.
669
670 =cut
671
672 sub parse_resolv_conf {
673 my ($self, $resolvconf) = @_;
674
675 $self->{server} = [];
676 $self->{search} = [];
677
678 my $attempts;
679
680 for (split /\n/, $resolvconf) {
681 if (/^\s*#/) {
682 # comment
683 } elsif (/^\s*nameserver\s+(\S+)\s*$/i) {
684 my $ip = $1;
685 if (my $ipn = AnyEvent::Socket::parse_address ($ip)) {
686 push @{ $self->{server} }, $ipn;
687 } else {
688 warn "nameserver $ip invalid and ignored\n";
689 }
690 } elsif (/^\s*domain\s+(\S*)\s+$/i) {
691 $self->{search} = [$1];
692 } elsif (/^\s*search\s+(.*?)\s*$/i) {
693 $self->{search} = [split /\s+/, $1];
694 } elsif (/^\s*sortlist\s+(.*?)\s*$/i) {
695 # ignored, NYI
696 } elsif (/^\s*options\s+(.*?)\s*$/i) {
697 for (split /\s+/, $1) {
698 if (/^timeout:(\d+)$/) {
699 $self->{timeout} = [$1];
700 } elsif (/^attempts:(\d+)$/) {
701 $attempts = $1;
702 } elsif (/^ndots:(\d+)$/) {
703 $self->{ndots} = $1;
704 } else {
705 # debug, rotate, no-check-names, inet6
706 }
707 }
708 }
709 }
710
711 $self->{timeout} = [($self->{timeout}[0]) x $attempts]
712 if $attempts;
713
714 $self->_compile;
715 }
716
717 =item $resolver->os_config
718
719 Tries so load and parse F</etc/resolv.conf> on portable operating systems. Tries various
720 egregious hacks on windows to force the DNS servers and searchlist out of the system.
721
722 =cut
723
724 sub os_config {
725 my ($self) = @_;
726
727 $self->{server} = [];
728 $self->{search} = [];
729
730 if (AnyEvent::WIN32 || $^O =~ /cygwin/i) {
731 no strict 'refs';
732
733 # there are many options to find the current nameservers etc. on windows
734 # all of them don't work consistently:
735 # - the registry thing needs separate code on win32 native vs. cygwin
736 # - the registry layout differs between windows versions
737 # - calling windows api functions doesn't work on cygwin
738 # - ipconfig uses locale-specific messages
739
740 # we use ipconfig parsing because, despite all it's brokenness,
741 # it seems most stable in practise.
742 # for good measure, we append a fallback nameserver to our list.
743
744 if (open my $fh, "ipconfig /all |") {
745 # parsing strategy: we go through the output and look for
746 # :-lines with DNS in them. everything in those is regarded as
747 # either a nameserver (if it parses as an ip address), or a suffix
748 # (all else).
749
750 my $dns;
751 while (<$fh>) {
752 if (s/^\s.*\bdns\b.*://i) {
753 $dns = 1;
754 } elsif (/^\S/ || /^\s[^:]{16,}: /) {
755 $dns = 0;
756 }
757 if ($dns && /^\s*(\S+)\s*$/) {
758 my $s = $1;
759 $s =~ s/%\d+(?!\S)//; # get rid of scope id
760 if (my $ipn = AnyEvent::Socket::parse_address ($s)) {
761 push @{ $self->{server} }, $ipn;
762 } else {
763 push @{ $self->{search} }, $s;
764 }
765 }
766 }
767
768 # always add one fallback server
769 push @{ $self->{server} }, $DNS_FALLBACK[rand @DNS_FALLBACK];
770
771 $self->_compile;
772 }
773 } else {
774 # try resolv.conf everywhere
775
776 if (open my $fh, "</etc/resolv.conf") {
777 local $/;
778 $self->parse_resolv_conf (<$fh>);
779 }
780 }
781 }
782
783 sub _compile {
784 my $self = shift;
785
786 my %search; $self->{search} = [grep 0 < length, grep !$search{$_}++, @{ $self->{search} }];
787 my %server; $self->{server} = [grep 0 < length, grep !$server{$_}++, @{ $self->{server} }];
788
789 unless (@{ $self->{server} }) {
790 # use 127.0.0.1 by default, and one opendns nameserver as fallback
791 $self->{server} = [v127.0.0.1, $DNS_FALLBACK[rand @DNS_FALLBACK]];
792 }
793
794 my @retry;
795
796 for my $timeout (@{ $self->{timeout} }) {
797 for my $server (@{ $self->{server} }) {
798 push @retry, [$server, $timeout];
799 }
800 }
801
802 $self->{retry} = \@retry;
803 }
804
805 sub _feed {
806 my ($self, $res) = @_;
807
808 $res = dns_unpack $res
809 or return;
810
811 my $id = $self->{id}{$res->{id}};
812
813 return unless ref $id;
814
815 $NOW = time;
816 $id->[1]->($res);
817 }
818
819 sub _recv {
820 my ($self, $pkt, $peer) = @_;
821
822 # we ignore errors (often one gets port unreachable, but there is
823 # no good way to take advantage of that.
824
825 my ($port, $host) = AnyEvent::Socket::unpack_sockaddr ($peer);
826
827 return unless $port == 53 && grep $_ eq $host, @{ $self->{server} };
828
829 $self->_feed ($pkt);
830 }
831
832 sub _free_id {
833 my ($self, $id, $timeout) = @_;
834
835 if ($timeout) {
836 # we need to block the id for a while
837 $self->{id}{$id} = 1;
838 push @{ $self->{reuse_q} }, [$NOW + $self->{reuse}, $id];
839 } else {
840 # we can quickly recycle the id
841 delete $self->{id}{$id};
842 }
843
844 --$self->{outstanding};
845 $self->_scheduler;
846 }
847
848 # execute a single request, involves sending it with timeouts to multiple servers
849 sub _exec {
850 my ($self, $req) = @_;
851
852 my $retry; # of retries
853 my $do_retry;
854
855 $do_retry = sub {
856 my $retry_cfg = $self->{retry}[$retry++]
857 or do {
858 # failure
859 $self->_free_id ($req->[2], $retry > 1);
860 undef $do_retry; return $req->[1]->();
861 };
862
863 my ($server, $timeout) = @$retry_cfg;
864
865 $self->{id}{$req->[2]} = [AnyEvent->timer (after => $timeout, cb => sub {
866 $NOW = time;
867
868 # timeout, try next
869 &$do_retry;
870 }), sub {
871 my ($res) = @_;
872
873 if ($res->{tc}) {
874 # success, but truncated, so use tcp
875 AnyEvent::Socket::tcp_connect (AnyEvent::Socket::format_address ($server), DOMAIN_PORT, sub {
876 my ($fh) = @_
877 or return &$do_retry;
878
879 my $handle = new AnyEvent::Handle
880 fh => $fh,
881 on_error => sub {
882 # failure, try next
883 &$do_retry;
884 };
885
886 $handle->push_write (pack "n/a", $req->[0]);
887 $handle->push_read (chunk => 2, sub {
888 $handle->unshift_read (chunk => (unpack "n", $_[1]), sub {
889 $self->_feed ($_[1]);
890 });
891 });
892 shutdown $fh, 1;
893
894 }, sub { $timeout });
895
896 } else {
897 # success
898 $self->_free_id ($req->[2], $retry > 1);
899 undef $do_retry; return $req->[1]->($res);
900 }
901 }];
902
903 my $sa = AnyEvent::Socket::pack_sockaddr (DOMAIN_PORT, $server);
904
905 my $fh = AF_INET == Socket::sockaddr_family ($sa)
906 ? $self->{fh4} : $self->{fh6}
907 or return &$do_retry;
908
909 send $fh, $req->[0], 0, $sa;
910 };
911
912 &$do_retry;
913 }
914
915 sub _scheduler {
916 my ($self) = @_;
917
918 $NOW = time;
919
920 # first clear id reuse queue
921 delete $self->{id}{ (shift @{ $self->{reuse_q} })->[1] }
922 while @{ $self->{reuse_q} } && $self->{reuse_q}[0][0] <= $NOW;
923
924 while ($self->{outstanding} < $self->{max_outstanding}) {
925
926 if (@{ $self->{reuse_q} } >= 30000) {
927 # we ran out of ID's, wait a bit
928 $self->{reuse_to} ||= AnyEvent->timer (after => $self->{reuse_q}[0][0] - $NOW, cb => sub {
929 delete $self->{reuse_to};
930 $self->_scheduler;
931 });
932 last;
933 }
934
935 my $req = shift @{ $self->{queue} }
936 or last;
937
938 while () {
939 $req->[2] = int rand 65536;
940 last unless exists $self->{id}{$req->[2]};
941 }
942
943 ++$self->{outstanding};
944 $self->{id}{$req->[2]} = 1;
945 substr $req->[0], 0, 2, pack "n", $req->[2];
946
947 $self->_exec ($req);
948 }
949 }
950
951 =item $resolver->request ($req, $cb->($res))
952
953 Sends a single request (a hash-ref formated as specified for
954 C<dns_pack>) to the configured nameservers including
955 retries. Calls the callback with the decoded response packet if a reply
956 was received, or no arguments on timeout.
957
958 =cut
959
960 sub request($$) {
961 my ($self, $req, $cb) = @_;
962
963 push @{ $self->{queue} }, [dns_pack $req, $cb];
964 $self->_scheduler;
965 }
966
967 =item $resolver->resolve ($qname, $qtype, %options, $cb->($rcode, @rr))
968
969 Queries the DNS for the given domain name C<$qname> of type C<$qtype>.
970
971 A C<$qtype> is either a numerical query type (e.g. C<1> for A recods) or
972 a lowercase name (you have to look at the source to see which aliases are
973 supported, but all types from RFC 1034, C<aaaa>, C<srv>, C<spf> and a few
974 more are known to this module). A qtype of "*" is supported and means
975 "any" record type.
976
977 The callback will be invoked with a list of matching result records or
978 none on any error or if the name could not be found.
979
980 CNAME chains (although illegal) are followed up to a length of 8.
981
982 The callback will be invoked with an result code in string form (noerror,
983 formerr, servfail, nxdomain, notimp, refused and so on), or numerical
984 form if the result code is not supported. The remaining arguments are
985 arraryefs of the form C<[$name, $type, $class, @data>], where C<$name> is
986 the domain name, C<$type> a type string or number, C<$class> a class name
987 and @data is resource-record-dependent data. For C<a> records, this will
988 be the textual IPv4 addresses, for C<ns> or C<cname> records this will be
989 a domain name, for C<txt> records these are all the strings and so on.
990
991 All types mentioned in RFC 1034, C<aaaa>, C<srv> and C<spf> are
992 decoded. All resource records not known to this module will just return
993 the raw C<rdata> field as fourth entry.
994
995 Note that this resolver is just a stub resolver: it requires a name server
996 supporting recursive queries, will not do any recursive queries itself and
997 is not secure when used against an untrusted name server.
998
999 The following options are supported:
1000
1001 =over 4
1002
1003 =item search => [$suffix...]
1004
1005 Use the given search list (which might be empty), by appending each one
1006 in turn to the C<$qname>. If this option is missing then the configured
1007 C<ndots> and C<search> define its value. If the C<$qname> ends in a dot,
1008 then the searchlist will be ignored.
1009
1010 =item accept => [$type...]
1011
1012 Lists the acceptable result types: only result types in this set will be
1013 accepted and returned. The default includes the C<$qtype> and nothing
1014 else. If this list includes C<cname>, then CNAME-chains will not be
1015 followed (because you asked for the CNAME record).
1016
1017 =item class => "class"
1018
1019 Specify the query class ("in" for internet, "ch" for chaosnet and "hs" for
1020 hesiod are the only ones making sense). The default is "in", of course.
1021
1022 =back
1023
1024 Examples:
1025
1026 # full example, you can paste this into perl:
1027 use Data::Dumper;
1028 use AnyEvent::DNS;
1029 AnyEvent::DNS::resolver->resolve (
1030 "google.com", "*", my $cv = AnyEvent->condvar);
1031 warn Dumper [$cv->recv];
1032
1033 # shortened result:
1034 # [
1035 # [ 'google.com', 'soa', 'in', 'ns1.google.com', 'dns-admin.google.com',
1036 # 2008052701, 7200, 1800, 1209600, 300 ],
1037 # [
1038 # 'google.com', 'txt', 'in',
1039 # 'v=spf1 include:_netblocks.google.com ~all'
1040 # ],
1041 # [ 'google.com', 'a', 'in', '64.233.187.99' ],
1042 # [ 'google.com', 'mx', 'in', 10, 'smtp2.google.com' ],
1043 # [ 'google.com', 'ns', 'in', 'ns2.google.com' ],
1044 # ]
1045
1046 # resolve a records:
1047 $res->resolve ("ruth.plan9.de", "a", sub { warn Dumper [@_] });
1048
1049 # result:
1050 # [
1051 # [ 'ruth.schmorp.de', 'a', 'in', '129.13.162.95' ]
1052 # ]
1053
1054 # resolve any records, but return only a and aaaa records:
1055 $res->resolve ("test1.laendle", "*",
1056 accept => ["a", "aaaa"],
1057 sub {
1058 warn Dumper [@_];
1059 }
1060 );
1061
1062 # result:
1063 # [
1064 # [ 'test1.laendle', 'a', 'in', '10.0.0.255' ],
1065 # [ 'test1.laendle', 'aaaa', 'in', '3ffe:1900:4545:0002:0240:0000:0000:f7e1' ]
1066 # ]
1067
1068 =cut
1069
1070 sub resolve($%) {
1071 my $cb = pop;
1072 my ($self, $qname, $qtype, %opt) = @_;
1073
1074 my @search = $qname =~ s/\.$//
1075 ? ""
1076 : $opt{search}
1077 ? @{ $opt{search} }
1078 : ($qname =~ y/.//) >= $self->{ndots}
1079 ? ("", @{ $self->{search} })
1080 : (@{ $self->{search} }, "");
1081
1082 my $class = $opt{class} || "in";
1083
1084 my %atype = $opt{accept}
1085 ? map +($_ => 1), @{ $opt{accept} }
1086 : ($qtype => 1);
1087
1088 # advance in searchlist
1089 my ($do_search, $do_req);
1090
1091 $do_search = sub {
1092 @search
1093 or (undef $do_search), (undef $do_req), return $cb->();
1094
1095 (my $name = lc "$qname." . shift @search) =~ s/\.$//;
1096 my $depth = 2;
1097
1098 # advance in cname-chain
1099 $do_req = sub {
1100 $self->request ({
1101 rd => 1,
1102 qd => [[$name, $qtype, $class]],
1103 }, sub {
1104 my ($res) = @_
1105 or return $do_search->();
1106
1107 my $cname;
1108
1109 while () {
1110 # results found?
1111 my @rr = grep $name eq lc $_->[0] && ($atype{"*"} || $atype{$_->[1]}), @{ $res->{an} };
1112
1113 (undef $do_search), (undef $do_req), return $cb->(@rr)
1114 if @rr;
1115
1116 # see if there is a cname we can follow
1117 my @rr = grep $name eq lc $_->[0] && $_->[1] eq "cname", @{ $res->{an} };
1118
1119 if (@rr) {
1120 $depth--
1121 or return $do_search->(); # cname chain too long
1122
1123 $cname = 1;
1124 $name = $rr[0][3];
1125
1126 } elsif ($cname) {
1127 # follow the cname
1128 return $do_req->();
1129
1130 } else {
1131 # no, not found anything
1132 return $do_search->();
1133 }
1134 }
1135 });
1136 };
1137
1138 $do_req->();
1139 };
1140
1141 $do_search->();
1142 }
1143
1144 use AnyEvent::Socket (); # circular dependency, so do not import anything and do it at the end
1145
1146 1;
1147
1148 =back
1149
1150 =head1 AUTHOR
1151
1152 Marc Lehmann <schmorp@schmorp.de>
1153 http://home.schmorp.de/
1154
1155 =cut
1156