Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Mass Domain Name Lookup

by argus (Acolyte)
on Nov 15, 2001 at 21:40 UTC ( [id://125625]=sourcecode: print w/replies, xml ) Need Help??
Category: networking
Author/Contact Info John Barrett
Description: This code will allow you to query several nameservers for different fully qualified domain names.

The help:

Mass domain name lookup Usage: ./mdnl.pl [--ns <nameserver> --addr <FQDN> --verbose --outfile +<filename> --nsfile <filename> --addrfile <filename>] --ns <namserver> Nameserver to query against --addr <FQDN> Fully qualified domin name (FQDN) to query --verbose Output data to the screen --outfile <filename> Tab delimited file to output to --nsfile <filename> File of nameservers to query against (nameserv +er<tab>ISP) --addrfile <filename> Tab delimited file of adresses to query --help Print help text (what you're reading now)
You must have either --verbose or --outfile, --addr or --addrfile & --ns or --nsfile.

The structure of the nsfile is IP\tISP Name
(the ISP Name was something folks here wanted, I just wrote a little script to query ARIN against all the IPs)
In the addrfile enter a fully qualified domain name (fqdn) on each line.

The outputfile structure is:
Nameserver\tfqdn\tttl (in seconds)\tIP resolved

On the command line you can can have either --addr fqdn --addr fqdn or --addr fqdn,fqdn, this holds true for --ns as well.

Please give me comments and feedback. anything I am doing wrong or could be done better is helpful.

Thanks

Update: Total code update based on comments. which were greatly appricated and help me learn.

#!/usr/local/bin/perl                                           

#--------------------------------------------------------------------#
# Mass domain name lookup
#       Author:         John Barrett
#
#       This is written to query multiple name servers for the 
#         same information.  To ensure proper propagation of 
#         DNS changes.
#--------------------------------------------------------------------#
#
#  Many thanks to www.perlmonks.com for help and resources!
#
#--------------------------------------------------------------------#
use Getopt::Long;

# Setup my variables
my $dig "/usr/local/bin/dig";                                    
my $help = 0;
my $verbose = 0;
my $outfile = '';
my $nsfile = '';
my $addrfile = '';
my @ns = ();
my @addr = ();
my $type = "IN A";                                           
my $i = 0;
my @time_values = qw [ d h m s ];
my %time_value  = ( d => 86400, h => 3600, m => 60, s => 1 );


# Define the options for getopts (These allow us to pass commands on t
+he commandline) 
GetOptions ('help' => \$help,
            'verbose' => \$verbose,
            'ns:s' => \@ns,
            'addr:s' => \@addr,
            'outfile:s' => \$outfile,
            'nsfile:s' => \$nsfile,
            'addrfile:s' => \$addrfile
);

# This will allow the use of multiple values for one comman switch
# e.g. --addr domain.name,domain.name2 
@ns = split(/,/,join(',',@ns));
@addr = split(/,/,join(',',@addr));

if ( $help
    ||  !@ns && !$nsfile
    ||  !@addr && !$addrfile
    ||  !$verbose && !$outfile ) {
    print <<END;
Mass domain name lookup
Usage: $0\t[--ns <nameserver> --addr <FQDN> --verbose 
\t\t--outfile <filename> --nsfile <filename> --addrfile <filename>]
--ns <namserver>\tNameserver to quer against
--addr <FQDN>\t\tFully qualified domin name (FQDN) to query
--verbose\t\tOutput data to th screen rathr than a file
--outfile <filename>\tTab delimited file to output to 
--nsfile <filename>\tTab delimited file of nameservers to query agains
+t 
--addrfile <filename>\tTab delimited file of adresses to query
--help\t\t\tPrint help text (what you're reading now)
END
    exit;
}

if (!@ns) {
  open (DATAIN, "< $nsfile") || die "can't open file: $!";  
                                                          
  while (<DATAIN>) {                                        
    chomp;                                                 
    my ($one,$two) = split(/\t/);
   if ($one ne '') { 
    push (@dns, $one);
    if($two){
      push (@provide, $two);
    } else {
      push (@provide, "Unknown");
    }                                       
   }
  }                                                         
                                                          
  close DATAIN;                                             
} else {
  @dns = @ns;
  push @provide, ('Unknown') x @ns; 
}

if (!@addr) {
  open (DATAIN, "< $addrfile") || die "can't open file: $!";  
                                                          
  while (<DATAIN>) {                                        
    chomp;                                                 
    my ($one,$two) = split(/\t/);
   if ($one ne '') { 
    push (@addr, $one);
   }                         
  }                                                         
                                                          
  close DATAIN;                                             
}


foreach $ldns (@dns) {   
   if ($verbose) {
      print "=========================================================
+======\n";
      print "== Local DNS: $ldns Provider: $provide[$i]\n";           
+      
      print "=========================================================
+======\n"; 
   }
   $i++;
       foreach $fqdn (@addr) {             
          lookup ($ldns, $fqdn);               
       }         
}                  

if (@data_out) {
    open (DATAOUT, ">> $outfile") || die "can't open $outfile: $!";
        print DATAOUT @data_out;
    close DATAOUT;
}

sub lookup {
    my ($ldns, $fqdn) = @_;

    my ($error, $error_a);

    open DIG, "$dig \@$ldns $type $fqdn | " or die $!;
        while (<DIG>) {
            if (/;; ->>HEADER<<- opcode: QUERY\s|QUERY, status: NOERRO
+R, .*/) {
                $error = 1;
                next;
            }
            next unless /$fqdn\.\s+(\w+)(\s+A|\s+IN\sA)\s+(.*)/;

            $error_a    = 1;
            my $time    = lc($1);
            my $ip      = $3;
            my $seconds = $time =~ /[dhms]/ ? string_to_time($time) : 
+$time;
            my $extended_time = time_to_string($seconds);
            print "Request: $fqdn\tTTL: $extended_time\tIP: $ip\n" if 
+$verbose;
            push @data_out, "$ldns\t$fqdn\t$seconds\t$ip\n" if $outfil
+e;
        }
    close DIG;

    if (!$error) {
        print "Request: $fqdn\tERROR (No domain found)\n" if $verbose;
        push @data_out, "$ldns\t$fqdn\tERROR\tNO DOMAIN\n" if $outfile
+;
    } elsif (!$error_a) {
        print "Request: $fqdn\tERROR (Domain found, no A record found)
+\n" if $verbose;
        push @data_out, "$ldns\t$fqdn\tERROR\tNOT A RECORD\n" if $outf
+ile;
    }
}

sub string_to_time
{
        my ($time) = @_;
        my $value  = 0;

        foreach my $letter (@time_values)
        {
                if ($time =~ s/(\d+)$letter//)
                {
                        $value += $1 * $time_value{$letter};
                }
        }

        return -1 if length $time;

        return $value;
}

sub time_to_string
{
        my ($time) = @_;
        my $value  = '';

        foreach my $letter (@time_values)
        {
                my $time_value = $time_value{$letter};

                if ($time > $time_value)
                {
                        $value .= int($time/$time_value).$letter;
                        $time %= $time_value;
                }
        }

        return $value;
}
Replies are listed 'Best First'.
Re: Mass Domain Name Lookup
by CharlesClarkson (Curate) on Nov 16, 2001 at 20:54 UTC

    You're opening and closing DATAOUT too often. How about loading an array and doing the write to file just once.

    sub lookup { my ($ldns, $fqdn) = @_; my ($error, $error_a); open DIG, "$dig \@$ldns $type $fqdn | " or die $!; while (<DIG>) { if (/;; ->>HEADER<<- opcode: QUERY\s|QUERY, status: NOERRO +R, .*/) { $error = 1; next; } next unless /$fqdn\.\s+(\w+)(\s+A|\s+IN\sA)\s+(.*)/; $error_a = 1; my $time = lc($1); my $ip = $3; my $seconds = $time =~ /[dhms]/ ? string_to_time($time) : +$time; my $extended_time = time_to_string($seconds); print "Request: $fqdn\tTTL: $extended_time\tIP: $ip\n" if +$verbose; push @data_out, "$ldns\t$fqdn\t$seconds\t$ip\n" if $outfil +e; } close DIG; if (!$error) { print "Request: $fqdn\tERROR (No domain found)\n" if $verbose; push @data_out, "$ldns\t$fqdn\tERROR\tNO DOMAIN\n" if $outfile +; } elsif (!$error_a) { print "Request: $fqdn\tERROR (Domain found, no A record found) +\n" if $verbose; push @data_out, "$ldns\t$fqdn\tERROR\tNOT A RECORD\n" if $outf +ile; } }

    As you can see, the code is tighter and easier to read. But, we now must add a test for @data_out.

    if (@data_out) { open (DATAOUT, ">> $outfile") || die "can't open $outfile: $!"; print DATAOUT @data_out; close DATAOUT; }

    This can be rewritten by taken advantage of the x operator.

    foreach $thing (@ns){ push (@provide, "Unknown"); }
    push @provide, ('Unknown') x @ns;

    And, finally, all those ifs in the beginning could be combined.

    if ( $help || !@ns && !$nsfile || !@addr && !$addrfile || !$verbose && !$outfile ) { print <<END; . . .



    HTH,
    Charles K. Clarkson
      Outstanding! Thank you. I gave you credit in the copy of the code I have here. This helped me learn a lot.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (4)
As of 2025-06-23 15:23 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?
    erzuuliAnonymous Monks are no longer allowed to use Super Search, due to an excessive use of this resource by robots.