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