File Coverage

blib/lib/Net/Whois/RIPE.pm
Criterion Covered Total %
statement 100 109 91.7
branch 36 54 66.6
condition 8 9 88.8
subroutine 29 30 96.6
pod 18 18 100.0
total 191 220 86.8


line stmt bran cond sub pod time code
1             package Net::Whois::RIPE;
2              
3 5     5   364562 use 5.006;
  5         66  
4 5     5   29 use warnings;
  5         12  
  5         196  
5 5     5   30 use strict;
  5         10  
  5         106  
6 5     5   2565 use Net::Whois::Generic;
  5         16  
  5         208  
7 5     5   38 use IO::Socket::INET;
  5         11  
  5         49  
8 5     5   2923 use IO::Select;
  5         12  
  5         187  
9 5     5   28 use Iterator;
  5         10  
  5         401  
10              
11 5         8528 use constant { SOON => 30,
12             END_OF_OBJECT_MARK => "\n\n",
13             EOL => "\015\012",
14             QUERY_KEEPALIVE => q{-k },
15             QUERY_NON_RECURSIVE => q{-r },
16             QUERY_REFERRAL => q{-R },
17             QUERY_GROUPING => q{-G },
18             QUERY_UNFILTERED => q{-B },
19             QUERY_LIST_OBJECTS => q{-qtypes },
20             QUERY_LIST_SOURCES => q{-qsources },
21             QUERY_FETCH_TEMPLATE => q{-t%s },
22             QUERY_LIMIT_OBJECT_TYPE => q{-T%s },
23 5     5   35 };
  5         10  
24              
25             =head1 NAME
26              
27             Net::Whois::RIPE - a pure-Perl implementation of the RIPE Database client.
28              
29             =head1 VERSION
30              
31             Version 2.008001
32              
33             =cut
34              
35             our $VERSION = 2.008001;
36              
37             =head1 SYNOPSIS
38              
39             This is an evolution of the module I (Arnaud Assad) inherited from Luis Motta Campos.
40             Which was a complete rewrite of the old version of the module inherited from
41             Paul Gampe. It incorporated new concepts Luis have learned while working at the
42             RIPE NCC between Nov 2007 and Jan 2010.
43              
44             It intends to provide a cleaner, simpler, and complete implementation of a RIPE
45             Database client.
46              
47             The usage should remain mostly the same:
48              
49             use Net::Whois::RIPE;
50              
51             my $whois = Net::Whois::RIPE->new( %options );
52             $iterator = $whois->query( 'AS333' );
53              
54             From version 2.005000 you can also use the Net::Whois::Generic interface
55             that mimics Net::Whois::Object while offering access to data from other sources
56             than RIPE (AFRINIC, APNIC)
57              
58             use Net::Whois::RIPE;
59              
60             my @objects = Net::Whois::Generic->query( 'ORG-AFNC1-AFRINIC' );
61              
62             Please see L documentation for more details
63              
64             Of course, comments are more than welcome. If you believe you can help, please
65             do not hesitate in contacting me.
66              
67             =head1 BACKWARD COMPATIBILITY
68              
69             I've choose to break backwards compatibility with older versions of the L
70             module for several different reasons. I will try to explain and justify them here, as design documentation.
71             I will also strive to provide practical solutions for porting problems, if any.
72              
73             =head2 Architecture
74              
75             The old module provided it's own L implementation. This was common
76             practice 10 years ago, when the module was initially written. I believe Perl
77             has a stable and useful standard implementation of L now, and
78             adopted it instead of maintaining my own. This allows me to reduce the
79             necessary code base without losing features.
80              
81             =head2 Query Options
82              
83             From release 2.0 onwards, L will allow almost all query
84             options understanded by the RIPE Database Server. I bumped in the lack of
85             options myself, sometimes, and I believe other programmers can also use the
86             extra features offered.
87              
88             There are nice, sane defaults provided for most of the options. This should
89             make it possible for a beginner to just ignore all options and settings and
90             still be able to make some use of the module.
91              
92             =head2 Memory Footprint
93              
94             I had the intention of reducing the memory footprint of this module when doing
95             heavy-lifting. I still don't have measurements, but that was the idea behind
96             adopting an L wrapping the L used to return results.
97              
98             =head2 Better Data Structures
99              
100             A production release of this module will be able to feed a L with
101             RPSL objects extracted from the RIPE Database and return full-fledged objects
102             containing a parsed version of the text (way more useful than a text blob, I
103             believe).
104             L (from release 2.00_010) is the first attempt toward this
105             goal.
106              
107             # You can now do
108             my @objects = Net::Whois::Object->query( 'AS333' );
109              
110             # And manipulate the object the OO ways
111             for my $object (@objects) {
112             print $object->remarks();
113             }
114              
115             =head1 METHODS
116              
117             =head2 B
118              
119             Constructor. Returns a new L object with an open connection
120             to the RIPE Database service of choice (defaulting to C).
121              
122             The C<%options> hash migth contain configuration options for the RIPE Database
123             server. Not all options provided by the RIPE Database server are suitable for
124             this implementation, but the idea is to provide everything someone can show a
125             use for. The options currently recognized are:
126              
127             =over 4
128              
129             =item B (IPv4 address or DNS name. Default is C)
130              
131             The hostname or IP address of the service to connect to
132              
133             =item B (integer, default is C<43>)
134              
135             The TCP port of the service to connect to
136              
137             =item B (integer, default is C<5>)
138              
139             The time-out (in seconds) for the TCP connection.
140              
141             =item B (boolean, default is C)
142              
143             Wherever we want (C) or not (C) to keep the connection to the
144             server open. This option implements the functionality available through RIPE
145             Database's "-k" parameter.
146              
147             =item B (boolean, default is C)
148              
149             When true, prevents the server from using the referral mechanism for domain
150             lookups, so that the RIPE Database server returns an object in the RIPE
151             Database with the exact match with the lookup argument, rather than doing a
152             referral lookup.
153              
154             =item B (boolean, default is C)
155              
156             When set to C, prevents recursion into queried objects for personal
157             information. This prevents lots of unsolicited objects from showing up on
158             queries.
159              
160             =item B (boolean, default is C)
161              
162             When C enables object grouping in server responses. There's little
163             utility to enable this option, as the objects will be parsed and returned on a
164             much reasonable format most of the time. For the brave or more knowledgeable
165             people that want to have they answers in plain text, this can help stablishing
166             a 'good' ordering for the RPSL objects returned by a query ('good' is RIPE
167             NCC's definition of 'good' in this case).
168              
169             =item B (boolean, default is C)
170              
171             When C enables unfiltered object output responses. This produces objects
172             that can be presented back to the RIPE Database for updating.
173              
174             =item B (list of valid RIPE Database object types, default is empty, meaning all types)
175              
176             Restrict the RPSL object types allowed in the response to those in the list.
177             Using this option will cause the L object to query the RIPE
178             Database for the available object types for validating the list. The response
179             will be cached for speed and bandwidth.
180              
181             =item B (boolean, default is C)
182              
183             Prevents the constructor from automatically opening a connection to the service
184             specified (conneting the socket is the default behavior). When set (C),
185             the programmer is responsible for calling C in order to stablish a
186             connection to the RIPE Database service desired.
187              
188             =back
189              
190             =cut
191              
192             {
193             my %default_options = ( hostname => 'whois.ripe.net',
194             port => '43',
195             timeout => 5,
196             keepalive => 0,
197             referral => 0,
198             recursive => 0,
199             grouping => 1,
200             unfiltered => 0,
201             types => undef,
202             disconnected => 0,
203             );
204              
205             sub new {
206 3     3 1 1755 my ( $class, %options ) = @_;
207 3         7 my %known_options;
208 3 100       47 $known_options{$_} = exists $options{$_} ? $options{$_} : $default_options{$_} foreach keys %default_options;
209              
210 3         12 my $self = bless { __options => \%known_options }, $class;
211              
212 3 100       19 $self->connect unless delete $self->{__options}{disconnected};
213 3         12 return $self;
214             }
215             }
216              
217             =head2 B
218              
219             Accessor to the hostname. Accepts an optional hostname, always return the
220             current hostname.
221              
222             =cut
223              
224             sub hostname {
225 8     8 1 368 my ( $self, $hostname ) = @_;
226 8 100       23 $self->{__options}{hostname} = $hostname if defined $hostname;
227 8         29 return $self->{__options}{hostname};
228             }
229              
230             =head2 B
231              
232             Accessor to the port. Accepts an optional port, always return the current
233             port.
234              
235             =cut
236              
237             sub port {
238 10     10 1 21 my ( $self, $port ) = @_;
239 10 100 100     43 $self->{__options}{port} = $port if defined $port && $port =~ m{^\d+$};
240 10         36 return $self->{__options}{port};
241             }
242              
243             =head2 B
244              
245             Accessor to the timeout configuration option. Accepts an optional timeout,
246             always return the current timeout.
247              
248             =cut
249              
250             sub timeout {
251 16     16 1 36 my ( $self, $timeout ) = @_;
252 16 100 100     55 $self->{__options}{timeout} = $timeout
253             if defined $timeout && $timeout =~ m{^\d+$};
254 16         76 return $self->{__options}{timeout};
255             }
256              
257             =begin UNDOCUMENTED
258              
259             =head2 B<__boolean_accessor( $self, $attribute [, $value ] )>
260              
261             Private method. Shouldn't be used from other modules.
262              
263             Generic implementation of an accessor for booleans. Receives a reference to the
264             current instance, the attribute name, and a value to be interpreted under
265             Perl's boolean rules. Sets or gets the named attribute with the given value.
266             Always returns the most up-to-date value of the attribute.
267              
268             =end UNDOCUMENTED
269              
270             =cut
271              
272             sub __boolean_accessor {
273 32     32   62 my ( $self, $attribute ) = ( shift, shift );
274 32 100       73 if ( scalar @_ == 1 ) {
275 8         11 my $value = shift;
276 8 100       51 $self->{__options}{$attribute} = $value ? 1 : 0;
277             }
278 32         144 return $self->{__options}{$attribute};
279             }
280              
281             =head2 B
282              
283             Accessor to the keepalive configuration option. Accepts an optional keepalive,
284             always return the current keepalive.
285              
286             =cut
287              
288             sub keepalive {
289 13     13 1 23 my $self = shift;
290 13         37 return $self->__boolean_accessor( 'keepalive', @_ );
291             }
292              
293             =head2 B
294              
295             Accessor to the referral configuration option. Accepts an optional referral,
296             always return the current referral.
297              
298             =cut
299              
300             sub referral {
301 6     6 1 11 my $self = shift;
302 6         16 return $self->__boolean_accessor( 'referral', @_ );
303             }
304              
305             =head2 B
306              
307             Accessor to the recursive configuration option. Accepts an optional recursive,
308             always return the current recursive.
309              
310             =cut
311              
312             sub recursive {
313 6     6 1 12 my $self = shift;
314 6         14 return $self->__boolean_accessor( 'recursive', @_ );
315             }
316              
317             =head2 B
318              
319             Accessor to the grouping configuration option. Accepts an optional grouping,
320             always return the current grouping.
321              
322             =cut
323              
324             sub grouping {
325 5     5 1 10 my $self = shift;
326 5         13 return $self->__boolean_accessor( 'grouping', @_ );
327             }
328              
329             =head2 B
330              
331             Accessor to the unfiltered configuration option.
332              
333             =cut
334              
335             sub unfiltered {
336 2     2 1 6 my $self = shift;
337 2         6 return $self->__boolean_accessor( 'unfiltered', @_ );
338             }
339              
340             =head2 B
341              
342             Initiates a connection with the current object's configuration.
343              
344             =cut
345              
346             sub connect {
347 5     5 1 14 my $self = shift;
348 5         17 my %connection = ( Proto => 'tcp',
349             Type => SOCK_STREAM,
350             PeerAddr => $self->hostname,
351             PeerPort => $self->port,
352             Timeout => $self->timeout,
353             Domain => AF_INET,
354             Multihomed => 1,
355             );
356              
357             # Create a new IO::Socket object
358 5         43 my $socket = $self->{__state}{socket} = IO::Socket::INET->new(%connection);
359 5 50       153252 die q{Can't connect to "} . $self->hostname . ':' . $self->port . qq{". Reason: [$@].\n}
360             unless defined $socket;
361              
362             # Register $socket with the IO::Select object
363 5 100       43 if ( my $ios = $self->ios ) {
364 3 50       12 $ios->add($socket) unless $ios->exists($socket);
365             } else {
366 2         15 $self->{__state}{ioselect} = IO::Select->new($socket);
367             }
368              
369             # Set RIPE Database's "keepalive" capability
370 5 50       311 $self->send(QUERY_KEEPALIVE) if $self->keepalive;
371             }
372              
373             =head2 B
374              
375             Accessor to the L object coordinating the I/O to the L
376             object used by this module to communicate with the RIPE Database Server. You
377             shouldn't use this object, but the L and L<"query( $query_string )">
378             methods instead.
379              
380             =cut
381              
382 13     13 1 58 sub ios { return $_[0]->{__state}{ioselect} }
383              
384             =head2 B
385              
386             Read-only accessor to the L object used by this module.
387              
388             =cut
389              
390 22     22 1 535 sub socket { return $_[0]->{__state}{socket} }
391              
392             =head2 B
393              
394             Sends a message to the RIPE Database server instance to which we're connected
395             to. Dies if it cannot write, or if there's no open connection to the server.
396              
397             Return C if the message could be written to the socket, C
398             otherwise.
399              
400             =cut
401              
402             sub send {
403 0     0 1 0 my ( $self, $message ) = @_;
404 0 0       0 die q{Not connected} unless $self->is_connected;
405 0 0       0 if ( $self->ios->can_write( SOON + $self->timeout ) ) {
406 0         0 $self->socket->print( $message, EOL );
407 0         0 $self->socket->flush;
408 0         0 return 1;
409             }
410 0         0 return 0;
411             }
412              
413             =head2 B
414              
415             Reconnects to the server in case we lost connection.
416              
417             =cut
418              
419             sub reconnect {
420 3     3 1 7 my $self = shift;
421 3 50       7 $self->disconnect if $self->is_connected;
422 3         10 $self->connect;
423             }
424              
425             =head2 B
426              
427             Disconnects this client from the server. This renders the client useless until
428             you call L again. This method is called by L as part of
429             an object's clean-up process.
430              
431             =cut
432              
433             sub disconnect {
434 7     7 1 1182 my $self = shift;
435 7 100       30 if ( $self->is_connected ) {
436 5         101 my $socket = $self->{__state}{socket};
437 5         29 $socket->close;
438             $self->{__state}{ioselect}->remove($socket)
439 5 50       579 if $self->{__state}{ioselect};
440 5         474 delete $self->{__state}{socket};
441             }
442             }
443              
444             =head2 B
445              
446             Returns C if this instance is connected to the RIPE Database service
447             configured.
448              
449             =cut
450              
451             sub is_connected {
452 15     15 1 1152 my $self = shift;
453 15         33 my $socket = $self->socket;
454 15 100 66     245 return UNIVERSAL::isa( $socket, 'IO::Socket' )
455             && $socket->connected ? 1 : 0;
456             }
457              
458             =head2 B
459              
460             Net::Whois::RIPE object destructor. Called by the Perl interpreter upon
461             destruction of an instance.
462              
463             =cut
464              
465             sub DESTROY {
466 3     3   1376 my $self = shift;
467 3         11 $self->disconnect;
468             }
469              
470             =head2 B
471              
472             Sends a query to the server. Returns an L object that will return one RPSL block at a time.
473              
474             =cut
475              
476             # TODO: Identify and ignore comments within the Iterator scope?
477             # TODO: Identify and rise as soon as possible "%ERROR:\d+:.+" results.
478              
479             sub query {
480 1     1 1 3 my ( $self, $query ) = @_;
481 1         3 my $parameters = "";
482 1 50       3 $parameters .= q{ } . QUERY_KEEPALIVE if $self->keepalive;
483 1 50       4 $parameters .= q{ } . QUERY_UNFILTERED if $self->unfiltered;
484 1 50       3 $parameters .= q{ } . QUERY_NON_RECURSIVE unless $self->recursive;
485 1 50       4 $parameters .= q{ } . QUERY_REFERRAL if $self->referral;
486 1         3 my $fullquery = $parameters . $query;
487 1         5 return $self->__query($fullquery);
488             }
489              
490             # Allows me to pass in queries without having all the automatic options added
491             # up to it.
492             sub __query {
493 2     2   17 my ( $self, $query ) = @_;
494 2 50       9 $self->reconnect unless $self->keepalive;
495 2 50       6 die "Not connected" unless $self->is_connected;
496              
497 2 50       49 if ( $self->ios->can_write( SOON + $self->timeout ) ) {
498 2         99 $self->socket->print( $query, EOL );
499              
500             return Iterator->new(
501             sub {
502 4     4   94 local $/ = "\n\n";
503 4 50       14 if ( $self->ios->can_read( SOON + $self->timeout ) ) {
504 4         35428 my $block = $self->socket->getline;
505 4 50       274 return $block if defined $block;
506             }
507 0         0 Iterator::is_done;
508             }
509 2         252 );
510             }
511             }
512              
513             =head2 B
514              
515             Return a list of known object types from the RIPE Database.
516              
517             RIPE currently returns 21 types (Limerik have been removed):
518             as-block as-set aut-num domain filter-set inet6num inetnum inet-rtr irt
519             key-cert mntner organisation peering-set person poem poetic-form role route
520             route6 route-set rtr-set
521              
522             Due to some strange mis-behaviour in the protocol (or documentation?) the RIPE
523             Database server won't allow a keep-alive token with this query, meaning the
524             connection will be terminated after this query.
525              
526             =cut
527              
528             sub object_types {
529 1     1 1 1045 my $self = shift;
530 1         4 my $iterator = $self->__query(QUERY_LIST_OBJECTS);
531 1         29 while ( !$iterator->is_exhausted ) {
532 2         19 my $value = $iterator->value;
533 2 100       69 return split /\s+/, $value if $value !~ /^%\s/;
534             }
535 0           return;
536             }
537              
538             =head1 CAVEATS
539              
540             =over 4
541              
542             =item B
543              
544             There's no support for IPv6 still on this module. I'm planning to add it in a
545             future version.
546              
547             =item B
548              
549             As this is the initial alpha release, there is still some work to do in terms
550             of testing. One of the first things I must work on is to eliminate the
551             dependency on connectivity to the RIPE Database.
552              
553             =item B
554              
555             I plan to implement a drop-in replacement to the old interface soon, as an extension to this module. For now, this module just breaks compatibility with the old interface. Please read the full discussion about compatibility with older version of the L in the L section.
556              
557             =back
558              
559             =head1 BUGS
560              
561             Please report any bugs or feature requests to C
562             rt.cpan.org>, or through the web interface at
563             L. I will be
564             notified, and then you'll automatically be notified of progress on your bug as
565             I make changes.
566              
567              
568             =head1 SUPPORT
569              
570             You can find documentation for this module with the perldoc command.
571              
572             perldoc Net::Whois::RIPE
573              
574              
575             You can also look for information at:
576              
577             =over 4
578              
579             =item * RT: CPAN's request tracker
580              
581             L
582              
583             =item * AnnoCPAN: Annotated CPAN documentation
584              
585             L
586              
587             =item * CPAN Ratings
588              
589             L
590              
591             =item * Search CPAN
592              
593             L
594              
595             =back
596              
597             =head1 AUTHORS
598              
599             Arnaud Assad, C<< aassad at cpan.org >>
600             Luis Motta Campos, C<< >>
601             Paul Gampe
602             Kevin Backer
603              
604             =head1 ACKNOWLEDGEMENTS
605              
606             Thanks to DOOLTA (L) for allowing Arnaud Assad to work on this module during some of his office
607             hours.
608              
609             Thanks to Luis Motta Campos for allowing me (Arnaud Assad) the maintenance of this
610             module on CPAN;
611              
612             Thanks to RIPE NCC for allowing Luis Motta Campos to work on this during some of his office
613             hours.
614              
615             Thanks to Paul Gampe for allowing Luis Motta Campos to handle me the maintenance of this
616             module on CPAN;
617              
618             Thanks to Paul Gampe and Kevin Backer for writing previous versions of this
619             module;
620              
621             Thanks to Carlos Fuentes for the nice patch with bugfixes for version 2.00_008.
622             Thanks to Moritz Lenz for all his contributions
623             Thanks to Noris Network AG for allowing him to contribute to this module.
624              
625             =head1 COPYRIGHT & LICENSE
626              
627             Copyright 2012-2020 Arnaud Assad
628             Copyright 2010 Luis Motta Campos, all rights reserved.
629              
630             This program is free software; you can redistribute it and/or modify it
631             under the same terms as Perl itself.
632              
633             =cut
634              
635             1;