File Coverage

blib/lib/Net/Whois/IP.pm
Criterion Covered Total %
statement 130 273 47.6
branch 37 134 27.6
condition 28 71 39.4
subroutine 17 23 73.9
pod 0 2 0.0
total 212 503 42.1


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