#
#	$RCSfile: net2net.pl,v $
#	$Revision: 0.14 $
#	$Author: ripe-dbm $
#	$Date: 1995/05/30 16:45:19 $
#
# net2net.pl 
#
# net2net ($net_rep, $flag)
#
# This is a general routine which will take various network representations 
# as input and returns the desired transformed representation.
#
# The general supported conversions are as follow:
# 
# 1) CLASSFUL NET to NET/PREFIX
#    i.e. 128.86.0.0 -> 128.86.0.0/16
#
# 2) CLASSFUL RANGE to NET/PREFIX multiples
#    i.e. 128.86.0.0 - 128.91.0.0 
#                          |
#                          +-> 128.86.0.0/15
#                          +-> 128.88.0.0/14
#
# 3) NET/PREFIX verification
#    i.e. This will tell you if this a valid combination
#         If not, it will tell you what you want to know
#
# 4) Integer represented NET / PREFIX to NET/PREFIX
#    i.e. 2153119744/15 -> 128.86.0.0/15
#
# 5) NET/PREFIX to Integer represented NET
#     i.e. 128.86.0.0/15 -> 2153119744/15
#
# 6) NET and MASK to NET/PREFIX
#     i.e. 128.86.0.0 255.254.0.0 -> 128.86.0.0/15
#
#
# Due to possible ambiguity net2net can  take a optional
# defined flag ($flag) for the conversion. If $flag is null or not
# recognised net2net will attempt to work out the desired converstion for
# you. net2net is essentially a wrapper for other and each routine can be
# used on its own if preferred.
#
# It returns a status, $STAT
#            a optional message, $text
#            the converted rep as an array, @return_rep
#
#            return($STAT, $text, @return_rep)
# 
# The individual routines are
#
# clasfn_to_netpre ($net_rep)
# clasfr_to_netpre ($net_rep)
# netpre_verify ($net_rep)
# intnetpre_to_netpre ($net_rep)
# netpre_to_intnetpre ($net_rep)
# netmask_to_netpre ($net_rep)
#
# 
require "misc.pl";		# needed for quad2int and int2quad
require "defines.pl";
#
$CFN2NP     = 1;		# CLASSFUL NET to NET/PREFIX
$CFR2NP	    = 2;		# CLASSFUL RANGE to NET/PREFIX multiples
$NPVRFY	    = 3;		# NET/PREFIX Verification
$INP2NP     = 4;		# Integer represented NET/PREFIX to NET/PREFIX
$NP2INP     = 5;		# NET/PREFIX to Integer represented NET/PREFIX
$NM2NP	    = 6;		# NET and MASK to NET/PREFIX
#
$MODDED     = 2;		# Used to signify something has been modified
#
#
# Not used right now
%mapping = ( $CFN2NP, "CLASSFUL NET to NET/PREFIX",
	    $CFR2NP, "CLASSFUL RANGE to NET/PREFIX multiple",
	    $NPVRFY, "NET/PREFIX verfication",
	    $INP2NP, "Integer represented NET/PREFIX to NET/PREFIX",
	    $NP2INP, "NET/PREFIX to Integer represented NET/PREFIX",
	    $NM2NP, "NET and MASK to NET/PREFIX" );
# 
sub net2net {
    local($net_rep, $flag) = @_;

    local($len);
    local($STAT) = 1;
    local($text) = "";
    local(@returnstring) = ();
    local($one_net);

    if($opt_h) {
	foreach (sort keys %mapping) {
	    print "- $_ - $mapping{$_}\n";
	}
	exit;
    }
    if($opt_v) {
	printf "\$net_rep is \"$net_rep\" - \$flag is \"$flag\"\n";
    }
    if($opt_v && $flag) {
	printf "** Converting %s\n", $mapping{$flag} ;
    }
    
    if($flag eq "") {
	return $NOK, "cant work it out without a flag", @returnstring;
    }
    if($flag == $CFN2NP) { return(&clasfn_to_netpre($net_rep)); }
    
    if($flag == $CFR2NP) { return(&clasfr_to_netpre($net_rep)); }

    if($flag == $NPVRFY)  { return(&netpre_verify($net_rep)); }

    if($flag == $INP2NP) { return(&intnetpre_to_netpre($net_rep)); }

    if($flag == $NP2INP) { return(&netpre_to_intnetpre($net_rep)); }

    if($flag == $NM2NP)  { return(&netmask_to_netpre($net_rep)); } 
    
    return $NOK, "I'm lost", @returnstring;
}
#
sub clasfn_to_netpre {
    local($net_rep) = @_;
    local($len);
    local(@return_rep) = ();
    if ($net_rep =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) {
	if ($1 > 254 || $1 < 1 ||
	    $2 > 255 || $2 < 0 ||
	    $3 > 255 || $3 < 0 ||
	    $4 > 255 || $4 < 0 ) {
	    return $NOK,
	    "$net_rep is not a classful net representation I understand",
	    @return_rep;
	}
	if ($1 >= 224) {	# and why not !!!
	    $len = 32;
	} elsif ($1 >= 192 && $4 == 0) {
            $len = 24;
	} elsif ($1 >= 128 && ($3 == 0 && $4 == 0)) {
            $len = 16;
	} elsif ($2 == 0 && $3 == 0 && $4 == 0) {
	    $len = 8;
	} else {
	    return $NOK,
	    "$net_rep is not a classful net representation I understand",
	    @return_rep;     
	}
	local($val) = &trimnet($net_rep);
	@return_rep = (@return_rep, $val."/".$len);
	return $OK, "", @return_rep;
    } else {
	return $NOK, 
	"$net_rep is not a classful net representation I understand", 
	@return_rep;
    } 
}    

sub clasfr_to_netpre {
    local($net_rep) = @_;
    local($len);
    local(@returnstring) = ();
    local($one_net);
    
    if ($net_rep =~ /^(\d+\.\d+\.\d+\.\d+)\s+\-\s+(\d+\.\d+\.\d+\.\d+)$/) {
	local($oldnet) = $1;	
	local($begin) = &quad2int($oldnet);
	local($end) = &quad2int($2);
	if($end < $begin) {
	    return $NOK, "range is invalid", @returnstring;
	}

	if ($oldnet =~ /(\d+)\.\d+\.\d+\.\d+/) {
	    if ($1 >= 192) {
		$len = 24;
		$one_net = 0x00000100;
	    } elsif ($1 >= 128) {
		$len = 16;
		$one_net = 0x00010000;
	    } else {
		$len = 8;
		$one_net = 0x01000000;
	    }
	}
	local($newbegin) = $begin;
	while ($newbegin <= $end) {
	    for ($pwr=1; $pwr<24; $pwr++) {
		$pwr2 = 2 ** $pwr;
		$thisend = $newbegin + ($pwr2-1)*$one_net;
		return @returnstring if !$newbegin;
		if (($thisend > $end) ||
		    $newbegin != ($newbegin & $masks[$len-$pwr])) {
		    $pwr--;
		    $giveback = sprintf("%s/%d", &int2quad($newbegin),
					$len-$pwr);
		    @returnstring = (@returnstring, $giveback);
		    $newbegin = $newbegin + $one_net * 2**$pwr;
		    last;
		}
	    }
	}
	return $OK, "", @returnstring; 
    } else {
	return $NOK,
	"$net_rep is not a classful range representation I understand", 
	@returnstring;
    } 
}    

sub netpre_verify {
    local($net_rep) = @_;
    local(@returnstring) = ();
    local($i);

    if($net_rep =~ /^(\d+\.\d+\.\d+\.\d+)\/(\d+)$/) {
	local($net) = $1;
	local($len) = $2;
	if(!&isnet($net) || !&islen($len)){
	    return $NOK,
	    "$net_rep has invalid syntax",
	    @returnstring;
	}
	@bytes = split(/\./, $net);
	for ($i = 0 ; $i < 4 ; $i++) {
	    $hval = ($hval << 8) + $bytes[$i];
	}
	$mask = $masks[$len];
       
	$newhval = $hval & $mask;
	if($newhval != $hval) {
	    $m =&printip($mask,$len); # Just get the mask for those who care
	    for ($c = 0 , $bits=$hval ; $bits ; $bits <<= 1 ) {
		$c++;
	    }
	    $usepre = &printip($hval, $len)."/".$c;
	    $uselen = &printip($newhval, $len)."/".$len;
	    	    return $NOK,
	    "$net_rep is an invalid net and prefix combination:-\nlength $len".
		" \(mask $m\) is outside the range of $net\n".
		    "Possible values with these combinates are:\n".
			"based on length $uselen\n".
			    "based on prefix $usepre",
            @returnstring;
	}
	local($val) = &trimnet($net);
	@returnstring = "$val"."/".$len;
	return $OK, "", @returnstring;
    } else {
	return $NOK,
	"$net_rep is not an representation I understand",
	@returnstring;
    }
}	
    

sub intnetpre_to_netpre {
    local($net_rep) = @_;
    local(@returnstring) = ();
    if($net_rep =~ /^(\d+)\/(\d+)$/) {
	local($int) = $1;
	local($len) = $2;
	@returnstring = &int2quad($int)."/"."$len";
	return $OK, "", @returnstring;
    } else {
	return $NOK,
	"$net_rep is not an representation I understand",
	@returnstring;
    }
}
sub netpre_to_intnetpre {
    local($net_rep) = @_;
    local(@returnstring) = ();
    if($net_rep =~ /^(\d+\.\d+\.\d+\.\d+)\/(\d+)$/) {
	local($int) = $1;
	local($len) = $2;
	@returnstring = &quad2int($int)."/"."$len";
	return $OK, "", @returnstring;
    } else {
	return $NOK,
	"$net_rep is not an net/prefix representation I understand",
	@returnstring;
    }
}

sub netmask_to_netpre {
    local($net_rep) = @_;
    local(@returnstring) = ();
    local($NETMASK) = "[nN]*[eE]*[tT]*[mM][aA][sS][kK]";
    local($HEX) = "[0-9a-fA-f]";
    local($HEXMASK) = "0x$HEX$HEX$HEX$HEX$HEX$HEX$HEX$HEX";
    local($IPADDR) = "\\d+\\.\\d+\\.\\d+\\.\\d+";
    local($i);


    if($net_rep =~ /^$IPADDR\s+($NETMASK)*\s*$IPADDR$/
       || $net_rep =~ /^$IPADDR\s+($NETMASK)*\s*$HEXMASK$/) {
	local($n) = $1;
	local($m) = $3;
	
	if (&isnet($n)) {
	    if($m =~ /\./) {
		$hexm = &quad2int($m);
	    }  else {
		$hexm = hex($m);
	    }
	    
	    foreach $i (0..$#masks) {
		if($hexm == $masks[$i]) {
		    local($val) = "$n"."/"."$i";
		    local($stat, $text, @array) = 
			&netpre_verify("$val");
		    if($stat == $OK) {
			@returnstring = "$val";
			return $OK, "", @returnstring;
		    } else {
			return $NOK, 
			"$net_rep translates to $val\n$text", @returnstring;
		    }
		}     
	    }
	    local($tmp) = $hexm;
	    $tmp =~ s/^0x//;
	    local($msk) = &int2quad($tmp+0);
	    local($hex) = sprintf("0x%8x\n", $tmp);
	    chop($hex);
	    $hex =~ s/ /0/g;
	    return $NOK, "mask $msk \($hex\) is invalid", @returnstring;

	} else {
	    return $NOK, "$net_rep is invalid", @returnstring;
	}
    }
}

sub isnet {
    local($net) = @_;
    local($i);
    if($net !~ /^\d+\.\d+\.\d+\.\d+$/) {
	return 0;
    }
    local(@quads) = split(/\./, $net);
    for ($i=0; $i<4 ; $i++ ) {
	if ($quads[$i] < 0 || $quads[$i] > 255) {
	    return 0;
	}
    }
    return 1;
}

sub islen {
    local($len) = @_;
    if( $len < 0 || $len > 32) {
	return 0;
    } else {
	return 1;
    }
}

sub printip
{
    local($ip, $len) = @_ ;
    local( $s );

    $byte0 = $ip >> 24 ;
    $byte1 = ($ip & 0x00ff0000) >> 16 ;
    $byte2 = ($ip & 0x0000ff00) >>  8 ;
    $byte3 = ($ip & 0x000000ff) ;

    if (($byte3) || $len > 24 ) { 
	$s=sprintf ("%d.%d.%d.%d", $byte0, $byte1, $byte2, $byte3 );
    } elsif ( ($byte2) || $len > 16 ) { 
	$s=sprintf ("%d.%d.%d.0", $byte0, $byte1, $byte2 );
    }
    elsif ( ($byte1) || $len > 8 ) { 
	$s=sprintf ("%d.%d.0.0", $byte0, $byte1 );
    } else { 
	$s=sprintf ("%d.0.0.0", $byte0 );
    }
    return( $s );
}

sub trimnet {
    local($quad) = @_;
    return(&int2quad(&quad2int($quad)));
}
1;

