Net-Whois-IANA

 view release on metacpan or  search on metacpan

lib/Net/Whois/IANA.pm  view on Meta::CPAN

package Net::Whois::IANA;
$Net::Whois::IANA::VERSION = '0.50';
use 5.006;

use strict;
use warnings;

use Carp       ();
use IO::Socket ();
use Net::CIDR  ();

use base 'Exporter';

# ABSTRACT: Net::Whois::IANA - A universal WHOIS data extractor.

our $WHOIS_PORT           = 43;
our $WHOIS_TIMEOUT        = 30;
our @DEFAULT_SOURCE_ORDER = qw(arin ripe apnic lacnic afrinic);

our %IANA;
our @IANA;

BEGIN {
    # populate the hash at compile time

    %IANA = (
        apnic   => [ [ 'whois.apnic.net', $WHOIS_PORT, $WHOIS_TIMEOUT, \&apnic_query ], ],
        ripe    => [ [ 'whois.ripe.net', $WHOIS_PORT, $WHOIS_TIMEOUT, \&ripe_query ], ],
        arin    => [ [ 'whois.arin.net', $WHOIS_PORT, $WHOIS_TIMEOUT, \&arin_query ], ],
        lacnic  => [ [ 'whois.lacnic.net', $WHOIS_PORT, $WHOIS_TIMEOUT, \&lacnic_query ], ],
        afrinic => [ [ 'whois.afrinic.net', $WHOIS_PORT, $WHOIS_TIMEOUT, \&afrinic_query ],
        ],
    );

    @IANA = sort keys %IANA;

    # accessors
    # do not use AUTOLOAD - only accept lowercase function name
    # define accessors at compile time
    my @accessors = qw{country netname descr status source server inetnum inet6num cidr abuse fullinfo};

    foreach my $accessor (@accessors) {
        no strict 'refs';
        *$accessor = sub {
            my ($self) = @_;
            die qq[$accessor is a method call] unless ref $self;
            return unless $self->{QUERY};
            return $self->{QUERY}->{$accessor};
        };
    }

    *desc = \&descr; # backward compatibility
}

our @EXPORT = qw( @IANA %IANA );

sub new ($) {

    my $proto = shift;
    my $class = ref $proto || $proto;
    my $self  = {};

    bless $self, $class;

    return $self;
}

sub whois_connect ($;$$) {
    my ( $host, $port, $timeout ) = @_;

    ( $host, $port, $timeout ) = @$host if ref $host;

    $port    ||= $WHOIS_PORT;
    $timeout ||= $WHOIS_TIMEOUT;

    #my $port    = $host_ref->[1] || $WHOIS_PORT;
    #my $timeout = $host_ref->[2] || $WHOIS_TIMEOUT;
    #my $host    = $host_ref->[0];
    my $retries = 2;
    my $sleep   = 2;

    my $sock;

    foreach my $iter ( 0 .. $retries ) {
        local $@;

        # catch errors
        eval {
            $sock = IO::Socket::INET->new(
                PeerAddr => $host,
                PeerPort => $port,
                Timeout  => $timeout,
            );
            1;
        } and return $sock;

        Carp::carp "Cannot connect to $host at port $port";
        Carp::carp $@;
        sleep $sleep unless $iter == $retries;    # avoid the last sleep
    }

lib/Net/Whois/IANA.pm  view on Meta::CPAN

    $self->init_query(%params);
    $self->{QUERY} = {};

    for my $source_name (@DEFAULT_SOURCE_ORDER) {
        print STDERR "Querying $source_name ...\n" if $params{-debug};
        my $sock = $self->source_connect($source_name)
          || Carp::carp "Connection failed to $source_name." && next;
        my %query = $self->{query_sub}( $sock, $params{-ip} );

        next unless keys %query;
        do { Carp::carp "Warning: permission denied at $source_name server $self->{whois_host}\n"; next }
          if $query{permission} && $query{permission} eq 'denied';
        $query{server} = uc $source_name;
        $self->{QUERY} = { post_process_query(%query) };

        return $self->{QUERY};
    }

    return {};
}

sub default_query ($$) {

    return arin_query(@_);
}

sub ripe_read_query ($$) {

    my ( $sock, $ip ) = @_;

    my %query = ( fullinfo => '' );
    print $sock "-r $ip\n";
    while (<$sock>) {
        $query{fullinfo} .= $_;
        close $sock and return ( permission => 'denied' ) if /ERROR:201/;
        next if ( /^(\%|\#)/ || !/\:/ );
        s/\s+$//;
        my ( $field, $value ) = split( /:/, $_, 2 );
        $value =~ s/^\s+//;
        $query{ lc($field) } .= ( $query{ lc($field) } ? ' ' : '' ) . $value;
    }
    close $sock;
    return %query;
}

sub ripe_process_query (%) {

    my %query = @_;

    if (
        ( defined $query{remarks} && $query{remarks} =~ /The country is really world wide/ )
        || ( defined $query{netname}
            && $query{netname} =~ /IANA-BLK/ )
        || ( defined $query{netname}
            && $query{netname} =~ /AFRINIC-NET-TRANSFERRED/ )
        || ( defined $query{country}
            && $query{country} =~ /world wide/ )
    ) {
        return ();
    }
    elsif ( !$query{inet6num} && !$query{inetnum} ) {
        return ();
    }
    else {
        $query{permission} = 'allowed';
        $query{cidr} = [ Net::CIDR::range2cidr( uc( $query{inet6num} || $query{inetnum} ) ) ];
    }
    return %query;
}

sub ripe_query ($$) {
    my ( $sock, $ip ) = @_;

    my %query = ripe_read_query( $sock, $ip );
    return () unless defined $query{country};
    return ripe_process_query(%query);
}

sub apnic_read_query ($$) {
    my ( $sock, $ip ) = @_;

    my %query = ( fullinfo => '' );
    my %tmp;
    print $sock "-r $ip\n";
    my $skip_block = 0;
    while (<$sock>) {
        $query{fullinfo} .= $_;
        close $sock and return ( permission => 'denied' ) if /^\%201/;
        if (m{^\%}) {

            # Always skip 0.0.0.0 data
            # It looks like:
            # % Information related to '0.0.0.0 - 255.255.255.255'
            if (m{^\%.*0\.0\.0\.0\s+}) {
                $skip_block = 1;
                next;
            }
            $skip_block = 0;
            next;
        }
        next if $skip_block;
        next if ( !/\:/ );
        s/\s+$//;
        my ( $field, $value ) = split( /:/, $_, 2 );
        $value =~ s/^\s+//;
        if ( $field =~ /^inet6?num$/ ) {
            next if $value =~ m{0\.0\.0\.0\s+};
            %tmp             = %query;
            %query           = ();
            $query{fullinfo} = $tmp{fullinfo};
        }
        my $lc_field = lc($field);
        next if $lc_field eq 'country' && defined $query{$lc_field};
        $query{$lc_field} .= ( $query{$lc_field} ? ' ' : '' ) . $value;
    }
    close $sock;
    for ( keys %tmp ) {
        $query{$_} = $tmp{$_} if !defined $query{$_};
    }
    return %query;
}

sub apnic_process_query (%) {
    my %query = @_;

    if (
        ( defined $query{remarks} && $query{remarks} =~ /address range is not administered by APNIC|This network in not allocated/ )
        || ( defined $query{descr}
            && $query{descr} =~ /not allocated to|by APNIC|placeholder reference/i )
    ) {
        return ();
    }
    elsif ( !$query{inet6num} && !$query{inetnum} ) {
        return ();
    }
    else {
        $query{permission} = 'allowed';
        $query{cidr} = [ Net::CIDR::range2cidr( uc( $query{inet6num} || $query{inetnum} ) ) ];
    }

    return %query;
}

sub apnic_query ($$) {
    my ( $sock, $ip ) = @_;

    my %query = apnic_read_query( $sock, $ip );
    return apnic_process_query(%query);
}

sub arin_read_query ($$) {
    my ( $sock, $ip ) = @_;

    my %query = ( fullinfo => '' );
    my %tmp = ();

    print $sock "+ $ip\n";
    while (<$sock>) {
        $query{fullinfo} .= $_;
        close $sock and return ( permission => 'denied' ) if /^\#201/;
        return () if /no match found for/i;
        next if ( /^\#/ || !/\:/ );
        s/\s+$//;
        my ( $field, $value ) = split( /:/, $_, 2 );
        $value =~ s/^\s+//;
        if (   $field eq 'OrgName'
            || $field eq 'CustName' ) {
            %tmp             = %query;
            %query           = ();
            $query{fullinfo} = $tmp{fullinfo};
        }
        $query{ lc($field) } .= ( $query{ lc($field) } ? ' ' : '' ) . $value;
    }
    close $sock;

    $query{orgname} = $query{custname} if defined $query{custname};

    for ( keys %tmp ) {
        $query{$_} = $tmp{$_} unless defined $query{$_};
    }

    return %query;
}

sub arin_process_query (%) {
    my %query = @_;

    return ()
      if $query{orgid} && $query{orgid} =~ /^\s*RIPE|LACNIC|APNIC|AFRINIC\s*$/;

    $query{permission} = 'allowed';
    $query{descr}      = $query{orgname};
    $query{remarks}    = $query{comment};
    $query{status}     = $query{nettype};
    $query{inetnum}    = $query{netrange};
    $query{source}     = 'ARIN';
    if ( defined $query{cidr} && $query{cidr} =~ /\,/ ) {
        $query{cidr} = [ split( /\s*\,\s*/, $query{cidr} ) ];

lib/Net/Whois/IANA.pm  view on Meta::CPAN

        close $sock
          and return ( permission => 'denied' )
          if /^\%201/ || /^\% Query rate limit exceeded/ || /^\% Not assigned to LACNIC/ || /\% Permission denied/;
        if (/^\% (\S+) resource:/) {
            my $srv = $1;
            close $sock and return () if $srv !~ /lacnic|brazil/i;
        }
        next if ( /^\%/ || !/\:/ );
        s/\s+$//;
        my ( $field, $value ) = split( /:/, $_, 2 );
        $value =~ s/^\s+//;
        next if $field eq 'country' && $query{country};
        $query{ lc($field) } .= ( $query{ lc($field) } ? ' ' : '' ) . $value;
    }
    close $sock;
    return %query;
}

sub lacnic_process_query (%) {
    my %query = @_;

    $query{permission} = 'allowed';
    $query{descr}      = $query{owner};
    $query{netname}    = $query{ownerid};
    $query{source}     = 'LACNIC';
    if ( $query{inetnum} ) {
        $query{cidr}    = $query{inetnum};
        $query{inetnum} = ( Net::CIDR::cidr2range( $query{cidr} ) )[0];
    }
    unless ( $query{country} ) {
        if ( $query{nserver} && $query{nserver} =~ /\.(\w\w)$/ ) {
            $query{country} = uc $1;
        }
        elsif ( $query{descr} && $query{descr} =~ /\s(\w\w)$/ ) {
            $query{country} = uc $1;
        }
        else {
            return ();
        }
    }
    return %query;
}

sub lacnic_query ($$) {
    my ( $sock, $ip ) = @_;

    my %query = lacnic_read_query( $sock, $ip );

    return lacnic_process_query(%query);
}

*afrinic_read_query = *apnic_read_query;

sub afrinic_process_query (%) {
    my %query = @_;

    return ()
      if defined $query{remarks} && $query{remarks} =~ /country is really worldwide/
      or defined $query{descr}   && $query{descr} =~ /Here for in-addr\.arpa authentication/;

    if ( !$query{inet6num} && !$query{inetnum} ) {
        return ();
    }

    $query{permission} = 'allowed';
    $query{cidr} =
      [ Net::CIDR::range2cidr( uc( $query{inet6num} || $query{inetnum} ) ) ];
    return %query;
}

sub afrinic_query ($$) {
    my ( $sock, $ip ) = @_;

    my %query = afrinic_read_query( $sock, $ip );

    return afrinic_process_query(%query);
}

sub is_mine ($$;@) {
    my ( $self, $ip, @cidr ) = @_;

    return 0 unless is_valid_ip($ip);
    if ( !scalar @cidr ) {
        my $out = $self->cidr();
        @cidr = @$out if ref $out;
    }

    @cidr = map {
        my @dots = ( split /\./ );
        my $pad = '.0' x ( 4 - @dots );
        s|(/.*)|$pad$1|;
        $_;
      }
      map  { split(/\s+/) }
      grep { defined $_ } @cidr;

    return Net::CIDR::cidrlookup( $ip, @cidr );
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Net::Whois::IANA - Net::Whois::IANA - A universal WHOIS data extractor.

=head1 VERSION

version 0.50

=head1 SYNOPSIS

  use Net::Whois::IANA;
  my $ip = '132.66.16.2';
  my $iana = Net::Whois::IANA->new;
  $iana->whois_query(-ip=>$ip);
  print "Country: " , $iana->country()            , "\n";
  print "Netname: " , $iana->netname()            , "\n";
  print "Descr: "   , $iana->descr()              , "\n";
  print "Status: "  , $iana->status()             , "\n";
  print "Source: "  , $iana->source()             , "\n";
  print "Server: "  , $iana->server()             , "\n";



( run in 0.563 second using v1.01-cache-2.11-cpan-8f98c5d2c55 )