Permalink
Browse files

Merge branch 'release/Net-Whois-RIPE-2.005'

  • Loading branch information...
2 parents 7cfa8ef + 256cdb6 commit cb23e73085332703a91b23efb53ae121c4e9f010 @arhuman committed Oct 10, 2013
Showing with 5,416 additions and 141 deletions.
  1. +5 −0 Changes
  2. +25 −0 MANIFEST
  3. +3 −0 TODO
  4. +722 −0 lib/Net/Whois/Generic.pm
  5. +112 −76 lib/Net/Whois/Object.pm
  6. +181 −0 lib/Net/Whois/Object/AsBlock/AFRINIC.pm
  7. +206 −0 lib/Net/Whois/Object/AsSet/AFRINIC.pm
  8. +259 −0 lib/Net/Whois/Object/AutNum/AFRINIC.pm
  9. +236 −0 lib/Net/Whois/Object/Domain/AFRINIC.pm
  10. +211 −0 lib/Net/Whois/Object/FilterSet/AFRINIC.pm
  11. +263 −0 lib/Net/Whois/Object/Inet6Num/AFRINIC.pm
  12. +276 −0 lib/Net/Whois/Object/Inet6Num/APNIC.pm
  13. +264 −0 lib/Net/Whois/Object/InetNum/AFRINIC.pm
  14. +226 −0 lib/Net/Whois/Object/InetRtr/AFRINIC.pm
  15. +51 −0 lib/Net/Whois/Object/Information/AFRINIC.pm
  16. +261 −0 lib/Net/Whois/Object/Irt/AFRINIC.pm
  17. +205 −0 lib/Net/Whois/Object/KeyCert/AFRINIC.pm
  18. +114 −0 lib/Net/Whois/Object/Limerick/AFRINIC.pm
  19. +237 −0 lib/Net/Whois/Object/Mntner/AFRINIC.pm
  20. +6 −0 lib/Net/Whois/Object/Organisation.pm
  21. +191 −0 lib/Net/Whois/Object/Organisation/AFRINIC.pm
  22. +143 −0 lib/Net/Whois/Object/PeeringSet/AFRINIC.pm
  23. +141 −0 lib/Net/Whois/Object/Person/AFRINIC.pm
  24. +160 −0 lib/Net/Whois/Object/Role/AFRINIC.pm
  25. +220 −0 lib/Net/Whois/Object/Route/AFRINIC.pm
  26. +206 −0 lib/Net/Whois/Object/Route6/AFRINIC.pm
  27. +152 −0 lib/Net/Whois/Object/RouteSet/AFRINIC.pm
  28. +154 −0 lib/Net/Whois/Object/RtrSet/AFRINIC.pm
  29. +52 −50 lib/Net/Whois/RIPE.pm
  30. +11 −5 t/03-objects.t
  31. +89 −0 t/04-generic.t
  32. +4 −4 t/140-Inet6Num.t
  33. +4 −4 t/205-Irt.t
  34. +2 −2 t/215-SyncUpdates.t
  35. +24 −0 t/300-AFRINIC.t
View
5 Changes
@@ -1,5 +1,10 @@
Revision history for net-whois-ripe
+
+2.005000 2013-10-10
+ - Add support for generic objects (from various sources, AFRINIC being the first implemented)
+ through Net::Whois::Generic
+
2.004002 2013-07-31
- Add 'abuse_c' to AutNum object
(https://www.ripe.net/ripe/docs/ripe-563)
View
25 MANIFEST
@@ -1,30 +1,53 @@
bin/awhois.pl
Changes
+lib/Net/Whois/Generic.pm
lib/Net/Whois/Object.pm
lib/Net/Whois/Object/AsBlock.pm
+lib/Net/Whois/Object/AsBlock/AFRINIC.pm
lib/Net/Whois/Object/AsSet.pm
+lib/Net/Whois/Object/AsSet/AFRINIC.pm
lib/Net/Whois/Object/AutNum.pm
+lib/Net/Whois/Object/AutNum/AFRINIC.pm
lib/Net/Whois/Object/Domain.pm
+lib/Net/Whois/Object/Domain/AFRINIC.pm
lib/Net/Whois/Object/FilterSet.pm
+lib/Net/Whois/Object/FilterSet/AFRINIC.pm
lib/Net/Whois/Object/Inet6Num.pm
+lib/Net/Whois/Object/Inet6Num/AFRINIC.pm
+lib/Net/Whois/Object/Inet6Num/APNIC.pm
lib/Net/Whois/Object/InetNum.pm
+lib/Net/Whois/Object/InetNum/AFRINIC.pm
lib/Net/Whois/Object/InetRtr.pm
+lib/Net/Whois/Object/InetRtr/AFRINIC.pm
lib/Net/Whois/Object/Information.pm
+lib/Net/Whois/Object/Information/AFRINIC.pm
lib/Net/Whois/Object/Irt.pm
+lib/Net/Whois/Object/Irt/AFRINIC.pm
lib/Net/Whois/Object/KeyCert.pm
+lib/Net/Whois/Object/KeyCert/AFRINIC.pm
lib/Net/Whois/Object/Limerick.pm
+lib/Net/Whois/Object/Limerick/AFRINIC.pm
lib/Net/Whois/Object/Mntner.pm
+lib/Net/Whois/Object/Mntner/AFRINIC.pm
lib/Net/Whois/Object/Organisation.pm
+lib/Net/Whois/Object/Organisation/AFRINIC.pm
lib/Net/Whois/Object/PeeringSet.pm
+lib/Net/Whois/Object/PeeringSet/AFRINIC.pm
lib/Net/Whois/Object/Person.pm
+lib/Net/Whois/Object/Person/AFRINIC.pm
lib/Net/Whois/Object/Poem.pm
lib/Net/Whois/Object/PoeticForm.pm
lib/Net/Whois/Object/Response.pm
lib/Net/Whois/Object/Role.pm
+lib/Net/Whois/Object/Role/AFRINIC.pm
lib/Net/Whois/Object/Route.pm
+lib/Net/Whois/Object/Route/AFRINIC.pm
lib/Net/Whois/Object/Route6.pm
+lib/Net/Whois/Object/Route6/AFRINIC.pm
lib/Net/Whois/Object/RouteSet.pm
+lib/Net/Whois/Object/RouteSet/AFRINIC.pm
lib/Net/Whois/Object/RtrSet.pm
+lib/Net/Whois/Object/RtrSet/AFRINIC.pm
lib/Net/Whois/RIPE.pm
Makefile.PL
MANIFEST
@@ -37,6 +60,7 @@ t/00-load.t
t/01-instantiation.t
t/02-methods.t
t/03-objects.t
+t/04-generic.t
t/105-AsBlock.t
t/110-AutNum.t
t/115-Person.t
@@ -63,6 +87,7 @@ t/205-Irt.t
t/210-FilterSet.t
t/215-SyncUpdates.t
t/216-SyncUpdates-Signed.t
+t/300-AFRINIC.t
t/boilerplate.t
t/common.pl
t/pod-coverage.t
View
3 TODO
@@ -0,0 +1,3 @@
+* make abuse-c attribute mandatory by end of september
+* add option to query to get unfiltered data (-B on RIPE) and check for mail on aassad-ripe
+ (make it default ?)
View
722 lib/Net/Whois/Generic.pm
@@ -0,0 +1,722 @@
+package Net::Whois::Generic;
+
+use 5.006;
+use warnings;
+use strict;
+use IO::Socket::INET;
+use IO::Select;
+use Iterator;
+use Net::Whois::Object;
+use Data::Dumper;
+
+use constant {
+ SOON => 30,
+ END_OF_OBJECT_MARK => "\n\n",
+ EOL => "\015\012",
+ QUERY_KEEPALIVE => q{-k },
+ QUERY_NON_RECURSIVE => q{-r },
+ QUERY_REFERRAL => q{-R },
+ QUERY_GROUPING => q{-G },
+ QUERY_UNFILTERED => q{-B },
+ QUERY_LIST_OBJECTS => q{-qtypes },
+ QUERY_LIST_SOURCES => q{-qsources },
+ QUERY_FETCH_TEMPLATE => q{-t%s },
+ QUERY_LIMIT_OBJECT_TYPE => q{-T%s },
+};
+
+our %RIR = (
+ apnic => { SERVER => 'whois.apnic.net', QUERY => \&apnic_query },
+ ripe => { SERVER => 'whois.ripe.net', QUERY => \&ripe_query },
+ arin => { SERVER => 'whois.arin.net', QUERY => \&arin_query },
+ lacnic => { SERVER => 'whois.lacnic.net', QUERY => \&lacnic_query },
+ afrinic => { SERVER => 'whois.afrinic.net', QUERY => \&afrinic_query },
+);
+
+=head1 NAME
+
+Net::Whois::Generic - a pure-Perl implementation of the RIPE Database client.
+
+=head1 VERSION
+
+Version 2.004001
+
+=cut
+
+our $VERSION = 2.004001;
+
+=head1 SYNOPSIS
+
+Net::Whois::Generic is my first attempt to unify Whois information from different sources.
+Historically Net::Whois::RIPE was the first written, then Net::Whois::Object was added to provide
+a RPSL encapsultation of the data returned from RIPE database, with an API more object oriented.
+
+Net::Whois::Generic is a new interface designed to be more generic and encapsulated data from
+various sources (RIPE, but also AFRINIC, APNIC...)
+The current implementation is barely a proof of concept, and AFINIC is the only other source implemented,
+but I expect to turn it into a generic/robust implementation based on the users feedback.
+
+=head1 METHODS
+
+=head2 B<new( %options )>
+
+Constructor. Returns a new L<Net::Whois::Generic> object with an open connection
+to the RIPE Database service of choice (defaulting to C<whois.ripe.net:43>).
+
+The C<%options> hash migth contain configuration options for the RIPE Database
+server. Not all options provided by the RIPE Database server are suitable for
+this implementation, but the idea is to provide everything someone can show a
+use for. The options currently recognized are:
+
+=over 4
+
+=item B<hostname> (IPv4 address or DNS name. Default is C<whois.ripe.net>)
+
+The hostname or IP address of the service to connect to
+
+=item B<port> (integer, default is C<43>)
+
+The TCP port of the service to connect to
+
+=item B<timeout> (integer, default is C<5>)
+
+The time-out (in seconds) for the TCP connection.
+
+=item B<keepalive> (boolean, default is C<false>)
+
+Wherever we want (C<true>) or not (C<false>) to keep the connection to the
+server open. This option implements the functionality available through RIPE
+Database's "-k" parameter.
+
+=item B<referral> (boolean, default is C<false>)
+
+When true, prevents the server from using the referral mechanism for domain
+lookups, so that the RIPE Database server returns an object in the RIPE
+Database with the exact match with the lookup argument, rather than doing a
+referral lookup.
+
+=item B<recursive> (boolean, default is C<false>)
+
+When set to C<true>, prevents recursion into queried objects for personal
+information. This prevents lots of unsolicited objects from showing up on
+queries.
+
+=item B<grouping> (boolean, default is C<false>)
+
+When C<true> enables object grouping in server responses. There's little
+utility to enable this option, as the objects will be parsed and returned on a
+much reasonable format most of the time. For the brave or more knowledgeable
+people that want to have they answers in plain text, this can help stablishing
+a 'good' ordering for the RPSL objects returned by a query ('good' is RIPE
+NCC's definition of 'good' in this case).
+
+=item B<unfiltered> (boolean, default is C<false>)
+
+When C<true> enables unfiltered object output responses. This produces objects
+that can be presented back to the RIPE Database for updating.
+
+=item B<types> (list of valid RIPE Database object types, default is empty, meaning all types)
+
+Restrict the RPSL object types allowed in the response to those in the list.
+Using this option will cause the L<Net::Whois::Generic> object to query the RIPE
+Database for the available object types for validating the list. The response
+will be cached for speed and bandwidth.
+
+=item B<disconnected> (boolean, default is C<false>)
+
+Prevents the constructor from automatically opening a connection to the service
+specified (conneting the socket is the default behavior). When set (C<true>),
+the programmer is responsible for calling C<connect> in order to stablish a
+connection to the RIPE Database service desired.
+
+=back
+
+=cut
+
+{
+ my %default_options = (
+ hostname => 'whois.ripe.net',
+ port => '43',
+ timeout => 5,
+ keepalive => 0,
+ referral => 0,
+ recursive => 0,
+ grouping => 1,
+ unfiltered => 0,
+ types => undef,
+ disconnected => 0,
+ );
+
+ sub new
+ {
+ my ($class, %options) = @_;
+ my %known_options;
+ $known_options{$_} = exists $options{$_} ? $options{$_} : $default_options{$_} foreach keys %default_options;
+
+ my $self = bless { __options => \%known_options }, $class;
+
+ return $self;
+ }
+}
+
+=head2 B<hostname( [$hostname] )>
+
+Accessor to the hostname. Accepts an optional hostname, always return the
+current hostname.
+
+=cut
+
+sub hostname
+{
+ my ($self, $hostname) = @_;
+ $self->{__options}{hostname} = $hostname if defined $hostname;
+ return $self->{__options}{hostname};
+}
+
+=head2 B<port()>
+
+Accessor to the port. Accepts an optional port, always return the current
+port.
+
+=cut
+
+sub port
+{
+ my ($self, $port) = @_;
+ $self->{__options}{port} = $port if defined $port && $port =~ m{^\d+$};
+ return $self->{__options}{port};
+}
+
+=head2 B<timeout()>
+
+Accessor to the timeout configuration option. Accepts an optional timeout,
+always return the current timeout.
+
+=cut
+
+sub timeout
+{
+ my ($self, $timeout) = @_;
+ $self->{__options}{timeout} = $timeout
+ if defined $timeout && $timeout =~ m{^\d+$};
+ return $self->{__options}{timeout};
+}
+
+=begin UNDOCUMENTED
+
+=head2 B<__boolean_accessor( $self, $attribute [, $value ] )>
+
+Private method. Shouldn't be used from other modules.
+
+Generic implementation of an accessor for booleans. Receives a reference to the
+current instance, the attribute name, and a value to be interpreted under
+Perl's boolean rules. Sets or gets the named attribute with the given value.
+Always returns the most up-to-date value of the attribute.
+
+=end UNDOCUMENTED
+
+=cut
+
+sub __boolean_accessor
+{
+ my ($self, $attribute) = (shift, shift);
+ if (scalar @_ == 1) {
+ my $value = shift;
+ $self->{__options}{$attribute} = $value ? 1 : 0;
+ }
+ return $self->{__options}{$attribute};
+}
+
+=head2 B<keepalive()>
+
+Accessor to the keepalive configuration option. Accepts an optional keepalive,
+always return the current keepalive.
+
+=cut
+
+sub keepalive
+{
+ my $self = shift;
+ return $self->__boolean_accessor('keepalive', @_);
+}
+
+=head2 B<referral()>
+
+Accessor to the referral configuration option. Accepts an optional referral,
+always return the current referral.
+
+=cut
+
+sub referral
+{
+ my $self = shift;
+ return $self->__boolean_accessor('referral', @_);
+}
+
+=head2 B<recursive()>
+
+Accessor to the recursive configuration option. Accepts an optional recursive,
+always return the current recursive.
+
+=cut
+
+sub recursive
+{
+ my $self = shift;
+ return $self->__boolean_accessor('recursive', @_);
+}
+
+=head2 B<grouping()>
+
+Accessor to the grouping configuration option. Accepts an optional grouping,
+always return the current grouping.
+
+=cut
+
+sub grouping
+{
+ my $self = shift;
+ return $self->__boolean_accessor('grouping', @_);
+}
+
+=head2 B<unfiltered()>
+
+Accessor to the unfiltered configuration option.
+
+=cut
+
+sub unfiltered
+{
+ my $self = shift;
+ return $self->__boolean_accessor('unfiltered', @_);
+}
+
+=head2 B<connect()>
+
+Initiates a connection with the current object's configuration.
+
+=cut
+
+sub connect
+{
+ my $self = shift;
+ my %connection = (
+ Proto => 'tcp',
+ Type => SOCK_STREAM,
+ PeerAddr => $self->hostname,
+ PeerPort => $self->port,
+ Timeout => $self->timeout,
+ Domain => AF_INET,
+ Multihomed => 1,
+ );
+
+ # Create a new IO::Socket object
+ my $socket = $self->{__state}{socket} = IO::Socket::INET->new(%connection);
+ die q{Can't connect to "} . $self->hostname . ':' . $self->port . qq{". Reason: [$@].\n}
+ unless defined $socket;
+
+ # Register $socket with the IO::Select object
+ if (my $ios = $self->ios) {
+ $ios->add($socket) unless $ios->exists($socket);
+ }
+ else {
+ $self->{__state}{ioselect} = IO::Select->new($socket);
+ }
+
+ # Set RIPE Database's "keepalive" capability
+ $self->send(QUERY_KEEPALIVE) if $self->keepalive;
+}
+
+=head2 B<ios()>
+
+Accessor to the L<IO::Select> object coordinating the I/O to the L<IO::Socket>
+object used by this module to communicate with the RIPE Database Server. You
+shouldn't use this object, but the L</"send()"> and L<"query( $query_string )">
+methods instead.
+
+=cut
+
+sub ios { return $_[0]->{__state}{ioselect} }
+
+=head2 B<socket()>
+
+Read-only accessor to the L<IO::Socket> object used by this module.
+
+=cut
+
+sub socket { return $_[0]->{__state}{socket} }
+
+=head2 B<send()>
+
+Sends a message to the RIPE Database server instance to which we're connected
+to. Dies if it cannot write, or if there's no open connection to the server.
+
+Return C<true> if the message could be written to the socket, C<false>
+otherwise.
+
+=cut
+
+sub send
+{
+ my ($self, $message) = @_;
+ die q{Not connected} unless $self->is_connected;
+ if ($self->ios->can_write(SOON + $self->timeout)) {
+ $self->socket->print($message, EOL);
+ $self->socket->flush;
+ return 1;
+ }
+ return 0;
+}
+
+=head2 B<reconnect()>
+
+Reconnects to the server in case we lost connection.
+
+=cut
+
+sub reconnect
+{
+ my $self = shift;
+ $self->disconnect if $self->is_connected;
+ $self->connect;
+}
+
+=head2 B<disconnect()>
+
+Disconnects this client from the server. This renders the client useless until
+you call L</"connect()"> again. This method is called by L</DESTROY()> as part of
+an object's clean-up process.
+
+=cut
+
+sub disconnect
+{
+ my $self = shift;
+ if ($self->is_connected) {
+ my $socket = $self->{__state}{socket};
+ $socket->close;
+ $self->{__state}{ioselect}->remove($socket)
+ if $self->{__state}{ioselect};
+ delete $self->{__state}{socket};
+ }
+}
+
+=head2 B<is_connected()>
+
+Returns C<true> if this instance is connected to the RIPE Database service
+configured.
+
+=cut
+
+sub is_connected
+{
+ my $self = shift;
+ my $socket = $self->socket;
+ return UNIVERSAL::isa($socket, 'IO::Socket')
+ && $socket->connected ? 1 : 0;
+}
+
+=head2 B<DESTROY()>
+
+Net::Whois::Generic object destructor. Called by the Perl interpreter upon
+destruction of an instance.
+
+=cut
+
+sub DESTROY
+{
+ my $self = shift;
+ $self->disconnect;
+}
+
+=head2 B<_find_rir( $query_string )>
+
+Guess the associated RIR based on the query.
+
+=cut
+
+sub _find_rir
+{
+ my ($self, $query) = @_;
+
+ my $rir;
+
+ #
+ # AFRINIC has been allocated the
+ # IPv4 address blocks
+ # 41.0.0.0/8
+ # 102.0.0.0/8
+ # 105.0.0.0/8
+ # 154.0.0.0/8
+ # 197.0.0.0/8
+ # 196.0.0.0/8
+ # IPv6 blocks
+ # 2c00::/12
+ # 2001:4200::/23
+ #
+ if ( ($query =~ /^(41|102|105|154|196|197)\.\d+\.\d+\.\d+/)
+ or ($query =~ /AFRINIC/i)
+ or ($query =~ /^2c00::/))
+ {
+ $rir = 'afrinic';
+ }
+ elsif ( ( $query =~ /^(23|34|50|64|64|65|66|67|68|69|70|71|72|73|74|75|76|96|97|98|9|100|104|107|108|135|136|142|147|162|166|172|173|174|184|192|198|199|204|205|206|207|208|209|216)/
+ or ($query =~ /^(2001:0400|2001:1800|2001:4800:|2600|2610:0000):/)
+ or $query =~ /ARIN/
+ )
+ )
+ {
+ $rir = 'arin';
+
+ }
+ else {
+ $rir = 'ripe';
+ }
+
+ return $rir;
+}
+
+=head2 B<adapt_query( $query_string[, $rir] )>
+
+Adapt a query to set various parameter (whois server, query options...) based on the query.
+Takes an optional parameter $rir, to force a specific RIR to be used.
+
+=cut
+
+sub adapt_query
+{
+ my ($self, $query, $rir) = @_;
+ my $fullquery;
+
+ # determine RIR unless $rir;
+ $rir = $self->_find_rir($query) unless $rir;
+ if ($rir eq 'ripe') {
+ $self->hostname($RIR{ripe}{SERVER});
+ my $parameters = "";
+ $parameters .= q{ } . QUERY_KEEPALIVE if $self->keepalive;
+ $parameters .= q{ } . QUERY_UNFILTERED if $self->unfiltered;
+ $parameters .= q{ } . QUERY_NON_RECURSIVE unless $self->recursive;
+ $parameters .= q{ } . QUERY_REFERRAL if $self->referral;
+ $fullquery = $parameters . $query;
+ }
+ elsif ($rir eq 'afrinic') {
+ $fullquery = '-V Md5.0 ' . $query;
+ $self->hostname($RIR{afrinic}{SERVER});
+ }
+ elsif ($rir eq 'arin') {
+ $self->hostname($RIR{arin}{SERVER});
+ }
+ elsif ($rir eq 'lacnic') {
+ $self->hostname($RIR{lacnic}{SERVER});
+ }
+
+ return $fullquery;
+}
+
+=head2 B<query( $query_string )>
+
+Sends a query to the server. Returns an L<Iterator> object that will return one RPSL block at a time.
+
+=cut
+
+sub query
+{
+ my ($self, $query, $options) = @_;
+
+ my $attribute;
+ my $type;
+ my $response;
+
+ for my $opt (keys %$options) {
+ if ($opt =~ /^attribute$/i) {
+ $attribute = $options->{$opt};
+ }
+ elsif ($opt =~ /^type$/i) {
+ $type = $options->{$opt};
+ }
+ }
+
+ $query = $self->adapt_query($query);
+ my $parameters = "";
+ $parameters .= q{ } . QUERY_KEEPALIVE if $self->keepalive;
+ $parameters .= q{ } . QUERY_UNFILTERED if $self->unfiltered;
+ $parameters .= q{ } . QUERY_NON_RECURSIVE unless $self->recursive;
+ $parameters .= q{ } . QUERY_REFERRAL if $self->referral;
+ my $fullquery = $parameters . $query;
+ my $iterator = $self->__query($fullquery);
+
+ my @objects = Net::Whois::Object->new($iterator);
+
+ ($response) = grep { ref($_) =~ /response/i } @objects;
+
+ if ($response) {
+ $self->_process_response($response);
+
+ }
+
+ if ($type) {
+ @objects = grep { ref($_) =~ /$type/i } @objects;
+ }
+
+ if ($attribute) {
+ return grep {defined} map {
+ my $r;
+ eval { $r = $_->$attribute };
+ $@ ? undef : ref($r) eq 'ARRAY' ? @$r : $r
+ } @objects;
+ }
+ else {
+ return grep {defined} @objects;
+ }
+}
+
+# Allows me to pass in queries without having all the automatic options added
+# up to it.
+sub __query
+{
+ my ($self, $query) = @_;
+
+ $self->connect;
+
+ # $self->reconnect unless $self->keepalive;
+ # die "Not connected" unless $self->is_connected;
+
+ if ($self->ios->can_write(SOON + $self->timeout)) {
+ $self->socket->print($query, EOL);
+
+ return Iterator->new(
+ sub {
+ local $/ = "\n\n";
+ if ($self->ios->can_read(SOON + $self->timeout)) {
+ my $block = $self->socket->getline;
+ return $block if defined $block;
+ }
+ Iterator::is_done;
+ }
+ );
+ }
+}
+
+=head2 B<object_types()>
+
+Return a list of known object types from the RIPE Database.
+
+RIPE currently returns 21 types (Limerik have been removed):
+as-block as-set aut-num domain filter-set inet6num inetnum inet-rtr irt
+key-cert mntner organisation peering-set person poem poetic-form role route
+route6 route-set rtr-set
+
+Due to some strange mis-behaviour in the protocol (or documentation?) the RIPE
+Database server won't allow a keep-alive token with this query, meaning the
+connection will be terminated after this query.
+
+=cut
+
+sub object_types
+{
+ my $self = shift;
+ my $iterator = $self->__query(QUERY_LIST_OBJECTS);
+ while (!$iterator->is_exhausted) {
+ my $value = $iterator->value;
+ return split /\s+/, $value if $value !~ /^%\s/;
+ }
+ return;
+}
+
+=head2 B<_process_response( $response )>
+
+Process a response (error code, error message...)
+
+=cut
+
+sub _process_response
+{
+ my $self = shift;
+ my $response = shift;
+ my $code;
+ my $msg;
+
+ eval { $response->comment };
+ die "Dump : ".Dumper $response if $@;
+
+ if ($response->response =~ /ERROR.*:.*?(\d+)/) {
+ $code = $1;
+ $msg = join '', $response->comment();
+ }
+}
+
+=head1 AUTHOR
+
+Arnaud "Arhuman" Assad, C<< <arhuman at gmail.com> >>
+
+=head1 CAVEATS
+
+=over 4
+
+=item B<Update>
+
+Update of objects from database other than RIPE is not currently implemented...
+
+=item B<Sources>
+
+Currently the only sources implemented are RIPE, and AFRINIC.
+
+=item B<Maturity>
+
+The Net::Whois::Generic interface is highly experimental.
+There are probably bugs, without any doubt missing documentation and
+examples but please don't hesitate to contact me to suggest corrections
+and improvments.
+
+=back
+
+=head1 BUGS
+
+Please report any bugs or feature requests to C<bug-net-whois-ripe at
+rt.cpan.org>, or through the web interface at
+L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=net-whois-ripe>. I will be
+notified, and then you'll automatically be notified of progress on your bug as
+I make changes.
+
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command.
+
+ perldoc Net::Whois::Generic
+
+
+You can also look for information at:
+
+=over 4
+
+=item * RT: CPAN's request tracker
+
+L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=net-whois-ripe>
+
+=item * AnnoCPAN: Annotated CPAN documentation
+
+L<http://annocpan.org/dist/net-whois-ripe>
+
+=item * CPAN Ratings
+
+L<http://cpanratings.perl.org/d/net-whois-ripe>
+
+=item * Search CPAN
+
+L<http://search.cpan.org/dist/net-whois-ripe>
+
+=back
+
+
+=head1 ACKNOWLEDGEMENTS
+
+Thanks to Jaguar Networks which grants me time to work on this module.
+
+=head1 COPYRIGHT & LICENSE
+
+Copyright 2013 Arnaud "Arhuman" Assad, all rights reserved.
+
+This program is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=cut
View
188 lib/Net/Whois/Object.pm
@@ -3,9 +3,9 @@ use strict;
use warnings;
use Carp;
-use Net::Whois::RIPE;
use IPC::Open2 qw/open2/;
use List::Util qw/max/;
+use Data::Dumper;
our $LWP;
@@ -252,7 +252,7 @@ sub new {
$block = 'comment' unless $block;
# Comment line
- $attribute = "comment";
+ $attribute = 'comment';
$value = $1;
} elsif ( $line =~ /^[^%]\s*(.+)/ ) {
@@ -263,10 +263,13 @@ sub new {
} elsif ( $line =~ /^$/ ) {
# Blank line
- push @results, $object;
- $attribute = undef;
- $block = undef;
- $object = undef;
+ if ($object) {
+ $object = _object_factory( $object->{block}, $object->{value}, $object );
+ push @results, $object;
+ $attribute = undef;
+ $block = undef;
+ $object = undef;
+ }
next;
}
@@ -278,13 +281,25 @@ sub new {
$block = $attribute unless $block;
if ( !$object ) {
- $object = _object_factory( $block, $value ) unless $object;
- } elsif ( $object->can($attribute) ) {
- $object->$attribute($value);
- } else {
- warn "Objects of type " . ref($object) . " do not support attribute '$attribute', but it was supplied with value '$value'\n";
+ $object = { block => $block, value => $value, attributes => [] };
+
+ # $object = _object_factory( $block, $value ) unless $object;
+ # } elsif ( $object->can($attribute) ) {
+ # $object->$attribute($value);
+ if ( $block eq 'comment' ) {
+
+ # push @{$object->{attributes}},[ 'comment', $value ];
+ next;
+ }
}
+ # } else {
+ push @{ $object->{attributes} }, [ $attribute, $value ];
+
+ # } else {
+ # warn "Objects of type " . ref($object) . " do not support attribute '$attribute', but it was supplied with value '$value'\n";
+ # }
+
}
# TODO: fix the trailing undef
@@ -621,43 +636,43 @@ Net::Whois::Objects
=cut
-sub query {
- my ( $class, $query, $options ) = @_;
-
- my $attribute;
- my $type;
-
- for my $opt ( keys %$options ) {
- if ( $opt =~ /^attribute$/i ) {
- $attribute = $options->{$opt};
- } elsif ( $opt =~ /^type$/i ) {
- $type = $options->{$opt};
- }
- }
-
- my $whois = Net::Whois::RIPE->new(%$options);
- my $iterator = $whois->query($query);
-
- my @objects = Net::Whois::Object->new($iterator);
-
- if ($type) {
- @objects = grep { ref($_) =~ /$type/i } @objects;
- }
-
- if ($attribute) {
- return grep {defined} map {
- my $r;
- eval { $r = $_->$attribute };
- $@ ? undef : ref($r) eq 'ARRAY' ? @$r : $r
- } @objects;
- } else {
- return grep {defined} @objects;
- }
-}
+# sub query {
+# my ( $class, $query, $options ) = @_;
+#
+# my $attribute;
+# my $type;
+#
+# for my $opt ( keys %$options ) {
+# if ( $opt =~ /^attribute$/i ) {
+# $attribute = $options->{$opt};
+# } elsif ( $opt =~ /^type$/i ) {
+# $type = $options->{$opt};
+# }
+# }
+#
+# my $whois = Net::Whois::Generic->new(%$options);
+# my $iterator = $whois->query($query);
+#
+# my @objects = Net::Whois::Object->new($iterator);
+#
+# if ($type) {
+# @objects = grep { ref($_) =~ /$type/i } @objects;
+# }
+#
+# if ($attribute) {
+# return grep {defined} map {
+# my $r;
+# eval { $r = $_->$attribute };
+# $@ ? undef : ref($r) eq 'ARRAY' ? @$r : $r
+# } @objects;
+# } else {
+# return grep {defined} @objects;
+# }
+# }
=begin UNDOCUMENTED
-=head2 B<_object_factory( $type => $value )>
+=head2 B<_object_factory( $type => $value, $attributes_hashref )>
Private method. Shouldn't be used from other modules.
@@ -667,8 +682,12 @@ the type passed as parameter.
=cut
sub _object_factory {
- my $type = shift;
- my $value = shift;
+ my $type = shift;
+ my $value = shift;
+ my $object = shift;
+ my $rir;
+
+ my $object_returned;
my %class = ( as_block => 'AsBlock',
as_set => 'AsSet',
@@ -684,7 +703,6 @@ sub _object_factory {
limerick => 'Limerick',
mntner => 'Mntner',
organisation => 'Organisation',
- organisation => 'Organisation',
peering_set => 'PeeringSet',
person => 'Person',
poem => 'Poem',
@@ -697,24 +715,42 @@ sub _object_factory {
rtr_set => 'RtrSet',
);
- die "Unrecognized Object (first attribute: $type = $value)" unless $class{$type};
+ die "Unrecognized Object (first attribute: $type = $value)\n" . Dumper($object) unless defined $type and $class{$type};
my $class = "Net::Whois::Object::" . $class{$type};
+ for my $a ( @{ $object->{attributes} } ) {
+ if ( $a->[0] =~ /source/ ) {
+ $rir = $a->[1];
+ $rir =~ s/^(\S+)\s*#.*/$1/;
+ $rir = uc $rir;
+ $rir = undef if $rir =~ /^(RIPE|TEST)$/; # For historical/compatibility reason RIPE objects aren't derived
+ }
+ }
+
+ $class .= "::$rir" if $rir;
+
eval "require $class" or die "Can't require $class ($!)";
# my $object = $class->new( $type => $value );
- my $object = $class->new( class => $class{$type} );
+ $object_returned = $class->new( class => $class{$type} );
# First attribute is always single valued, except for comments
if ( $type eq 'comment' ) {
- $object->_multiple_attribute_setget( $type => $value );
+ $object_returned->_multiple_attribute_setget( $type => $value );
} else {
- $object->_single_attribute_setget( $type => $value );
+ $object_returned->_single_attribute_setget( $type => $value );
+ }
+
+ if ( $object->{attributes} ) {
+ for my $a ( @{ $object->{attributes} } ) {
+ my $method = $a->[0];
+ $object_returned->$method( $a->[1] );
+ }
}
# return $class->new( $type => $value );
- return $object;
+ return $object_returned;
}
@@ -746,6 +782,7 @@ sub _single_attribute_setget {
if ( defined $value ) {
if ( $mode eq 'replace' ) {
+
# Store attribute order for dump, unless this attribute as already been set
push @{ $self->{order} }, $attribute unless $self->{$attribute} or $attribute eq 'class';
@@ -754,7 +791,7 @@ sub _single_attribute_setget {
if ( ref $value ne 'HASH' or !$value->{old} ) {
croak " {old=>...} expected as value for $attribute update in delete mode";
} else {
- $self->_delete_attribute($attribute,$value->{old});
+ $self->_delete_attribute( $attribute, $value->{old} );
}
}
}
@@ -811,8 +848,9 @@ sub _multiple_attribute_setget {
if ( ref $value ne 'HASH' or !$value->{old} ) {
croak " {old=>...} expected as value for $attribute update in delete mode";
} else {
+
# $self->{$attribute} = [grep {!/$old/} @{$self->{$attribute}}];
- $self->_delete_attribute($attribute,$value->{old});
+ $self->_delete_attribute( $attribute, $value->{old} );
}
} else {
croak "Unknown mode $mode for attribute $attribute";
@@ -832,36 +870,34 @@ Delete an attribute if its value match the pattern value
sub _delete_attribute {
my ( $self, $attribute, $pattern ) = @_;
- my @lines;
+ my @lines;
- for my $a ( @{ $self->{order} } ) {
- my $val = ref $self->{$a} ? shift @{ $self->{$a} } : $self->{$a};
- push @lines, [ $a, $val ];
- }
+ for my $a ( @{ $self->{order} } ) {
+ my $val = ref $self->{$a} ? shift @{ $self->{$a} } : $self->{$a};
+ push @lines, [ $a, $val ];
+ }
- @lines = grep {$attribute ne $_->[0] or $_->[1] !~ /$pattern/} @lines;
- delete $self->{$attribute} if $self->attribute_is($attribute, 'single') and $self->{$attribute} =~ /$pattern/;
+ @lines = grep { $attribute ne $_->[0] or $_->[1] !~ /$pattern/ } @lines;
+ delete $self->{$attribute} if $self->attribute_is( $attribute, 'single' ) and $self->{$attribute} =~ /$pattern/;
- $self->{order} = [];
- for my $l (@lines) {
- $self->{ $l->[0] } = [] if ref( $self->{ $l->[0] } ) ;
- }
+ $self->{order} = [];
+ for my $l (@lines) {
+ $self->{ $l->[0] } = [] if ref( $self->{ $l->[0] } );
+ }
- for my $i ( 0 .. $#lines ) {
- push @{ $self->{order} }, $lines[$i]->[0];
- if ( $self->attribute_is( $lines[$i]->[0], 'multiple' ) ) {
- push @{ $self->{ $lines[$i]->[0] } }, $lines[$i]->[1];
- } else {
- $self->{ $lines[$i]->[0] } = $lines[$i]->[1];
+ for my $i ( 0 .. $#lines ) {
+ push @{ $self->{order} }, $lines[$i]->[0];
+ if ( $self->attribute_is( $lines[$i]->[0], 'multiple' ) ) {
+ push @{ $self->{ $lines[$i]->[0] } }, $lines[$i]->[1];
+ } else {
+ $self->{ $lines[$i]->[0] } = $lines[$i]->[1];
- }
+ }
- }
+ }
}
-
-
=head2 B<_init( @options )>
Initialize self with C<@options>
View
181 lib/Net/Whois/Object/AsBlock/AFRINIC.pm
@@ -0,0 +1,181 @@
+package Net::Whois::Object::AsBlock::AFRINIC;
+
+use base qw/Net::Whois::Object/;
+
+# whois -t as-block -h whois.afrinic.net
+# % This is the AfriNIC Whois server.
+#
+# as-block: [mandatory] [single] [primary/look-up key]
+# descr: [optional] [multiple] [ ]
+# remarks: [optional] [multiple] [ ]
+# org: [optional] [multiple] [inverse key]
+# admin-c: [mandatory] [multiple] [inverse key]
+# tech-c: [mandatory] [multiple] [inverse key]
+# notify: [optional] [multiple] [inverse key]
+# mnt-by: [mandatory] [multiple] [inverse key]
+# mnt-lower: [optional] [multiple] [inverse key]
+# changed: [mandatory] [multiple] [ ]
+# source: [mandatory] [single] [ ]
+
+
+__PACKAGE__->attributes( 'primary', ['as_block'] );
+__PACKAGE__->attributes( 'mandatory', [ 'as_block', 'admin_c', 'tech_c', 'mnt_by', 'changed', 'source' ] );
+__PACKAGE__->attributes( 'optional', [ 'descr', 'remarks', 'org', 'notify', 'mnt_lower' ] );
+__PACKAGE__->attributes( 'single', [ 'as_block', 'source' ] );
+__PACKAGE__->attributes( 'multiple', [ 'descr', 'remarks', 'org', 'admin_c', 'tech_c', 'notify', 'mnt_lower', 'mnt_by', 'changed' ] );
+
+=head1 NAME
+
+Net::Whois::Object::AsBlock::AFRINIC - an object representation of the RPSL AsBlock block
+
+=head1 DESCRIPTION
+
+An as-block object is needed to delegate a range of AS numbers to a
+given repository. This object may be used for authorisation of the
+creation of aut-num objects within the range specified by the
+"as-block:" attribute.
+
+=head1 METHODS
+
+=head2 B<new( %options )>
+
+Constructor for the Net::Whois::Object::AsBlock::AFRINIC class
+
+=cut
+
+sub new {
+ my ( $class, @options ) = @_;
+
+ my $self = bless {}, $class;
+ $self->_init(@options);
+
+
+ return $self;
+}
+
+=head2 B<as_block( [$as_block] )>
+
+Accessor to the as_block attribute.
+Accepts an optional as_block, always return the current as_block value.
+
+An as_block is a range of AS numbers delegated to a Regional or National Internet Registry
+(NIR).
+
+The AS numbers in the range are subsequently assigned by the registry to
+members or end-users in the region.
+Information on individual AS numbers within an as-block object are
+stored in the appropriate Internet Registry's Whois Database.
+
+=head2 B<descr( [$descr] )>
+
+Accessor to the descr attribute.
+Accepts an optional descr, always return the current descr value.
+
+Description of the Internet Registry delegated the range of AS numbers shown
+in the as-block.
+
+=head2 B<remarks( [$remarks] )>
+
+Accessor to the remarks attribute.
+Accepts an optional remarks to be added to the remarks array,
+always return the current remarks array.
+
+Information on the registry that maintains details of AS numbers assigned from
+the as-block.
+
+Also includes where to direct a whois client to find further information on
+the AS numbers.
+
+=head2 B<tech_c( [$tech_c] )>
+
+Accessor to the tech_c attribute.
+Accepts an optional tech_c to be added to the tech_c array,
+always return the current tech_c array.
+
+The NIC-handle of a technical contact 'person' or 'role' object. As more than
+one person often fulfills a role function, there may be more than one tech-c
+listed.
+
+A technical contact (tech-c) must be a person responsible for the
+day-to-day operation of the network, but does not need to be
+physically located at the site of the network.
+
+
+=head2 B<admin_c( [$admin_c])>
+
+Accessor to the admin_c attribute.
+Accepts an optional admin_c to be added to the admin_c array,
+always return the current admin_c array.
+
+The NIC-handle of an on-site contact 'person' object. As more than one person
+often fulfills a role function, there may be more than one admin-c listed.
+
+An administrative contact(admin-c) must be someone who is physically
+located at the site of the network.
+
+=head2 B<notify( [$notify] )>
+
+Accessor to the notify attribute.
+Accepts an optional value to be added notify array,
+ always return the current notify array.
+
+The email address to which notifications of changes
+to the object should be sent.
+
+=head2 B<mnt_lower( [$mnt_lower] )>
+
+Accessor to the mnt_lower attribute.
+Accepts an optional mnt_lower value to be added to the mnt_lower array,
+always return the current mnt_lower array.
+
+The identifier of a registered 'mntner' object used to authorize the creation
+of 'aut-num' objects within the range specified by the as-block.
+
+If no 'mnt-lower' is specified, the 'mnt-by' attribute is used for
+authorization.
+
+=head2 B<mnt_by( [$mnt_by] )>
+
+Accessor to the mnt_by attribute.
+Accepts an optional mnt_by value to be added to the mnt_by array,
+always return the current mnt_by array.
+
+Lists a registered 'mntner' used to authorize and authenticate changes to this
+object.
+
+When your database details are protected by a 'mntner' object, then
+only persons with access to the security information of that 'mntner'
+object will be able to change details.
+
+=head2 B<changed( [$changed] )>
+
+Accessor to the changed attribute.
+Accepts an optional changed value to be added to the changed array,
+always return the current changed array.
+
+The email address of who last updated the database object and the date it
+occurred.
+
+Every time a change is made to a database object, this attribute will show
+the email address of the person who made those changes.
+Please use the address format specified in RFC 822 - Standard for
+the Format of ARPA Internet Text Message and provide the date
+format using one of the following two formats: YYYYMMDD or YYMMDD.
+
+=head2 B<source( [$source] )>
+
+Accessor to the source attribute.
+Accepts an optional source, always return the current source.
+
+The database where the object is registered.
+
+=head2 B<org( [$org] )>
+
+Accessor to the org attribute.
+Accepts an optional org, always return the current org.
+
+The organisation entity this object is bound to.
+
+=cut
+
+1;
View
206 lib/Net/Whois/Object/AsSet/AFRINIC.pm
@@ -0,0 +1,206 @@
+package Net::Whois::Object::AsSet::AFRINIC;
+
+use base qw/Net::Whois::Object/;
+
+# whois -t as-block -h whois.afrinic.net
+# % This is the AfriNIC Whois server.
+#
+# as-block: [mandatory] [single] [primary/look-up key]
+# descr: [optional] [multiple] [ ]
+# remarks: [optional] [multiple] [ ]
+# org: [optional] [multiple] [inverse key]
+# admin-c: [mandatory] [multiple] [inverse key]
+# tech-c: [mandatory] [multiple] [inverse key]
+# notify: [optional] [multiple] [inverse key]
+# mnt-by: [mandatory] [multiple] [inverse key]
+# mnt-lower: [optional] [multiple] [inverse key]
+# changed: [mandatory] [multiple] [ ]
+# source: [mandatory] [single] [ ]
+
+__PACKAGE__->attributes( 'primary', ['as_set'] );
+__PACKAGE__->attributes( 'mandatory', [ 'as_set', 'admin_c', 'tech_c', 'mnt_by', 'changed', 'source' ] );
+__PACKAGE__->attributes( 'optional', [ 'descr', 'remarks', 'org', 'notify', 'mnt_lower' ] );
+__PACKAGE__->attributes( 'single', [ 'as_set', 'source' ] );
+__PACKAGE__->attributes( 'multiple', [ 'descr', 'remarks', 'org', 'admin_c', 'tech_c', 'notify', 'mnt_by', 'mnt_lower', 'changed' ] );
+
+=head1 NAME
+
+Net::Whois::Object::AsSet::AFRINIC - an object representation of a RPSL AsSet block
+
+=head1 DESCRIPTION
+
+An as-set object defines a set of aut-num objects.
+It defines a group of Autonomous Systems with the same routing policies.
+
+=head1 METHODS
+
+=head2 new ( @options )
+
+Constructor for the Net::Whois::Object::AsSet::AFRINIC class
+
+=cut
+
+sub new {
+ my ( $class, @options ) = @_;
+
+ my $self = bless {}, $class;
+ $self->_init(@options);
+
+ return $self;
+}
+
+=head2 B<as_set( [$as_set])>
+
+Accessor to the as_set attribute.
+Accepts an optional as_set, always return the current as_set value.
+
+The as_set attribute defines the name of the set. It is an RPSL name that
+starts with "as-" or as_set names and AS numbers separated by colon (':').
+The later form is called Hierarchical form, the former non-hierarchical.
+There must be at least one set-name within the hierarchical form that starts
+with 'as-'.
+
+
+=head2 B<descr( [$descr] )>
+
+Accessor to the descr attribute.
+Accepts an optional descr to be added to the descr array,
+always return the current descr array.
+
+A short description related to the object's purpose.
+
+=head2 B<members( [$member] )>
+
+Accessor to the members attribute.
+Accepts an optional member to be added to the members array,
+always return the current 'members' array.
+
+The members attribute lists the members of the set. It can be either a list
+of AS Numbers, or other as-set names.
+
+=head2 B<mbrs_by_ref( [$mbr] )>
+
+Accessor to the mbrs_by_ref attribute.
+Accepts an optional mbr to be added to the mbrs_by_ref array,
+always return the current mbrs_by_ref array.
+
+The identifier of a registered 'mntner' object that can be used to add members
+to the as-set indirectly.
+
+The mbrs_by_ref attribute can be used in all "set" objects; it allows
+indirect population of a set. If this attribute is used, the set also includes
+objects of the corresponding type (aut-num objects for as-set, for example)
+that are protected by one of these maintainers and whose "member-of:"
+attributes refer to the name of the set. If the value of a mbrs_by_ref
+attribute is ANY, any object of the corresponding type referring to the set is
+a member of the set. If the mbrs_by_ref attribute is missing, the set is
+defined explicitly by the members attribute.
+
+=head2 B<remarks( [$remark] )>
+
+Accessor to the remarks attribute.
+Accepts an optional remark to be added to the remarks array,
+always return the current remarks array.
+
+Information about the object that cannot be stated in other attributes.
+May include a URL or email address.
+
+=head2 B<org( [$org] )>
+
+Accessor to the org attribute.
+Accepts an optional org, always return the current org.
+
+Points to an existing organisation object representing the entity that
+holds the resource.
+
+The 'ORG-' string followed by 2 to 4 characters, followed by up to 5 digits
+followed by a source specification. The first digit must not be "0".
+Source specification starts with "-" followed by source name up to
+9-character length.
+
+=head2 B<tech_c( [$tech_c] )>
+
+Accessor to the tech_c attribute.
+Accepts an optional tech_c to be added to the tech_c array,
+always return the current tech_c array.
+
+The NIC-handle of a technical contact 'person' or 'role' object. As more than
+one person often fulfills a role function, there may be more than one tech-c
+listed.
+
+A technical contact (tech-c) must be a person responsible for the
+day-to-day operation of the network, but does not need to be
+physically located at the site of the network.
+
+=head2 B<admin_c( [$admin_c] )>
+
+Accessor to the admin_c attribute.
+Accepts an optional admin_c to be added to the admin_c array,
+always return the current admin_c array.
+
+The NIC-handle of an on-site contact 'person' object. As more than one person
+often fulfills a role function, there may be more than one admin-c listed.
+
+An administrative contact (admin-c) must be someone who is physically
+located at the site of the network.
+
+=head2 B<notify( [$notify] )>
+
+Accessor to the notify attribute.
+Accepts an optional notify value to be added to the notify array,
+always return the current notify array.
+
+The email address to which notifications of changes to this object will be
+sent.
+
+=head2 B<mnt_by( [$mnt] )>
+
+Accessor to the mnt_by attribute.
+Accepts an optional mnt to be added to the mnt_by array,
+always return the current mnt_by array.
+
+Lists a registered 'mntner' used to authorize and authenticate changes to this
+object.
+
+When your database details are protected by a 'mntner' object, then
+only persons with access to the security information of that 'mntner'
+object will be able to change details.
+
+=head2 B<mnt_lower( [$mnt] )>
+
+Accessor to the mnt_lower attribute.
+Accepts an optional mnt to be added to the mnt_lower array,
+always return the current mnt_lower array.
+
+Specifies the identifier of a registered mntner object used
+for hierarchical authorisation. Protects creation of objects
+directly (one level) below in the hierarchy of an object type.
+The authentication method of this maintainer object will then
+be used upon creation of any object directly below the object
+that contains the "mnt-lower:" attribute.
+
+=head2 B<changed( [$changed] )>
+
+Accessor to the changed attribute.
+Accepts an optional changed value to be added to the changed array,
+always return the current changed array.
+
+The email address of who last updated the database object and the date it
+occurred.
+
+Every time a change is made to a database object, this attribute will show
+the email address of the person who made those changes.
+Please use the address format specified in RFC 822 - Standard for
+the Format of ARPA Internet Text Message and provide the date
+format using one of the following two formats: YYYYMMDD or YYMMDD.
+
+=head2 B<source( [$source] )>
+
+Accessor to the source attribute.
+Accepts an optional source, always return the current source.
+
+The database where the object is registered.
+
+=cut
+
+1;
View
259 lib/Net/Whois/Object/AutNum/AFRINIC.pm
@@ -0,0 +1,259 @@
+package Net::Whois::Object::AutNum::AFRINIC;
+
+use base qw/Net::Whois::Object/;
+
+# whois -t aut-num -h whois.afrinic.net
+# % This is the AfriNIC Whois server.
+#
+# aut-num: [mandatory] [single] [primary/look-up key]
+# as-name: [mandatory] [single] [ ]
+# descr: [mandatory] [multiple] [ ]
+# member-of: [optional] [multiple] [ ]
+# import: [optional] [multiple] [ ]
+# mp-import: [optional] [multiple] [ ]
+# export: [optional] [multiple] [ ]
+# mp-export: [optional] [multiple] [ ]
+# default: [optional] [multiple] [ ]
+# mp-default: [optional] [multiple] [ ]
+# remarks: [optional] [multiple] [ ]
+# org: [optional] [single] [inverse key]
+# admin-c: [mandatory] [multiple] [inverse key]
+# tech-c: [mandatory] [multiple] [inverse key]
+# notify: [optional] [multiple] [inverse key]
+# mnt-lower: [optional] [multiple] [inverse key]
+# mnt-routes: [optional] [multiple] [inverse key]
+# mnt-by: [mandatory] [multiple] [inverse key]
+# mnt-irt: [optional] [multiple] [inverse key]
+# changed: [mandatory] [multiple] [ ]
+# source: [mandatory] [single] [ ]
+
+
+__PACKAGE__->attributes( 'primary', ['aut_num'] );
+__PACKAGE__->attributes( 'mandatory', [ 'aut_num', 'as_name', 'descr', 'admin_c', 'tech_c', 'mnt_by', 'changed', 'source' ] );
+__PACKAGE__->attributes( 'optional', [ 'member_of', 'import', 'mp_import', 'export', 'mp_export', 'default', 'mp_default', 'remarks', 'org', 'notify', 'mnt_lower', 'mnt_routes', 'mnt_irt' ] );
+__PACKAGE__->attributes( 'single', [ 'aut_num', 'as_name', 'org', 'source' ] );
+__PACKAGE__->attributes( 'multiple', [ 'descr', 'member_of', 'import', 'mp_import', 'export', 'mp_export', 'default', 'mp_default', 'remarks', 'admin_c', 'tech_c', 'notify', 'mnt_lower', 'mnt_routes', 'mnt_by', 'mnt_irt', 'changed' ] );
+
+
+=head1 NAME
+
+Net::Whois::Object::AutNum::AFRINIC - an object representation of a RPSL AutNum block
+
+=head1 DESCRIPTION
+
+Autonomous System numbers ('aut-num' objects) are globally unique identifiers
+for Autonomous Systems.
+
+The aut-num object specifies routing policies. It refers to a group of IP
+networks that have a single and clearly defined external routing policy,
+operated by one or more network operators – an Autonomous System (AS).
+
+=head1 METHODS
+
+=head2 new ( @options )
+
+Constructor for the Net::Whois::Object::AutNum::AFRINIC class
+
+=cut
+
+sub new {
+ my ( $class, @options ) = @_;
+
+ my $self = bless {}, $class;
+ $self->_init(@options);
+
+ return $self;
+}
+
+=head2 B<aut_num( [$aut_num] )>
+
+Accessor to the aut_num attribute.
+Accepts an optional aut_num, always return the current aut_num array.
+
+The value of the aut_num attribute is the AS Number of the Autonomous
+System that this object describes and takes the form of ASn where n is
+a 32 bit number..
+
+Leading zeroes (AS0352) are not allowed and will be removed (AS352) by the
+database software.
+
+=cut
+
+sub aut_num {
+ my ( $self, $aut_num ) = @_;
+
+ if ( $aut_num and $aut_num !~ /^AS\d+/ ) {
+ warn "Illegal aut-num ($aut_num) : should be ASn, n being a 32 bit number with no leading 0";
+ }
+
+ return $self->_single_attribute_setget( 'aut_num', $aut_num );
+}
+
+=head2 B<as_name( [$as_name] )>
+
+Accessor to the as_name attribute.
+Accepts an optional as_name, always return the current as_name array.
+
+The as-name attribute is a symbolic name of the AS.
+
+=head2 B<descr( [$descr] )>
+
+Accessor to the descr attribute.
+Accepts an optional descr value to be added to the descr array, always return the current descr array.
+
+=head2 B<member_of( [$mbr_of] )>
+
+Accessor to the member_of attribute.
+Accepts an optional member_of value to be added to the member_of array,
+always return the current member_of array.
+
+The member_of attribute value identifies a set object that this object
+wants to be a member of. This claim, however, should be acknowledged by a
+respective mbrs_by_ref attribute in the referenced object.
+
+An aut-num may be useful to include in an 'as-set' if your network maintains a
+number of Autonomous Systems or wishes to include its routes in an upstream
+provider's routing information.
+
+To be included in an 'as-set', the 'as-set' object must:
+include the keyword "ANY" or the AS number's mbrs_by_ref attribute
+and/or list the AS number in the members attribute
+
+=head2 B<import( [$import] )>
+
+Accessor to the import attribute.
+Accepts an optional import line to be added to the import array,
+always return the current import array.
+
+The inbound IPv4 routing policy of the AS.
+
+=head2 B<mp_import( [$import] )>
+
+Accessor to the mp_import attribute.
+Accepts an optional mp_import line to be added to the import array,
+always return the current mp_import array.
+
+The inbound IPv6 routing policy of the AS.
+
+=head2 B<export( [$export] )>
+
+Accessor to the export attribute.
+Accepts an optional export line to be added to the export array,
+always return the current export array.
+
+The outbound routing policy of the AS.
+
+=head2 B<mp_export( [$mp_export] )>
+
+Accessor to the mp_export attribute.
+Accepts an optional mp_export line to be added to the mp_export array,
+always return the current mp_export array.
+
+The outbound IPv6 routing policy of the AS.
+
+=head2 B<default( [$default] )>
+
+Accessor to the default attribute.
+Accepts an optional default value to be added to the default array,
+always return the current default array.
+
+The peer network the AS will use for as a default, that is, when the AS has no
+more-specific information on where to send the traffic.
+
+=head2 B<mp_default( [$mp_default] )>
+
+Accessor to the mp_default attribute.
+Accepts an optional mp_default line to be added to the mp_default array,
+always return the current mp_default array.
+
+This attribute performs the same function as the 'default' attribute above.
+The difference is that mp-default allows both IPv4 and IPv6 addresses to be
+specified.
+
+=head2 B<remarks( [$remark] )>
+
+Accessor to the remarks attribute.
+Accepts an optional remark to be added to the remarks array,
+always return the current remarks array.
+
+Information about the object that cannot be stated in other attributes. May
+include a URL or email address.
+
+=cut
+
+=head2 B<admin_c( [$contact] )>
+
+Accessor to the admin_c attribute.
+Accepts an optional contact to be added to the admin_c array,
+always return the current admin_c array.
+
+The NIC-handle of an on-site contact 'person' object. As more than one person
+often fulfills a role function, there may be more than one admin_c listed.
+
+An administrative contact (admin_c) must be someone who is physically
+located at the site of the network.
+
+=head2 B<tech_c( [$contact] )>
+
+Accessor to the tech_c attribute.
+Accepts an optional contact to be added to the tech_c array,
+always return the current tech_c array.
+
+The NIC-handle of a technical contact 'person' or 'role' object. As more than
+one person often fulfills a role function, there may be more than one tech_c
+listed.
+
+A technical contact (tech_c) must be a person responsible for the
+day-to-day operation of the network, but does not need to be
+physically located at the site of the network.
+
+=head2 B<notify( [$notify] )>
+
+Accessor to the notify attribute.
+Accepts an optional notify value to be added to the notify array,
+always return the current notify array.
+
+=cut
+
+=head2 B<mnt_lower( [$mnt_lower] )>
+
+Accessor to the mnt_lower attribute.
+Accepts an optional mnt_lower value to be added to the mnt_lower array,
+always return the current mnt_lower array.
+
+=head2 B<mnt_routes( [$mnt_routes] )>
+
+Accessor to the mnt_routes attribute.
+Accepts an optional mnt_routes value to be added to the mnt_routes array,
+always return the current mnt_routes array.
+
+=head2 B<mnt_by( [$mnt_by] )>
+
+Accessor to the mnt_by attribute.
+Accepts an optional mnt_by value to be added to the mnt_by array,
+always return the current mnt_by array.
+
+=head2 B<changed( [$changed] )>
+
+Accessor to the changed attribute.
+Accepts an optional changed value to be added to the changed array,
+always return the current changed array.
+
+=head2 B<source( [$source] )>
+
+Accessor to the source attribute.
+Accepts an optional source, always return the current source.
+
+The database where the object is registered.
+
+=head2 B<org( [$org] )>
+
+Accessor to the org attribute.
+Accepts an optional org, always return the current org.
+
+Only a single value for the org attribute is allowed in the aut-num object.
+This is to ensure only one organisation is responsible for this resource.
+
+=cut
+
+1;
View
236 lib/Net/Whois/Object/Domain/AFRINIC.pm
@@ -0,0 +1,236 @@
+package Net::Whois::Object::Domain::AFRINIC;
+
+use base qw/Net::Whois::Object/;
+
+# whois -t domain -h whois.afrinic.net
+# % This is the AfriNIC Whois server.
+#
+# domain: [mandatory] [single] [primary/look-up key]
+# descr: [mandatory] [multiple] [ ]
+# org: [optional] [multiple] [inverse key]
+# admin-c: [mandatory] [multiple] [inverse key]
+# tech-c: [mandatory] [multiple] [inverse key]
+# zone-c: [mandatory] [multiple] [inverse key]
+# nserver: [optional] [multiple] [inverse key]
+# ds-rdata: [optional] [multiple] [inverse key]
+# sub-dom: [optional] [multiple] [inverse key]
+# dom-net: [optional] [multiple] [ ]
+# remarks: [optional] [multiple] [ ]
+# notify: [optional] [multiple] [inverse key]
+# mnt-by: [optional] [multiple] [inverse key]
+# mnt-lower: [optional] [multiple] [inverse key]
+# refer: [optional] [single] [ ]
+# changed: [mandatory] [multiple] [ ]
+# source: [mandatory] [single] [ ]
+
+__PACKAGE__->attributes( 'primary', ['domain'] );
+__PACKAGE__->attributes( 'mandatory', [ 'domain', 'descr', 'admin_c', 'tech_c', 'zone_c', 'changed', 'source' ] );
+__PACKAGE__->attributes( 'optional', [ 'org', 'nserver', 'ds_rdata', 'sub_dom', 'dom_net', 'remarks', 'notify', 'mnt_by', 'mnt_lower', 'refer' ] );
+__PACKAGE__->attributes( 'single', [ 'domain', 'refer', 'source' ] );
+__PACKAGE__->attributes( 'multiple', [ 'descr', 'org', 'admin_c', 'tech_c', 'zone_c', 'nserver', 'ds_rdata', 'sub_dom', 'dom_net', 'remarks', 'notify', 'mnt_by', 'mnt_lower', 'changed' ] );
+
+
+=head1 NAME
+
+Net::Whois::Object::Domain::AFRINIC - an object representation of a RPSL Domain block
+
+=head1 DESCRIPTION
+
+The domain object represents Top Level Domain (TLD) and other domain
+registrations. It is also used for Reverse Delegations. The domain
+name is written in fully qualified format, without a trailing " . "
+
+=head1 METHODS
+
+=head2 new ( @options )
+
+Constructor for the Net::Whois::Object::Domain::AFRINIC class
+
+=cut
+
+sub new {
+ my ( $class, @options ) = @_;
+
+ my $self = bless {}, $class;
+ $self->_init(@options);
+
+ return $self;
+}
+
+=head2 B<domain( [$domain] )>
+
+Accessor to the domain attribute.
+Accepts an optional domain, always return the current domain.
+
+The domain name in fully qualified format, without a trailing dot. If a
+trailing dot is included it will be removed.
+
+=cut
+
+sub domain {
+ my ( $self, $domain ) = @_;
+
+ # Enforce the format
+ $domain =~ s/\.$// if $domain;
+
+ return $self->_single_attribute_setget( 'domain', $domain );
+}
+
+=head2 B<descr( [$descr] )>
+
+Accessor to the descr attribute.
+Accepts an optional descr line to be added to the descr array,
+always return the current descr array.
+
+The name of the organization responsible for the reverse delegation. Or can
+describe the use of the IP range described in the domain object.
+
+=head2 B<org( [$org] )>
+
+Accessor to the org attribute.
+Accepts an optional org value to be added to the org array,
+always return the current org array.
+
+The organisation responsible for this domain.
+
+=head2 B<admin_c( [$contact] )>
+
+Accessor to the admin_c attribute.
+Accepts an optional contact to be added to the admin_c array,
+always return the current admin_c array.
+
+The NIC-handle of an on-site contact 'person' object. As more than one person
+often fulfills a role function, there may be more than one admin_c listed.
+
+An administrative contact (admin_c) must be someone who is physically
+located at the site of the network.
+
+=head2 B<tech_c( [$contact] )>
+
+Accessor to the tech_c attribute.
+Accepts an optional contact to be added to the tech_c array,
+always return the current tech_c array.
+
+The NIC-handle of a technical contact 'person' or 'role' object. As more than
+one person often fulfills a role function, there may be more than one tech_c
+listed.
+
+A technical contact (tech_c) must be a person responsible for the
+day-to-day operation of the network, but does not need to be
+physically located at the site of the network.
+
+=head2 B<zone_c( [$contact] )>
+
+Accessor to the zone_c attribute.
+Accepts an optional contact to be added to the zone_c array,
+always return the current zone_c array.
+
+The NIC-handle of a 'person' or 'role' object with authority over a zone.
+
+=head2 B<nserver( [$server] )>
+
+Accessor to the nserver attribute.
+Accepts an optional server to be added to the nserver array,
+always return the current nserver array.
+
+A list of nameservers for a domain object. A minimum of one nameserver is
+mandatory.
+
+=head2 B<ds_rdata( [$server] )>
+
+Accessor to the ds_rdata attribute.
+Accepts an optional server to be added to the ds_rdata array,
+always return the current ds_rdata array.
+
+The ds_rdata attribute holds information about a signed delegation record
+for DNSSEC (short for DNS Security Extensions)
+
+=head2 B<sub_dom( [$dom] )>
+
+Accessor to the sub_dom attribute.
+Accepts an optional dom to be added to the sub_dom array,
+always return the current sub_dom array.
+
+The sub_dom attribute specifies a list of sub-domains of a domain. Domain
+names are relative to the domain represented by the domain object that
+contains this attribute
+
+=head2 B<dom_net( [$dom_net] )>
+
+Accessor to the dom_net attribute.
+Accepts an optional dom_net value to be added to the dom_net array,
+always return the current dom_net array.
+
+The dom_net attribute contains a list of IP networks in a domain.
+
+=head2 B<remarks( [$remark] )>
+
+Accessor to the remarks attribute.
+Accepts an optional remark to be added to the remarks array,
+always return the current remarks array.
+
+General remarks. May include a URL or email address.
+
+=head2 B<notify( [$notify] )>
+
+Accessor to the notify attribute.
+Accepts an optional notify value to be added to the notify array,
+always return the current notify array.
+
+The email address to which notifications of changes to this object should be
+sent.
+
+=head2 B<mnt_by( [$mnt_by] )>
+
+Accessor to the mnt_by attribute.
+Accepts an optional mnt_by value to be added to the mnt_by array,
+always return the current mnt_by array.
+
+Lists a registered 'mntner' used to authorize and authenticate changes to
+this object.
+
+=head2 B<mnt_lower( [$mnt_lower] )>
+
+Accessor to the mnt_lower attribute.
+Accepts an optional mnt_lower value to be added to the mnt_lower array,
+always return the current mnt_lower array.
+
+The identifier of a registered mntner object used to authorize the creation of
+reverse domain objects more specific than the reverse domain specified by this
+object.
+
+=head2 B<refer( [$refer] )>
+
+Accessor to the refer attribute.
+Accepts an optional refer, always return the current refer.
+
+The refer attribute is used to refer a query to another authorative
+database. See the "RIPE Database Query Reference Manual" for an
+explanation of its use. This will be redundant when forward domains are
+removed and may be deprecated.
+
+=head2 B<changed( [$changed] )>
+
+Accessor to the changed attribute.
+Accepts an optional changed value to be added to the changed array,
+always return the current changed array.
+
+The email address of who last updated the database object and the date it
+occurred.
+
+Every time a change is made to a database object, this attribute will show
+the email address of the person who made those changes.
+Please use the address format specified in RFC 822 - Standard for
+the Format of ARPA Internet Text Message and provide the date
+format using one of the following two formats: YYYYMMDD or YYMMDD.
+
+=head2 B<source( [$source] )>
+
+Accessor to the source attribute.
+Accepts an optional source, always return the current source.
+
+The database where the object is registered.
+
+=cut
+
+1;
View
211 lib/Net/Whois/Object/FilterSet/AFRINIC.pm
@@ -0,0 +1,211 @@
+package Net::Whois::Object::FilterSet::AFRINIC;
+
+use base qw/Net::Whois::Object/;
+
+# whois -t filter-set -h whois.afrinic.net
+# % This is the AfriNIC Whois server.
+#
+# filter-set: [mandatory] [single] [primary/look-up key]
+# descr: [mandatory] [multiple] [ ]
+# filter: [optional] [single] [ ]
+# mp-filter: [optional] [single] [ ]
+# remarks: [optional] [multiple] [ ]
+# org: [optional] [multiple] [inverse key]
+# tech-c: [mandatory] [multiple] [inverse key]
+# admin-c: [mandatory] [multiple] [inverse key]
+# notify: [optional] [multiple] [inverse key]
+# mnt-by: [mandatory] [multiple] [inverse key]
+# mnt-lower: [optional] [multiple] [inverse key]
+# changed: [mandatory] [multiple] [ ]
+# source: [mandatory] [single] [ ]
+
+__PACKAGE__->attributes( 'primary', ['filter_set'] );
+__PACKAGE__->attributes( 'mandatory', [ 'filter_set', 'descr', 'tech_c', 'admin_c', 'mnt_by', 'changed', 'source' ] );
+__PACKAGE__->attributes( 'optional', [ 'filter', 'mp_filter', 'remarks', 'org', 'notify', 'mnt_lower' ] );
+__PACKAGE__->attributes( 'single', [ 'filter_set', 'filter', 'mp_filter', 'source' ] );
+__PACKAGE__->attributes( 'multiple', [ 'descr', 'remarks', 'org', 'tech_c', 'admin_c', 'notify', 'mnt_by', 'mnt_lower', 'changed' ] );
+
+=head1 NAME
+
+Net::Whois::Object::FilterSet::AFRINIC - an object representation of a RPSL FilterSet block
+
+=head1 DESCRIPTION
+
+A FilterSet object defines a set of routes that match the criteria that you
+specify in your 'filter' – in other words it filters out routes that you do
+not want to see.
+
+=head1 METHODS
+
+=head2 new ( @options )
+
+Constructor for the Net::Whois::Object::FilterSet::AFRINIC class
+
+=cut
+
+sub new {
+ my ( $class, @options ) = @_;
+
+ my $self = bless {}, $class;
+ $self->_init(@options);
+
+ return $self;
+}
+
+=head2 B<filter_set( [$filter_set] )>
+
+Accessor to the filter_set attribute.
+Accepts an optional filter_set value, always return the current filter_set value.
+
+The filter_set attribute defines the name of the filter. It is an RPSL
+name that starts with "fltr-".
+
+The name of a filter_set object can be hierarchical:
+
+A hierarchical filter_set name is a sequence of filter_set names and AS
+Numbers separated by colons. At least one component of the name must be an
+actual filter_set name (i.e. start with "fltr-"). All the set name
+components of a hierarchical filter-name have to be filter_set names.
+
+=cut
+
+sub filter_set {
+ my ( $self, $filter_set ) = @_;
+ if ( $filter_set and $filter_set !~ /^fltr-/i ) {
+ warn "Incorrect FilterSet's name ($filter_set) : Should start with 'FLTR-'";
+ }
+
+ return $self->_single_attribute_setget( 'filter_set', $filter_set );
+}
+
+=head2 B<descr( [$descr] )>
+
+Accessor to the descr attribute.
+Accepts an optional descr value to be added to the descr array,
+always return the current descr array.
+
+A short description related to the object's purpose.
+
+=head2 B<filter( [$filter] )>
+
+Accessor to the filter attribute.
+Accepts an optional filter value, always return the current filter.
+
+The filter attribute defines the policy filter of the set.
+
+A policy filter is a logical expression which, when applied to a
+set of routes, returns a subset of these routes – the ones that
+you have said you want to see.
+
+=head2 B<mp_filter( [$mp_filter] )>
+
+Accessor to the mp_filter attribute.
+Accepts an optional mp_filter value, always return the current mp_filter.
+
+Logical expression which when applied to a set of IPv4 or IPv6 routes returns
+a subset of these routes.
+
+=head2 B<remarks( [$remark] )>
+
+Accessor to the remarks attribute.
+Accepts an optional remark to be added to the remarks array,
+always return the current remarks array.
+
+General remarks. May include a URL or email address.
+
+=head2 B<tech_c( [$contact] )>
+
+Accessor to the tech_c attribute.
+Accepts an optional contact to be added to the tech_c array,
+always return the current tech_c array.
+
+The NIC-handle of a technical contact 'person' or 'role' object. As more than
+one person often fulfills a role function, there may be more than one tech_c
+listed.
+
+A technical contact (tech_c) must be a person responsible for the
+day-to-day operation of the network, but does not need to be
+physically located at the site of the network.
+
+=head2 B<admin_c( [$contact] )>
+
+Accessor to the admin_c attribute.
+Accepts an optional contact to be added to the admin_c array,
+always return the current admin_c array.
+
+The NIC-handle of an on-site contact 'person' object. As more than one person
+often fulfills a role function, there may be more than one admin_c listed.
+
+An administrative contact (admin_c) must be someone who is physically
+located at the site of the network.
+
+=head2 B<org( [$org] )>
+
+Accessor to the org attribute.
+Accepts an optional org value to be added to the org array,
+always return the current org array.
+
+The organisation responsible for this FilterSet object.
+
+=head2 B<notify( [$notify] )>
+
+Accessor to the notify attribute.
+Accepts an optional notify value to be added to the notify array,
+always return the current notify array.
+
+The email address of who last updated the database object and the date it
+occurred.
+
+Every time a change is made to a database object, this attribute will show
+the email address of the person who made those changes.
+Please use the address format specified in RFC 822 - Standard for
+the Format of ARPA Internet Text Message and provide the date
+format using one of the following two formats: YYYYMMDD or YYMMDD.
+
+=head2 B<mnt_by( [$mnt_by] )>
+
+Accessor to the mnt_by attribute.
+Accepts an optional mnt_by value to be added to the mnt_by array,
+always return the current mnt_by array.
+
+Lists a registered 'mntner' used to authorize and authenticate changes to this
+object.
+
+When the database details are protected by a Mntner object, then
+only persons with access to the security information of that Mntner
+object will be able to change details.
+
+=head2 B<mnt_lower( [$mnt_lower] )>
+
+Accessor to the mnt_lower attribute.
+Accepts an optional mnt_lower value to be added to the mnt_lower array,
+always return the current mnt_lower array.
+
+Sometimes there is a hierarchy of maintainers. In these cases, mnt_lower is
+used as well as mnt_by.
+
+=head2 B<changed( [$changed] )>
+
+Accessor to the changed attribute.
+Accepts an optional changed value to be added to the changed array,
+always return the current changed array.
+
+The email address of who last updated the database object and the date it
+occurred.
+
+Every time a change is made to a database object, this attribute will show
+the email address of the person who made those changes.
+Please use the address format specified in RFC 822 - Standard for
+the Format of ARPA Internet Text Message and provide the date
+format using one of the following two formats: YYYYMMDD or YYMMDD.
+
+=head2 B<source( [$source] )>
+
+Accessor to the source attribute.
+Accepts an optional source, always return the current source.
+
+The database where the object is registered.
+
+=cut
+
+1;
View
263 lib/Net/Whois/Object/Inet6Num/AFRINIC.pm
@@ -0,0 +1,263 @@
+package Net::Whois::Object::Inet6Num::AFRINIC;
+
+use base qw/Net::Whois::Object/;
+
+# whois -t inet6num -h whois.afrinic.net
+# % This is the AfriNIC Whois server.
+#
+# inet6num: [mandatory] [single] [primary/look-up key]
+# netname: [mandatory] [single] [lookup key]
+# descr: [mandatory] [multiple] [ ]
+# country: [mandatory] [multiple] [ ]
+# org: [optional] [single] [inverse key]
+# admin-c: [mandatory] [multiple] [inverse key]
+# tech-c: [mandatory] [multiple] [inverse key]
+# status: [mandatory] [single] [ ]
+# remarks: [optional] [multiple] [ ]
+# notify: [optional] [multiple] [inverse key]
+# mnt-by: [mandatory] [multiple] [inverse key]
+# mnt-lower: [optional] [multiple] [inverse key]
+# mnt-routes: [optional] [multiple] [inverse key]
+# mnt-domains: [optional] [multiple] [inverse key]
+# mnt-irt: [optional] [multiple] [inverse key]
+# changed: [mandatory] [multiple] [ ]
+# source: [mandatory] [single] [ ]
+# parent: [generated] [multiple] [ ]
+
+__PACKAGE__->attributes( 'primary', ['inet6num'] );
+__PACKAGE__->attributes( 'mandatory', [ 'inet6num', 'netname', 'descr', 'country', 'admin_c', 'tech_c', 'status', 'mnt_by', 'changed', 'source' ] );
+__PACKAGE__->attributes( 'optional', ['org', 'remarks', 'notify', 'mnt_lower', 'mnt_routes', 'mnt_domains', 'mnt_irt' ] );
+__PACKAGE__->attributes( 'single', [ 'inet6num', 'netname', 'org', 'status', 'source' ] );
+__PACKAGE__->attributes( 'multiple', [ 'descr', 'country', 'admin_c', 'tech_c', 'remarks', 'notify', 'mnt_by', 'mnt_lower', 'mnt_routes', 'mnt_domains', 'mnt_irt', 'changed' ] );
+
+=head1 NAME
+
+Net::Whois::Object::Inet6Num::AFRINIC - an object representation of a RPSL Inet6Num block
+
+=head1 DESCRIPTION
+
+An inet6num object contains information on allocations and assignments
+of IPv6 address space.
+
+=head1 METHODS
+
+=head2 new ( @options )
+
+Constructor for the Net::Whois::Object::Inet6Num::AFRINIC class
+
+=cut
+
+sub new {
+ my ( $class, @options ) = @_;
+
+ my $self = bless {}, $class;
+ $self->_init(@options);
+
+ return $self;
+}
+
+=head2 B<inet6num( [$inet6num] )>
+
+Accessor to the inet6num attribute.
+Accepts an optional inet6num value, always return the current inet6num value.
+
+The inet6num attribute specifies a range of IPv6 addresses that the
+inet6num object presents. The range may be a single address.
+
+Addresses can only be expressed in prefix notation
+
+=head2 B<netname( [$netname] )>
+
+Accessor to the netname attribute.
+Accepts an optional netname, always return the current netname.
+
+The netname attribute is the name of a range of IP address space. It is
+recommended that the same netname is used for any set of assignment ranges
+used for a common purpose.
+
+=head2 B<descr( [$descr] )>
+
+Accessor to the descr attribute.
+Accepts an optional descr to be added to the descr array,
+always return the current descr array.
+
+Description of the organization allocated or assigned the address space shown
+in the inet6num.
+
+=head2 B<country( [$country] )>
+
+Accessor to the country attribute.
+Accepts an optional country to be added to the country array,
+always return the current country array.
+
+The country attribute identifies the country. It has never been specified
+if this is the country where the addresses are used, where the issuing
+organisation is based or some transit country in between. There are no rules
+defined for this attribute. It cannot therefore be used in any reliable way to
+map IP addresses to countries.
+
+=head2 B<geoloc( [$geoloc] )>
+
+Accessor to the geoloc attribute.
+Accepts an optional geoloc, always return the current geoloc.
+
+The location coordinates for the resource
+
+Location coordinates of the resource. Can take one of the following forms:
+[-90,90][-180,180]
+
+=head2 B<language( [$language] )>
+
+Accessor to the language attribute.
+Accepts an optional language to be added to the language array,
+always return the current language array.
+
+Identifies the language.
+
+Valid two-letter ISO 639-1 language code.
+
+=head2 B<org( [$org] )>
+
+Accessor to the org attribute.
+Accepts an optional org, always return the current org.
+
+Only a single value for the org attribute is allowed in the inet6num
+object. This is to ensure only one organisation is responsible for this
+resource.
+
+=head2 B<admin_c( [$contact] )>
+
+Accessor to the admin_c attribute.
+Accepts an optional contact to be added to the admin_c array,
+always return the current admin_c array.
+
+The NIC-handle of an on-site contact 'person' object. As more than one person
+often fulfills a role function, there may be more than one admin_c listed.