File Coverage

blib/lib/Net/Whois/IP.pm
Criterion Covered Total %
statement 34 274 12.4
branch 6 134 4.4
condition 2 71 2.8
subroutine 10 23 43.4
pod 0 2 0.0
total 52 504 10.3


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