… | |
… | |
5 | =head1 SYNOPSIS |
5 | =head1 SYNOPSIS |
6 | |
6 | |
7 | use AnyEvent::DNS; |
7 | use AnyEvent::DNS; |
8 | |
8 | |
9 | my $cv = AnyEvent->condvar; |
9 | my $cv = AnyEvent->condvar; |
10 | AnyEvent::DNS::a "www.google.de", sub { $cv->send (@_) }; |
10 | AnyEvent::DNS::a "www.google.de", $cv; |
11 | # ... later |
11 | # ... later |
12 | my @addrs = $cv->recv; |
12 | my @addrs = $cv->recv; |
13 | |
13 | |
14 | =head1 DESCRIPTION |
14 | =head1 DESCRIPTION |
15 | |
15 | |
… | |
… | |
29 | package AnyEvent::DNS; |
29 | package AnyEvent::DNS; |
30 | |
30 | |
31 | no warnings; |
31 | no warnings; |
32 | use strict; |
32 | use strict; |
33 | |
33 | |
|
|
34 | use Socket qw(AF_INET SOCK_DGRAM SOCK_STREAM); |
|
|
35 | |
|
|
36 | use AnyEvent (); |
34 | use AnyEvent::Handle (); |
37 | use AnyEvent::Handle (); |
|
|
38 | |
|
|
39 | our @DNS_FALLBACK = (v208.67.220.220, v208.67.222.222); |
35 | |
40 | |
36 | =item AnyEvent::DNS::addr $node, $service, $proto, $family, $type, $cb->([$family, $type, $proto, $sockaddr], ...) |
41 | =item AnyEvent::DNS::addr $node, $service, $proto, $family, $type, $cb->([$family, $type, $proto, $sockaddr], ...) |
37 | |
42 | |
38 | Tries to resolve the given nodename and service name into protocol families |
43 | 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 |
44 | and sockaddr structures usable to connect to this node and service in a |
… | |
… | |
199 | ############################################################################# |
204 | ############################################################################# |
200 | |
205 | |
201 | sub addr($$$$$$) { |
206 | sub addr($$$$$$) { |
202 | my ($node, $service, $proto, $family, $type, $cb) = @_; |
207 | my ($node, $service, $proto, $family, $type, $cb) = @_; |
203 | |
208 | |
204 | unless (&AnyEvent::Socket::AF_INET6) { |
209 | unless (&AnyEvent::Util::AF_INET6) { |
205 | $family != 6 |
210 | $family != 6 |
206 | or return $cb->(); |
211 | or return $cb->(); |
207 | |
212 | |
208 | $family ||= 4; |
213 | $family ||= 4; |
209 | } |
214 | } |
… | |
… | |
213 | |
218 | |
214 | $family ||=4 unless $AnyEvent::PROTOCOL{ipv6}; |
219 | $family ||=4 unless $AnyEvent::PROTOCOL{ipv6}; |
215 | $family ||=6 unless $AnyEvent::PROTOCOL{ipv4}; |
220 | $family ||=6 unless $AnyEvent::PROTOCOL{ipv4}; |
216 | |
221 | |
217 | $proto ||= "tcp"; |
222 | $proto ||= "tcp"; |
218 | $type ||= $proto eq "udp" ? Socket::SOCK_DGRAM : Socket::SOCK_STREAM; |
223 | $type ||= $proto eq "udp" ? SOCK_DGRAM : SOCK_STREAM; |
219 | |
224 | |
220 | my $proton = (getprotobyname $proto)[2] |
225 | my $proton = (getprotobyname $proto)[2] |
221 | or Carp::croak "$proto: protocol unknown"; |
226 | or Carp::croak "$proto: protocol unknown"; |
222 | |
227 | |
223 | my $port; |
228 | my $port; |
… | |
… | |
238 | my @res; |
243 | my @res; |
239 | my $cv = AnyEvent->condvar (cb => sub { |
244 | my $cv = AnyEvent->condvar (cb => sub { |
240 | $cb->( |
245 | $cb->( |
241 | map $_->[2], |
246 | map $_->[2], |
242 | sort { |
247 | sort { |
243 | $AnyEvent::PROTOCOL{$a->[1]} <=> $AnyEvent::PROTOCOL{$b->[1]} |
248 | $AnyEvent::PROTOCOL{$b->[1]} <=> $AnyEvent::PROTOCOL{$a->[1]} |
244 | or $a->[0] <=> $b->[0] |
249 | or $a->[0] <=> $b->[0] |
245 | } |
250 | } |
246 | @res |
251 | @res |
247 | ) |
252 | ) |
248 | }); |
253 | }); |
… | |
… | |
251 | for my $idx (0 .. $#target) { |
256 | for my $idx (0 .. $#target) { |
252 | my ($node, $port) = @{ $target[$idx] }; |
257 | my ($node, $port) = @{ $target[$idx] }; |
253 | |
258 | |
254 | if (my $noden = AnyEvent::Socket::parse_ip ($node)) { |
259 | if (my $noden = AnyEvent::Socket::parse_ip ($node)) { |
255 | if (4 == length $noden && $family != 6) { |
260 | if (4 == length $noden && $family != 6) { |
256 | push @res, [$idx, "ipv4", [Socket::AF_INET, $type, $proton, |
261 | push @res, [$idx, "ipv4", [AF_INET, $type, $proton, |
257 | AnyEvent::Socket::pack_sockaddr ($port, $noden)]] |
262 | AnyEvent::Socket::pack_sockaddr ($port, $noden)]] |
258 | } |
263 | } |
259 | |
264 | |
260 | if (16 == length $noden && $family != 4) { |
265 | if (16 == length $noden && $family != 4) { |
261 | push @res, [$idx, "ipv6", [&AnyEvent::Socket::AF_INET6, $type, $proton, |
266 | push @res, [$idx, "ipv6", [&AnyEvent::Util::AF_INET6, $type, $proton, |
262 | AnyEvent::Socket::pack_sockaddr ( $port, $noden)]] |
267 | AnyEvent::Socket::pack_sockaddr ( $port, $noden)]] |
263 | } |
268 | } |
264 | } else { |
269 | } else { |
265 | # ipv4 |
270 | # ipv4 |
266 | if ($family != 6) { |
271 | if ($family != 6) { |
267 | $cv->begin; |
272 | $cv->begin; |
268 | a $node, sub { |
273 | a $node, sub { |
269 | push @res, [$idx, "ipv4", [Socket::AF_INET, $type, $proton, |
274 | push @res, [$idx, "ipv4", [AF_INET, $type, $proton, |
270 | AnyEvent::Socket::pack_sockaddr ($port, AnyEvent::Socket::parse_ipv4 ($_))]] |
275 | AnyEvent::Socket::pack_sockaddr ($port, AnyEvent::Socket::parse_ipv4 ($_))]] |
271 | for @_; |
276 | for @_; |
272 | $cv->end; |
277 | $cv->end; |
273 | }; |
278 | }; |
274 | } |
279 | } |
… | |
… | |
410 | ); |
415 | ); |
411 | |
416 | |
412 | our %class_str = reverse %class_id; |
417 | our %class_str = reverse %class_id; |
413 | |
418 | |
414 | # names MUST have a trailing dot |
419 | # names MUST have a trailing dot |
415 | sub _enc_qname($) { |
420 | sub _enc_name($) { |
416 | pack "(C/a)*", (split /\./, shift), "" |
421 | pack "(C/a*)*", (split /\./, shift), "" |
417 | } |
422 | } |
418 | |
423 | |
419 | sub _enc_qd() { |
424 | sub _enc_qd() { |
420 | (_enc_qname $_->[0]) . pack "nn", |
425 | (_enc_name $_->[0]) . pack "nn", |
421 | ($_->[1] > 0 ? $_->[1] : $type_id {$_->[1]}), |
426 | ($_->[1] > 0 ? $_->[1] : $type_id {$_->[1]}), |
422 | ($_->[2] > 0 ? $_->[2] : $class_id{$_->[2] || "in"}) |
427 | ($_->[2] > 0 ? $_->[2] : $class_id{$_->[2] || "in"}) |
423 | } |
428 | } |
424 | |
429 | |
425 | sub _enc_rr() { |
430 | sub _enc_rr() { |
… | |
… | |
493 | |
498 | |
494 | our $ofs; |
499 | our $ofs; |
495 | our $pkt; |
500 | our $pkt; |
496 | |
501 | |
497 | # bitches |
502 | # bitches |
498 | sub _dec_qname { |
503 | sub _dec_name { |
499 | my @res; |
504 | my @res; |
500 | my $redir; |
505 | my $redir; |
501 | my $ptr = $ofs; |
506 | my $ptr = $ofs; |
502 | my $cnt; |
507 | my $cnt; |
503 | |
508 | |
504 | while () { |
509 | while () { |
505 | return undef if ++$cnt >= 256; # to avoid DoS attacks |
510 | return undef if ++$cnt >= 256; # to avoid DoS attacks |
506 | |
511 | |
507 | my $len = ord substr $pkt, $ptr++, 1; |
512 | my $len = ord substr $pkt, $ptr++, 1; |
508 | |
513 | |
509 | if ($len & 0xc0) { |
514 | if ($len >= 0xc0) { |
510 | $ptr++; |
515 | $ptr++; |
511 | $ofs = $ptr if $ptr > $ofs; |
516 | $ofs = $ptr if $ptr > $ofs; |
512 | $ptr = (unpack "n", substr $pkt, $ptr - 2, 2) & 0x3fff; |
517 | $ptr = (unpack "n", substr $pkt, $ptr - 2, 2) & 0x3fff; |
513 | } elsif ($len) { |
518 | } elsif ($len) { |
514 | push @res, substr $pkt, $ptr, $len; |
519 | push @res, substr $pkt, $ptr, $len; |
… | |
… | |
519 | } |
524 | } |
520 | } |
525 | } |
521 | } |
526 | } |
522 | |
527 | |
523 | sub _dec_qd { |
528 | sub _dec_qd { |
524 | my $qname = _dec_qname; |
529 | my $qname = _dec_name; |
525 | my ($qt, $qc) = unpack "nn", substr $pkt, $ofs; $ofs += 4; |
530 | my ($qt, $qc) = unpack "nn", substr $pkt, $ofs; $ofs += 4; |
526 | [$qname, $type_str{$qt} || $qt, $class_str{$qc} || $qc] |
531 | [$qname, $type_str{$qt} || $qt, $class_str{$qc} || $qc] |
527 | } |
532 | } |
528 | |
533 | |
529 | our %dec_rr = ( |
534 | our %dec_rr = ( |
530 | 1 => sub { join ".", unpack "C4" }, # a |
535 | 1 => sub { join ".", unpack "C4", $_ }, # a |
531 | 2 => sub { local $ofs = $ofs - length; _dec_qname }, # ns |
536 | 2 => sub { local $ofs = $ofs - length; _dec_name }, # ns |
532 | 5 => sub { local $ofs = $ofs - length; _dec_qname }, # cname |
537 | 5 => sub { local $ofs = $ofs - length; _dec_name }, # cname |
533 | 6 => sub { |
538 | 6 => sub { |
534 | local $ofs = $ofs - length; |
539 | local $ofs = $ofs - length; |
535 | my $mname = _dec_qname; |
540 | my $mname = _dec_name; |
536 | my $rname = _dec_qname; |
541 | my $rname = _dec_name; |
537 | ($mname, $rname, unpack "NNNNN", substr $pkt, $ofs) |
542 | ($mname, $rname, unpack "NNNNN", substr $pkt, $ofs) |
538 | }, # soa |
543 | }, # soa |
539 | 11 => sub { ((join ".", unpack "C4"), unpack "C a*", substr $_, 4) }, # wks |
544 | 11 => sub { ((join ".", unpack "C4", $_), unpack "C a*", substr $_, 4) }, # wks |
540 | 12 => sub { local $ofs = $ofs - length; _dec_qname }, # ptr |
545 | 12 => sub { local $ofs = $ofs - length; _dec_name }, # ptr |
541 | 13 => sub { unpack "C/a C/a", $_ }, # hinfo |
546 | 13 => sub { unpack "C/a* C/a*", $_ }, # hinfo |
542 | 15 => sub { local $ofs = $ofs + 2 - length; ((unpack "n", $_), _dec_qname) }, # mx |
547 | 15 => sub { local $ofs = $ofs + 2 - length; ((unpack "n", $_), _dec_name) }, # mx |
543 | 16 => sub { unpack "(C/a)*", $_ }, # txt |
548 | 16 => sub { unpack "(C/a*)*", $_ }, # txt |
544 | 28 => sub { AnyEvent::Socket::format_ip ($_) }, # aaaa |
549 | 28 => sub { AnyEvent::Socket::format_ip ($_) }, # aaaa |
545 | 33 => sub { local $ofs = $ofs + 6 - length; ((unpack "nnn", $_), _dec_qname) }, # srv |
550 | 33 => sub { local $ofs = $ofs + 6 - length; ((unpack "nnn", $_), _dec_name) }, # srv |
546 | 99 => sub { unpack "(C/a)*", $_ }, # spf |
551 | 99 => sub { unpack "(C/a*)*", $_ }, # spf |
547 | ); |
552 | ); |
548 | |
553 | |
549 | sub _dec_rr { |
554 | sub _dec_rr { |
550 | my $qname = _dec_qname; |
555 | my $name = _dec_name; |
551 | |
556 | |
552 | my ($rt, $rc, $ttl, $rdlen) = unpack "nn N n", substr $pkt, $ofs; $ofs += 10; |
557 | my ($rt, $rc, $ttl, $rdlen) = unpack "nn N n", substr $pkt, $ofs; $ofs += 10; |
553 | local $_ = substr $pkt, $ofs, $rdlen; $ofs += $rdlen; |
558 | local $_ = substr $pkt, $ofs, $rdlen; $ofs += $rdlen; |
554 | |
559 | |
555 | [ |
560 | [ |
556 | $qname, |
561 | $name, |
557 | $type_str{$rt} || $rt, |
562 | $type_str{$rt} || $rt, |
558 | $class_str{$rc} || $rc, |
563 | $class_str{$rc} || $rc, |
559 | ($dec_rr{$rt} || sub { $_ })->(), |
564 | ($dec_rr{$rt} || sub { $_ })->(), |
560 | ] |
565 | ] |
561 | } |
566 | } |
… | |
… | |
737 | =cut |
742 | =cut |
738 | |
743 | |
739 | sub new { |
744 | sub new { |
740 | my ($class, %arg) = @_; |
745 | my ($class, %arg) = @_; |
741 | |
746 | |
742 | socket my $fh, &Socket::AF_INET, &Socket::SOCK_DGRAM, 0 |
747 | socket my $fh, AF_INET, &Socket::SOCK_DGRAM, 0 |
743 | or Carp::croak "socket: $!"; |
748 | or Carp::croak "socket: $!"; |
744 | |
749 | |
745 | AnyEvent::Util::fh_nonblocking $fh, 1; |
750 | AnyEvent::Util::fh_nonblocking $fh, 1; |
746 | |
751 | |
747 | my $self = bless { |
752 | my $self = bless { |
748 | server => [v127.0.0.1], |
753 | server => [], |
749 | timeout => [2, 5, 5], |
754 | timeout => [2, 5, 5], |
750 | search => [], |
755 | search => [], |
751 | ndots => 1, |
756 | ndots => 1, |
752 | max_outstanding => 10, |
757 | max_outstanding => 10, |
753 | reuse => 300, # reuse id's after 5 minutes only, if possible |
758 | reuse => 300, # reuse id's after 5 minutes only, if possible |
… | |
… | |
832 | =cut |
837 | =cut |
833 | |
838 | |
834 | sub os_config { |
839 | sub os_config { |
835 | my ($self) = @_; |
840 | my ($self) = @_; |
836 | |
841 | |
837 | if ($^O =~ /mswin32|cygwin/i) { |
842 | $self->{server} = []; |
838 | # yeah, it suxx... lets hope DNS is DNS in all locales |
843 | $self->{search} = []; |
|
|
844 | |
|
|
845 | if (AnyEvent::WIN32 || $^O =~ /cygwin/i) { |
|
|
846 | no strict 'refs'; |
|
|
847 | |
|
|
848 | # there are many options to find the current nameservers etc. on windows |
|
|
849 | # all of them don't work consistently: |
|
|
850 | # - the registry thing needs separate code on win32 native vs. cygwin |
|
|
851 | # - the registry layout differs between windows versions |
|
|
852 | # - calling windows api functions doesn't work on cygwin |
|
|
853 | # - ipconfig uses locale-specific messages |
|
|
854 | |
|
|
855 | # we use ipconfig parsing because, despite all it's brokenness, |
|
|
856 | # it seems most stable in practise. |
|
|
857 | # for good measure, we append a fallback nameserver to our list. |
839 | |
858 | |
840 | if (open my $fh, "ipconfig /all |") { |
859 | if (open my $fh, "ipconfig /all |") { |
841 | delete $self->{server}; |
860 | # parsing strategy: we go through the output and look for |
842 | delete $self->{search}; |
861 | # :-lines with DNS in them. everything in those is regarded as |
|
|
862 | # either a nameserver (if it parses as an ip address), or a suffix |
|
|
863 | # (all else). |
843 | |
864 | |
|
|
865 | my $dns; |
844 | while (<$fh>) { |
866 | while (<$fh>) { |
845 | # first DNS.* is suffix list |
867 | if (s/^\s.*\bdns\b.*://i) { |
846 | if (/^\s*DNS/) { |
868 | $dns = 1; |
847 | while (/\s+([[:alnum:].\-]+)\s*$/) { |
869 | } elsif (/^\S/ || /^\s[^:]{16,}: /) { |
|
|
870 | $dns = 0; |
|
|
871 | } |
|
|
872 | if ($dns && /^\s*(\S+)\s*$/) { |
|
|
873 | my $s = $1; |
|
|
874 | $s =~ s/%\d+(?!\S)//; # get rid of scope id |
|
|
875 | if (my $ipn = AnyEvent::Socket::parse_ip ($s)) { |
|
|
876 | push @{ $self->{server} }, $ipn; |
|
|
877 | } else { |
848 | push @{ $self->{search} }, $1; |
878 | push @{ $self->{search} }, $s; |
849 | $_ = <$fh>; |
|
|
850 | } |
879 | } |
851 | last; |
|
|
852 | } |
880 | } |
853 | } |
881 | } |
854 | |
882 | |
855 | while (<$fh>) { |
883 | # always add one fallback server |
856 | # second DNS.* is server address list |
884 | push @{ $self->{server} }, $DNS_FALLBACK[rand @DNS_FALLBACK]; |
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 | |
885 | |
868 | $self->_compile; |
886 | $self->_compile; |
869 | } |
887 | } |
870 | } else { |
888 | } else { |
871 | # try resolv.conf everywhere |
889 | # try resolv.conf everywhere |
… | |
… | |
878 | } |
896 | } |
879 | |
897 | |
880 | sub _compile { |
898 | sub _compile { |
881 | my $self = shift; |
899 | my $self = shift; |
882 | |
900 | |
|
|
901 | # we currently throw away all ipv6 nameservers, we do not yet support those |
|
|
902 | |
|
|
903 | my %search; $self->{search} = [grep 0 < length, grep !$search{$_}++, @{ $self->{search} }]; |
|
|
904 | my %server; $self->{server} = [grep 4 == length, grep !$server{$_}++, @{ $self->{server} }]; |
|
|
905 | |
|
|
906 | unless (@{ $self->{server} }) { |
|
|
907 | # use 127.0.0.1 by default, and one opendns nameserver as fallback |
|
|
908 | $self->{server} = [v127.0.0.1, $DNS_FALLBACK[rand @DNS_FALLBACK]]; |
|
|
909 | } |
|
|
910 | |
883 | my @retry; |
911 | my @retry; |
884 | |
912 | |
885 | for my $timeout (@{ $self->{timeout} }) { |
913 | for my $timeout (@{ $self->{timeout} }) { |
886 | for my $server (@{ $self->{server} }) { |
914 | for my $server (@{ $self->{server} }) { |
887 | push @retry, [$server, $timeout]; |
915 | push @retry, [$server, $timeout]; |
… | |
… | |
906 | } |
934 | } |
907 | |
935 | |
908 | sub _recv { |
936 | sub _recv { |
909 | my ($self) = @_; |
937 | my ($self) = @_; |
910 | |
938 | |
|
|
939 | # we ignore errors (often one gets port unreachable, but there is |
|
|
940 | # no good way to take advantage of that. |
911 | while (my $peer = recv $self->{fh}, my $res, 4096, 0) { |
941 | while (my $peer = recv $self->{fh}, my $res, 4096, 0) { |
912 | my ($port, $host) = AnyEvent::Socket::unpack_sockaddr ($peer); |
942 | my ($port, $host) = AnyEvent::Socket::unpack_sockaddr ($peer); |
913 | |
943 | |
914 | return unless $port == 53 && grep $_ eq $host, @{ $self->{server} }; |
944 | return unless $port == 53 && grep $_ eq $host, @{ $self->{server} }; |
915 | |
945 | |
… | |
… | |
970 | # failure, try next |
1000 | # failure, try next |
971 | &$do_retry; |
1001 | &$do_retry; |
972 | }; |
1002 | }; |
973 | |
1003 | |
974 | $handle->push_write (pack "n/a", $req->[0]); |
1004 | $handle->push_write (pack "n/a", $req->[0]); |
975 | $handle->push_read_chunk (2, sub { |
1005 | $handle->push_read (chunk => 2, sub { |
976 | $handle->unshift_read_chunk ((unpack "n", $_[1]), sub { |
1006 | $handle->unshift_read (chunk => (unpack "n", $_[1]), sub { |
977 | $self->_feed ($_[1]); |
1007 | $self->_feed ($_[1]); |
978 | }); |
1008 | }); |
979 | }); |
1009 | }); |
980 | shutdown $fh, 1; |
1010 | shutdown $fh, 1; |
981 | |
1011 | |