Skip Menu |

Preferred bug tracker

Please visit the preferred bug tracker to report your issue.

This queue is for tickets about the Net-DNS-Paranoid CPAN distribution.

Report information
The Basics
Id: 106791
Status: open
Priority: 0/
Queue: Net-DNS-Paranoid

People
Owner: Nobody in particular
Requestors: BBYRD [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Important
Broken in: 0.07
Fixed in: (no value)



Subject: Resolver does not check other name servers
Net::DNS::Paranoid uses Resolver's bgsend method, which has no support for retries, timeouts, or alternate nameservers. Therefore, if the primary NS goes down, the entire app goes down. This defeats the purpose of having a backup NS in the first place. Net::DNS::Paranoid should instead use something like send_udp, with an alarm call to make sure it doesn't fall below the timer. Also, the default settings for Net::DNS::Resolver should be changed to match Paranoid's short 15 second timeout. Something like this would work: $args{resolver} ||= Net::DNS::Resolver->new( # 1+2+4+8 = 15 seconds retrans => 1, retry => 4, udp_timeout => 15, );
Subject: Re: [rt.cpan.org #106791] Resolver does not check other name servers
Date: Tue, 01 Sep 2015 03:14:35 +0000
To: bug-Net-DNS-Paranoid [...] rt.cpan.org
From: Tokuhiro Matsuno <tokuhirom [...] gmail.com>
patches welcome On Tue, Sep 1, 2015 at 12:47 AM Brendan Byrd via RT < bug-Net-DNS-Paranoid@rt.cpan.org> wrote: Show quoted text
> Mon Aug 31 11:47:39 2015: Request 106791 was acted upon. > Transaction: Ticket created by BBYRD > Queue: Net-DNS-Paranoid > Subject: Resolver does not check other name servers > Broken in: 0.07 > Severity: Important > Owner: Nobody > Requestors: BBYRD@cpan.org > Status: new > Ticket <URL: https://rt.cpan.org/Ticket/Display.html?id=106791 > > > > Net::DNS::Paranoid uses Resolver's bgsend method, which has no support for > retries, timeouts, or alternate nameservers. Therefore, if the primary NS > goes down, the entire app goes down. This defeats the purpose of having a > backup NS in the first place. > > Net::DNS::Paranoid should instead use something like send_udp, with an > alarm call to make sure it doesn't fall below the timer. Also, the default > settings for Net::DNS::Resolver should be changed to match Paranoid's short > 15 second timeout. Something like this would work: > > $args{resolver} ||= Net::DNS::Resolver->new( > # 1+2+4+8 = 15 seconds > retrans => 1, > retry => 4, > udp_timeout => 15, > ); > >
Sorry about the wait. Had to get the patch approved. --- Paranoid.pm.old 2015-08-31 12:49:35.000000000 -0400 +++ lib/Net/DNS/Paranoid.pm 2015-08-31 17:25:04.000000000 -0400 @@ -8,17 +8,32 @@ rw => [qw(timeout blocked_hosts whitelisted_hosts resolver)] ); use Net::DNS; +use Time::HiRes qw( alarm ); +use POSIX qw( ceil ); sub new { my $class = shift; my %args = @_ ==1 ? %{$_[0]} : @_; - $args{resolver} ||= Net::DNS::Resolver->new; + + $args{timeout} ||= 15; + + unless ($args{resolver}) { + my $res = $args{resolver} = Net::DNS::Resolver->new( + # Calculate the nearest base 2 exponent that would cover the timeout period + # So, 1+2+4+8 = 15 seconds, which would be 4 retries + retrans => 1, + retry => ceil( log($args{timeout} + 1) / log(2) ), + udp_timeout => $args{timeout}, + ); + + # no staggered retries, full time used is $timeout * $num_of_ns + my $num_of_ns = scalar $res->nameservers; + $res->tcp_timeout( ceil( $args{timeout} / $num_of_ns ) ); + } + $args{whitelisted_hosts} ||= []; $args{blocked_hosts} ||= []; - bless { - timeout => 15, - %args - }, $class; + bless { %args }, $class; } sub resolve { @@ -41,22 +56,21 @@ # return the IP address if it looks like one and wasn't marked bad return ([$host]) if $host =~ /^\d+\.\d+\.\d+\.\d+$/; - my $sock = $res->bgsend($host) - or return (undef, "No sock from bgsend"); - - # wait for the socket to become readable, unless this is from our test - # mock resolver. - unless ($sock && $sock eq "MOCK") { - my $rin = ''; - vec($rin, fileno($sock), 1) = 1; - my $nf = select($rin, undef, undef, $self->_time_remain($start_time)); - return (undef, "DNS lookup timeout") unless $nf; + # Find the host using Resolver's send method, which supports timeouts + # and alternate NSs. Most of the time, this will use UDP, but may + # switch to TCP in certain situations. + my $packet; + local $SIG{ALRM} = sub { die "DNS lookup timeout\n" }; + alarm $self->_time_remain($start_time); + eval { $packet = $res->send($host) }; + alarm 0; + + unless ($packet) { + my $errstr = $@ || "DNS send failure: ".$res->errorstring; + chomp $errstr; + return (undef, $errstr); } - my $packet = $res->bgread($sock) - or return (undef, "DNS bgread failure"); - $sock = undef; - my @addr; my $cname; foreach my $rr ($packet->answer) { --- MockResolver.pm.old 2015-08-31 12:58:19.000000000 -0400 +++ t/MockResolver.pm 2015-08-31 12:59:49.000000000 -0400 @@ -32,6 +32,11 @@ if $ENV{VERBOSE}; return $self->{next_fake_packet}; } + if ($method eq "send" && $fr->{$_[0]}) { + Test::More::note("mock DNS resolver doing fake send() of $_[0]\n") + if $ENV{VERBOSE}; + return $fr->{$_[0]}; + } # No verbose conditional on this one because it shouldn't happen: Test::More::note("Calling through to Net::DNS::Resolver proxy method '$method'"); return $self->{proxy}->$method(@_);