ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent/lib/AnyEvent/DNS.pm
Revision: 1.21
Committed: Sat May 24 01:15:19 2008 UTC (16 years, 1 month ago) by root
Branch: MAIN
Changes since 1.20: +25 -27 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 =head1 NAME
2
3 AnyEvent::DNS - fully asynchronous DNS resolution
4
5 =head1 SYNOPSIS
6
7 use AnyEvent::DNS;
8
9 =head1 DESCRIPTION
10
11 This module offers both a number of DNS convenience functions as well
12 as a fully asynchronous and high-performance pure-perl stub resolver.
13
14 The stub resolver supports DNS over UDP, optional EDNS0 support for up to
15 4kiB datagrams and automatically falls back to virtual circuit mode for
16 large responses.
17
18 =head2 CONVENIENCE FUNCTIONS
19
20 =over 4
21
22 =cut
23
24 package AnyEvent::DNS;
25
26 no warnings;
27 use strict;
28
29 use AnyEvent::Handle ();
30
31 =item AnyEvent::DNS::addr $node, $service, $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<60>) that a query id cannot be re-used
726 after a request. Since AnyEvent::DNS will only allocate up to 30000 ID's
727 at the same time, the long-term maximum number of requests per second is
728 C<30000 / $seconds> (and thus C<500> requests/s by default).
729
730 =back
731
732 =cut
733
734 sub new {
735 my ($class, %arg) = @_;
736
737 socket my $fh, &Socket::AF_INET, &Socket::SOCK_DGRAM, 0
738 or Carp::croak "socket: $!";
739
740 AnyEvent::Util::fh_nonblocking $fh, 1;
741
742 my $self = bless {
743 server => [v127.0.0.1],
744 timeout => [2, 5, 5],
745 search => [],
746 ndots => 1,
747 max_outstanding => 10,
748 reuse => 60, # reuse id's after 5 minutes only, if possible
749 %arg,
750 fh => $fh,
751 reuse_q => [],
752 }, $class;
753
754 # search should default to gethostname's domain
755 # but perl lacks a good posix module
756
757 Scalar::Util::weaken (my $wself = $self);
758 $self->{rw} = AnyEvent->io (fh => $fh, poll => "r", cb => sub { $wself->_recv });
759
760 $self->_compile;
761
762 $self
763 }
764
765 =item $resolver->parse_resolv_conv ($string)
766
767 Parses the given string a sif it were a F<resolv.conf> file. The following
768 directives are supported (but not neecssarily implemented).
769
770 C<#>-style comments, C<nameserver>, C<domain>, C<search>, C<sortlist>,
771 C<options> (C<timeout>, C<attempts>, C<ndots>).
772
773 Everything else is silently ignored.
774
775 =cut
776
777 sub parse_resolv_conf {
778 my ($self, $resolvconf) = @_;
779
780 $self->{server} = [];
781 $self->{search} = [];
782
783 my $attempts;
784
785 for (split /\n/, $resolvconf) {
786 if (/^\s*#/) {
787 # comment
788 } elsif (/^\s*nameserver\s+(\S+)\s*$/i) {
789 my $ip = $1;
790 if (AnyEvent::Util::dotted_quad $ip) {
791 push @{ $self->{server} }, AnyEvent::Util::socket_inet_aton $ip;
792 } else {
793 warn "nameserver $ip invalid and ignored\n";
794 }
795 } elsif (/^\s*domain\s+(\S*)\s+$/i) {
796 $self->{search} = [$1];
797 } elsif (/^\s*search\s+(.*?)\s*$/i) {
798 $self->{search} = [split /\s+/, $1];
799 } elsif (/^\s*sortlist\s+(.*?)\s*$/i) {
800 # ignored, NYI
801 } elsif (/^\s*options\s+(.*?)\s*$/i) {
802 for (split /\s+/, $1) {
803 if (/^timeout:(\d+)$/) {
804 $self->{timeout} = [$1];
805 } elsif (/^attempts:(\d+)$/) {
806 $attempts = $1;
807 } elsif (/^ndots:(\d+)$/) {
808 $self->{ndots} = $1;
809 } else {
810 # debug, rotate, no-check-names, inet6
811 }
812 }
813 }
814 }
815
816 $self->{timeout} = [($self->{timeout}[0]) x $attempts]
817 if $attempts;
818
819 $self->_compile;
820 }
821
822 =item $resolver->os_config
823
824 Tries so load and parse F</etc/resolv.conf> on portable opertaing systems. Tries various
825 egregious hacks on windows to force the dns servers and searchlist out of the config.
826
827 =cut
828
829 sub os_config {
830 my ($self) = @_;
831
832 if ($^O =~ /mswin32|cygwin/i) {
833 # yeah, it suxx... lets hope DNS is DNS in all locales
834
835 if (open my $fh, "ipconfig /all |") {
836 delete $self->{server};
837 delete $self->{search};
838
839 while (<$fh>) {
840 # first DNS.* is suffix list
841 if (/^\s*DNS/) {
842 while (/\s+([[:alnum:].\-]+)\s*$/) {
843 push @{ $self->{search} }, $1;
844 $_ = <$fh>;
845 }
846 last;
847 }
848 }
849
850 while (<$fh>) {
851 # second DNS.* is server address list
852 if (/^\s*DNS/) {
853 while (/\s+(\d+\.\d+\.\d+\.\d+)\s*$/) {
854 my $ip = $1;
855 push @{ $self->{server} }, AnyEvent::Util::socket_inet_aton $ip
856 if AnyEvent::Util::dotted_quad $ip;
857 $_ = <$fh>;
858 }
859 last;
860 }
861 }
862
863 $self->_compile;
864 }
865 } else {
866 # try resolv.conf everywhere
867
868 if (open my $fh, "</etc/resolv.conf") {
869 local $/;
870 $self->parse_resolv_conf (<$fh>);
871 }
872 }
873 }
874
875 sub _compile {
876 my $self = shift;
877
878 my @retry;
879
880 for my $timeout (@{ $self->{timeout} }) {
881 for my $server (@{ $self->{server} }) {
882 push @retry, [$server, $timeout];
883 }
884 }
885
886 $self->{retry} = \@retry;
887 }
888
889 sub _feed {
890 my ($self, $res) = @_;
891
892 $res = dns_unpack $res
893 or return;
894
895 my $id = $self->{id}{$res->{id}};
896
897 return unless ref $id;
898
899 $NOW = time;
900 $id->[1]->($res);
901 }
902
903 sub _recv {
904 my ($self) = @_;
905
906 while (my $peer = recv $self->{fh}, my $res, 4096, 0) {
907 my ($port, $host) = AnyEvent::Socket::unpack_sockaddr ($peer);
908
909 return unless $port == 53 && grep $_ eq $host, @{ $self->{server} };
910
911 $self->_feed ($res);
912 }
913 }
914
915 sub _exec {
916 my ($self, $req, $retry) = @_;
917
918 if (my $retry_cfg = $self->{retry}[$retry]) {
919 my ($server, $timeout) = @$retry_cfg;
920
921 $self->{id}{$req->[2]} = [AnyEvent->timer (after => $timeout, cb => sub {
922 $NOW = time;
923
924 # timeout, try next
925 $self->_exec ($req, $retry + 1);
926 }), sub {
927 my ($res) = @_;
928
929 if ($res->{tc}) {
930 # success, but truncated, so use tcp
931 AnyEvent::Socket::tcp_connect ((Socket::inet_ntoa $server), 53, sub {
932 my ($fh) = @_
933 or return $self->_exec ($req, $retry + 1);
934
935 my $handle = new AnyEvent::Handle
936 fh => $fh,
937 on_error => sub {
938 # failure, try next
939 $self->_exec ($req, $retry + 1);
940 };
941
942 $handle->push_write (pack "n/a", $req->[0]);
943 $handle->push_read_chunk (2, sub {
944 $handle->unshift_read_chunk ((unpack "n", $_[1]), sub {
945 $self->_feed ($_[1]);
946 });
947 });
948 shutdown $fh, 1;
949
950 }, sub { $timeout });
951
952 } else {
953 # success
954 $self->{id}{$req->[2]} = 1;
955 push @{ $self->{reuse_q} }, [$NOW + $self->{reuse}, $req->[2]];
956 --$self->{outstanding};
957 $self->_scheduler;
958
959 $req->[1]->($res);
960 }
961 }];
962
963 send $self->{fh}, $req->[0], 0, AnyEvent::Socket::pack_sockaddr (53, $server);
964 } else {
965 # failure
966 $self->{id}{$req->[2]} = 1;
967 push @{ $self->{reuse_q} }, [$NOW + $self->{reuse}, $req->[2]];
968 --$self->{outstanding};
969 $self->_scheduler;
970
971 $req->[1]->();
972 }
973 }
974
975 sub _scheduler {
976 my ($self) = @_;
977
978 $NOW = time;
979
980 # first clear id reuse queue
981 delete $self->{id}{ (shift @{ $self->{reuse_q} })->[1] }
982 while @{ $self->{reuse_q} } && $self->{reuse_q}[0][0] <= $NOW;
983
984 while ($self->{outstanding} < $self->{max_outstanding}) {
985
986 if (@{ $self->{reuse_q} } >= 30000) {
987 # we ran out of ID's, wait a bit
988 $self->{reuse_to} ||= AnyEvent->timer (after => $self->{reuse_q}[0][0] - $NOW, cb => sub {
989 delete $self->{reuse_to};
990 $self->_scheduler;
991 });
992 last;
993 }
994
995 my $req = shift @{ $self->{queue} }
996 or last;
997
998 while () {
999 $req->[2] = int rand 65536;
1000 last unless exists $self->{id}{$req->[2]};
1001 }
1002
1003 $self->{id}{$req->[2]} = 1;
1004 substr $req->[0], 0, 2, pack "n", $req->[2];
1005
1006 ++$self->{outstanding};
1007 $self->_exec ($req, 0);
1008 }
1009 }
1010
1011 =item $resolver->request ($req, $cb->($res))
1012
1013 Sends a single request (a hash-ref formated as specified for
1014 C<dns_pack>) to the configured nameservers including
1015 retries. Calls the callback with the decoded response packet if a reply
1016 was received, or no arguments on timeout.
1017
1018 =cut
1019
1020 sub request($$) {
1021 my ($self, $req, $cb) = @_;
1022
1023 push @{ $self->{queue} }, [dns_pack $req, $cb];
1024 $self->_scheduler;
1025 }
1026
1027 =item $resolver->resolve ($qname, $qtype, %options, $cb->($rcode, @rr))
1028
1029 Queries the DNS for the given domain name C<$qname> of type C<$qtype> (a
1030 qtype of "*" is supported and means "any").
1031
1032 The callback will be invoked with a list of matching result records or
1033 none on any error or if the name could not be found.
1034
1035 CNAME chains (although illegal) are followed up to a length of 8.
1036
1037 Note that this resolver is just a stub resolver: it requires a nameserver
1038 supporting recursive queries, will not do any recursive queries itself and
1039 is not secure when used against an untrusted name server.
1040
1041 The following options are supported:
1042
1043 =over 4
1044
1045 =item search => [$suffix...]
1046
1047 Use the given search list (which might be empty), by appending each one
1048 in turn to the C<$qname>. If this option is missing then the configured
1049 C<ndots> and C<search> define its value. If the C<$qname> ends in a dot,
1050 then the searchlist will be ignored.
1051
1052 =item accept => [$type...]
1053
1054 Lists the acceptable result types: only result types in this set will be
1055 accepted and returned. The default includes the C<$qtype> and nothing
1056 else.
1057
1058 =item class => "class"
1059
1060 Specify the query class ("in" for internet, "ch" for chaosnet and "hs" for
1061 hesiod are the only ones making sense). The default is "in", of course.
1062
1063 =back
1064
1065 Examples:
1066
1067 $res->resolve ("ruth.plan9.de", "a", sub {
1068 warn Dumper [@_];
1069 });
1070
1071 [
1072 [
1073 'ruth.schmorp.de',
1074 'a',
1075 'in',
1076 '129.13.162.95'
1077 ]
1078 ]
1079
1080 $res->resolve ("test1.laendle", "*",
1081 accept => ["a", "aaaa"],
1082 sub {
1083 warn Dumper [@_];
1084 }
1085 );
1086
1087 [
1088 [
1089 'test1.laendle',
1090 'a',
1091 'in',
1092 '10.0.0.255'
1093 ],
1094 [
1095 'test1.laendle',
1096 'aaaa',
1097 'in',
1098 '3ffe:1900:4545:0002:0240:0000:0000:f7e1'
1099 ]
1100 ]
1101
1102 =cut
1103
1104 sub resolve($%) {
1105 my $cb = pop;
1106 my ($self, $qname, $qtype, %opt) = @_;
1107
1108 my @search = $qname =~ s/\.$//
1109 ? ""
1110 : $opt{search}
1111 ? @{ $opt{search} }
1112 : ($qname =~ y/.//) >= $self->{ndots}
1113 ? ("", @{ $self->{search} })
1114 : (@{ $self->{search} }, "");
1115
1116 my $class = $opt{class} || "in";
1117
1118 my %atype = $opt{accept}
1119 ? map +($_ => 1), @{ $opt{accept} }
1120 : ($qtype => 1);
1121
1122 # advance in searchlist
1123 my $do_search; $do_search = sub {
1124 @search
1125 or return $cb->();
1126
1127 (my $name = lc "$qname." . shift @search) =~ s/\.$//;
1128 my $depth = 2;
1129
1130 # advance in cname-chain
1131 my $do_req; $do_req = sub {
1132 $self->request ({
1133 rd => 1,
1134 qd => [[$name, $qtype, $class]],
1135 }, sub {
1136 my ($res) = @_
1137 or return $do_search->();
1138
1139 my $cname;
1140
1141 while () {
1142 # results found?
1143 my @rr = grep $name eq lc $_->[0] && ($atype{"*"} || $atype{$_->[1]}), @{ $res->{an} };
1144
1145 return $cb->(@rr)
1146 if @rr;
1147
1148 # see if there is a cname we can follow
1149 my @rr = grep $name eq lc $_->[0] && $_->[1] eq "cname", @{ $res->{an} };
1150
1151 if (@rr) {
1152 $depth--
1153 or return $do_search->(); # cname chain too long
1154
1155 $cname = 1;
1156 $name = $rr[0][3];
1157
1158 } elsif ($cname) {
1159 # follow the cname
1160 return $do_req->();
1161
1162 } else {
1163 # no, not found anything
1164 return $do_search->();
1165 }
1166 }
1167 });
1168 };
1169
1170 $do_req->();
1171 };
1172
1173 $do_search->();
1174 }
1175
1176 use AnyEvent::Socket (); # circular dependency, so do not import anything and do it at the end
1177
1178 1;
1179
1180 =back
1181
1182 =head1 AUTHOR
1183
1184 Marc Lehmann <schmorp@schmorp.de>
1185 http://home.schmorp.de/
1186
1187 =cut
1188