#!/usr/bin/perl #--------------------------------------- # # hosts : expantion and lookups # Writen by MadHat (madhat@unspecific.com) # # http://www.unspecific.com/scanner/hosts/ # # a 'host' replacement (sort of) that expands subnets into a list of IPs # can be used with or without hostnames. Can also be used as a perl # include file to use the expanding capabilities (as seen in the scanners) # # Copyright (c) 2001-2002, MadHat (madhat@unspecific.com) # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the distribution. # * Neither the name of MadHat Productions nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # #--------------------------------------- use Socket qw(:DEFAULT :crlf); use Getopt::Std; $VERSION = '1.3.0'; getopts("d:vh"); if (defined $ENV{'REQUEST_METHOD'}) { $HTML = 1; use CGI ":standard"; use CGI::Carp "fatalsToBrowser"; print header, start_html('Unspecific.com "hosts"'), "
";
if (param('net') and param('net') !~ /\.\./ ) {
@ARGV = param('net');
if (param('v')) {
$opt_v = 1;
}
} else {
$opt_h = 1;
}
}
if (!$ARGV[0] or $opt_h) {
print "hosts v$VERSION by MadHat (madhat\@unspecific.com)\n";
if (!$HTML) {
print " $0 [-v] \n" if (!$HTML);
} else {
print "\n";
}
print " returns list of IPs\n";
print " supported formats are:
\t a.b.c.d/n - 10.0.0.1/25
\t a.b.c.* - 10.0.0.*
\t a.b.c.d/w.x.y.z - 10.0.0.0/255.255.224.0 (standard format)
\t a.b.c.d/w.x.y.z - 10.0.0.0/0.0.16.255 (cisco format)
\t a.b.c.d-z - 10.1.2.0-12
\t a.b.c-x.* - 10.0.0-3.*
\t a.b.c-x.d - 10.0.0-3.0
\t hostname - unspecific.com\n";
print " can also use a comma or space seperated list\n";
print " can also point to a file with host list (lines starting with # are ignored)\n";
print " if a single IP is used, hostname will be displayed by default\n";
print " -v will also add in host names when using subnets\n\n";
exit;
} else {
if ($HTML and !$opt_v) {
print "Show DNS *May Be Slow*
\n";
}
for (@ARGV) {
if ( -e $_ ) {
open (IN, $_);
push (@nets, );
close (IN);
} else {
push (@nets, split(',', $_));
}
}
foreach $net (@nets){
chomp $net;
next if ($net =~ /^#/ or $net =~ /^$/);
foreach $ip (calculate_ip_range($net)) {
print "$ip\n";
}
}
}
# generate an array of IPs based on multiple input types
#---------------------------------------
sub calculate_ip_range {
# 1st IP scalar
# formats allowed include
# a.b.c.d/n - 10.0.0.1/25
# a.b.c.* - 10.0.0.*
# a.b.c.d/w.x.y.z - 10.0.0.0/255.255.224.0 (standard format)
# a.b.c.d/w.x.y.z - 10.0.0.0/0.0.16.255 (cisco format)
# a.b.c.d-z - 10.1.2.0-12
# a.b.c-x.* - 10.0.0-3.*
# a.b.c-x.d - 10.0.0-3.0
# hostname - unspecific.com
# 2nd wether or not to return an error message or nothing
# default is to return nothing on error
# 3rd is max number IPs to return
# default max is 65536 and can not be raised at this time
my ($ip, $return_error, $max_ip) = @_;
my @msg = ();
my $err = '';
$max_ip = $max_ip || 65536;
my $a, $b, $c, $d, $sub_a, $sub_b, $sub_c, $sub_d, $num_ip,
$nm, $d_s, $d_f, $c_s, $c_f, @msg, $err, $num_sub,
$start_sub, $count_sub;
# lets start now...
# does it look just like a single IP address?
if ($ip =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/) {
print STDERR "$cli_exec ($$): x.x.x.x format $ip\n" if ($opt_d);
$a = $1; $b = $2; $c = $3; $d = $4;
if ( $a > 255 or $a < 0 or $b > 255 or $b < 0 or $c > 255 or $c < 0 or
$d > 255 or $d < 0) {
$err = "ERROR: Appears to be a bad IP address ($ip)";
} else {
push @msg, add_ip($ip);
}
# does it look like the format x.x.x.x/n
} elsif ($ip =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\/(\d{1,2})$/) {
print STDERR "$cli_exec ($$): x.x.x.x/n format $ip\n" if ($opt_d);
$a = $1; $b = $2; $c = $3; $d = $4; $nm = $5;
if ( $a > 255 or $a < 0 or $b > 255 or $b < 0 or $c > 255 or $c < 0 or
$d > 255 or $d < 0 or $nm > 32 or $nm < 0) {
$err = "ERROR: Something appears to be wrong ($ip)";
} else {
$num_ip = 2**(32-$nm);
if ($num_ip > $max_ip) {
$err = "ERROR: Too many IPs returned ($num_ip)";
} elsif ($num_ip <= 256) {
$num_sub = 256/$num_ip;
SUBNET: for $count_sub (0..($num_sub - 1)) {
$start_sub = $count_sub * $num_ip;
if ($d > $start_sub and $d < ($start_sub + $num_ip)) {
$d = $start_sub;
last SUBNET;
}
}
for $d ($d..($d + $num_ip - 1)) {
push @msg, add_ip("$a.$b.$c.$d");
}
} elsif ($num_ip <= 65536) {
$num_sub = 256/($num_ip/256); $num_ip = $num_ip/256;
SUBNET: for $count_sub (0..($num_sub - 1)) {
$start_sub = $count_sub * $num_ip;
if ($c > $start_sub and $c < ($start_sub + $num_ip)) {
$c = $start_sub;
last SUBNET;
}
}
for $c ($c..($c + $num_ip - 1)) {
for $d (0..255) {
push @msg, add_ip("$a.$b.$c.$d");
}
}
}
}
# does it look like the format x.x.x.x-y
} elsif ($ip =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\-(\d{1,3})$/) {
print STDERR "$cli_exec ($$): x.x.x.x-y format $ip\n" if ($opt_d);
$a = $1; $b = $2; $c = $3; $d_s = $4; $d_f = $5;
if ( $d_f > 255 or $d_s > 255 or $d_s < 0 or $d_f < 0 or $a < 0 or
$a > 255 or $b < 0 or $b > 255 or $c < 0 or $c > 255 ) {
$err = "ERROR: Something appears to be wrong ($ip).";
} elsif ($d_f < $d_s) {
LOOP: for $d ($d_f .. $d_s) {
if ($#msg > $max_ip) {
$err = "ERROR: Too many IPs returned ($#msg+)";
last LOOP;
}
push @msg, add_ip("$a.$b.$c.$d");
}
# $err = "Sorry, we don't count backwards.";
} elsif ($d_f == $d_s) {
push @msg, add_ip("$a.$b.$c.$d");
} else {
LOOP: for $d ($d_s .. $d_f) {
if ($#msg > $max_ip) {
$err = "ERROR: Too many IPs returned ($#msg+)";
last LOOP;
}
push @msg, add_ip("$a.$b.$c.$d");
}
}
# does it look like the format x.x.x-y.*
} elsif ($ip =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\-(\d{1,3})\.(.*)$/) {
print STDERR "$cli_exec ($$): x.x.x-y.* format $ip\n" if ($opt_d);
$a = $1; $b = $2; $c_s = $3; $c_f = $4; $d = $5;
if ( $c_f > 255 or $c_s > 255 or $c_s < 0 or $c_f < 0 or
$a < 0 or $a > 255 or $b < 0 or $b > 255 or
( ($d < 0 or $d > 255) and $d ne "*") ) {
$err = "ERROR: Something appears to be wrong ($ip)";
} elsif ($c_f < $c_s) {
LOOP: for $c ($c_f .. $c_s) {
for $d (0..255) {
if ($#msg > $max_ip) {
$err = "ERROR: Too many IPs returned ($#msg+)";
last LOOP;
}
push @msg, add_ip("$a.$b.$c.$d");
}
}
} elsif ($c_f == $c_s) {
push @msg, add_ip("$a.$b.$c.$d");
} else {
LOOP: for $c ($c_s .. $c_f) {
for $d (0..255) {
if ($#msg > $max_ip) {
$err = "ERROR: Too many IPs returned ($#msg+)";
last LOOP;
}
push @msg, add_ip("$a.$b.$c.$d");
}
}
}
# does it look like the format x.x.x.*
} elsif ($ip =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.\*$/) {
print STDERR "$cli_exec ($$): x.x.x.* format $ip\n" if ($opt_d);
$a = $1; $b = $2; $c = $3;
if ( $a < 0 or $a > 255 or $b < 0 or $b > 255 or $c < 0 or $c > 255 ) {
$err = "ERROR: Something appears to be wrong ($ip)";
} else {
LOOP: for $d (0 .. 255) {
if ($#msg > $max_ip) {
$err = "ERROR: Too many IPs returned ($#msg+)";
last LOOP;
}
push @msg, add_ip("$a.$b.$c.$d");
}
}
# does it look like the format x.x.x.x/y.y.y.y
} elsif ($ip =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/) {
print STDERR "$cli_exec ($$): x.x.x.x/y.y.y.y format $ip\n" if ($opt_d);
$a = $1; $b = $2; $c = $3; $d = $4;
$sub_a = $5; $sub_b = $6; $sub_c = $7; $sub_d = $8;
# if it appears to be in "cisco" format, convert it
if ($sub_a == 0 and $sub_b == 0) {
$sub_a = 255 - $sub_a; $sub_b = 255 - $sub_b;
$sub_c = 255 - $sub_c; $sub_d = 255 - $sub_d;
}
# check to see if the input looks valid
if ( $a > 255 or $a < 0 or $b > 255 or $b < 0 or $c > 255 or $c < 0 or
$d > 255 or $d < 0 or $sub_a > 255 or $sub_a < 0 or
$sub_b > 255 or $sub_b < 0 or $sub_c > 255 or $sub_c < 0 or
$sub_d > 255 or $sub_d < 0 or ($sub_d < 255 and $sub_c != 255 and
$sub_b != 255 and $sub_a != 255) or ($sub_d != 0 and
$sub_c == 0 and $sub_b < 255 and $sub_a == 255) or
($sub_d != 0 and $sub_c < 255 and $sub_b == 255 and
$sub_a == 255)) {
$err = "ERROR: Something appears to be wrong ($ip)";
# if it looked valid, but it appears to be an IP, return that IP
} elsif ($sub_d == 255) {
push @msg, add_ip("$a.$b.$c.$d");
# if the range appears to be part of a class C
} elsif ($sub_d < 255 and $sub_d >= 0 and $sub_c == 255) {
$num_ip = 256 - $sub_d; $num_sub = 256/$num_ip;
if ($num_ip > $max_ip) {
$err = "ERROR: Too many IPs returned ($num_ip)";
} else {
SUBNET: for $count_sub (0..($num_sub - 1)) {
$start_sub = $count_sub * $num_ip;
if ($d > $start_sub and $d < ($start_sub + $num_ip)) {
$d = $start_sub;
last SUBNET;
}
}
LOOP: for $d ($d..($d + $num_ip - 1)) {
if ($#msg > $max_ip) {
$err = "ERROR: Too many IPs returned ($#msg+)";
last LOOP;
}
push @msg, add_ip("$a.$b.$c.$d");
}
}
# if the range appears to be part of a class B
} elsif ($sub_c < 255 and $sub_c >= 0) {
$num_ip = 256 - $sub_c; $num_sub = 256/$num_ip;
if ($num_ip > $max_ip) {
$err = "ERROR: Too many IPs returned ($num_ip)";
} else {
SUBNET: for $count_sub (0..($num_sub - 1)) {
$start_sub = $count_sub * $num_ip;
if ($c > $start_sub and $c < ($start_sub + $num_ip)) {
$c = $start_sub;
last SUBNET;
}
}
LOOP: for $c ($c..($c + $num_ip - 1)) {
for $d (0..255) {
if ($#msg > $max_ip) {
$err = "ERROR: Too many IPs returned ($#msg+)";
last LOOP;
}
push @msg, add_ip("$a.$b.$c.$d");
}
}
}
}
} elsif ($ip =~ /[\w\.]+/) {
print STDERR "$cli_exec ($$): DNS name $ip\n" if ($opt_d);
if ($ip =~ /^(\w+)\[(\d{1,})\-(\d{1,})\]([\w\.]+)$/) {
print "$1, $2, $3, $4\n" if ($opt_d);
if ($3 <= $2) {
return 0;
} else {
for $current ($2..$3) {
my $ip = "$1$current$4";
my ($name,$aliases,$type,$len,@thisaddr) = gethostbyname($ip);
my ($a,$b,$c,$d) = unpack('C4',$thisaddr[0]);
if ($a and $b and $c and $d) {
if (calculate_ip_range("$a.$b.$c.$d")) {
print STDERR "$cli_exec ($$): $ip points to $a.$b.$c.$d\n"
if ($opt_d);
push @msg, add_ip("$a.$b.$c.$d");
}
} else {
$err = "ERROR: Something appears to be wrong ($ip)";
}
}
}
} else {
my ($name,$aliases,$type,$len,@thisaddr) = gethostbyname($ip);
my ($a,$b,$c,$d) = unpack('C4',$thisaddr[0]);
if ($a and $b and $c and $d) {
if (calculate_ip_range("$a.$b.$c.$d")) {
print STDERR "$cli_exec ($$): $ip points to $a.$b.$c.$d\n"
if ($opt_d);
push @msg, add_ip("$a.$b.$c.$d");
}
} else {
$err = "ERROR: Something appears to be wrong ($ip)";
}
}
# if it doesn't match one of those...
} else {
print STDERR "$cli_exec ($$): Not Recognised $ip\n" if ($opt_d);
$err = "ERROR: Something appears to be wrong ($ip)";
}
if ( ($err and $return_error) or $opt_d ) {
return "$err\n";
} elsif (@msg) {
return @msg;
} else {
print "$0 ($$) : Nothing to return\n" if ($opt_d);
return;
}
}
sub add_ip {
my ($ip) = @_;
print "$0 ($$) : adding $ip\n" if ($opt_d);
if ( ($ip eq $net and $#nets == 0) or $opt_v) {
$dnsaddr = inet_aton($ip);
$dnsname = gethostbyaddr($dnsaddr, AF_INET);
$dnsname = $dnsname?$dnsname:'Not in DNS';
$ip = sprintf("%-16s %-60s", $ip, $dnsname);
}
return($ip);
}