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