security-scripts/dns-scripts/netdns.pl

166 lines
5.3 KiB
Perl
Raw Permalink Normal View History

2013-06-04 15:24:27 +02:00
#!/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 <doug@unixlore.net>
#
# 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 <IP or network spec> [-d N] [-t N] [-n <Comma-separated list of IPs or hostnames>] [-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";