#!/usr/bin/perl
#
# This program is the perl implementation of the getreserved.py by Andy Wiggin
# (ftp://ftp.shorewall.net/pub/shorewall/contrib/iana_reserved/getreserved.py).
# It can be used to automatically generate Shorewall 2.x bogons file according
# to IANA allocations (http://www.iana.org/assignments/ipv4-address-space).
#
# update-bogons ver. 0.1 
# Copyright (C) 2004 Lorenzo Martignoni <lorenzo.martignoni@poste.it> 
#
# This program is licensed under the GPL license. For more information about
# the license please visit http://www.gnu.org/licenses/gpl.txt

sub error {
	 ($msg) = @_;
	 
	 print "update-bogons: Update aborted. $msg\n";
	 exit 1;
}

sub download {
	 do {
		  $f = `mktemp`;
	 } while (-f $f);

	 chomp($f);

	 $res = system("wget -q http://www.iana.org/assignments/ipv4-address-space -O $f");
	 if ($res != 0) {
		  unlink($f);
		  error("Unable to download the up-to-date ipv4-address-space.");
	 }

	 return $f;
}

sub parse {
	 ($file) = @_;
	 $i = 0;
	 @addr;
	 
	 
	 $res = open(TMP, $file);
	 if ($res == undef) {
		  unlink($res);
		  error("Unable to open the temporary file: $f.");
	 }

	 while ($line = <TMP>) {
		  chomp($line);
		  if ($line =~ /IANA - Reserved/ || $line =~ /IANA - Private Use/ || $line =~ /Returned to IANA/) {
				@tmp = split(/   /, $line);
				($ip, $mask) = split(/\//, $tmp[0]);
				
				@ip_range = split(/-/, $ip);
				$ip_min = $ip_range[0];
				if ($#ip_range >= 1) {
					 $ip_max = $ip_range[1];
				} else {
					 $ip_max = $ip_min;
				}

				for ($ip = $ip_min; $ip <= $ip_max; $ip++) {
					 $tmp = {};
					 $tmp->{ip} = $ip << 24;
					 $tmp->{mask} = $mask;
					 push @addr, $tmp;
				}
		  }
	 }

	 close LIST;
	 return @addr;
}

sub maskbits {
	 ($in) = @_;

	 $numbits = 32 - $in;
	 $retmask = 0;

	 for ($i = 0; $i < $numbits; $i++) {
		  $retmask = ($retmask << 1) + 0x1;
	 }

	 return($retmask);
}

sub compact {
	 (@in) = @_;

	 # sort the networks 
	 @oldlist = sort { ($a->{ip}) <=> ($b->{ip}) } @in;

	 $done = 0;
	 
	 while (!$done) {
		  undef @newlist;
		  undef $head;

		  $done = 1;
		  while ($#oldlist >= 0) {
			  if (not defined $head) {
					$head = shift @oldlist;
			  } else {
					$next = shift @oldlist;
					$canmerge = 0;
	
					if ($head->{mask} == $next->{mask}) {
						 $nnxor = $head->{ip} ^ $next->{ip};
						 $mask = maskbits($head->{mask});
						 $nextbit = ($mask << 1) & ~$mask;
						 if ($nnxor == $nextbit) {
							  $canmerge = 1;
						 }
					}
	
					if ($canmerge == 1) {
						 $tmp = {};
						 $tmp->{ip} = $head->{ip};
						 $tmp->{mask} = $head->{mask} - 1;
						 push @newlist, $tmp;
						 undef $next;
						 undef $head;
						 
						 $done = 0;
					} else {
						 push @newlist, $head;
						 $head = $next;
					}
			  }

		 }

		  if (defined $head) {
				push @newlist, $head;
		  }
		  
		  @oldlist = @newlist;
	 }
	 
	 return(@oldlist);
}

sub output {
	 ($addr, $outf) = @_;

	 $date = `date`;

	 $res = open(OUT, "> $outf");
	 if ($res == undef) {
		  error("Unable to write to output file: $outf.");
	 }

print OUT <<EOF;
###############################################################################
#
# Shorewall 2.0 -- Bogons File 
#
# Lists the subnetworks that are blocked by the 'nobogons' interface option. 
#
# The list includes those ip ADDRESSES listed as 'reserved' by the IANA and is
# automatically generated by shorewall using
# http://www.iana.org/assignments/ipv4-address-space
#
# Moreover the list includes the DHCP Autoconfig class B, and the class C
# reserved for use in documentation and examples.
#
###############################################################################
#SUBNET                 TARGET

0.0.0.0                 RETURN          # Stop the DHCP whining
255.255.255.255         RETURN          # We need to allow limited broadcast
169.254.0.0/16          DROP            # DHCP autoconfig
192.0.2.0/24            logdrop         # Example addresses (RFC 3330)

# The list below has been updated on $date
EOF

for ($i = 0; $i <= $#addr; $i++) {
	 print(OUT ($addr[$i]->{ip} >> 24).".0.0.0/$addr[$i]->{mask}\t\tlogdrop\t\t\# Reserved by IANA\n");	 
}

	 print OUT <<EOF;

# end of file
EOF

	 close(OUT);
}

###########################
$file = download();

$outd = "/var/lib/shorewall/updated-bogons";

if (! -d $outd) {
	 mkdir($outd) || error("Unable to create directory: $outd");
}

$outf = "$outd/bogons";

# check whether the bogons is up-to-date or not
if (-f $outf) {
	 @outstat = stat($outf);
	 @fstat = stat($file);
}

if (! -f $outf || $outstat[9] < $fstat[9]) {
	 @addr = parse($file);
	 @addr = compact(@addr);

	 output(\@addr, $outf);
}

unlink($file);
