#!/usr/local/bin/perl -w

# Copyright 2005 Thomas A. Limoncelli
# 
#     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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA

use strict;

# Defaults and formats:
my @FORMATS = (
		'#$ip\tUNUSED$hex.$DOMAIN',
		'$ip\tUNUSED$hex.$DOMAIN',
		'$ip\tUNUSED-$hex.$DOMAIN',
		'$ip\th-$ipsep.$DOMAIN',
		'$ip\tdyn-$ip.$DOMAIN\@EXTERNAL',
		);
my $DOMAIN  = 'domain.com';
my $DEFAULT_IPSEPARATOR = '-';

# Process command line info::
use Getopt::Std;
my %opts;
getopts("hd:1234s:", \%opts);
sub usage {
        print <<HERE

Prints a range of IP addresses in a specifed format.

$0 [-h] [-1234] [-d domain] [-s str] startip count [format]
   or
$0 [-h] [-1234] [-d domain] [-s str] startip endip [format]

  startip      The starting IP address.
  count        How many IP following to print.
  endip        Last IP address to list.
  format       The format to use (defaults to $FORMATS[0])
  Format strings may include:
                \$ip     ip address in 1.2.3.4 format
                \$ipsep  ip address in 1x2x3x4 format where "x" is defined by -s
                \$hex    ip address as a hex string: A016406
                \$a \$b \$c \$d    the a.b.c.d of the IP address

  -h          This help menu.
  -d domain   The domain to use. Default is $DOMAIN
  -s str      Use str instead of "." in \$ipsep addresses (default $DEFAULT_IPSEPARATOR)

Pre-defined formats:
Default  $FORMATS[0]
         #10.1.100.4	UNUSED0A016404.domain.com
     -1  $FORMATS[1]
         10.1.100.4	UNUSED0A016404.domain.com
     -2  $FORMATS[2]
         10.1.100.4	UNUSED-0A016404.domain.com
     -3  $FORMATS[3]
         10.1.100.4	h-10-1-100-4.domain.com
     -4  $FORMATS[4]
         10.1.100.4	dyn-10.1.100.4.domain.com\@EXTERNAL

Examples:
	Generate a Class C sized set of hosts:
		$0    10.1.100.0 256
	Generate a range of external addresses:
		$0 -4 64.32.179.65 64.32.179.68
	Generate a custom format to make hostnames ending with the 4th octet:
		$0    64.32.179.65 4 \$ip\\thost\$d.mydomain.com'
	Generate using a very serious custom format:
		$0    64.32.179.65 4 '\$ip\\thost\$hex.mydomain.com'
	Generate using a very silly custom format:
		$0    64.32.179.65 1 '\$ip is \$hex in hex'
HERE
;
        exit 1;
}

# Process the options:

# Help
&usage if $#ARGV < 1 or $opts{'h'};

# Parameters:
my $STARTIP=shift @ARGV; #print "IP=$STARTIP\n";
my $COUNT=shift @ARGV; #print "COUNT=$COUNT\n";

# Domain:
$DOMAIN = $opts{'d'} if $opts{'d'};
my $IPSEPARATOR = $opts{'s'} || $DEFAULT_IPSEPARATOR;

# Select the format string:
my $FORMAT = $FORMATS[0];		# default
$FORMAT = $FORMATS[1] if $opts{'1'};	# preset #1
$FORMAT = $FORMATS[2] if $opts{'2'};	# preset #2
$FORMAT = $FORMATS[3] if $opts{'3'};	# preset #3
$FORMAT = $FORMATS[4] if $opts{'4'};	# preset #4
$FORMAT=shift @ARGV if $#ARGV > -1;	# on the command line
#print "FORMAT=$FORMAT\n";

# Calculate the start and end IP address and numeric equiv:
my $startnum = ip2num($STARTIP);

my $endnum;
if ($COUNT =~ /\./) {		# does value include a "."?
	# it's an IP addr:
	$endnum = ip2num($COUNT);
} else {
	# it's a count:
	$endnum = $startnum + $COUNT - 1;
	$endnum = 0xffffffff if $endnum > 0xffffffff;
}

my($ip, $ipsep, $hex);
my($a, $b, $c, $d);
for (my $i = $startnum; $i <= $endnum; $i++ ) {
	$ip = num2ip($i);
	($a, $b, $c, $d) = split(/\./, $ip);
	$ipsep = num2ipsep($i, $IPSEPARATOR);
	$hex = num2hex($i);
	#print "#", $ip, "\tUNUSED", $hex, ".", $DOMAIN, "\n";
	eval print eval qq{"$FORMAT\n";};
}

# --------------

sub ip2num {
	my ($ip) = @_;
	# I'm sure there's a better way to convert an IP address into an integer but...
	my @parts = split(/\./, $ip);
	return	($parts[0] << 24) +
		($parts[1] << 16) +
		($parts[2] << 8 ) +
		$parts[3];
}

sub num2ip {
	my ($num) = @_;
	return	         ($num >> 24)
		. '.' . (($num >> 16) % 256)
		. '.' . (($num >>  8) % 256)
		. '.' .  ($num        % 256);
}

sub num2ipsep {
	my ($num, $sep) = @_;
	return	          ($num >> 24)
		. $sep . (($num >> 16) % 256)
		. $sep . (($num >>  8) % 256)
		. $sep .  ($num        % 256);
}

sub num2hex {
	my ($num) = @_;
	return sprintf("%08X", $num);
}
