# Copyright (c) 1999						RIPE NCC
#
# All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose and without fee is hereby granted,
# provided that the above copyright notice appear in all copies and that
# both that copyright notice and this permission notice appear in
# supporting documentation, and that the name of the author not be
# used in advertising or publicity pertaining to distribution of the
# software without specific, written prior permission.
#
# THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
# AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
# AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#------------------------------------------------------------------------------
# Module Header
# Filename          : Reg::Allocation.pm
# Purpose           : Get an allocation 
# Author            : AA <antony@ripe.net> <softies@ripe.net>
# Date              : 19980921
# Description       : Get inetnum objects from RIPE Whois db 2.1 
# Language Version  : perl5.04004, perl5.00502
# OSs Tested        : BSDI
# Command Line      : 
# Input Files       : 
# Output Files      :
# External Programs : 
# Problems          :
# To Do             : use conf file from whois db to read attributs
#                     Don't use STDERR in the module
#
#
# Comments          : if RIPE DB change changed attribute check the consistancy
#                   : this module  uses -F to get fast output.
#		    : Module uses %main::opt which isn't graceful.
# $Id: in.pm,v 2.49 2003/07/28 14:21:56 timur Exp $
#------------------------------------------------------------------------------

package Net::RIPEWhois::in;

use strict;
use vars qw(@ISA @EXPORT @EXPORT_OK);

require Exporter;

@ISA = qw(Exporter);

@EXPORT = qw();

use vars qw($VERSION);

$VERSION = '0.03';

# Time functions
use Time::Local;
# RIPE NCC delegations
use NCC::RipeDelegations;
# Make Whois queries
use RipeWhois;
# Manipulations with ip address ranges and prefixes
use ipv4pack;

use vars qw($NETNAME_MISMATCH $MULTIPLE_INETNUM $INVALID_IP4_RANGE 
            $INVALID_DATE $MNT_BY_MISMATCH $STATUS_MISMATCH);

#2xx  Allocations
$MNT_BY_MISMATCH =  211;  # incorrect MNT-BY Attribute
my $MNT_BY_MULTIPLE = 212;    # multiple mnt-by attribute
my $STATUS_MISSING  = 213;    # missing status
my $STATUS_OTHER   = 214;    # other status
$NETNAME_MISMATCH  = 215;    # netname mismatch
$STATUS_MISMATCH   = 216;    # status mismatch 
# NO_ALLOC_INREG   = 217;
# NO_REGID_FOUND   = 218;
# REGID_MISMATH    = 219;

#5xx 
my $NOT_IN_RIPE_DELEGATION = 511;
$INVALID_IP4_RANGE = 512;
$INVALID_DATE  = 513;
my $SOURCE_NOT_RIPE = 514;

#3xx  $Assignments 
my $NO_ALLOC_INDB = 301;
my $SIZE_MISMATCH = 302;
my $NOT_WITHIN_ALLOCATION = 311;
my $NOT_APPROVED = 321;
$MULTIPLE_INETNUM = 322;

#4xxx
my $NO_VALID_ALLOCS = 401;
my $NO_ALLOCS = 402;

# Preloaded methods go here.
@EXPORT_OK = qw($MNT_BY_MISMATCH $INVALID_DATE $INVALID_IP4_RANGE
        	$STATUS_MISMATCH $NETNAME_MISMATCH $MULTIPLE_INETNUM  
    		&checkYYYYmmDD);

# We do have 3 RIPE maintainers at the moment:
# 'RIPE-NCC-MNT', 'RIPE-NCC-HM-MNT', 'RIPE-NCC-HM-PI-MNT'

my %default = (
    'validNa'  => [],  # list of the valid netnames for allocations
    'validMb'  => ['RIPE-NCC-HM-MNT'],	# valid mnt-by for allocations
    'whoisOpt' => '-r -T in',		# default options for inetnum query
    'validSt'  => [			# valid status for allocations 
		    'ALLOCATED\s+PA',
		    'ALLOCATED\s+PI',
		    'ALLOCATED\s+UNSPECIFIED',
		    'LIR\-PARTITIONED\s+P[AI]',
		    'SUB\-ALLOCATED\s+P[AI]',
		  ],
    # Correct status allocation inetnum objects
    'strict'   => [], # not yet used. for checking error levels
    'errNo'    => '', # error number. 
    'errMsg'   => '', # error Message 
    'dbAlloc'  => [], # list of db allocations
    'query'    => [], # inetnum query
    'dbAssign' => [], # list of assignments
     # list of assignments of  size counted 
    'classFullSizes' => ['256', '512', '1024', '2048', '4096'],
);

#------------------------------------------------------------------------------
# Purpose           : bless the new progeny
# Side Effects      : 
# Comments          : 
# 
sub new {
    my $proto = shift;
    my $class = ref($proto) || $proto;
    my %param = (%default, @_);
    my $self = { %param };
    
    bless $self, $class;
    
    # Create a list IP delegations to RIPE
    my $deleg = new NCC::RipeDelegations();
    # XXX: We treate abcence of the list as 
    # non-fatal error at the moment
    if(ref($deleg)) {
	$self->{'Delegations'} = $deleg;
    }
    
    # Make new whois object if necessary
    unless(ref($self->{'Whois'})) {
	# We create an object with stripped comments by default
	my $whois = new RipeWhois('FormatMode' => 1);
	# On error don't create and instance of the class
	return unless($whois);
	# Store the object for farther use
	$self->{'Whois'} = $whois;
    }
    
    return $self;
}

# Some people have reported that Net::DNS dies because AUTOLOAD picks up
# calls to DESTROY.
# I saw that once.

sub DESTROY {}

#------------------------------------------------------------------------------
# Purpose           : set/get the mnt-by:  attribute
# Side Effects      : 
# Comments          : Want do better syntax check 
#                     has to read RIPE DOC
sub validMb {
    my $self = shift;
    my(@validMb) = @_; # list of valid mnt-by values
    $self->{'validMb'} = [@validMb] if(@validMb);
    return  @{$self->{'validMb'}};  # valid mnt-by values
}

#------------------------------------------------------------------------------
# Purpose           : set/get the NetName
# Side Effects      : 
# Comments          : Want do better syntax check 
#                     has to read RIPE DOC
sub validNa {
    my $self = shift;
    my(@validNa) = @_;
    $self->{'validNa'} = [@validNa] if(@validNa);
    return  @{$self->{'validNa'}};  # valid Na
}

#------------------------------------------------------------------------------
# Purpose           : set/get the error message 
# Side Effects      : 
# Comments          :
# IN                : errorno, errmsg to set message
#                     undef, undef, true to reset message
#                     with out any arg to get error
# OUT               : list error no. errmsg
#                     undef  when called with undef, undef, true
#
#
# The errors are stored in list, return value is recent one scalar
# If you need a list access  @{$self->{'errMsgAll'}},set $debug 

sub error {
  my $self = shift;  

  my($num,  #error no 
     $msg,  #scalar message 
     $reset #reset flag used for resetting list 
     ) = @_; 

    #set error message and number

    #reset the error message and number 
    if($reset){
	$self->{'errNo'} = '';
	$self->{'errMsg'} = '';
    } 
    # We have error code
    elsif($num) {
	$msg ||= ''; # but, maybe, don't have message
	
	$self->{'errNo'} = $num;
	$self->{'errMsg'} = $msg;
      
	#if debug  is used keep a list of errors
	if($self->{'debug'}){
    	    push(@{$self->{'errNoAll'}}, $num);
	    push(@{$self->{'errMsgAll'}}, $msg);
	}
    }
    # else - why did you call me :)?

  return ($self->{'errNo'},   #error number
          $self->{'errMsg'}) if($self->{'errNo'}); #error message

  return; #on no errors
}

#------------------------------------------------------------------------------
# Purpose           : Get inetnum objects from database.
# Side Effects      :  
# Comments          : We enforce fast (-F) output
# IN                : ref RipeWhois, query string, query opt
# OUT               : list of inetnums, undef on error

sub getIn {
    my $self = shift;
    my($inQuery, $whoisOpt) = @_; 
    
    # Use default options if nothing passed
    $whoisOpt = $self->{'whoisOpt'} unless($whoisOpt);
    
    # Get inetnum objects 
    my @objects = $self->{'Whois'}->QueryObjects('-F', $whoisOpt, $inQuery);
    # Return nothing if no results
    return unless(@objects);
    
    my @in; # List of recognized inetnum objects
    
    # Parse each of the objects
    foreach my $object (@objects) {
        my $inetnum; # temp inetnum
	my %in;	     # inetnum object
        # Split each object into lines
        foreach my $line (split(/\n/, $object)) {
	    # Parse each line
	    # XXX: (.*) means, that value can be empty; not v.3 compliant
	    if($line =~ /^\*(\w\w):\s+(.*?)\s*$/) {
		# Extract attribute/value pair
		my($attr, $value) = ($1, $2);
		
		if($attr eq 'in') {
		    # Normalize the inetnum range
		    my($in, $resp) = normalizerange($value);
		    
		    # BaT 20010410
		    # XXX: Skip the encompassing object, we hardcode it here
		    if($resp == $O_OK && $value eq '0.0.0.0 - 255.255.255.255') { 
                	# Skip this object
			last;
		    }
		    elsif($resp == $O_PRIVATERANGE && $value eq '192.0.0.0 - 192.255.255.255') { 
                	# Skip this object
			last;
		    }
		    elsif(($resp > 1) && ($resp != $O_CLASSFULL) && ($resp != $O_IPADDRONLY)) {
			# Error for normalizerange
			my $error = error2str($resp);
			$self->error($INVALID_IP4_RANGE, 
				"Invalid IP address range $value: $error");
			return;
		    }
		    
		    # Get start and end of the IP range
		    my($quadStart, $quadEnd) = split(/\-/, $in);
		    my $start = quad2int($quadStart);
		    my $end   = quad2int($quadEnd);
		    
		    # If it is strange object  probably DB people will say it is not possible
		    if(($start < 0) || ($end < 0) || ($end < $start)) {
			# Strange Object, never seen one so far,but IMHO it is possible
			$self->error($INVALID_IP4_RANGE,
                    		"Invalid IP address range $value");
                	return;
		    }
		    # New valid normalized inetnum object
		    $inetnum = $in;
                    # Initialize the hash values
                    initHashValues(\%in);
                    # Set ref to self
                    $self->{$inetnum} = \%in;
		    # Fill in the hash
                    $in{'start'} = $start;
                    $in{'end'} = $end;
                    $in{'size'} = $end - $start + 1;

                    # Catch the old style classfull object
                    # Produce the warning.
		    if($resp == $O_CLASSFULL) {
			my $warning = sprintf("Classfull notation %s?", $in{'size'});
			push(@{$in{'warning'}}, $warning);
                    }
		    elsif($resp == $O_IPADDRONLY) {
                	#special case for /32 inetnum in the format a.b.c.d
			my $warning = sprintf("Range with one address %s?", $in{'size'});
                        push(@{$in{'warning'}}, $warning);
		    }
		}
		
		# Next get all needed attributes into hash
		elsif($inetnum && $attr eq 'na') {
		    # Netname
		    $in{'na'} = $value;
		}
		elsif($inetnum && $attr eq 'st') {
		    # Status
		    $in{'st'} = $value;
		}
		elsif($inetnum && $attr eq 'mb') {
		    # Mnt-by
		    push(@{$in{'mb'}}, $value);
		}
		elsif($inetnum && $attr eq 'ac') {
		    # Admin-c
		    push(@{$in{'ac'}}, $value);
		}
		elsif($inetnum && $attr eq 'tc') {
		    # Tech-c
		    push(@{$in{'tc'}}, $value);
		}
		elsif($inetnum && $attr eq 'ch') {
		    # Changed
		    push(@{$in{'ch'}}, $value);
		}
		elsif($inetnum && $attr eq 'so') {
		    # Source
		    $in{'so'} = $value;
		}
		elsif($inetnum && $attr eq 'ml') {
		    # Mnt-lwr
		    push(@{$in{'ml'}}, $value);
		}
		elsif($inetnum && $attr eq 'rm') {
		    # Remarks
		    $in{'infra'} = 1 if($value =~ /^\bINFRA\-AW\b/i);
		}
	    }
	}
        
        # Get creation date for valid inetnum object
        if($inetnum) {
            # Validate date
            my $date = $self->creationDate(@{$in{'ch'}});
	    
            if($date) {
                $in{'created'} = $date;
            }
            else {
                $self->error($INVALID_DATE, 
                             ("Invalid date in " . join('; ', @{$in{'ch'}})));
                
                push(@{$in{'warning'}}, ($self->error())[1]);
            }
	    # Skip LIR-PARTITIONED PA
	    next if($in{'st'} =~ /^LIR\-PARTITIONED\s+P[AI]$/i);
	    # Special treatment for SUB-ALLOCATED PA
	    if($in{'st'} =~ /^SUB\-ALLOCATED\s+P[AI]$/i) {
		push(@{$self->{'sub-alloc'}}, $inetnum);
		next;
	    }
	    # This is a valid inetnum, put it into the list
	    push(@in, $inetnum);
        }
    }
    # We do have objects to show
    return(@in) if(@in);
    # Spit the whole ans for debug.
    $self->error($NO_ALLOCS, "No inetnum object found");
    # on error
    return;
}

#------------------------------------------------------------------------------
# Purpose           : Get valid allocations from Hash of Inetnum
# Side Effects      : error no will be the last error, Error messages are
#                     concatenated.
# Comments          : 
# IN :              : list of inetnums
# OUT :             : list of valid allocations 

sub validAlloc {
    my $self = shift ;
    my(@inList) = @_;       # list of inetnums 
    my($netName, @dbInetNums);

    # If empty whois respond
    unless(@inList) {
	$self->error($NO_ALLOCS, 'No object(s) in DB');
	return;  #on error
    }
    
    foreach my $inetnum (@inList){

        # Skip if it is an IANA delegation and we 
	# have a delegations list
	if(defined($self->{'Delegations'})) {
	    next if($self->{'Delegations'}->isDelegation($inetnum));
	}

        # check status, netname and mnt-by to verify identity as allocation
        if($self->checkSt($inetnum, $self->{$inetnum}{'st'}) && # Status 
	   $self->checkNa($inetnum, $self->{$inetnum}{'na'}) && # Netname  
	   $self->checkMb($inetnum, @{$self->{$inetnum}{'mb'}}) # Mnt-by
          ) {
            # valid allocation
            push(@dbInetNums, $inetnum);
        }
    }

    #push to allocation list
    if (@dbInetNums){
	# Reset errors
	$self->error();
        push(@{$self->{'dbAlloc'}}, @dbInetNums);
        return(@dbInetNums); # on sucess
    }

    # if there is no error message 
    $self->error($NO_ALLOC_INDB, 'No allocation object in DB, inetnum(s) found '
                 . join(', ', @inList)) unless($self->error());

    return;  # on error
}

#------------------------------------------------------------------------------
# Purpose           : check for correct status form list of valid status 
# Side Effects      : is a special case of error concatinate the error message.
#                     error number will be last message
# Comments          : 
# IN :              : inetnum value, status value.
# OUT :             : 1 if matches, undef otherwise

sub checkSt {
    my $self = shift;
    my($inetnum,           #inetnum 
       $st                 #status attribute 
       ) = @_;

    my $err;

    #check against valid status
    foreach my $validSt (@{$self->{'validSt'}}) {
        # Valid status
	return 1 if($st =~ /$validSt/i);
    }

    $err .= sprintf("status %s is invalid %s\n\t", $st, $inetnum);
    $err .= ($self->error())[1] if($self->error());
    $self->error($STATUS_MISMATCH, $err); 
    return; #on error
}


#------------------------------------------------------------------------------
# Purpose           : check mnt-by value
# Side Effects      : is a special case of error concatinate the error message.
#                     error number will be last message
# Comments          : 
# IN :              : inetnum value, mnt-by  value.
# OUT :             : 1 if matches, undef otherwise
sub checkMb {
    my $self = shift;
    my($inetnum,            # inetnum
       @mb                  # list of mnt-by found from DB
       ) = @_;
    
    my $err;
    
    #check for each mnt-by value
    foreach my $mbValue (@mb) {
        #against valid values
        foreach my $validMb (@{$self->{'validMb'}}) {
	    # Valid mnt-by
            return 1 if($mbValue =~ /$validMb/i);
        }
    $err .= sprintf("mnt-by: %s is invalid for %s\n\t", $mbValue, $inetnum);
    }
    #is a special case of error concatinate the error message. 
    # error number will be last message
    $err .= ($self->error())[1] if($self->error());
    $self->error($MNT_BY_MISMATCH, $err);
    return;  
}

#------------------------------------------------------------------------------
# Purpose           : #check valid netname 
# Side Effects      : is a special case of error concatinate the error message.
#                     error number will be last message
# Comments          : 
# IN :              : inetnum value, na value.
# OUT :             : 1 if matches, undef otherwise
sub checkNa {
    my $self = shift;
    my($inetnum,           # inetnum
       $na                 # netname from db
       ) = @_;

    my $err;
    
    #check against valid na
    foreach  my $validNa ($self->validNa){
        $validNa =~ s/\./-/; # tranlate first "." to - 
	# Skip empty values
        next unless($validNa);
	# Valid netname(case-insensitive match according to RIPE 223)
        return 1 if($na =~ /\Q$validNa\E/i);
    }
    #Keep error list in Buffer may correct one follows
    $err .= sprintf("netname %s is invalid for %s\n\t", $na, $inetnum);
    $err .= ($self->error())[1] if($self->error());
    $self->error($NETNAME_MISMATCH, $err);
    return; # on error
}

#------------------------------------------------------------------------------
# Purpose           :  get the create date from ch attribute
# Side Effects      : 
# Comments          :  date related routine if changed attribute
#                      in db cahnge may need modifcication 
# IN :              : list of changed value from DB
# OUT :             : date of createion in YYYYMMDD 
sub  creationDate {
    my $self = shift;
    my(@ch                  #list of ch from DB
       ) = @_;
    
    my @dateList;
    #don't trust the date from db
    foreach my $dateStr (@ch) {
	if($dateStr =~ /(\d+)$/) {
    	    my $date = $1;
            return  unless ($date = checkYYYYmmDD($date)) ; #look if it is valid date
            push(@dateList, $date); #make list of valid dates
        }
    }
    my @sortedDate = sort { $a <=> $b } @dateList;
    return $sortedDate[0]; #on success return the earliest date
}

#------------------------------------------------------------------------------
# Purpose           : Check a date is valid date for unix too 
# Side Effects      :  if passed arb. string  localtime will produce warning
# Comments          :  a is valid if  a == (1/(1/a))
# IN                :  date as YYYYMMDD or YYMMDD
# OUT               :  date in the format YYYYMMDD on success,
#                      undef on error.
sub checkYYYYmmDD {

        my($yyyyMMdd) = @_;     #date 
        my($yy, $mm, $dd, $utc, $yyActual, $mmActual, $ddActual, $date);
        my($sec, $min, $hour, $wday, $yday, $isdst); # Temp vars
	
        #YYYYMMDD
        if($yyyyMMdd =~ /^(\d{4})(\d{2})(\d{2})$/) {
            $yy = $1 - 1900;
            $mm = $2;
            $mm--;
            $dd = $3;
        }

        #YYMMDD
        #asume it is 20th century date  
        elsif($yyyyMMdd =~ /^(\d{2})(\d{2})(\d{2})$/) {
            #in the format yyddmm
            $yy = $1;
            $mm = $2; $mm--;
            $dd = $3;
	    # just in case
	    if($yy > 70) { # 20th century
		$yyyyMMdd += 19000000;
	    }
	    else { # looks like 21st
		$yyyyMMdd += 20000000;
	    }
        }
        else {
            # print STDERR "invalid date $yyyyMMdd YYYYMMDD or yyddmm\n";
            return 0;          #error  in input format
        }

        if($mm < 0 or $mm > 11)  { #invalid month  
            return;
        }
        return if($dd < 1 or $dd > 31); #invalid date
	
	# Sometimes date is so weird, it dies here...
        $utc = eval { timelocal('0', '0', '0', $dd, $mm, $yy); };
	# Invalid date
	return if($@ || !$utc);
	
        ($sec, $min, $hour, $ddActual, $mmActual, $yyActual, $wday, $yday, $isdst) = localtime($utc);

        $yyActual +=1900;
        $mmActual++;

        #make YYYYMMDD format
        $date = sprintf("%04d%02d%02d", $yyActual, $mmActual, $ddActual);
	
        return $date if($date == $yyyyMMdd); #success
        #print  "$yyyyMMdd not  $date invalid date\n" if $$debug;

        return;                #date mismatch
    }



#------------------------------------------------------------------------------
# Purpose           :  initialize a nested hash so perl -w wont complain 
# Side Effects      : 
# Comments          :  
#                      
# IN :              : ref to the hash to be initialized,
# OUT :             : undef

sub initHashValues  {
  my($dest, # ref to the hash to be initialized,
   ) = @_;


  #inetnum attributes as returned by whois -F 
  $dest->{'mb'} = []; # list of mnt-by  values
  $dest->{'ac'} = []; # list of admin-c values
  $dest->{'tc'} = []; # list of tech-c values
  $dest->{'ch'} = []; # list of changed values
  $dest->{'so'} = ''; # source 
  $dest->{'ml'} = []; # list of mnt-lower
  $dest->{'na'} = ''; # netname
  $dest->{'st'} = ''; # status 
  
  # derived values from attributes
  
  $dest->{'start'} = '';    # start of inetnum in decimal
  $dest->{'end'}   = '';    # end of inetnum in decimal  after normalizing
  $dest->{'size'}  = '';    # end  +1 - start
  $dest->{'created'} = '';  #  normalized date in the format YYYYMMDD
  $dest->{'warning'} = [];  # list of warnings if any
  $dest->{'valid'}   = '';  # scalar if inetnum is valid
  $dest->{'invalid'} = '';  # scalar if inetnum is invalid
  $dest->{'overlap'} = [];# list of overlapping inetnums             

  return; #on success
}

1;
__END__
# Below is the stub of documentation for your module. You better edit it!

=head1 NAME

Net::RIPWhois::in - Perl extension for accessing inetnum objects from RIPE whois database. 

=head1 SYNOPSIS

use Net::RIPEWhois::in

$in = new Net::RIPEWhois::in;

@in = getIn($whois, $prefix, $ALLOCOPT);

$whois->closeWhois(); 

=head1 DESCRIPTION

Get inetnum objects from RIPE whois server. Validate as allocation object.

=head1 METHODS

=head2 debug

Get or set the debug.

$in->debug(1); set the debug.

$in->debug(undef);  turn off the $debug;

=head2 error 

($errNo, $errMsg) = $in->error;
$in->(undef, undef, true); reset the error message

Will get the last error no and error string;

if $whois->debug is set  @{$in->{'errMsgAll'}}  and   @{$in->{'errNoAll'}} will have the list of all errors  and error numbers.

=head2  new 

$in = new Net::RIPEWhois::in

Creates a new inetnum object. ENV variables WHOISHOST and WHOISPORT

=head2 getIn 

Return a list of inetnum objects returned by whois.

$whois = new Net::RIPEWhois;

@in = $in->getIn($whois, $prefix, $ALLOCOPT);


Selected attributes of inetnum are stored in data structure i<inetnum>.
Should always query with fast raw output used by the RIPE whois db.


$in->{inetnum}{attribute}

return  undef on error. use $in->error() to get error no. and message.

=head2 splitWhoisAns 

Split the response string from whois ans and return list of inetnum on sucess.


@inList  = $in->splitWhoisAns( $whoisAnsRef);  

Returns undef on error.

Spliting depending on fast raw output format from whois db. Use C<-F> to get fast raw output.

=head2  validAlloc 

Check the inetnum  is an alloation or not.

Using netname  same as regid, translated "." to C<-> .

mnt-by RIPE-NCC-MNT ,RIPE-NCC-HM-MNT   

status  ALLOCATED PA, ALLOCATED PI, ALLOCATED UNSPECIFIED and not in IANA Delegated list. As specified in the specification when queried C<-L> C<-T> in the range database should return max 3 inetnum objects. Assignment, Allocation & IANA delegation to RIPE. Skip if it is an IANA delegation and aply the previous check.


@valid = $in->validAlloc(@inlist);

undef on error. 

=head2 checkYYYYmmDD 

Function used to check dates are valid or not. Converts to UTC on  YYYYMMDD and concert back to date format if these to matchs treated ad valid date.

$YYYYMMDD = checkYYYYmmDD($YYYYMMDD); 
Accepts YYMMDD and YYYYMMDD formats. YYMMDD is treated as 19YYMMDD
    

=head1 FILES
    
F</ncc/ip-reg/delegations> perl file with list of IANA delegations.

=head1  REQUIRES

perl module ipv4pack

=head1 AUTHOR

Antony <antony@ripe.net>  software group RIPE NCC <softies@ripe.net>

=head1 SEE ALSO

perl(1), Net::RIPEWhois(3), whois(1)

=cut
