I just got our company's new VPN client installed. Whow, installed and worked, can't believe it... *g*

But, why is every normal internet access soooo slow to start? Gotcha, it's the DNS server. All DNS queries have to been answered by the DNS server behind the VPN connection, because routing is done on IPs only. Ok, let's speed that up.

Here's the result, a simple DNS sever, that forwards all requests to two (or more) real DNS servers at the same time and returns the answer that comes first. Does some caching, too; no cache timeouts so, can always do that with Ctrl-C.

#!/usr/bin/perl use strict; use warnings; use Net::DNS; use Net::DNS::Nameserver; use Time::HiRes qw(sleep time); our @res = (); for my $nameserver ( '1.2.3.4', # enter some real ones here! '999.99.9.0', ) { my $res = Net::DNS::Resolver->new; $res->nameservers($nameserver); $res->retry(1); $res->recurse(1); $res->dnsrch(0); $res->udp_timeout(2); push @res, $res; } our %cache = ( '1.20.168.192.in-addr.arpa IN PTR' => [ # must reverse lookup my +self 'NOERROR', [Net::DNS::RR->new("1.20.168.192.in-addr.arpa. 86400 PTR local +host")], [], # VPN client doesn't allow 127.0.0.1 for DNS server, so [], # use one of my VMWARE interfaces for it {} ] ); sub reply_handler { my ($qname, $qclass, $qtype, $peerhost) = @_; print "\n"; # cache? if (exists $cache{"$qname $qclass $qtype"}) { print " Answering from cache\n"; return @{ $cache{"$qname $qclass $qtype"} }; } # send queries to all servers my @bgres = (); RES0: foreach my $res (@res) { print ' Querying '.$res->{nameservers}[0]."\n"; push @bgres, [ $res, $res->bgsend($qname, $qtype, $qclass) ]; } # ans wait for the answers my $starttime = time(); my $failedcount = 0; RES1: while (grep { defined($_) } @bgres) { # finish if all server +s have answered RES: foreach my $resl (@bgres) { next unless defined $resl; my ($res, $sock) = @$resl; next RES unless $res->bgisready($sock); my $answer = $res->bgread($sock); $resl = undef; # mark this server as 'has answered' next RES unless $answer; if ($answer->{header}->{rcode} eq 'NOERROR') { print " Gotcha from ".$res->{nameservers}[0]."!\n"; $cache{"$qname $qclass $qtype"} = [ $answer->{header}->{rcode}, $answer->{answer}, $answer->{authority}, $answer->{additional}, { aa => $answer->{header}->{aa}, ad => $answer->{header}->{ad}, ra => $answer->{header}->{ra} } ]; return @{ $cache{"$qname $qclass $qtype"} }; } else { print " Failed from ".$res->{nameservers}[0]."!\n"; $failedcount++; } } last RES1 if (time() - $starttime) > 5; # finish after timeout sleep 0.05; # be nice to out CPU } if ($failedcount == @bgres) { # cache negative answers only if there was no timeout, # that is, when all servers reported NXDOMAIN... $cache{"$qname $qclass $qtype"} = [ 'NXDOMAIN', [], [], [], +{} ]; } return ( 'NXDOMAIN', [], [], [], {} ); } # add some interface to it, if you're not firewalled already my $ns = Net::DNS::Nameserver->new( LocalPort => 53, ReplyHandler => \&reply_handler, Verbose => 1, ) || die "couldn't create nameserver object\n"; while (1) { eval { $ns->main_loop; } }
For this to work for you, you must enter some real name server addresses. I'd suggest to use one from behind the VPN tunnel und one from your internet connection.

You also must change the IP address in the precached PTR record to match the IP you'll let this program run on.

Oh, I nearly forgot: FOR PERSONAL USE ONLY. NOT SUITABLE FOR MULTIUSER USAGE. MUST BE PROTECTED FROM UNAUTHORIZED ACCESS---SECURITY RISK! DON'T CONFIGURE TOO MANY DNS SERVERS. DON'T CONFIGURE DNS SERVER YOU'RE NOT AUTHORIZED TO USE.

Feel free to ask questions or make suggestions.


Search, Ask, Know

Replies are listed 'Best First'.
Re: Speeding up DNS queries for VPN connections
by eric256 (Parson) on Oct 01, 2004 at 20:16 UTC

    I'm not sure if you are aware but most VPN clients let you choose to use the VPN server as your gateway or not. I've never seen any reason to use it as my gateway and turning this option off has always solved internect connection issues related to VPN. I'm by no means a VPN expert though so if this is horribly bad practice then i would appreciate anyones input setting me straight. :)


    ___________
    Eric Hodges
      VPN is a horrible complex thing. Let's concentrate on the kind of VPN we both seem to have.

      In this kind the VPN client software creates a new network interface that has an IP address of the intranet it tunnels to. Then it creates routes for all IP networks/ranges that make up that intranet. Then there are two things where there are choices:

      1) Default gateway. What about all IP traffic that is not for the intranet? That can be handled as either DualAccess or SingleAccess.

      1a) DualAcces means, that all other IP traffic is NOT routed through the VPN interface. This means that if will flow the usual way as if you did not use the VPN client at all. From you description I guess that that option you switched off put your client into this mode.

      1b) SingleAccess means that ALL traffic is routed through the intranet. So every access to perlmonks is routed into your intranet and from there into the internet. This usually only makes sense if you do the VPN over a connection that is not internet-connected. Some ISPs have special dial-in networks that have no connection to the internet but only top VPN acces points. Then you need this mode.

      2) DNS servers. What happens if you type in "www.intranet.mycompany.com"? That webserver is obviosly not reachable from the internet, but from your VPN connection. But what about its name resolution?

      2a) All public: It may be that your company puts all DNS records for the internal servers into the public DNS. Then I can get their IP addresses from every DNS server in the world. That is fast, but not every company want the whole world to know its internal names and IPs.

      2b) Internal servers: The other way is to make the only intranet-internal nameservers know these names. But then you HAVE to ask one of them for every query that might be for an internal host. The VPN client will therefore set the internal name servers into your config on connect. Now every DNS query will be tunneled to your intranet. Hey, that can be slooow... (ping times up to 2 seconds here)

      '2b' is the problem I posted the solution for. That script will act as a "DNS proxy", it will forward all queries to all configured name servers and relay the first successful answer. So you enter one DNS server from your ISP and one from your intranet. The ISP's name server will deny the existance of "www.intranet.mycompany.com" but my script will not be fooled and will wait until the intranet's server either confirms that or provides an IP address. And queries for www.perlmonks.org will be answered by the ISP's name server long before the query even reaches the internet's name servers...


      Search, Ask, Know
Re: Speeding up DNS queries for VPN connections
by elwarren (Priest) on Oct 19, 2004 at 22:45 UTC
    That is too cool. I didn't know there was a Net::DNS::Nameserver! I'm going to hack your code to return ip addresses for my oracle database names. Currently I have code to generate a hosts file, but this is much more slick. Now I'll have a way to seemlessly do ssh database.standby and not have to look it up. Sweet!

    Well, as soon as I find time :-)