Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Re^2: Windows: AnyEvent -> HTTP -> DNS - > Blocking for minutes

by sectokia (Pilgrim)
on Oct 19, 2022 at 09:28 UTC ( [id://11147515]=note: print w/replies, xml ) Need Help??


in reply to Re: Windows: AnyEvent -> HTTP -> DNS - > Blocking for minutes
in thread Windows: AnyEvent -> HTTP -> DNS - > Blocking for minutes

Thanks for response, sorry I took so long to reply I have been ill.

Here is simple test that causes the issue on windows (disable all network adaptors on windows before running, so there are no DNS servers):

use AnyEvent; use AnyEvent::HTTP; my $cv = AnyEvent->condvar; my $timer = AnyEvent->timer(after=>1,interval=>1,cb=>sub{ print STDOUT + "."; flush STDOUT; }); my $sig = AnyEvent->signal (signal => "INT", cb => sub { $cv->send; }) +; http_get "http://www.google.com/", sub { print "Get completed (".$_[1]->{Status}.")"; $cv->send; }; $cv->recv;

I have figured out the call chain, but I am unable to determine what to do about it. It seems like Resolver is trying to send a udp packet to a blank IP address?

Anyevent::DNS is doing this:

my $r = Net::DNS::Resolver->new;

Net::DNS::Resolver::MSWin32 in its _init is calling:

$defaults->nameservers(@nameservers);

nameservers() is in Net::DNS::Resolver::Base which calls send() and _send_udp()

_send_udp is then calling can_read from IO::Select

This results in a blocking call at this line with the timeout being several minutes:

defined($r) && (select($r,undef,undef,$timeout) > 0)

I cannot figure out what resolver is trying to do here or who it is even trying to send to. Any help would be great! Thanks

Replies are listed 'Best First'.
Re^3: Windows: AnyEvent -> HTTP -> DNS - > Blocking for minutes
by NERDVANA (Deacon) on Oct 20, 2022 at 02:36 UTC
    So, looking at Net::DNS::Resolver::Base::nameservers(), I see one code path that tries to run a name lookup:
    foreach my $ns ( grep {defined} @_ ) { if ( _ipv4($ns) || _ipv6($ns) ) { push @ip, $ns; } else { my $defres = ref($self)->new( debug => $self->{debug} ); $defres->{persistent} = $self->{persistent}; my $names = {}; my $packet = $defres->send( $ns, 'A' );

    That would happen if the nameserver list passed to  ->nameservers(@list) contained a string that wasn't an IP address. It seems really odd to me that name servers would ever be specified as names that needed resolved before they could be used to resolve names... maybe you should debug that first to see what the input is?

    Try this: (example of monkey-patching)

    use Net::DNS::Resolver::Base; my $orig= \&Net::DNS::Resolver::Base::nameservers; local *Net::DNS::Resolver::Base::nameservers= sub { my ($self, @servers)= @_; print "Current nameservers value: " .join(", ", @{$self->{nameservers}//[]}) ."\n"; print "Assigning nameservers: ".join(", ", @servers)."\n" if @servers > 1; $orig->(@_); }; # and then the rest of your program

    I don't have a windows system to test on at the moment (well, not one where I can disable the network adapters and still use it) but that will at least show you what is getting assigned there. On my system, it reveals that the default resovlers are "127.0.0.1" and "::1" before the first call to ->nameservers, so yes that would hang if there was not a nameserver listening on localhost and something tried to assign "foo.example.com" as a nameserver.

    Meanwhile, I realized that you can override the default timeouts like this:

    use Net::DNS::Resolver; Net::DNS::Resolver->tcp_timeout(1); Net::DNS::Resolver->udp_timeout(1); # remainder of your program
    That effect comes from the AUTOLOAD method on ::Base which generates accessors on demand:
    sub AUTOLOAD { ## Default method my ($self) = @_; my $name = $AUTOLOAD; $name =~ s/.*://; croak qq[unknown method "$name"] unless $public_attr{$name}; no strict 'refs'; ## no critic ProhibitNoStrict *{$AUTOLOAD} = sub { my $self = shift; $self = $self->_defaults unless ref($self); $self->{$name} = shift || 0 if scalar @_; return $self->{$name}; }; goto &{$AUTOLOAD}; }
    so any time you call an accessor on the package itself, it pulls up the _default resolver and sets the attribute on it instead. Those attributes get used as the defaults for any new resolvers created.
      It seems really odd to me that name servers would ever be specified as names that needed resolved before they could be used to resolve names...

      This is used in the testing/using of authoritative and/or delegated nameservers which are usually specified by name rather than by address (so that they may be rotated, migrated, etc. by the hoster without affecting the delegation). So long as you have bootstrapped correctly by having an initial resolver to do the lookups of the nameserver names it is a handy feature.


      🦛

      Yes for me I get attempts to 127.0.0.1 and ::1 as well

      Setting tcp_timeout and udp_timeout changed nothing for me. Having a look in _udp_send the timeout used for can_read is made from the 'retrans' and 'retry' settings. Based on the default values (5 and 4) with the DNS timeout of 2.5s doubling, this results in a timeouts of: 2.5,2.5,5,5,10,10,20,20 for both servers (total 150 seconds).

      This reduced the issue to 2.5 seconds:

      Net::DNS::Resolver->tcp_timeout(1); Net::DNS::Resolver->udp_timeout(1);

      But then I noticed that the local host name servers are in the defaults as well! So this 'fixes' it by setting the default nameservers to nothing:

      Net::DNS::Resolver->nameserver4([]); Net::DNS::Resolver->nameserver6([]);
        Well it sounds like you have enough info now that your problem is solved? If so, wonderful :-)

        ...but your post still leaves *me* with all kinds of questions, like why you need AnyEvent DNS to work if you don't have any network adapters in the first place, or how you're going to use a name server by host name if you don't have an initial default nameserver to resolve that with :-) I'll point back to my first post here about how if you know what name servers you want to use (like Cloudflare 1.1.1.1 or google 8.8.4.4) you can just pass those directly to AnyEvent::DNS and skip messing with Net::DNS::Resolver. Then if your network is connected, you can reach 1.1.1.1, and if it isn't, it should time out within AnyEvent rather than Net::DNS::Resolver.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11147515]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (4)
As of 2024-04-23 15:19 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found