… | |
… | |
33 | |
33 | |
34 | use Socket qw(AF_INET SOCK_DGRAM SOCK_STREAM); |
34 | use Socket qw(AF_INET SOCK_DGRAM SOCK_STREAM); |
35 | |
35 | |
36 | use AnyEvent (); |
36 | use AnyEvent (); |
37 | use AnyEvent::Handle (); |
37 | use AnyEvent::Handle (); |
|
|
38 | use AnyEvent::Util qw(AF_INET6); |
|
|
39 | |
|
|
40 | our $VERSION = '1.0'; |
38 | |
41 | |
39 | our @DNS_FALLBACK = (v208.67.220.220, v208.67.222.222); |
42 | our @DNS_FALLBACK = (v208.67.220.220, v208.67.222.222); |
40 | |
43 | |
41 | =item AnyEvent::DNS::a $domain, $cb->(@addrs) |
44 | =item AnyEvent::DNS::a $domain, $cb->(@addrs) |
42 | |
45 | |
… | |
… | |
89 | |
92 | |
90 | Tries to resolve the given domain and passes all resource records found to |
93 | Tries to resolve the given domain and passes all resource records found to |
91 | the callback. |
94 | the callback. |
92 | |
95 | |
93 | =cut |
96 | =cut |
|
|
97 | |
|
|
98 | sub MAX_PKT() { 4096 } # max packet size we advertise and accept |
|
|
99 | |
|
|
100 | sub DOMAIN_PORT() { 53 } # if this changes drop me a note |
94 | |
101 | |
95 | sub resolver; |
102 | sub resolver; |
96 | |
103 | |
97 | sub a($$) { |
104 | sub a($$) { |
98 | my ($domain, $cb) = @_; |
105 | my ($domain, $cb) = @_; |
… | |
… | |
147 | my ($ip, $cb) = @_; |
154 | my ($ip, $cb) = @_; |
148 | |
155 | |
149 | $ip = AnyEvent::Socket::parse_address ($ip) |
156 | $ip = AnyEvent::Socket::parse_address ($ip) |
150 | or return $cb->(); |
157 | or return $cb->(); |
151 | |
158 | |
152 | if (4 == length $ip) { |
159 | my $af = AnyEvent::Socket::address_family ($ip); |
|
|
160 | |
|
|
161 | if ($af == AF_INET) { |
153 | $ip = join ".", (reverse split /\./, $ip), "in-addr.arpa."; |
162 | $ip = join ".", (reverse split /\./, $ip), "in-addr.arpa."; |
|
|
163 | } elsif ($af == AF_INET6) { |
|
|
164 | $ip = join ".", (reverse split //, unpack "H*", $ip), "ip6.arpa."; |
154 | } else { |
165 | } else { |
155 | $ip = join ".", (reverse split //, unpack "H*", $ip), "ip6.arpa."; |
166 | return $cb->(); |
156 | } |
167 | } |
157 | |
168 | |
158 | resolver->resolve ($ip => "ptr", sub { |
169 | resolver->resolve ($ip => "ptr", sub { |
159 | $cb->(map $_->[3], @_); |
170 | $cb->(map $_->[3], @_); |
160 | }); |
171 | }); |
… | |
… | |
336 | (join "", map _enc_qd, @{ $req->{qd} || [] }), |
347 | (join "", map _enc_qd, @{ $req->{qd} || [] }), |
337 | (join "", map _enc_rr, @{ $req->{an} || [] }), |
348 | (join "", map _enc_rr, @{ $req->{an} || [] }), |
338 | (join "", map _enc_rr, @{ $req->{ns} || [] }), |
349 | (join "", map _enc_rr, @{ $req->{ns} || [] }), |
339 | (join "", map _enc_rr, @{ $req->{ar} || [] }), |
350 | (join "", map _enc_rr, @{ $req->{ar} || [] }), |
340 | |
351 | |
341 | ($EDNS0 ? pack "C nnNn", 0, 41, 4096, 0, 0 : "") # EDNS0, 4kiB udp payload size |
352 | ($EDNS0 ? pack "C nnNn", 0, 41, MAX_PKT, 0, 0 : "") # EDNS0, 4kiB udp payload size |
342 | } |
353 | } |
343 | |
354 | |
344 | our $ofs; |
355 | our $ofs; |
345 | our $pkt; |
356 | our $pkt; |
346 | |
357 | |
… | |
… | |
587 | =cut |
598 | =cut |
588 | |
599 | |
589 | sub new { |
600 | sub new { |
590 | my ($class, %arg) = @_; |
601 | my ($class, %arg) = @_; |
591 | |
602 | |
|
|
603 | # try to create a ipv4 and an ipv6 socket |
|
|
604 | # only fail when we cnanot create either |
|
|
605 | |
592 | socket my $fh, AF_INET, &Socket::SOCK_DGRAM, 0 |
606 | socket my $fh4, AF_INET , &Socket::SOCK_DGRAM, 0; |
593 | or Carp::croak "socket: $!"; |
607 | socket my $fh6, AF_INET6, &Socket::SOCK_DGRAM, 0; |
594 | |
608 | |
595 | AnyEvent::Util::fh_nonblocking $fh, 1; |
609 | $fh4 || $fh6 |
|
|
610 | or Carp::croak "unable to create either an IPv6 or an IPv4 socket"; |
596 | |
611 | |
597 | my $self = bless { |
612 | my $self = bless { |
598 | server => [], |
613 | server => [], |
599 | timeout => [2, 5, 5], |
614 | timeout => [2, 5, 5], |
600 | search => [], |
615 | search => [], |
601 | ndots => 1, |
616 | ndots => 1, |
602 | max_outstanding => 10, |
617 | max_outstanding => 10, |
603 | reuse => 300, # reuse id's after 5 minutes only, if possible |
618 | reuse => 300, # reuse id's after 5 minutes only, if possible |
604 | %arg, |
619 | %arg, |
605 | fh => $fh, |
|
|
606 | reuse_q => [], |
620 | reuse_q => [], |
607 | }, $class; |
621 | }, $class; |
608 | |
622 | |
609 | # search should default to gethostname's domain |
623 | # search should default to gethostname's domain |
610 | # but perl lacks a good posix module |
624 | # but perl lacks a good posix module |
611 | |
625 | |
612 | Scalar::Util::weaken (my $wself = $self); |
626 | Scalar::Util::weaken (my $wself = $self); |
|
|
627 | |
|
|
628 | if ($fh4) { |
|
|
629 | AnyEvent::Util::fh_nonblocking $fh4, 1; |
|
|
630 | $self->{fh4} = $fh4; |
613 | $self->{rw} = AnyEvent->io (fh => $fh, poll => "r", cb => sub { $wself->_recv }); |
631 | $self->{rw4} = AnyEvent->io (fh => $fh4, poll => "r", cb => sub { |
|
|
632 | if (my $peer = recv $fh4, my $pkt, MAX_PKT, 0) { |
|
|
633 | $wself->_recv ($pkt, $peer); |
|
|
634 | } |
|
|
635 | }); |
|
|
636 | } |
|
|
637 | |
|
|
638 | if ($fh6) { |
|
|
639 | $self->{fh6} = $fh6; |
|
|
640 | AnyEvent::Util::fh_nonblocking $fh6, 1; |
|
|
641 | $self->{rw6} = AnyEvent->io (fh => $fh6, poll => "r", cb => sub { |
|
|
642 | if (my $peer = recv $fh6, my $pkt, MAX_PKT, 0) { |
|
|
643 | $wself->_recv ($pkt, $peer); |
|
|
644 | } |
|
|
645 | }); |
|
|
646 | } |
614 | |
647 | |
615 | $self->_compile; |
648 | $self->_compile; |
616 | |
649 | |
617 | $self |
650 | $self |
618 | } |
651 | } |
… | |
… | |
741 | } |
774 | } |
742 | |
775 | |
743 | sub _compile { |
776 | sub _compile { |
744 | my $self = shift; |
777 | my $self = shift; |
745 | |
778 | |
746 | # we currently throw away all ipv6 nameservers, we do not yet support those |
|
|
747 | |
|
|
748 | my %search; $self->{search} = [grep 0 < length, grep !$search{$_}++, @{ $self->{search} }]; |
779 | my %search; $self->{search} = [grep 0 < length, grep !$search{$_}++, @{ $self->{search} }]; |
749 | my %server; $self->{server} = [grep 4 == length, grep !$server{$_}++, @{ $self->{server} }]; |
780 | my %server; $self->{server} = [grep 0 < length, grep !$server{$_}++, @{ $self->{server} }]; |
750 | |
781 | |
751 | unless (@{ $self->{server} }) { |
782 | unless (@{ $self->{server} }) { |
752 | # use 127.0.0.1 by default, and one opendns nameserver as fallback |
783 | # use 127.0.0.1 by default, and one opendns nameserver as fallback |
753 | $self->{server} = [v127.0.0.1, $DNS_FALLBACK[rand @DNS_FALLBACK]]; |
784 | $self->{server} = [v127.0.0.1, $DNS_FALLBACK[rand @DNS_FALLBACK]]; |
754 | } |
785 | } |
… | |
… | |
777 | $NOW = time; |
808 | $NOW = time; |
778 | $id->[1]->($res); |
809 | $id->[1]->($res); |
779 | } |
810 | } |
780 | |
811 | |
781 | sub _recv { |
812 | sub _recv { |
782 | my ($self) = @_; |
813 | my ($self, $pkt, $peer) = @_; |
783 | |
814 | |
784 | # we ignore errors (often one gets port unreachable, but there is |
815 | # we ignore errors (often one gets port unreachable, but there is |
785 | # no good way to take advantage of that. |
816 | # no good way to take advantage of that. |
786 | while (my $peer = recv $self->{fh}, my $res, 4096, 0) { |
817 | |
787 | my ($port, $host) = AnyEvent::Socket::unpack_sockaddr ($peer); |
818 | my ($port, $host) = AnyEvent::Socket::unpack_sockaddr ($peer); |
788 | |
819 | |
789 | return unless $port == 53 && grep $_ eq $host, @{ $self->{server} }; |
820 | return unless $port == 53 && grep $_ eq $host, @{ $self->{server} }; |
790 | |
821 | |
791 | $self->_feed ($res); |
822 | $self->_feed ($pkt); |
792 | } |
|
|
793 | } |
823 | } |
794 | |
824 | |
795 | sub _free_id { |
825 | sub _free_id { |
796 | my ($self, $id, $timeout) = @_; |
826 | my ($self, $id, $timeout) = @_; |
797 | |
827 | |
… | |
… | |
833 | }), sub { |
863 | }), sub { |
834 | my ($res) = @_; |
864 | my ($res) = @_; |
835 | |
865 | |
836 | if ($res->{tc}) { |
866 | if ($res->{tc}) { |
837 | # success, but truncated, so use tcp |
867 | # success, but truncated, so use tcp |
838 | AnyEvent::Socket::tcp_connect ((Socket::inet_ntoa $server), 53, sub { |
868 | AnyEvent::Socket::tcp_connect (AnyEvent::Socket::format_address ($server), DOMAIN_PORT, sub { |
839 | my ($fh) = @_ |
869 | my ($fh) = @_ |
840 | or return &$do_retry; |
870 | or return &$do_retry; |
841 | |
871 | |
842 | my $handle = new AnyEvent::Handle |
872 | my $handle = new AnyEvent::Handle |
843 | fh => $fh, |
873 | fh => $fh, |
… | |
… | |
860 | # success |
890 | # success |
861 | $self->_free_id ($req->[2], $retry > 1); |
891 | $self->_free_id ($req->[2], $retry > 1); |
862 | undef $do_retry; return $req->[1]->($res); |
892 | undef $do_retry; return $req->[1]->($res); |
863 | } |
893 | } |
864 | }]; |
894 | }]; |
|
|
895 | |
|
|
896 | my $sa = AnyEvent::Socket::pack_sockaddr (DOMAIN_PORT, $server); |
865 | |
897 | |
866 | send $self->{fh}, $req->[0], 0, AnyEvent::Socket::pack_sockaddr (53, $server); |
898 | my $fh = AF_INET == Socket::sockaddr_family ($sa) |
|
|
899 | ? $self->{fh4} : $self->{fh6} |
|
|
900 | or return &$do_retry; |
|
|
901 | |
|
|
902 | send $fh, $req->[0], 0, $sa; |
867 | }; |
903 | }; |
868 | |
904 | |
869 | &$do_retry; |
905 | &$do_retry; |
870 | } |
906 | } |
871 | |
907 | |