File Coverage

blib/lib/Net/Whois/ARIN.pm
Criterion Covered Total %
statement 153 206 74.2
branch 59 84 70.2
condition 6 12 50.0
subroutine 15 18 83.3
pod 8 8 100.0
total 241 328 73.4


line stmt bran cond sub pod time code
1             package Net::Whois::ARIN;
2              
3             =head1 NAME
4              
5             Net::Whois::ARIN - ARIN whois client
6              
7             =head1 SYNOPSIS
8              
9             use Net::Whois::ARIN;
10              
11             my $w = Net::Whois::ARIN->new(
12             host => 'whois.arin.net',
13             port => 43,
14             timeout => 30,
15             );
16              
17             # fetch raw whois output as a scalar
18             my $result = $w->query( '207.173.0.0' );
19              
20             # fetch raw whois output as a list
21             my @results = $w->query( 'NET-207-173-0-0-1' );
22              
23             # search for a network record
24             my @output = $w->network( '207.173.0.0' );
25             foreach my $net (@output) {
26             printf(
27             "%s\t(%s)\t%s\n",
28             $net->OrgName,
29             $net->NetHandle,
30             $net->NetRange,
31             );
32              
33             # display the network's contact information
34             foreach my $cust ($net->contacts) {
35             printf "Contact: %s (%s)\n", $cust->Name, $cust->Email;
36             }
37             }
38              
39             # lookup an autonomous system number
40             my($asn) = $w->asn( 5650 );
41             printf "AS5650 was assigned to %s\n", $asn->OrgName;
42              
43             # search for a point-of-contact by handle
44             my @contact = $w->contact('DM2339-ARIN');
45              
46             my @contact_records = $w->domain('eli.net');
47              
48             # search for an organization record by the OrgId
49             my @org = $w->organization('FRTR');
50              
51             # search for a customer record by Handle
52             my @customers = $w->customer('C00823787');
53              
54             =head1 DESCRIPTION
55              
56             This module provides a Perl interface to the ARIN Whois server. The module takes care of connecting to an ARIN whois server, sending your whois requests, and parsing the whois output. The whois records are returned as lists of Net::Whois::ARIN::* instances.
57              
58             =cut
59              
60 6     6   7204 use strict;
  6         15  
  6         306  
61              
62 6     6   34 use vars qw/ $VERSION /;
  6         11  
  6         499  
63             $VERSION = '0.12';
64              
65 6     6   35 use Carp;
  6         9  
  6         504  
66 6     6   6393 use IO::Socket;
  6         163239  
  6         29  
67 6     6   8231 use Net::Whois::ARIN::AS;
  6         18  
  6         172  
68 6     6   4291 use Net::Whois::ARIN::Contact;
  6         17  
  6         173  
69 6     6   4164 use Net::Whois::ARIN::Customer;
  6         16  
  6         164  
70 6     6   3982 use Net::Whois::ARIN::Network;
  6         17  
  6         164  
71 6     6   3986 use Net::Whois::ARIN::Organization;
  6         16  
  6         14584  
72              
73             my $CONTACT_REGEX = qr/(RTech|Tech|NOC|OrgAbuse|OrgTech|RAbuse|Abuse|Admin)(\w+)/;
74              
75             =head1 METHODS
76              
77             In the calling conventions below C<[]>'s represent optional parameters.
78              
79             =over 4
80              
81             =item B - create a Net::Whois::ARIN object
82              
83             my $o = Net::Whois::ARIN->new(
84             [-hostname=> 'whois.arin.net',]
85             [-port => 43,]
86             [-timeout => 45,]
87             [-retries => 3,]
88             );
89              
90             This is the constuctor for Net::Whois::ARIN. The object returned can be used to query the whois database.
91              
92             =cut
93              
94             sub new {
95 6     6 1 5399 my $class = shift;
96 6         37 my %param = @_;
97 6         15 my %args;
98              
99 6         27 foreach (keys %param) {
100 19 100       129 if (/^-?host(?:name)?$/i) { $args{'host'} = $param{$_} }
  6 100       23  
    100          
    50          
101 6         19 elsif (/^-?port$/i) { $args{'port'} = $param{$_} }
102 6         20 elsif (/^-?timeout$/i) { $args{'timeout'} = $param{$_} }
103 1         3 elsif (/^-?retries$/i) { $args{'retries'} = $param{$_} }
104             else {
105 0         0 carp("$_ is not a valid argument to ${class}->new()");
106             }
107             }
108              
109 6   50     126 my $self = bless {
      50        
      100        
110             '_host' => $args{'host'} || 'whois.arin.net',
111             '_port' => $args{'port'} || 43,
112             '_timeout' => $args{'timeout'},
113             '_retries' => $args{'retries'} || 3,
114             }, $class;
115              
116 6         28 return $self;
117             }
118              
119             sub _connect {
120 0     0   0 my $self = shift;
121 0         0 my $host = $self->{'_host'};
122 0         0 my $port = $self->{'_port'};
123 0         0 my $retries = $self->{'_retries'};
124 0         0 my $sock = undef;
125              
126 0   0     0 do {
127 0 0       0 $sock = IO::Socket::INET->new(
128             PeerHost => $host,
129             PeerPort => $port,
130             Proto => 'tcp',
131             ( ( defined $self->{'_timeout'} )
132             ? ('Timeout' => $self->{'_timeout'})
133             : (),
134             ),
135             )
136             } while (!$sock && --$retries);
137            
138 0 0       0 unless ($sock) {
139 0         0 my $error = $@;
140 0 0       0 if($error eq 'IO::Socket::INET: ') {
141 0         0 $error = 'connection time out';
142             }
143 0         0 croak "can't connect to ${host}\[$port\]: $error";
144             }
145              
146 0         0 $sock->autoflush();
147 0         0 return $sock;
148             }
149              
150             =item B - make a raw query to the whois server
151              
152             my @output = $o->query('207.173.112.0');
153              
154             =cut
155              
156             # open connection, send a whois query, close connection, return whois response
157             sub query {
158 0     0 1 0 my($self, $query) = @_;
159 0         0 my $s = $self->_connect();
160 0         0 print $s '' . $query . "\x0d\x0a";
161 0         0 local $/;
162 0         0 my $results = <$s>;
163 0         0 undef $s;
164 0 0       0 return (wantarray) ? split(/\n/, $results) : $results;
165             }
166              
167             =item B - request a network record
168              
169             my @records = $o->network('207.173.112.0');
170              
171             This method requires a single argument. The argument indicates the network to use in the whois lookup. The method returns a list of Net::Whois::ARIN::Network records that matched your search criteria.
172              
173             =cut
174              
175             sub network {
176 1     1 1 713 my ($self, $query) = @_;
177 1         5 my @output = $self->query("n + $query");
178 1         126 my @contacts;
179             my @records;
180 0         0 my %attributes;
181 1         1 my $record_count = 0;
182 1         2 my $found_contact_info = 0;
183 1         3 foreach (@output) {
184 31 100       88 next unless $_ =~ /^(\S+):\s+(.*)$/;
185 24         48 my ($key, $value) = ($1, $2);
186 24         100 $value =~ s/\s*$//;
187            
188 24 100 66     85 if ($key eq 'OrgName' || $key eq 'CustName') {
189 1         1 $record_count++;
190 1 50       4 unless ($record_count > 1) {
191 1         3 $attributes{$key} = $value;
192 1         2 next;
193             }
194 0         0 my $net = Net::Whois::ARIN::Network->new( %attributes );
195 0         0 $net->contacts( @contacts );
196 0         0 push @records, $net;
197 0         0 $found_contact_info = 0;
198 0         0 @contacts = ();
199 0         0 %attributes = ();
200             }
201            
202 23 100       145 if ($key =~ /^$CONTACT_REGEX$/ ) {
    50          
203 8         8 $found_contact_info ++;
204 8 100       22 if ($2 eq 'Handle') {
205 2         8 my @data = $self->contact( $value );;
206 2         2 push @contacts, @data;
207 2         8 $contacts[-1]->Type( $1 );
208             }
209             }
210             elsif( !$found_contact_info ) {
211 15         41 $attributes{$key} = $value;
212             }
213             }
214              
215 1         10 my $net = Net::Whois::ARIN::Network->new( %attributes );
216 1         10 $net->contacts( @contacts );
217 1         2 push @records, $net;
218              
219 1         7 return @records;
220             }
221              
222             =item B - request an ASN record
223              
224             my @record = $o->asn(5650);
225              
226             This method requires a single argument. The argument indicates the autonomous system number to use in the whois lookup. The method returns a list of Net::Whois::ARIN::AS objects.
227              
228             =cut
229              
230             sub asn {
231 1     1 1 1481 my ($self, $query) = @_;
232 1         5 my @output = $self->query("a + $query");
233 1         134 my(%attributes, @contacts);
234              
235 1         2 foreach ( @output ) {
236 35 100       116 next unless $_ =~ /^(\S+):\s+(.*)$/;
237 26         53 my ($key, $value) = ($1, $2);
238 26         101 $value =~ s/\s*$//;
239 26 100       178 if ($key eq 'Address') {
    100          
240 1         4 $attributes{Address} .= "$value\n";
241             }
242             elsif( $key =~ /^$CONTACT_REGEX$/ ) {
243 12 100       39 if ($2 eq 'Handle') {
244 3         12 push @contacts, $self->contact( $value );
245 3         11 $contacts[-1]->Type( $1 );
246             }
247             }
248             else {
249 13         65 $attributes{$key} = $value;
250             }
251             }
252              
253 1 50       5 chomp( $attributes{Address} )
254             if exists $attributes{Address};
255              
256 1         12 my $as = Net::Whois::ARIN::AS->new( %attributes );
257 1         5 $as->contacts( @contacts );
258 1         7 return $as;
259             }
260              
261             =item B - request an organization record
262              
263             my @record = $w->org('ELIX');
264              
265             =cut
266              
267             sub organization {
268 1     1 1 732 my ($self, $query) = @_;
269 1         5 my @output = $self->query("o + $query");
270              
271 1         173 my @records;
272 1         2 my(%attributes, @contacts);
273 1         3 my $record_count = 0;
274 1         1 my $found_contact_info = 0;
275              
276 1         3 foreach ( @output ) {
277 31 100       110 next unless $_ =~ /^(\S+):\s+(.*)$/;
278 23         50 my ($key, $value) = ($1, $2);
279 23         104 $value =~ s/\s*$//;
280              
281 23 100       47 if ($key eq 'OrgName') {
282 1         1 $record_count++;
283 1 50       4 unless ($record_count > 1) {
284 1         2 $attributes{$key} = $value;
285 1         2 next;
286             }
287 0         0 my $org = Net::Whois::ARIN::Organization->new( %attributes );
288 0         0 $org->contacts( @contacts );
289 0         0 push @records, $org;
290 0         0 $found_contact_info = 0;
291 0         0 @contacts = ();
292 0         0 %attributes = ();
293             }
294 22 100       155 if ($key eq 'Address') {
    100          
    50          
295 1         4 $attributes{Address} .= "$value\n";
296             }
297             elsif( $key =~ /^$CONTACT_REGEX$/ ) {
298 12         11 $found_contact_info ++;
299 12 100       34 if ($2 eq 'Handle') {
300 3         11 push @contacts, $self->contact( $value );
301 3         11 $contacts[-1]->Type( $1 );
302             }
303             }
304             elsif( !$found_contact_info ) {
305 9         27 $attributes{$key} = $value;
306             }
307             }
308              
309 1 50       5 chomp( $attributes{Address} )
310             if exists $attributes{Address};
311              
312 1         9 my $org = Net::Whois::ARIN::Organization->new( %attributes );
313 1         6 $org->contacts( @contacts );
314 1         3 push @records, $org;
315 1         9 return @records;
316             }
317              
318             =item B - request a customer record
319              
320             my @records = $w->customer('ELIX');
321              
322             =cut
323              
324             sub customer {
325 1     1 1 906 my ($self, $query) = @_;
326 1         5 my @output = $self->query("c + $query");
327              
328 1         167 my @records;
329 1         2 my(%attributes, @contacts);
330 1         3 my $record_count = 0;
331 1         3 my $found_contact_info = 0;
332              
333 1         2 foreach ( @output ) {
334 27 100       119 next unless $_ =~ /^(\S+):\s+(.*)$/;
335 21         54 my ($key, $value) = ($1, $2);
336 21         110 $value =~ s/\s*$//;
337              
338 21 100       58 if ($key eq 'CustName') {
339 1         2 $record_count++;
340 1 50       4 unless ($record_count > 1) {
341 1         3 $attributes{$key} = $value;
342 1         4 next;
343             }
344 0         0 my $cust = Net::Whois::ARIN::Customer->new( %attributes );
345 0         0 $cust->contacts( @contacts );
346 0         0 push @records, $cust;
347 0         0 $found_contact_info = 0;
348 0         0 @contacts = ();
349 0         0 %attributes = ();
350             }
351              
352 20 100       196 if ($key eq 'Address') {
    100          
    50          
353 1         5 $attributes{Address} .= "$value\n";
354             }
355             elsif( $key =~ /^$CONTACT_REGEX$/ ) {
356 4         6 $found_contact_info ++;
357 4 100       16 if ($2 eq 'Handle') {
358             # do a whois lookup for point of contact information
359 1         8 my @data = $self->contact($value);
360 1         3 push @contacts, @data;
361 1         7 $contacts[-1]->Type( $1 );
362             }
363             }
364             elsif( !$found_contact_info ) {
365 15         49 $attributes{$key} = $value;
366             }
367             }
368              
369 1 50       6 chomp( $attributes{Address} )
370             if exists $attributes{Address};
371              
372 1         15 my $cust = Net::Whois::ARIN::Customer->new( %attributes );
373 1         7 $cust->contacts( @contacts );
374 1         3 push @records, $cust;
375 1         10 return @records;
376             }
377              
378             =item B - request a point-of-contact record
379              
380             my @record = $w->contact('DM2339-ARIN');
381              
382             =cut
383              
384             sub contact {
385 10     10 1 771 my ($self, $query) = @_;
386 10         40 my @output = $self->query("p + $query");
387 10         887 my @records;
388 10         15 my $n = -1;
389 10         21 foreach ( @output ) {
390 140 100       476 next unless $_ =~ /^(\S+):\s+(.*)$/;
391 112         218 my ($key, $value) = ($1, $2);
392 112         436 $value =~ s/\s*$//;
393             # $records[++$n] = {} if /^(Name):/;
394 112 100       232 $records[++$n] = {} if $n < 0;
395 112 100       183 if ($key eq 'Address') {
396 14         42 $records[$n]->{Address} .= "$value\n";
397             }
398             else {
399 98         227 $records[$n]->{$key} = $value;
400             }
401             }
402              
403 10         14 my @contacts;
404 10         20 foreach ( @records ) {
405 7         78 my %attributes = %$_;
406 7 50       42 chomp($attributes{Address})
407             if exists $attributes{Address};
408 7         3123 push @contacts, Net::Whois::ARIN::Contact->new( %attributes );
409             }
410              
411 10         56 return @contacts;
412             }
413              
414             =item B - request all records from a given domain
415              
416             @output = $w->domain('eli.net');
417              
418             =back
419              
420             =cut
421              
422             sub domain {
423 0     0 1   my ($self, $query) = @_;
424 0 0         $query = "\@$query" if $query !~ /^\@/;
425 0           $query = "+ $query";
426 0           my @output = $self->query($query);
427 0           my @contacts;
428             my %attr;
429 0           foreach (@output) {
430 0 0         if(/^(\S+):\s+(.*)$/) {
431 0           $attr{$1} = $2;
432             }
433 0 0         if(/^Email:\s+.*$/) {
434 0           push @contacts, Net::Whois::ARIN::Contact->new( %attr );
435 0           %attr = ();
436             }
437             }
438 0           return @contacts;
439             }
440              
441             =head1 SEE ALSO
442              
443             L
444              
445             L
446              
447             L
448              
449             L
450              
451             L
452              
453             =head1 AUTHOR
454              
455             Todd Caine
456              
457             =head1 COPYRIGHT AND LICENSE
458              
459             Copyright (c) 2004-2011 Todd Caine. All rights reserved.
460              
461             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
462              
463             =cut
464              
465              
466             1;
467             __END__