File Coverage

blib/lib/Net/Whois/IP.pm
Criterion Covered Total %
statement 131 274 47.8
branch 37 134 27.6
condition 28 71 39.4
subroutine 17 23 73.9
pod 0 2 0.0
total 213 504 42.2


line stmt bran cond sub pod time code
1             package Net::Whois::IP;
2              
3             =head1 NAME
4              
5             Net::Whois::IP - Perl extension for looking up the whois information for
6             ip addresses
7              
8             =head1 SYNOPSIS
9              
10             use Net::Whois::IP qw(whoisip_query);
11              
12             my $ip = "192.168.1.1";
13             my ($response, $array_of_responses) =
14             whoisip_query($ip,
15             $optional_registry,
16             $optional_multiple_flag,
17             $optional_raw_flag,
18             $optional_array_of_search_options);
19              
20             In scalar context (single response hash returned):
21              
22             my $response = whoisip_query($ip);
23              
24             The response will be a reference to a hash containing all information
25             provided by the whois registrar.
26              
27             In list context (response hash and response chain returned):
28              
29             my ($response, $array_of_responses) = whoisip_query($ip,
30             undef,
31             "true");
32              
33             N.B.: See NOTES, below.
34              
35             The array_of_responses is a reference to an array containing references
36             to hashes for each level of query performed. For example, many records
37             must be searched several times to obtain the most detailed information;
38             this array contains the responses from each level.
39              
40             If $optional_multiple_flag is not undef, all duplicate values for a given
41             field will be returned.
42              
43             For example, normally only the last instance of TechPhone will be
44             returned if a record contains more than one. However, setting this flag
45             to a non-undef value will return all values as an array.
46              
47             As a consequence, all returned field values in the response hash become
48             references to arrays and must be dereferenced before use.
49              
50             If $optional_raw_flag is not undef, the response will be a reference to
51             an array containing the raw responses from the registrar instead of a
52             reference to a hash. In raw mode, no parsed response chain is returned.
53              
54             If $optional_array_of_search_options is not undef, the first two entries
55             will be used to replace TechPhone and OrgTechPhone in the search method.
56             This is fairly dangerous and can cause the module not to work at all if
57             set incorrectly.
58              
59             Normal unwrap of $response ($optional_multiple_flag not set):
60              
61             my $response = whoisip_query($ip);
62             foreach (sort keys(%{$response}) ) {
63             print "$_ $response->{$_} \n";
64             }
65              
66             $optional_multiple_flag set to a value:
67              
68             my $response = whoisip_query($ip, undef, "true");
69             foreach ( sort keys %$response ) {
70             print "$_ is\n";
71             foreach ( @{ $response->{ $_ } } ) { print " $_\n"; }
72             }
73              
74             $optional_raw_flag set to a value:
75              
76             my $response = whoisip_query( $ip, undef, undef, "true");
77             foreach (@{$response}) { print $_; }
78              
79             $optional_array_of_search_options set but not $optional_multiple_flag or
80             $optional_raw_flag:
81              
82             my $search_options = ["NetName","OrgName"];
83             my $response = whoisip_query($ip, undef, undef, undef, $search_options);
84             foreach (sort keys(%{$response}) ) { print "$_ $response->{$_} \n"; }
85              
86             =head1 TESTING
87              
88             Some tests perform live WHOIS queries and therefore require
89             outbound TCP connectivity to WHOIS servers on port 43.
90              
91             When running under automated smoke-testing environments
92             (C), live-query tests are skipped only if
93             port 43 connectivity is unavailable. This allows automated
94             test systems with working WHOIS access to exercise the live
95             query functionality while avoiding false failures on systems
96             that restrict outbound WHOIS traffic.
97              
98             Local development and manual testing are unaffected.
99              
100             =head1 NOTES
101              
102             For certain ARIN queries, additional synthesized parent/ancestor
103             records may be prepended to the returned WHOIS response array
104             ($array_of_responses). These records are synthesized from ARIN
105             summary/hierarchy output and are normalized into standard WHOIS
106             response hash format where possible.
107              
108             Synthesized parent/ancestor records are tagged with the key
109             "Synthetic", currently containing the value "ARIN-SUMMARY".
110              
111             Because ARIN summary records are abbreviated, synthesized records
112             may contain fewer fields than full WHOIS responses.
113              
114             =head1 DESCRIPTION
115              
116             Perl module to allow whois lookup of ip addresses. This module should
117             recursively query the various whois providers until it gets more
118             detailed information including either TechPhone or OrgTechPhone by
119             default; however, this is overrideable.
120              
121             =head1 AUTHOR
122              
123             Ben Schmitz -- ben@foink.com
124              
125             Thanks to Orbitz for allowing the community access to this work
126              
127             Please email me any suggestions, complaints, etc.
128              
129             =head1 SEE ALSO
130              
131             perl(1). Net::Whois
132              
133             =cut
134              
135 4     4   317627 use strict;
  4         7  
  4         199  
136 4     4   35 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
  4         9  
  4         340  
137 4     4   456 use IO::Socket;
  4         19854  
  4         21  
138 4     4   3596 use Regexp::IPv6 qw($IPv6_re);
  4         5585  
  4         500  
139 4     4   27 use File::Spec;
  4         8  
  4         104  
140             require Exporter;
141 4     4   14 use Carp;
  4         6  
  4         235  
142 4     4   19 use feature 'state';
  4         7  
  4         800  
143              
144             @ISA = qw(Exporter);
145             @EXPORT = qw(
146             whoisip_query
147             set_debug
148             );
149             $VERSION = '1.20_02';
150              
151             my %whois_servers = (
152             'RIPE' => 'whois.ripe.net',
153             'APNIC' => 'whois.apnic.net',
154             'KRNIC' => 'whois.krnic.net',
155             'LACNIC' => 'whois.lacnic.net',
156             'ARIN' => 'whois.arin.net',
157             'AFRINIC' => 'whois.afrinic.net',
158             );
159              
160             # For queries:
161             # If ARIN add n param. If RIPE or Afrinic add -B param
162             my %query_prefix = (
163             $whois_servers{ARIN} => 'n ',
164             $whois_servers{RIPE} => '-B ',
165             $whois_servers{AFRINIC} => '-B ',
166             );
167              
168             # Are we debugging?
169             my $do_debugging = 0;
170              
171 4     4   42 use constant ARIN_EXACT_MATCH_PREFIX => '! ';
  4         7  
  4         19999  
172              
173             my $whois_query_delay = 2; # Be conservative to avoid getting refused
174             my $first_arin_query_delay = 1;
175              
176              
177             ######################################
178             # Public Subs
179             ######################################
180              
181             sub whoisip_query {
182 19     19 0 763224 my($ip,$reg,$multiple_flag,$raw_flag,$search_options) = @_;
183              
184             # It allows to set the first registry to query
185 19 50 66     892 if(($ip !~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/) && ($ip !~ /^$IPv6_re$/) ) {
186 1         193 croak("$ip is not a valid ip address");
187             }
188 18 100       54 if(!defined($reg)) {
189 17         29 $reg = 'ARIN';
190             }
191 18         64 _do_debug("looking up $ip - at $reg");
192 18         54 my ($response, $array_of_responses) =
193             _do_lookup($ip, $reg, $multiple_flag, $raw_flag, $search_options);
194              
195 18         98 _do_debug("whois_ip_query sees: \$array_of_responses: " . scalar(@$array_of_responses));
196              
197             # Preserve historical scalar-context behavior while restoring
198             # documented list-context behavior.
199 18 100       304 return wantarray? ($response, $array_of_responses) : $response;
200             }
201              
202             # Enabled/disable debugging
203             sub set_debug {
204 0     0 0 0 my ($state) = @_;
205              
206 0 0       0 $do_debugging = $state ? 1 : 0;
207             }
208              
209              
210             ######################################
211             #Private Subs
212             ######################################
213             sub _do_lookup {
214 15     15   40 my($ip,$registrar,$multiple_flag,$raw_flag,$search_options) = @_;
215 15         60 _do_debug("do lookup $ip at $registrar");
216             # let's not beat up on them too much
217 15         26 my $extraflag = '1';
218 15         62 my $whois_response;
219             my $whois_raw_response;
220 15         0 my $whois_response_hash;
221 15         0 my @whois_response_array;
222 15         0 my @arin_summary_records;
223              
224 15         61 LOOP: while($extraflag ne '') {
225 27         89 _do_debug("Entering loop $extraflag");
226              
227             # Guard against unknown WHOIS registrars
228             croak("Unknown WHOIS registrar: $registrar")
229 27 50       85 unless exists $whois_servers{$registrar};
230              
231 27         48 my $lookup_host = $whois_servers{$registrar};
232 27         70 ($whois_response,$whois_response_hash) = _do_query($lookup_host,$ip,$multiple_flag);
233 27         267 _inspect_whois_response_lines($whois_response);
234 27         52 push(@whois_response_array,$whois_response_hash);
235 27         44 push(@{$whois_raw_response}, @{$whois_response});
  27         99  
  27         576  
236 27         113 my($new_ip,$new_registrar) =
237             _do_processing($whois_response,
238             $registrar,
239             $ip,
240             $whois_response_hash,
241             $search_options,
242             \@arin_summary_records
243             );
244              
245 27 100 66     121 if(($new_ip ne $ip) || ($new_registrar ne $registrar) ) {
246 12         36 _do_debug("ip was $ip -- new ip is $new_ip");
247 12         27 _do_debug("registrar was $registrar -- new registrar is $new_registrar");
248 12         17 $ip = $new_ip;
249 12         15 $registrar = $new_registrar;
250 12         21 $extraflag++;
251 12         45 next LOOP;
252             }else{
253 15         33 $extraflag='';
254 15         33 last LOOP;
255             }
256             }
257            
258             # Return raw response from registrar
259 15 50 33     47 if( ($raw_flag) && ($raw_flag ne '') ) {
260 0         0 return ($whois_raw_response);
261             }
262              
263 15 50       23 if(%{$whois_response_hash}) {
  15         52  
264 15         26 foreach my $key (sort keys %{$whois_response_hash}) {
  15         202  
265 401         504 my $value = $whois_response_hash->{$key};
266              
267 401 50       727 if (!defined $value) {
    50          
    50          
268 0         0 _do_debug("sub -- $key -- undef");
269             }
270             elsif (ref($value) eq 'ARRAY') {
271 0         0 foreach my $item (@{$value}) {
  0         0  
272 0 0 0     0 next unless defined($item) && $item =~ /\S/;
273 0 0       0 _do_debug("sub -- $key -- " . (defined $item ? $item : 'undef'));
274             }
275             }
276             elsif (!ref($value)) {
277 401         570 _do_debug("sub -- $key -- $value");
278             }
279             else {
280 0         0 _do_debug("sub -- $key -- " . ref($value));
281             }
282             }
283              
284             # If we've multiple records, normalize the add'l records into WHOIS-hash shape,
285             # putting the "oldest" ancestor (widest range) at $whois_response_array[0]
286             #
287             # N.B.: These records are often highly-abbreviated. Preserve what ARIN
288             # provides, but do not synthesize fields such as Country from child records.
289 15 50       89 if(@arin_summary_records) {
290             unshift(@whois_response_array,
291 0         0 map { _arin_summary_to_whois_response($_) } @arin_summary_records);
  0         0  
292             }
293 15         86 _inspect_whois_response_array(\@whois_response_array);
294              
295 15         253 return($whois_response_hash,\@whois_response_array);
296             }else{
297 0         0 return($whois_response,\@whois_response_array);
298             }
299             }
300              
301             # Convert ARIN summary data to "standard" WHOIS response format
302             sub _arin_summary_to_whois_response {
303 0     0   0 my ($rec) = @_;
304              
305 0         0 my %out;
306              
307 0         0 my %map = (
308             netname => 'NetName',
309             nethandle => 'NetHandle',
310             description => 'OrgName',
311             orgname => 'OrgName',
312             custname => 'CustName',
313             customer => 'Customer',
314             country => 'Country',
315             source => 'Source',
316             );
317              
318 0         0 for my $src_key (keys %map) {
319 0 0       0 next if !defined $rec->{$src_key};
320 0         0 $out{$map{$src_key}} = [ $rec->{$src_key} ];
321             }
322              
323 0   0     0 $out{Source} ||= [ 'ARIN' ];
324 0         0 $out{Synthetic} = [ 'ARIN-SUMMARY' ]; # Tag it for what it is
325              
326 0 0 0     0 if (defined $rec->{start} && defined $rec->{end}) {
327 0         0 $out{NetRange} = [ "$rec->{start} - $rec->{end}" ];
328             $out{CIDR} = [
329             _range_to_cidr_strings(
330             _ipv4_to_int($rec->{start}),
331             _ipv4_to_int($rec->{end})
332 0         0 )
333             ];
334             }
335              
336 0         0 return \%out;
337             }
338              
339             sub _do_query {
340 27     27   58 my($registrar,$ip,$multiple_flag) = @_;
341 27         36 my @response;
342 27         55 my $i =0;
343              
344             # Prevent abusing the registrars --- they may disable an ip if too many queries per minute
345 27         87 _throttle_whois_query($registrar);
346              
347 27         44 LOOP: while(1) {
348 27         41 $i++;
349 27         100 my $sock = _get_connect($registrar);
350              
351 27   100     375 _do_debug("Querying $registrar with " . ($query_prefix{$registrar} // q{}) . "$ip");
352 27   100     2960 print $sock (($query_prefix{$registrar} // q{}) . "$ip\n");
353              
354 27         2889083 @response = <$sock>;
355              
356 27         2528 close($sock);
357              
358 27 50       202 if($#response < 0) {
359 0         0 _do_debug("No valid response recieved from $registrar -- attempt $i ");
360 0 0       0 if($i <=3) {
361 0         0 next LOOP;
362             }else{
363 0         0 croak("No valid response for 4th time... dying....");
364             }
365             }else{
366 27         463 last LOOP;
367             }
368             }
369              
370 27         157 my %hash_response;
371 27   50     364 _do_debug("multiple flag = |" . ($multiple_flag // '') . "|");
372              
373 27         83 foreach my $line (@response) {
374 1993 100       4873 if ( $line =~ /^([^:]+):\s*(.*)$/ ) {
375 1383         2695 my ($key, $val) = ($1, $2);
376              
377 1383 50 33     2210 if ( $multiple_flag && $multiple_flag ne '' ) {
378             # Multiple_flag is set, so get all responses for a given record item
379 0         0 push @{ $hash_response{$key} }, $val;
  0         0  
380             } else {
381             # Multiple_flag is not set, so only the last entry for any given record item
382 1383         2783 $hash_response{$key} = $val;
383             }
384             }
385             }
386              
387 27         174 return(\@response,\%hash_response);
388             }
389              
390             sub _do_processing {
391 27     27   87 my($response,$registrar,$ip,$hash_response,$search_options,$arin_summary_records) = @_;
392              
393             # Response to comment.
394             # Bug report stating the search method will work better with different options. Easy way to do it now.
395             # this way a reference to an array can be passed in, the defaults will still
396             # be TechPhone and OrgTechPhone
397 27         81 my $pattern1 = 'TechPhone';
398 27         45 my $pattern2 = 'OrgTechPhone';
399              
400 27 0 33     147 if(ref($search_options) eq 'ARRAY' && defined $search_options->[0] && $search_options->[0] ne '') {
      33        
401 0         0 $pattern1 = $search_options->[0];
402 0         0 $pattern2 = $search_options->[1];
403             }
404              
405 27         103 _do_debug("pattern1 = $pattern1 || pattern2 == $pattern2");
406              
407 27         44 LOOP:foreach (@{$response}) {
  27         70  
408 1026 50 100     5372 if (/Contact information can be found in the (\S+)\s+database/) {
    100 66        
    100 100        
    50 66        
409 0         0 $registrar = $1;
410 0         0 _do_debug("Contact -- registrar = $registrar -- trying again");
411 0         0 last LOOP;
412             }elsif((/OrgID:\s+(\S+)/i || /source:\s+(\S+)/i) && !defined($hash_response->{$pattern1})) {
413 27         63 my $val = $1;
414 27         93 _do_debug("Org/source match: value was $val--if not known registrar, will skip");
415 27 100       89 if(exists $whois_servers{$val}) {
416 21         34 $registrar = $val;
417 21         61 _do_debug(" Known registrar match --> $registrar --> trying again ");
418 21         53 last LOOP;
419             }
420             }elsif(/Parent:\s+(\S+)/) {
421             # Use $pattern1 instead of default TechPhone
422 15 50 33     120 if(($1 ne '') && (!defined($hash_response->{$pattern1})) && (!defined($hash_response->{$pattern2})) ) {
      33        
423             # End Modif
424 0         0 $ip = $1;
425 0         0 _do_debug(" Parent match ip will be $ip --> trying again");
426 0         0 last LOOP;
427             }
428             # Test Loop via Jason Kirk -- Thanks
429             }elsif($registrar eq 'ARIN' && (/.+\((.+)\).+$/) && ($_ !~ /.+\:.+/)) {
430 0         0 my $arin_handle = $1;
431              
432 0 0       0 if(/^(.+?)\s+(\S+)\s+\((NET-[^)]+)\)\s+
433             (\d+\.\d+\.\d+\.\d+)\s+-\s+
434             (\d+\.\d+\.\d+\.\d+)\s*$/x)
435             {
436 0         0 push @$arin_summary_records, {
437             description => $1,
438             netname => $2,
439             nethandle => $3,
440             start => $4,
441             end => $5,
442             };
443             }
444              
445 0         0 my $origIp = $ip;
446 0         0 $ip = ARIN_EXACT_MATCH_PREFIX . $arin_handle;
447              
448             # Modif: Keep the smallest block
449 0 0       0 if ($origIp =~ /! NET-(\d{1,3}\-\d{1,3}\-\d{1,3}\-\d{1,3})/) {
450 0         0 my $orIP = $1;
451 0 0       0 if ($ip =~ /! NET-(\d{1,3}\-\d{1,3}\-\d{1,3}\-\d{1,3})/) {
452 0         0 my $nwIP = $1;
453 0 0       0 if (pack('C4', split(/\-/,$orIP)) ge pack('C4', split(/\-/,$nwIP))) {
454 0         0 $ip = $origIp;
455             }
456             }
457             }
458 0 0       0 if ($ip !~ /\d{1,3}\-\d{1,3}\-\d{1,3}\-\d{1,3}/){
459 0         0 $ip = $origIp;
460             }
461 0         0 _do_debug("parens match $ip $registrar --> trying again");
462             }else{
463 984         975 $ip = $ip;
464 984         992 $registrar = $registrar;
465             }
466             }
467 27         111 _do_debug("_do_processing returns arin_summary_records: ARIN summary records captured: " . scalar(@$arin_summary_records));
468 27         103 return($ip,$registrar);
469             }
470            
471            
472              
473             sub _get_connect {
474 27     27   66 my($whois_registrar) = @_;
475 27         534 my $sock = IO::Socket::INET->new(
476             PeerAddr=>$whois_registrar,
477             PeerPort=>'43',
478             Timeout=>'60',
479             );
480 27 50       3147440 unless($sock) {
481 0         0 carp("Failed to Connect to $whois_registrar at port 43: $!");
482 0         0 sleep(5);
483 0         0 $sock = IO::Socket::INET->new(
484             PeerAddr=>$whois_registrar,
485             PeerPort=>'43',
486             Timeout=>'60',
487             );
488 0 0       0 unless($sock) {
489 0         0 croak("Failed to Connect to $whois_registrar at port 43 for the second time - $@");
490             }
491             }
492 27         109 return($sock);
493             }
494              
495             sub _ipv4_to_int {
496 0     0   0 my ($ip) = @_;
497              
498 0 0       0 croak "Undefined IP address\n" if !defined $ip;
499 0 0       0 croak "Invalid IPv4 address: '$ip'\n"
500             if $ip !~ /\A(\d+)\.(\d+)\.(\d+)\.(\d+)\z/;
501              
502 0         0 my @octets = ($1, $2, $3, $4);
503              
504 0         0 for my $octet (@octets) {
505 0 0 0     0 die "Invalid IPv4 octet in '$ip'\n"
506             if $octet < 0 || $octet > 255;
507             }
508              
509 0         0 return (($octets[0] << 24) |
510             ($octets[1] << 16) |
511             ($octets[2] << 8) |
512             $octets[3]);
513             }
514              
515             sub _range_to_cidr_strings {
516 0     0   0 my ($start, $end) = @_;
517              
518 0 0       0 croak "Invalid range" if $start > $end;
519              
520 0         0 my @cidrs;
521              
522 0         0 while ($start <= $end) {
523              
524             # Special case: the entire IPv4 space
525 0 0 0     0 if ($start == 0 && $end == 0xFFFFFFFF) {
526 0         0 push @cidrs, '0.0.0.0/0';
527 0         0 last;
528             }
529              
530             # Largest power-of-two block aligned at $start.
531             # Special case: start==0, because the low-set-bit trick yields 0 there.
532 0 0       0 my $max_size = $start ? ($start & -$start) : 0x8000_0000;
533              
534             # Limit block size so it does not exceed remaining range
535 0         0 my $remaining = $end - $start + 1;
536              
537 0         0 while ($max_size > $remaining) {
538 0         0 $max_size >>= 1;
539             }
540              
541             # Convert block size to prefix length
542 0         0 my $prefix = 32 - _log2($max_size);
543              
544 0         0 push @cidrs, _int_to_ipv4($start) . "/$prefix";
545              
546 0         0 $start += $max_size;
547             }
548              
549 0         0 return @cidrs;
550             }
551              
552             sub _log2 {
553 0     0   0 my ($n) = @_;
554              
555 0 0 0     0 croak "log2(): undefined for n <= 0\n"
556             if !defined($n) || $n <= 0;
557              
558 0         0 return int(log($n) / log(2));
559             }
560              
561             sub _int_to_ipv4 {
562 0     0   0 my ($n) = @_;
563              
564 0 0       0 croak "Undefined integer IP\n" if !defined $n;
565 0 0 0     0 croak "Invalid IPv4 integer: '$n'\n"
566             if $n < 0 || $n > 0xFFFFFFFF;
567              
568 0         0 return join '.',
569             (($n >> 24) & 0xFF),
570             (($n >> 16) & 0xFF),
571             (($n >> 8) & 0xFF),
572             ( $n & 0xFF);
573             }
574              
575             sub _throttle_whois_query {
576 27     27   54 my ($registrar) = @_;
577              
578 27         34 state %last_query_time;
579              
580 27         49 my $now = time();
581 27         58 my $last = $last_query_time{$registrar};
582              
583 27 100       88 my $wait = defined($last)
584             ? $whois_query_delay - ($now - $last)
585             : 0;
586              
587 27 100 100     167 if (!defined($last) && $registrar eq $whois_servers{ARIN}) {
588 3         3 $wait = $first_arin_query_delay;
589             }
590              
591 27 100       68 if ($wait > 0) {
592 15         91 _do_debug("WHOIS throttle for $registrar: sleeping $wait second(s)");
593 15         24004974 sleep $wait;
594             }
595              
596 27         290 $last_query_time{$registrar} = time();
597             }
598              
599             sub _do_debug {
600 674 50   674   1270 return unless $do_debugging;
601              
602 0         0 state $did_warn = 0;
603              
604 0         0 my (@stuff) = @_;
605 0         0 my $date = scalar localtime;
606 0   0     0 my $tmp_dir = File::Spec->tmpdir() || '/tmp';
607 0         0 my $outdebug = File::Spec->catfile($tmp_dir, 'Net.WhoisIP.log');
608              
609 0 0       0 unless($did_warn) {
610 0         0 print STDERR "Net::Whois::IP: Debugging to \"$outdebug\" enabled!\n";
611 0         0 $did_warn = 1;
612             }
613              
614 0 0       0 open(my $debug_fh, '>>', $outdebug)
615             or warn "Unable to open $outdebug: $!";
616 0 0       0 return if !$debug_fh;
617              
618 0         0 for my $item (@stuff) {
619 0         0 print {$debug_fh} "$date|$item|\n";
  0         0  
620             }
621              
622 0         0 close($debug_fh);
623             }
624              
625             # More debugging
626             sub _inspect_whois_response_lines {
627 27     27   90 my ($lines, $label) = @_;
628              
629 27 50       72 return unless $do_debugging;
630              
631 0   0     0 $label //= 'WHOIS response';
632              
633 0         0 my @interesting;
634             my @unknown;
635              
636             LINE:
637 0         0 for my $line (@$lines) {
638 0         0 chomp $line;
639              
640 0 0       0 next LINE if $line =~ /^\s*$/;
641 0 0       0 next LINE if $line =~ /^#/;
642 0 0       0 next LINE if $line =~ /^%/;
643              
644 0 0       0 if($line =~ /^(NetRange|CIDR|NetName|NetHandle|Parent|OrgName|Country):\s*(.+)$/i) {
645 0         0 push @interesting, $line;
646 0         0 next LINE;
647             }
648              
649 0 0       0 if($line =~ /^(.+?)\s+(\S+)\s+\((NET-[^)]+)\)\s+(\d+\.\d+\.\d+\.\d+)\s+-\s+(\d+\.\d+\.\d+\.\d+)\s*$/) {
650 0         0 push @interesting, "ARIN-SUMMARY: $line";
651 0         0 next LINE;
652             }
653              
654 0 0       0 next LINE if $line =~ /^(Comment|Remarks|RegDate|Updated|Created|Last-Modified):/i;
655 0 0       0 next LINE if $line =~ /^(OrgAbuse|OrgTech|OrgNOC|RTech|RNOC|RAbuse)/i;
656              
657 0         0 push @unknown, $line;
658             }
659              
660 0         0 _do_debug(sprintf(
661             "|%s: %d interesting, %d unknown raw WHOIS lines|",
662             $label, scalar @interesting, scalar @unknown
663             ));
664              
665 0         0 _do_debug("|\n=== $label: INTERESTING raw WHOIS lines ===|");
666 0 0       0 if(@interesting) {
667 0         0 _do_debug("|$_|") for @interesting;
668             } else {
669 0         0 _do_debug("|none|");
670             }
671              
672 0         0 _do_debug("|\n=== $label: UNKNOWN raw WHOIS lines ===|");
673 0 0       0 if(@unknown) {
674 0         0 _do_debug("|$_|") for @unknown;
675             } else {
676 0         0 _do_debug("|none|");
677             }
678              
679 0         0 return;
680             }
681              
682             sub _inspect_whois_response_array {
683 15     15   34 my ($responses, $label) = @_;
684              
685 15 50       32 return unless $do_debugging;
686              
687 0   0       $label //= 'WHOIS response array';
688              
689 0           my $out = "$label:\n";
690              
691 0           for my $i (0 .. $#$responses) {
692 0           my $response = $responses->[$i];
693              
694 0           $out .= " Response [$i]:\n";
695              
696 0 0         if (ref($response) ne 'HASH') {
697 0 0         $out .= " \n";
698 0           next;
699             }
700              
701 0           for my $key (sort keys %$response) {
702 0           my $value = $response->{$key};
703              
704 0 0         if (ref($value) eq 'ARRAY') {
    0          
705 0           $out .= " $key:\n";
706 0           for my $item (@$value) {
707 0 0         $out .= " - " . (defined $item ? $item : '') . "\n";
708             }
709             } elsif (ref($value)) {
710 0           $out .= " $key: <" . ref($value) . " ref>\n";
711             } else {
712 0 0         $out .= " $key: " . (defined $value ? $value : '') . "\n";
713             }
714             }
715             }
716              
717 0           _do_debug($out);
718             }
719              
720             1;