[Mimedefang] Stupid question? - subnet

jmiller at purifieddata.net jmiller at purifieddata.net
Mon Feb 10 18:07:01 EST 2003


On Mon, 10 Feb 2003, David F. Skoll wrote:

> On Mon, 10 Feb 2003, Jim McCullars wrote:
>
> >    It's a string comparison, so you may be looking at a fairly complicated
> > regex to do the test you want (I say "may be" because I don't know how to
> > decode a CIDR address string to a range of addresses).
>
> You might want to look at a Perl module to do CIDR comparisons; I think
> Net::CIDR::Lite might be just the ticket:
>
> http://search.cpan.org/author/DOUGW/Net-CIDR-Lite-0.14/Lite.pm
>
> Or perhaps NetAddr::IP is more to your liking:
>
> http://search.cpan.org/author/LUISMUNOZ/NetAddr-IP-3.14/IP.pm
>
> Or Net::CIDR:
>
> http://search.cpan.org/author/MRSAM/Net-CIDR-0.04/CIDR.pm
>
> What was that horrible Perl slogan again?  "TIMTOWTDI"?
>

Just thought I'd append some ip calculation code I wrote a long time
ago (btw, I used code out of ipcalc.pl, but I don't know where that lives
anymore). This will do the job you want and a lot more without the
need for more modules, though they'll probably do the job just as well.

An easier way would be to specify the begining and end IP's in a range you
want to match, and use the ip2dec sub below to convert all ips to decimal,
and check to see if the given IP is in that range.

--
Josh I.

Disclaimer: this code was heavily tested, but pulled it out of another
module I wrote, so if it doesn't work, or blows up your machine, it's not
my fault, I could have missed something, etc :-) As it is here, it hasn't
been tested.
Also, it should probably be considered under the GPL cause a few pieces
are pulled right out of some other GPL'd code (ipcalc.pl).

	if (&is_in_netblock($RelayAddr,'62.253.177.96/27') {
		## do stuff if it's in there
	}
	# this should also work:
	if (&is_in_netblock($RelayAddr,'62.253.177.96/255.255.255.224') {
		## do stuff if it's in there
	}
	# this should also work (notice block IP isn't a valid start of a
	# netblock, the code will figure out where the block really stats/ends
	# and see if the RelayAddr is in the real block, allowing you to be
	# a little sloppy/careless)
	if (&is_in_netblock($RelayAddr,'62.253.177.101/27') {
		## do stuff if it's in there
	}

sub is_in_netblock
{
	my ($ip,$block) = @_;
	my ($b_ip,$b_mask) = split(/\//,$block);
	$ip = &is_valid_dq($ip);
	$b_ip = &is_valid_dq($ip);
	if ( (! $ip) || (! $b_ip) ) { return 0; }
	return 0 unless &is_valid_netmask($b_mask);

	my $d_ip = &ip2dec($ip);
	my ($network,$broadcast) = &get_net_broad($b_ip,$b_mask);
	my $d_net = &ip2dec($network);
	my $d_broad = &ip2dec($broadcast);
	if ( ($d_ip <= $d_broad) && ($d_ip >= $d_net) )
	{
		return 1;
	} else {
		return 0;
	}
}
sub get_net_broad
{
	my ($host,$mask);
	if ( (defined($_[0])) && (defined($_[1])) && ($host = &is_valid_dq($_[0])) && ($mask = is_valid_netmask($_[1])) )
	{
		my $m  = pack( "B*",("1" x $mask) . ("0" x (32 - $mask)) );
		my $h = dqtobin($host);
		my $n = $h & $m;
		my $nm = pack( "B*",("0" x $mask) . ("1" x (32 - $mask)) );
		my $b = $n | $nm;
		my $hmin  = pack("B*",("0"x31) . "1") | $n;
		my $hmax  = pack("B*",("0"x $mask) . ("1" x (31 - $mask)) . "0" ) | $n;

		my $origonaladdress = $host;
		my $network = &bintodq($n);
		my $bitmask = $mask;
		my $fullnetmask = &bintodq($m);
		my $minhost = &bintodq($hmin);
		my $maxhost = &bintodq($hmax);
		my $broadcast   = &bintodq($b);
		my $numberOhosts = (2 ** (32 - $mask)) -2  ;
		my $numberOips = (2 ** (32 - $mask));
#		my @returnvals = ($origonaladdress,$network,$bitmask,$fullnetmask,$minhost,$maxhost,$broadcast,$numberOhosts,$numberOips);
		return ($network,$broadcast);
	} else {
		return 0;
	}
}
sub is_valid_netmask {
	my $mask = $_[0];
	if ($mask =~ /^\d+$/) {
		if ( ($mask > 32) || ($mask < 1) ) {
			return 0;
		}
	} else {
		if (! ($mask = &is_valid_dq($mask)) ) {
			return 0;
		}
		$mask = dqtocidr($mask);
	}
	return $mask;
}
sub dqtocidr
{
	my $dq = shift;
	$b = &dqtobin($dq);
	my $cidr = 1;
	while (unpack("B$cidr",$b) !~ /0/) {
		$cidr++;
		last if ($cidr == 33);
	}
	$cidr--;
	return $cidr;
}
sub dqtobin {
	my @dq;
	my $q;
	my $i;
	my $bin;

	foreach $q (split /\./,$_[0]) {
		push @dq,$q;
	}
	for ($i = 0; $i < 4 ; $i++) {
		if (! defined $dq[$i]) {
			push @dq,0;
		}
	}
	$bin    = pack("CCCC", at dq);      # 4 unsigned chars
	return $bin;
}
sub ip2dec
{
	return(&bin2dec(&ip2bin(shift)));
}
sub bin2dec
{
	return unpack("N", pack("B32", substr("0" x 32 . shift, -32)));
}
sub ip2bin
{
	my $ipaddr;
	if ($ipaddr = &is_valid_dq(shift)) {
		my @ip = split('\.',$ipaddr);
		foreach(@ip) {
			my $bin = &dec2bin($_);
			$bin = substr($bin,-8); # takes just last 8 chars
			$_ = $bin;
		}
		my $bin_ip = join('', at ip);
		return $bin_ip;
	} else {
		return 0;
	}

}
sub is_valid_dq {
	my $value = $_[0];
	my $test = $value;
	my $i;
	my $corrected;
	$test =~ s/\.//g;
	if ($test !~ /^\d+$/) {
		return 0;
	}
	my @value = split /\./, $value, 4;
	for ($i = 0; $i<4; $i++) {
		if (! defined ($value[$i]) ) {
			$value[$i] = 0;
		}
		if ( ($value[$i] !~ /^\d+$/) ||
			 ($value[$i] < 0) ||
			 ($value[$i] > 255) )
		{
			return 0;
		}
	}
	$corrected = join ".", @value;
	return $corrected;
}




More information about the MIMEDefang mailing list