ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent/lib/AnyEvent/DNS.pm
Revision: 1.22
Committed: Sat May 24 02:06:43 2008 UTC (16 years, 1 month ago) by root
Branch: MAIN
Changes since 1.21: +49 -30 lines
Log Message:
seems to work

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, $proto, $family, $type, $cb->([$family, $type, $proto, $sockaddr], ...)
32
33 Tries to resolve the given nodename and service name into protocol families
34 and sockaddr structures usable to connect to this node and service in a
35 protocol-independent way. It works remotely similar to the getaddrinfo
36 posix function.
37
38 C<$node> is either an IPv4 or IPv6 address or a hostname, C<$service> is
39 either a service name (port name from F</etc/services>) or a numerical
40 port number. If both C<$node> and C<$service> are names, then SRV records
41 will be consulted to find the real service, otherwise they will be
42 used as-is. If you know that the service name is not in your services
43 database, then you cna specify the service in the format C<name=port>
44 (e.g. C<http=80>).
45
46 C<$proto> must be a protocol name, currently C<tcp>, C<udp> or
47 C<sctp>. The default is C<tcp>.
48
49 C<$family> must be either C<0> (meaning any protocol is ok), C<4> (use
50 only IPv4) or C<6> (use only IPv6). This setting might be influenced by
51 C<$ENV{PERL_ANYEVENT_PROTOCOLS}>.
52
53 C<$type> must be C<SOCK_STREAM>, C<SOCK_DGRAM> or C<SOCK_SEQPACKET> (or
54 C<undef> in which case it gets automatically chosen).
55
56 The callback will receive zero or more array references that contain
57 C<$family, $type, $proto> for use in C<socket> and a binary
58 C<$sockaddr> for use in C<connect> (or C<bind>).
59
60 The application should try these in the order given.
61
62 Example:
63
64 AnyEvent::DNS::addr "google.com", "http", 0, undef, undef, sub { ... };
65
66 =item AnyEvent::DNS::a $domain, $cb->(@addrs)
67
68 Tries to resolve the given domain to IPv4 address(es).
69
70 =item AnyEvent::DNS::aaaa $domain, $cb->(@addrs)
71
72 Tries to resolve the given domain to IPv6 address(es).
73
74 =item AnyEvent::DNS::mx $domain, $cb->(@hostnames)
75
76 Tries to resolve the given domain into a sorted (lower preference value
77 first) list of domain names.
78
79 =item AnyEvent::DNS::ns $domain, $cb->(@hostnames)
80
81 Tries to resolve the given domain name into a list of name servers.
82
83 =item AnyEvent::DNS::txt $domain, $cb->(@hostnames)
84
85 Tries to resolve the given domain name into a list of text records.
86
87 =item AnyEvent::DNS::srv $service, $proto, $domain, $cb->(@srv_rr)
88
89 Tries to resolve the given service, protocol and domain name into a list
90 of service records.
91
92 Each srv_rr is an arrayref with the following contents:
93 C<[$priority, $weight, $transport, $target]>.
94
95 They will be sorted with lowest priority, highest weight first (TODO:
96 should use the rfc algorithm to reorder same-priority records for weight).
97
98 Example:
99
100 AnyEvent::DNS::srv "sip", "udp", "schmorp.de", sub { ...
101 # @_ = ( [10, 10, 5060, "sip1.schmorp.de" ] )
102
103 =item AnyEvent::DNS::ptr $ipv4_or_6, $cb->(@hostnames)
104
105 Tries to reverse-resolve the given IPv4 or IPv6 address (in textual form)
106 into it's hostname(s).
107
108 Example:
109
110 AnyEvent::DNS::ptr "2001:500:2f::f", sub { print shift };
111 # => f.root-servers.net
112
113 =item AnyEvent::DNS::any $domain, $cb->(@rrs)
114
115 Tries to resolve the given domain and passes all resource records found to
116 the callback.
117
118 =cut
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 ($ip, $cb) = @_;
173
174 $ip = AnyEvent::Socket::parse_ip ($ip)
175 or return $cb->();
176
177 if (4 == length $ip) {
178 $ip = join ".", (reverse split /\./, $ip), "in-addr.arpa.";
179 } else {
180 $ip = join ".", (reverse split //, unpack "H*", $ip), "ip6.arpa.";
181 }
182
183 resolver->resolve ($ip => "ptr", sub {
184 $cb->(map $_->[3], @_);
185 });
186 }
187
188 sub any($$) {
189 my ($domain, $cb) = @_;
190
191 resolver->resolve ($domain => "*", $cb);
192 }
193
194 #############################################################################
195
196 sub addr($$$$$$) {
197 my ($node, $service, $proto, $family, $type, $cb) = @_;
198
199 unless (&AnyEvent::Socket::AF_INET6) {
200 $family != 6
201 or return $cb->();
202
203 $family ||= 4;
204 }
205
206 $cb->() if $family == 4 && !$AnyEvent::PROTOCOL{ipv4};
207 $cb->() if $family == 6 && !$AnyEvent::PROTOCOL{ipv6};
208
209 $family ||=4 unless $AnyEvent::PROTOCOL{ipv6};
210 $family ||=6 unless $AnyEvent::PROTOCOL{ipv4};
211
212 $proto ||= "tcp";
213 $type ||= $proto eq "udp" ? Socket::SOCK_DGRAM : Socket::SOCK_STREAM;
214
215 my $proton = (getprotobyname $proto)[2]
216 or Carp::croak "$proto: protocol unknown";
217
218 my $port;
219
220 if ($service =~ /^(\S+)=(\d+)$/) {
221 ($service, $port) = ($1, $2);
222 } elsif ($service =~ /^\d+$/) {
223 ($service, $port) = (undef, $service);
224 } else {
225 $port = (getservbyname $service, $proto)[2]
226 or Carp::croak "$service/$proto: service unknown";
227 }
228
229 my @target = [$node, $port];
230
231 # resolve a records / provide sockaddr structures
232 my $resolve = sub {
233 my @res;
234 my $cv = AnyEvent->condvar (cb => sub {
235 $cb->(
236 map $_->[1],
237 sort {
238 $AnyEvent::PROTOCOL{$a->[1][0]} <=> $AnyEvent::PROTOCOL{$b->[1][0]}
239 or $a->[0] <=> $b->[0]
240 }
241 @res
242 )
243 });
244
245 $cv->begin;
246 for my $idx (0 .. $#target) {
247 my ($node, $port) = @{ $target[$idx] };
248
249 if (my $noden = AnyEvent::Socket::parse_ip ($node)) {
250 if (4 == length $noden && $family != 6) {
251 push @res, [$idx, [Socket::AF_INET, $type, $proton,
252 AnyEvent::Socket::pack_sockaddr ($port, $noden)]]
253 }
254
255 if (16 == length $noden && $family != 4) {
256 push @res, [$idx, [&AnyEvent::Socket::AF_INET6, $type, $proton,
257 AnyEvent::Socket::pack_sockaddr ( $port, $noden)]]
258 }
259 } else {
260 # ipv4
261 if ($family != 6) {
262 $cv->begin;
263 a $node, sub {
264 push @res, [$idx, [Socket::AF_INET, $type, $proton,
265 AnyEvent::Socket::pack_sockaddr ($port, AnyEvent::Socket::parse_ipv4 ($_))]]
266 for @_;
267 $cv->end;
268 };
269 }
270
271 # ipv6
272 if ($family != 4) {
273 $cv->begin;
274 aaaa $node, sub {
275 push @res, [$idx, [&AnyEvent::Socket::AF_INET6, $type, $proton,
276 AnyEvent::Socket::pack_sockaddr ($port, AnyEvent::Socket::parse_ipv6 ($_))]]
277 for @_;
278 $cv->end;
279 };
280 }
281 }
282 }
283 $cv->end;
284 };
285
286 # try srv records, if applicable
287 if ($node eq "localhost") {
288 @target = ([v127.0.0.1, $port], [v0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1, $port]);
289 } elsif (defined $service && !AnyEvent::Socket::parse_ip ($node)) {
290 srv $service, $proto, $node, sub {
291 my (@srv) = @_;
292
293 # no srv records, continue traditionally
294 @srv
295 or return &$resolve;
296
297 # only srv record has "." => abort
298 $srv[0][2] ne "." || $#srv
299 or return $cb->();
300
301 # use srv records then
302 @target = map ["$_->[3].", $_->[2]],
303 grep $_->[3] ne ".",
304 @srv;
305
306 &$resolve;
307 };
308 } else {
309 &$resolve;
310 }
311 }
312
313 #############################################################################
314
315 =back
316
317 =head2 LOW-LEVEL DNS EN-/DECODING FUNCTIONS
318
319 =over 4
320
321 =item $AnyEvent::DNS::EDNS0
322
323 This variable decides whether dns_pack automatically enables EDNS0
324 support. By default, this is disabled (C<0>), unless overriden by
325 C<$ENV{PERL_ANYEVENT_EDNS0>), but when set to C<1>, AnyEvent::DNS will use
326 EDNS0 in all requests.
327
328 =cut
329
330 our $EDNS0 = $ENV{PERL_ANYEVENT_EDNS0} * 1; # set to 1 to enable (partial) edns0
331
332 our %opcode_id = (
333 query => 0,
334 iquery => 1,
335 status => 2,
336 notify => 4,
337 update => 5,
338 map +($_ => $_), 3, 6..15
339 );
340
341 our %opcode_str = reverse %opcode_id;
342
343 our %rcode_id = (
344 noerror => 0,
345 formerr => 1,
346 servfail => 2,
347 nxdomain => 3,
348 notimp => 4,
349 refused => 5,
350 yxdomain => 6, # Name Exists when it should not [RFC 2136]
351 yxrrset => 7, # RR Set Exists when it should not [RFC 2136]
352 nxrrset => 8, # RR Set that should exist does not [RFC 2136]
353 notauth => 9, # Server Not Authoritative for zone [RFC 2136]
354 notzone => 10, # Name not contained in zone [RFC 2136]
355 # EDNS0 16 BADVERS Bad OPT Version [RFC 2671]
356 # EDNS0 16 BADSIG TSIG Signature Failure [RFC 2845]
357 # EDNS0 17 BADKEY Key not recognized [RFC 2845]
358 # EDNS0 18 BADTIME Signature out of time window [RFC 2845]
359 # EDNS0 19 BADMODE Bad TKEY Mode [RFC 2930]
360 # EDNS0 20 BADNAME Duplicate key name [RFC 2930]
361 # EDNS0 21 BADALG Algorithm not supported [RFC 2930]
362 map +($_ => $_), 11..15
363 );
364
365 our %rcode_str = reverse %rcode_id;
366
367 our %type_id = (
368 a => 1,
369 ns => 2,
370 md => 3,
371 mf => 4,
372 cname => 5,
373 soa => 6,
374 mb => 7,
375 mg => 8,
376 mr => 9,
377 null => 10,
378 wks => 11,
379 ptr => 12,
380 hinfo => 13,
381 minfo => 14,
382 mx => 15,
383 txt => 16,
384 aaaa => 28,
385 srv => 33,
386 opt => 41,
387 spf => 99,
388 tkey => 249,
389 tsig => 250,
390 ixfr => 251,
391 axfr => 252,
392 mailb => 253,
393 "*" => 255,
394 );
395
396 our %type_str = reverse %type_id;
397
398 our %class_id = (
399 in => 1,
400 ch => 3,
401 hs => 4,
402 none => 254,
403 "*" => 255,
404 );
405
406 our %class_str = reverse %class_id;
407
408 # names MUST have a trailing dot
409 sub _enc_qname($) {
410 pack "(C/a)*", (split /\./, shift), ""
411 }
412
413 sub _enc_qd() {
414 (_enc_qname $_->[0]) . pack "nn",
415 ($_->[1] > 0 ? $_->[1] : $type_id {$_->[1]}),
416 ($_->[2] > 0 ? $_->[2] : $class_id{$_->[2] || "in"})
417 }
418
419 sub _enc_rr() {
420 die "encoding of resource records is not supported";
421 }
422
423 =item $pkt = AnyEvent::DNS::dns_pack $dns
424
425 Packs a perl data structure into a DNS packet. Reading RFC1034 is strongly
426 recommended, then everything will be totally clear. Or maybe not.
427
428 Resource records are not yet encodable.
429
430 Examples:
431
432 # very simple request, using lots of default values:
433 { rd => 1, qd => [ [ "host.domain", "a"] ] }
434
435 # more complex example, showing how flags etc. are named:
436
437 {
438 id => 10000,
439 op => "query",
440 rc => "nxdomain",
441
442 # flags
443 qr => 1,
444 aa => 0,
445 tc => 0,
446 rd => 0,
447 ra => 0,
448 ad => 0,
449 cd => 0,
450
451 qd => [@rr], # query section
452 an => [@rr], # answer section
453 ns => [@rr], # authority section
454 ar => [@rr], # additional records section
455 }
456
457 =cut
458
459 sub dns_pack($) {
460 my ($req) = @_;
461
462 pack "nn nnnn a* a* a* a* a*",
463 $req->{id},
464
465 ! !$req->{qr} * 0x8000
466 + $opcode_id{$req->{op}} * 0x0800
467 + ! !$req->{aa} * 0x0400
468 + ! !$req->{tc} * 0x0200
469 + ! !$req->{rd} * 0x0100
470 + ! !$req->{ra} * 0x0080
471 + ! !$req->{ad} * 0x0020
472 + ! !$req->{cd} * 0x0010
473 + $rcode_id{$req->{rc}} * 0x0001,
474
475 scalar @{ $req->{qd} || [] },
476 scalar @{ $req->{an} || [] },
477 scalar @{ $req->{ns} || [] },
478 $EDNS0 + scalar @{ $req->{ar} || [] }, # include EDNS0 option here
479
480 (join "", map _enc_qd, @{ $req->{qd} || [] }),
481 (join "", map _enc_rr, @{ $req->{an} || [] }),
482 (join "", map _enc_rr, @{ $req->{ns} || [] }),
483 (join "", map _enc_rr, @{ $req->{ar} || [] }),
484
485 ($EDNS0 ? pack "C nnNn", 0, 41, 4096, 0, 0 : "") # EDNS0, 4kiB udp payload size
486 }
487
488 our $ofs;
489 our $pkt;
490
491 # bitches
492 sub _dec_qname {
493 my @res;
494 my $redir;
495 my $ptr = $ofs;
496 my $cnt;
497
498 while () {
499 return undef if ++$cnt >= 256; # to avoid DoS attacks
500
501 my $len = ord substr $pkt, $ptr++, 1;
502
503 if ($len & 0xc0) {
504 $ptr++;
505 $ofs = $ptr if $ptr > $ofs;
506 $ptr = (unpack "n", substr $pkt, $ptr - 2, 2) & 0x3fff;
507 } elsif ($len) {
508 push @res, substr $pkt, $ptr, $len;
509 $ptr += $len;
510 } else {
511 $ofs = $ptr if $ptr > $ofs;
512 return join ".", @res;
513 }
514 }
515 }
516
517 sub _dec_qd {
518 my $qname = _dec_qname;
519 my ($qt, $qc) = unpack "nn", substr $pkt, $ofs; $ofs += 4;
520 [$qname, $type_str{$qt} || $qt, $class_str{$qc} || $qc]
521 }
522
523 our %dec_rr = (
524 1 => sub { join ".", unpack "C4" }, # a
525 2 => sub { local $ofs = $ofs - length; _dec_qname }, # ns
526 5 => sub { local $ofs = $ofs - length; _dec_qname }, # cname
527 6 => sub {
528 local $ofs = $ofs - length;
529 my $mname = _dec_qname;
530 my $rname = _dec_qname;
531 ($mname, $rname, unpack "NNNNN", substr $pkt, $ofs)
532 }, # soa
533 11 => sub { ((join ".", unpack "C4"), unpack "C a*", substr $_, 4) }, # wks
534 12 => sub { local $ofs = $ofs - length; _dec_qname }, # ptr
535 13 => sub { unpack "C/a C/a", $_ }, # hinfo
536 15 => sub { local $ofs = $ofs + 2 - length; ((unpack "n", $_), _dec_qname) }, # mx
537 16 => sub { unpack "(C/a)*", $_ }, # txt
538 28 => sub { AnyEvent::Socket::format_ip ($_) }, # aaaa
539 33 => sub { local $ofs = $ofs + 6 - length; ((unpack "nnn", $_), _dec_qname) }, # srv
540 99 => sub { unpack "(C/a)*", $_ }, # spf
541 );
542
543 sub _dec_rr {
544 my $qname = _dec_qname;
545
546 my ($rt, $rc, $ttl, $rdlen) = unpack "nn N n", substr $pkt, $ofs; $ofs += 10;
547 local $_ = substr $pkt, $ofs, $rdlen; $ofs += $rdlen;
548
549 [
550 $qname,
551 $type_str{$rt} || $rt,
552 $class_str{$rc} || $rc,
553 ($dec_rr{$rt} || sub { $_ })->(),
554 ]
555 }
556
557 =item $dns = AnyEvent::DNS::dns_unpack $pkt
558
559 Unpacks a DNS packet into a perl data structure.
560
561 Examples:
562
563 # an unsuccessful reply
564 {
565 'qd' => [
566 [ 'ruth.plan9.de.mach.uni-karlsruhe.de', '*', 'in' ]
567 ],
568 'rc' => 'nxdomain',
569 'ar' => [],
570 'ns' => [
571 [
572 'uni-karlsruhe.de',
573 'soa',
574 'in',
575 'netserv.rz.uni-karlsruhe.de',
576 'hostmaster.rz.uni-karlsruhe.de',
577 2008052201, 10800, 1800, 2592000, 86400
578 ]
579 ],
580 'tc' => '',
581 'ra' => 1,
582 'qr' => 1,
583 'id' => 45915,
584 'aa' => '',
585 'an' => [],
586 'rd' => 1,
587 'op' => 'query'
588 }
589
590 # a successful reply
591
592 {
593 'qd' => [ [ 'www.google.de', 'a', 'in' ] ],
594 'rc' => 0,
595 'ar' => [
596 [ 'a.l.google.com', 'a', 'in', '209.85.139.9' ],
597 [ 'b.l.google.com', 'a', 'in', '64.233.179.9' ],
598 [ 'c.l.google.com', 'a', 'in', '64.233.161.9' ],
599 ],
600 'ns' => [
601 [ 'l.google.com', 'ns', 'in', 'a.l.google.com' ],
602 [ 'l.google.com', 'ns', 'in', 'b.l.google.com' ],
603 ],
604 'tc' => '',
605 'ra' => 1,
606 'qr' => 1,
607 'id' => 64265,
608 'aa' => '',
609 'an' => [
610 [ 'www.google.de', 'cname', 'in', 'www.google.com' ],
611 [ 'www.google.com', 'cname', 'in', 'www.l.google.com' ],
612 [ 'www.l.google.com', 'a', 'in', '66.249.93.104' ],
613 [ 'www.l.google.com', 'a', 'in', '66.249.93.147' ],
614 ],
615 'rd' => 1,
616 'op' => 0
617 }
618
619 =cut
620
621 sub dns_unpack($) {
622 local $pkt = shift;
623 my ($id, $flags, $qd, $an, $ns, $ar)
624 = unpack "nn nnnn A*", $pkt;
625
626 local $ofs = 6 * 2;
627
628 {
629 id => $id,
630 qr => ! ! ($flags & 0x8000),
631 aa => ! ! ($flags & 0x0400),
632 tc => ! ! ($flags & 0x0200),
633 rd => ! ! ($flags & 0x0100),
634 ra => ! ! ($flags & 0x0080),
635 ad => ! ! ($flags & 0x0020),
636 cd => ! ! ($flags & 0x0010),
637 op => $opcode_str{($flags & 0x001e) >> 11},
638 rc => $rcode_str{($flags & 0x000f)},
639
640 qd => [map _dec_qd, 1 .. $qd],
641 an => [map _dec_rr, 1 .. $an],
642 ns => [map _dec_rr, 1 .. $ns],
643 ar => [map _dec_rr, 1 .. $ar],
644 }
645 }
646
647 #############################################################################
648
649 =back
650
651 =head2 THE AnyEvent::DNS RESOLVER CLASS
652
653 This is the class which does the actual protocol work.
654
655 =over 4
656
657 =cut
658
659 use Carp ();
660 use Scalar::Util ();
661 use Socket ();
662
663 our $NOW;
664
665 =item AnyEvent::DNS::resolver
666
667 This function creates and returns a resolver that is ready to use and
668 should mimic the default resolver for your system as good as possible.
669
670 It only ever creates one resolver and returns this one on subsequent
671 calls.
672
673 Unless you have special needs, prefer this function over creating your own
674 resolver object.
675
676 =cut
677
678 our $RESOLVER;
679
680 sub resolver() {
681 $RESOLVER || do {
682 $RESOLVER = new AnyEvent::DNS;
683 $RESOLVER->os_config;
684 $RESOLVER
685 }
686 }
687
688 =item $resolver = new AnyEvent::DNS key => value...
689
690 Creates and returns a new resolver.
691
692 The following options are supported:
693
694 =over 4
695
696 =item server => [...]
697
698 A list of server addressses (default: C<v127.0.0.1>) in network format (4
699 octets for IPv4, 16 octets for IPv6 - not yet supported).
700
701 =item timeout => [...]
702
703 A list of timeouts to use (also determines the number of retries). To make
704 three retries with individual time-outs of 2, 5 and 5 seconds, use C<[2,
705 5, 5]>, which is also the default.
706
707 =item search => [...]
708
709 The default search list of suffixes to append to a domain name (default: none).
710
711 =item ndots => $integer
712
713 The number of dots (default: C<1>) that a name must have so that the resolver
714 tries to resolve the name without any suffixes first.
715
716 =item max_outstanding => $integer
717
718 Most name servers do not handle many parallel requests very well. This option
719 limits the numbe rof outstanding requests to C<$n> (default: C<10>), that means
720 if you request more than this many requests, then the additional requests will be queued
721 until some other requests have been resolved.
722
723 =item reuse => $seconds
724
725 The number of seconds (default: C<300>) that a query id cannot be re-used
726 after a timeout. If there as no time-out then query id's can be reused
727 immediately.
728
729 =back
730
731 =cut
732
733 sub new {
734 my ($class, %arg) = @_;
735
736 socket my $fh, &Socket::AF_INET, &Socket::SOCK_DGRAM, 0
737 or Carp::croak "socket: $!";
738
739 AnyEvent::Util::fh_nonblocking $fh, 1;
740
741 my $self = bless {
742 server => [v127.0.0.1],
743 timeout => [2, 5, 5],
744 search => [],
745 ndots => 1,
746 max_outstanding => 10,
747 reuse => 300, # reuse id's after 5 minutes only, if possible
748 %arg,
749 fh => $fh,
750 reuse_q => [],
751 }, $class;
752
753 # search should default to gethostname's domain
754 # but perl lacks a good posix module
755
756 Scalar::Util::weaken (my $wself = $self);
757 $self->{rw} = AnyEvent->io (fh => $fh, poll => "r", cb => sub { $wself->_recv });
758
759 $self->_compile;
760
761 $self
762 }
763
764 =item $resolver->parse_resolv_conv ($string)
765
766 Parses the given string a sif it were a F<resolv.conf> file. The following
767 directives are supported (but not neecssarily implemented).
768
769 C<#>-style comments, C<nameserver>, C<domain>, C<search>, C<sortlist>,
770 C<options> (C<timeout>, C<attempts>, C<ndots>).
771
772 Everything else is silently ignored.
773
774 =cut
775
776 sub parse_resolv_conf {
777 my ($self, $resolvconf) = @_;
778
779 $self->{server} = [];
780 $self->{search} = [];
781
782 my $attempts;
783
784 for (split /\n/, $resolvconf) {
785 if (/^\s*#/) {
786 # comment
787 } elsif (/^\s*nameserver\s+(\S+)\s*$/i) {
788 my $ip = $1;
789 if (AnyEvent::Util::dotted_quad $ip) {
790 push @{ $self->{server} }, AnyEvent::Util::socket_inet_aton $ip;
791 } else {
792 warn "nameserver $ip invalid and ignored\n";
793 }
794 } elsif (/^\s*domain\s+(\S*)\s+$/i) {
795 $self->{search} = [$1];
796 } elsif (/^\s*search\s+(.*?)\s*$/i) {
797 $self->{search} = [split /\s+/, $1];
798 } elsif (/^\s*sortlist\s+(.*?)\s*$/i) {
799 # ignored, NYI
800 } elsif (/^\s*options\s+(.*?)\s*$/i) {
801 for (split /\s+/, $1) {
802 if (/^timeout:(\d+)$/) {
803 $self->{timeout} = [$1];
804 } elsif (/^attempts:(\d+)$/) {
805 $attempts = $1;
806 } elsif (/^ndots:(\d+)$/) {
807 $self->{ndots} = $1;
808 } else {
809 # debug, rotate, no-check-names, inet6
810 }
811 }
812 }
813 }
814
815 $self->{timeout} = [($self->{timeout}[0]) x $attempts]
816 if $attempts;
817
818 $self->_compile;
819 }
820
821 =item $resolver->os_config
822
823 Tries so load and parse F</etc/resolv.conf> on portable opertaing systems. Tries various
824 egregious hacks on windows to force the dns servers and searchlist out of the config.
825
826 =cut
827
828 sub os_config {
829 my ($self) = @_;
830
831 if ($^O =~ /mswin32|cygwin/i) {
832 # yeah, it suxx... lets hope DNS is DNS in all locales
833
834 if (open my $fh, "ipconfig /all |") {
835 delete $self->{server};
836 delete $self->{search};
837
838 while (<$fh>) {
839 # first DNS.* is suffix list
840 if (/^\s*DNS/) {
841 while (/\s+([[:alnum:].\-]+)\s*$/) {
842 push @{ $self->{search} }, $1;
843 $_ = <$fh>;
844 }
845 last;
846 }
847 }
848
849 while (<$fh>) {
850 # second DNS.* is server address list
851 if (/^\s*DNS/) {
852 while (/\s+(\d+\.\d+\.\d+\.\d+)\s*$/) {
853 my $ip = $1;
854 push @{ $self->{server} }, AnyEvent::Util::socket_inet_aton $ip
855 if AnyEvent::Util::dotted_quad $ip;
856 $_ = <$fh>;
857 }
858 last;
859 }
860 }
861
862 $self->_compile;
863 }
864 } else {
865 # try resolv.conf everywhere
866
867 if (open my $fh, "</etc/resolv.conf") {
868 local $/;
869 $self->parse_resolv_conf (<$fh>);
870 }
871 }
872 }
873
874 sub _compile {
875 my $self = shift;
876
877 my @retry;
878
879 for my $timeout (@{ $self->{timeout} }) {
880 for my $server (@{ $self->{server} }) {
881 push @retry, [$server, $timeout];
882 }
883 }
884
885 $self->{retry} = \@retry;
886 }
887
888 sub _feed {
889 my ($self, $res) = @_;
890
891 $res = dns_unpack $res
892 or return;
893
894 my $id = $self->{id}{$res->{id}};
895
896 return unless ref $id;
897
898 $NOW = time;
899 $id->[1]->($res);
900 }
901
902 sub _recv {
903 my ($self) = @_;
904
905 while (my $peer = recv $self->{fh}, my $res, 4096, 0) {
906 my ($port, $host) = AnyEvent::Socket::unpack_sockaddr ($peer);
907
908 return unless $port == 53 && grep $_ eq $host, @{ $self->{server} };
909
910 $self->_feed ($res);
911 }
912 }
913
914 sub _free_id {
915 my ($self, $id, $timeout) = @_;
916
917 if ($timeout) {
918 # we need to block the id for a while
919 $self->{id}{$id} = 1;
920 push @{ $self->{reuse_q} }, [$NOW + $self->{reuse}, $id];
921 } else {
922 # we can quickly recycle the id
923 delete $self->{id}{$id};
924 }
925
926 --$self->{outstanding};
927 $self->_scheduler;
928 }
929
930 # execute a single request, involves sending it with timeouts to multiple servers
931 sub _exec {
932 my ($self, $req) = @_;
933
934 my $retry; # of retries
935 my $do_retry;
936
937 $do_retry = sub {
938 my $retry_cfg = $self->{retry}[$retry++]
939 or do {
940 # failure
941 $self->_free_id ($req->[2], $retry > 1);
942 undef $do_retry; return $req->[1]->();
943 };
944
945 my ($server, $timeout) = @$retry_cfg;
946
947 $self->{id}{$req->[2]} = [AnyEvent->timer (after => $timeout, cb => sub {
948 $NOW = time;
949
950 # timeout, try next
951 &$do_retry;
952 }), sub {
953 my ($res) = @_;
954
955 if ($res->{tc}) {
956 # success, but truncated, so use tcp
957 AnyEvent::Socket::tcp_connect ((Socket::inet_ntoa $server), 53, sub {
958 my ($fh) = @_
959 or return &$do_retry;
960
961 my $handle = new AnyEvent::Handle
962 fh => $fh,
963 on_error => sub {
964 # failure, try next
965 &$do_retry;
966 };
967
968 $handle->push_write (pack "n/a", $req->[0]);
969 $handle->push_read_chunk (2, sub {
970 $handle->unshift_read_chunk ((unpack "n", $_[1]), sub {
971 $self->_feed ($_[1]);
972 });
973 });
974 shutdown $fh, 1;
975
976 }, sub { $timeout });
977
978 } else {
979 # success
980 $self->_free_id ($req->[2], $retry > 1);
981 undef $do_retry; return $req->[1]->($res);
982 }
983 }];
984
985 send $self->{fh}, $req->[0], 0, AnyEvent::Socket::pack_sockaddr (53, $server);
986 };
987
988 &$do_retry;
989 }
990
991 sub _scheduler {
992 my ($self) = @_;
993
994 $NOW = time;
995
996 # first clear id reuse queue
997 delete $self->{id}{ (shift @{ $self->{reuse_q} })->[1] }
998 while @{ $self->{reuse_q} } && $self->{reuse_q}[0][0] <= $NOW;
999
1000 while ($self->{outstanding} < $self->{max_outstanding}) {
1001
1002 if (@{ $self->{reuse_q} } >= 30000) {
1003 warn "reusing id's, waiting ",$self->{reuse_q}[0][0] - $NOW;#d#
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> (a
1047 qtype of "*" is supported and means "any").
1048
1049 The callback will be invoked with a list of matching result records or
1050 none on any error or if the name could not be found.
1051
1052 CNAME chains (although illegal) are followed up to a length of 8.
1053
1054 Note that this resolver is just a stub resolver: it requires a nameserver
1055 supporting recursive queries, will not do any recursive queries itself and
1056 is not secure when used against an untrusted name server.
1057
1058 The following options are supported:
1059
1060 =over 4
1061
1062 =item search => [$suffix...]
1063
1064 Use the given search list (which might be empty), by appending each one
1065 in turn to the C<$qname>. If this option is missing then the configured
1066 C<ndots> and C<search> define its value. If the C<$qname> ends in a dot,
1067 then the searchlist will be ignored.
1068
1069 =item accept => [$type...]
1070
1071 Lists the acceptable result types: only result types in this set will be
1072 accepted and returned. The default includes the C<$qtype> and nothing
1073 else.
1074
1075 =item class => "class"
1076
1077 Specify the query class ("in" for internet, "ch" for chaosnet and "hs" for
1078 hesiod are the only ones making sense). The default is "in", of course.
1079
1080 =back
1081
1082 Examples:
1083
1084 $res->resolve ("ruth.plan9.de", "a", sub {
1085 warn Dumper [@_];
1086 });
1087
1088 [
1089 [
1090 'ruth.schmorp.de',
1091 'a',
1092 'in',
1093 '129.13.162.95'
1094 ]
1095 ]
1096
1097 $res->resolve ("test1.laendle", "*",
1098 accept => ["a", "aaaa"],
1099 sub {
1100 warn Dumper [@_];
1101 }
1102 );
1103
1104 [
1105 [
1106 'test1.laendle',
1107 'a',
1108 'in',
1109 '10.0.0.255'
1110 ],
1111 [
1112 'test1.laendle',
1113 'aaaa',
1114 'in',
1115 '3ffe:1900:4545:0002:0240:0000:0000:f7e1'
1116 ]
1117 ]
1118
1119 =cut
1120
1121 sub resolve($%) {
1122 my $cb = pop;
1123 my ($self, $qname, $qtype, %opt) = @_;
1124
1125 my @search = $qname =~ s/\.$//
1126 ? ""
1127 : $opt{search}
1128 ? @{ $opt{search} }
1129 : ($qname =~ y/.//) >= $self->{ndots}
1130 ? ("", @{ $self->{search} })
1131 : (@{ $self->{search} }, "");
1132
1133 my $class = $opt{class} || "in";
1134
1135 my %atype = $opt{accept}
1136 ? map +($_ => 1), @{ $opt{accept} }
1137 : ($qtype => 1);
1138
1139 # advance in searchlist
1140 my ($do_search, $do_req);
1141
1142 $do_search = sub {
1143 @search
1144 or (undef $do_search), (undef $do_req), return $cb->();
1145
1146 (my $name = lc "$qname." . shift @search) =~ s/\.$//;
1147 my $depth = 2;
1148
1149 # advance in cname-chain
1150 $do_req = sub {
1151 $self->request ({
1152 rd => 1,
1153 qd => [[$name, $qtype, $class]],
1154 }, sub {
1155 my ($res) = @_
1156 or return $do_search->();
1157
1158 my $cname;
1159
1160 while () {
1161 # results found?
1162 my @rr = grep $name eq lc $_->[0] && ($atype{"*"} || $atype{$_->[1]}), @{ $res->{an} };
1163
1164 (undef $do_search), (undef $do_req), return $cb->(@rr)
1165 if @rr;
1166
1167 # see if there is a cname we can follow
1168 my @rr = grep $name eq lc $_->[0] && $_->[1] eq "cname", @{ $res->{an} };
1169
1170 if (@rr) {
1171 $depth--
1172 or return $do_search->(); # cname chain too long
1173
1174 $cname = 1;
1175 $name = $rr[0][3];
1176
1177 } elsif ($cname) {
1178 # follow the cname
1179 return $do_req->();
1180
1181 } else {
1182 # no, not found anything
1183 return $do_search->();
1184 }
1185 }
1186 });
1187 };
1188
1189 $do_req->();
1190 };
1191
1192 $do_search->();
1193 }
1194
1195 use AnyEvent::Socket (); # circular dependency, so do not import anything and do it at the end
1196
1197 1;
1198
1199 =back
1200
1201 =head1 AUTHOR
1202
1203 Marc Lehmann <schmorp@schmorp.de>
1204 http://home.schmorp.de/
1205
1206 =cut
1207