Monday, December 17, 2012

Comparing DNS Requests with Perl

I was recently helping someone troubleshoot an issue in which one of their DNS servers was returning incorrect IP information for certain domains. Below is a Perl script that makes use of the Net::DNS module to compare the resolved IP addresses for a specified list of domains from a specified list of nameservers. I have it commented out in the script below, but by uncommenting the appropriate line you can also execute a system call to flush the DNS cache of the machine before each set of DNS requests.

#!usr/bin/perl

use Net::DNS;
use strict;
use warnings;

my @domains = ('perl.org','cpan.org','perlmonks.org','perlfoundation.org','perlweekly.com','perlbuzz.com','perlsphere.net');
my @DNServers = ('167.206.112.138','8.8.8.8','208.67.222.222');

foreach my $DNS (@DNServers){
  #flush DNS cache by uncommenting OS specific option   
  #system('/etc/init.d/nscd restart');
  #system('ipconfig /flushdns');
  print "Results for $DNS:\n";
   my $res=Net::DNS::Resolver->new;
   $res->nameservers($DNS);
   foreach my $domain(@domains){
       #queries server
      my $answer = $res->search("$domain");
       #extract IPs specified in A records
      foreach my $record ($answer->answer) {
         next unless $record->type eq "A";
         print "$domain:" . $record->address . "\n";
      }
   };
   print "\n\n";
}

4 comments:

Unknown said...

nice.
I would add a timeout on the request :
$res->tcp_timeout(5) for example.
Moreover i would add an error handling in case $answer is undefined
if($answer) {
foreach my $record...
...
}
else{
print "unable to access $domain \n"
}

anyway, thanx

nanux said...

I found it useful to print the DNS response time:

#!/usr/bin/perl

use Net::DNS;
use Modern::Perl;
use Time::HiRes qw(tv_interval gettimeofday);

my @domains = ('perl.org','cpan.org','perlmonks.org','perlfoundation.org','perlweekly.com','perlbuzz.com','perlsphere.net');
my @DNServers = ('192.168.1.254','8.8.8.8','208.67.222.222');

foreach my $DNS (@DNServers){
#flush DNS cache by uncommenting OS specific option
#system('/etc/init.d/nscd restart');
#system('ipconfig /flushdns');
say "Results for $DNS:";
my $res=Net::DNS::Resolver->new;
my $t0 = [gettimeofday];
$res->nameservers($DNS);
foreach my $domain(@domains){
#queries server
my $answer = $res->search("$domain");
#extract IPs specified in A records
foreach my $record ($answer->answer) {
next unless $record->type eq "A";
say "$domain:" . $record->address;
}
}
say "Time elapsed: " . tv_interval($t0);
say "\n";
}

Anonymous said...

For those visitors coming in from search engines, this code discussed in the article is only good for trouble-shooting situations.

Most of the time, when you want to resolve names, you should use the operating system's resolver, because the operating system knows best. See IO::Socket::IP and Socket.

Christopher Cashell said...

FYI: A "cleaner" way of clearing the nscd cache is with /usr/sbin/nscd -i hosts.