package Net::IPNetMember;

use strict;
use Carp;
use Socket;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);

require Exporter;
use AutoLoader 'AUTOLOAD';

@ISA = qw(Exporter);
# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
@EXPORT = qw(
	
);
$VERSION = '1.08';

# Preloaded methods go here.

sub new
   {
   my $class = shift;
   my $NetGroup = {};

   bless $NetGroup, $class;
   return $NetGroup; 
   }

sub find
{
  my ($obj, $ipin) = @_;

  my $ip = unpack("N", inet_aton($ipin));
  
  foreach my $pre ( reverse 0 .. 32 )
  {
    my $mask = unpack("N", pack("B32", "1"x($pre)."0"x32 ) );

    my $net = $ip & $mask;
    
    my $network = inet_ntoa(pack("N",$net));
   
    if( defined $obj->{'Net'}[$pre]{$net} )
    {
      return $obj->{'Net'}[$pre]{$net};
    }
  }
  return;
}



sub print
{
  my ($obj) = @_;

  my $status = 0; # FALSE
  my ($ref, $name, $network, $mask);

  foreach my $pre ( 0 .. 32 )
  {
    foreach $ref ( keys %{$obj->{'Net'}[$pre]} )
    {
      my $name    = $obj->{'Net'}[$pre]{$ref};
      my $net     = inet_ntoa( pack( "N", $ref ) );
      print "Net: $net/$pre - Name: $name\n";
      $status = 1; # TRUE
    }
  }
  return($status);
}
   

sub add
{
  my ($obj, $grp_name, $netin, $maskin) = @_;

  my($status, @table, $pre, $ip );

  $status = 0; # FALSE

  # if mask given: get prefix
   
  if( defined $maskin )
  {
    if( verif_ip_is_ok($maskin) )
    {
      if( unpack( "B32", inet_aton($maskin) ) =~ /^(1*)0*$/ )
      {
        $pre = length($1);
      }
      else
      {
        carp "invalid subnet mask $maskin: no prefix!";
      }
    }
    else
    {
      carp "illegal netmask $maskin";
    }
  }
  elsif( $netin =~ /([0-9\.]+)\/([0-9]+)/ )
  {
    $netin = $1;
    $pre = $2;
  }
  else
  {
    $pre=32;
  }

  if ( verif_ip_is_ok($netin) )
  {
    # compute network from net and mask
    
    my $net  = unpack("N", inet_aton($netin) );
    my $mask = unpack("N", pack("B32", "1"x($pre)."0"x32 ) );

    $net  &= $mask;

    $obj->{'Net'}[$pre]{$net}=$grp_name;
    $status = 1; # TRUE
  }
  else
  {
    carp "illegal network $netin";
  }
  return($status);
}


sub verif_ip_is_ok
   {
   my($ip) = @_;

   my $status = 0; # FALSE

   if ( $ip =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ )
      {
      if ( $1 >= 0 and $1 <= 255 and
           $2 >= 0 and $2 <= 255 and
           $3 >= 0 and $3 <= 255 and
           $4 >= 0 and $4 <= 255 ) # IP is OK
         {
         $status = 1; # TRUE
         }
      }
   return($status);
   }

# Autoload methods go after =cut, and are processed by the autosplit program.

1;
__END__
=head1 NAME

Net::IPNetMember - Perl extension to determine in which network an IP address belongs. 

=head1 SYNOPSIS

  use Net::IPNetMember;

  $ipnets = new Net::IPNetMember();

  $ipnets->add(group name, network block, netmask);
  $ipnets->add(group name, network block/prefix);
  $IPNet = $ipnets->find(IP); 


=head1 DESCRIPTION

Net::IPNetMember creates groups of IP networks and allows searching 
for specific IPs, giving the name of the network group they belong to.

For example:

  use Net::IPNetMember;

  $ipnets = new Net::IPNetMember();

  $ipnets->add("New York", "210.210.10.0", "255.255.255.0");
  $ipnets->add("New York", "210.210.11.0", "255.255.255.0");
  $ipnets->add("New York", "210.210.12.0", "255.255.254.0");
  $ipnets->add("Rio de Janeiro", "200.255.49.128", "255.255.255.128");
  $ipnets->add("Rio de Janeiro", "200.255.50.0", "255.255.252.0");
  $ipnets->add("Rio de Janeiro", "200.255.60.0", "255.255.255.0");
  $ipnets->add("RfC1918", "192.168.0.0/16");
  $ipnets->add("RfC1918", "172.16.0.0/12");
  $ipnets->add("RfC1918", "10.0.0.0/8");
  $ipnets->add("MyLocalNetwork", "192.168.215.0/24");

  $City = $ipnets->find("200.255.60.10");  # $City will be set to "Rio de Janeiro"
  $rfcaddress = $ipnets->find("192.168.10.160"); # $rfcaddress will be "RfC1918"
  $rfcaddress = $ipnets->find("192.168.215.31"); # $rfcaddress will be "MyLocalNetwork"
                                                 # it's the most specific network
  if ( $City = $ipnets->find("210.210.9.5") ) # Will be false

=head1 COPYRIGHT

  Copyright (C) 2001 Marc Haber. All rights reserved. This program is
  free software; you can redistribute it and/or modify it under the same
  terms as Perl itself.

  Marc Haber <mh+debian-packages@zugschlus.de>

  This is a fork from Net::GrpNetworks, Copyright (c) 1997 Andre
  Rodrigues Viegas <andre.viegas@writeme.com.br>. All rights reserved.
  This program is free software; you can redistribute it and/or modify
  it under the same terms as Perl itself. 

  I offered Andre my code to incorporate into Net::GrpNetworks, but
  unfortunately, he stopped answering to my e-mails before anything
  could be accomplished. Hence, I had to fork. I still think that both
  code bases should be brought together again, but responsive upstream
  is needed for that.

=head1 AUTHOR

Marc Haber <mh+debian-packages@zugschlus.de>. Taken from Andre's
original man page

