#!/opt/local/bin/perl # # Script to do bulk PTR lookups on a network of IP's # # Updated 4/10 with more features and to make better use of underlying # CPAN modules: # # - Accepts IPv4/IPv6 addresses as singletons or a network in range or # CIDR format # - Allows you to configure which DNS server(s) to query # - Allows you to configure a fixed delay between PTR lookups # - Output to STDOUT for use in pipelines, or to a file in CSV or JSON # format # - Configurable timeout on PTR lookups # - Persistent UDP connections to help lessen the load on DNS servers # # Requires CPAN modules Net::DNS, Net::IP, JSON and Tie::IxHash # # perl -MCPAN -e 'CPAN::Shell->install(qw(Net::DNS Net:IP Tie::IxHash JSON))' # # should do the trick on any Unix OS. On Debian/Ubuntu, do: # # apt-get install libnet-dns-perl libnet-ip-perl libjson-perl libtie-ixhash-perl # # Usage: The only required parameter is an IPv4/IPv6 network specified # as a range or in CIDR format, or a single IP (see the Net::IP docs # at http://search.cpan.org/~manu/Net-IP-1.25/IP.pm). Output is a # simple CSV list of the IP addresses and the hostname they each # resolved to, or NXDOMAIN if no PTR record exists, or error text if # there is some other error with the DNS query. # # Output is always to STDOUT by default, or to a file if '-w' is # specified. Errors always go to STDERR via croak. Examples: # # ./netdns.pl -i 10.0.0/24 > ptr-list.csv # ./netdns.pl -i 10.0.0.1 # ./netdns.pl -i 10.0.0.5-10.0.0.33 -d 10 -j # ./netdns.pl -i 10.0.0/25 -d 3 -t 2 -n 8.8.8.8,8.8.4.4 -j > ptr-list.json # ./netdns.pl -i 10.0.0/25 -j -w ptr-list.json # ./netdns.pl -i dead:beef::/32 # # Sample JSON output: # # { # "10.0.0.0" : "foo1.example.com", # "10.0.0.1" : "foo2.example.com", # "10.0.0.2" : "foo3.example.com", # "10.0.0.3" : "foo4.example.com" # } # # Copyright (c) 2006-2010 Doug Maxwell # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # use strict; use warnings; use Net::DNS; use Net::IP; use Getopt::Std; use JSON; use Tie::IxHash; use English; use Carp; require 5.006_000; # Needed for $outfile our ($opt_h,$opt_j,$opt_i,$opt_d,$opt_n,$opt_t,$opt_w); getopts('hji:d:n:t:w:'); usage() && exit if ( !$opt_i || $opt_h ); sub usage { print STDERR "\n$0 synopsis: \n"; print STDERR "\n"; print STDERR "Usage: $0 -i [-d N] [-t N] [-n ] [-w filename] [-j] [-h]\n"; print STDERR "-i: IP address, range or CIDR (required)\n"; print STDERR "\t10.0.0.1\n\t10.0.0.3-10.0.0.55\n\t10.0.0/24\n\tdead:beef::/32\n"; print STDERR "-d: Delay in seconds between lookups\n"; print STDERR "-t: UDP timeout (defaults to five seconds)\n"; print STDERR "-n: Comma-separated list of nameserver IPs or hostnames (defaults to system resolver)\n"; print STDERR "-w: Output to the named file\n"; print STDERR "-j: Output in JSON (default is CSV)\n"; print STDERR "-h: This help text\n\n"; } # Make sure delays are non-negative my $delay = ( $opt_d && $opt_d > 0 ) ? $opt_d : 0; my $udp_timeout = ( $opt_t && $opt_t > 0 ) ? $opt_t : 5; # Initialize the hashref used for JSON output. Tie it so we can print # it out in insertion order. my $ptr_records = {}; tie %$ptr_records,"Tie::IxHash"; # $outfile is a filehandle pointing to the output file specified by # the 'w' option, or to STDOUT. my $outfile; if ( $opt_w ) { open $outfile, '>', $opt_w or croak "Unable to open '$opt_w': $OS_ERROR\n"; } else { open $outfile, '>-', or croak "Unable to open STDOUT: $OS_ERROR\n"; } my $ip = new Net::IP($opt_i) or croak "Unable to create Net::IP object\n"; my $res = Net::DNS::Resolver->new( persistent_udp => 1, udp_timeout => $udp_timeout, ) or croak "Unable to create Net::DNS::Resolver object\n"; # Set the nameservers to query as specified by '-n' args $res->nameservers(split(",",$opt_n)) if ( $opt_n ); do { my $ip_address = $ip->ip(); if ($ip_address) { my $query = $res->send("$ip_address",'PTR'); if (defined $query && $query->answer) { foreach my $rr ($query->answer) { unless ( $opt_j ) { print {$outfile} "$ip_address,",$rr->ptrdname, "\n" or croak "Couldn't write: $OS_ERROR\n"; } $ptr_records->{$ip_address} = $rr->ptrdname; } } else { unless ( $opt_j ) { print {$outfile} "$ip_address,",$res->errorstring,"\n" or croak "Couldn't write: $OS_ERROR\n"; } $ptr_records->{$ip_address} = $res->errorstring; } } sleep($delay) if ( $delay ); } while ( ++$ip ); # Pretty-print the results JSON if needed print {$outfile} JSON->new->pretty(1)->encode($ptr_records) if ( $opt_j ); close $outfile or croak "Unable to close file: $OS_ERROR\n";