ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-Whois-IP/IP.pm
Revision: 1.3
Committed: Sun Dec 1 14:51:23 2002 UTC (21 years, 5 months ago) by root
Branch: MAIN
CVS Tags: HEAD
Changes since 1.2: +464 -90 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 =head1 NAME
2    
3     Net::Whois::IP - find whois data for ip addresses
4    
5     =head1 SYNOPSIS
6    
7     use Net::Whois::IP;
8    
9     =head1 DESCRIPTION
10    
11     =over 4
12    
13     =cut
14    
15     package Net::Whois::IP;
16    
17     BEGIN {
18     $VERSION = 0.01;
19     @EXPORT_OK = qw();
20     }
21    
22     use base Exporter;
23    
24 root 1.2 use Carp;
25    
26     use Socket;
27    
28     use BerkeleyDB;
29    
30     use Coro;
31     use Coro::Event;
32     use Coro::Semaphore;
33     use Coro::SemaphoreSet;
34     use Coro::Socket;
35     use Coro::Timer;
36     use Coro::Channel;
37    
38     my %server;
39    
40     sub new_btree {
41     new BerkeleyDB::Btree
42     -Env => $db_env,
43     -Filename => $_[0],
44     -Flags => DB_CREATE,
45     or die "$_[0]: unable to create/open table";
46     }
47    
48 root 1.3 sub ip_range {
49     my $range = $_[0];
50    
51     if ($range =~ /^
52     (2[0-5][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])
53     (?:\.
54     (2[0-5][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])
55     (?:\.
56     (2[0-5][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])
57     (?:\.
58     (2[0-5][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])
59     )?
60     )?
61     )?
62     \/
63     ([0-9]+)
64     $/x) {
65     my $ip = inet_aton sprintf "%d.%d.%d.%d", $1, $2, $3, $4;
66     my $net = 1 << (31 - $5);
67     my $mask = inet_aton 2 ** 32 - $net;
68    
69     my $ip1 = $ip & $mask;
70     my $ip2 = $ip1 | inet_aton $net * 2 - 1;
71     return unpack "N2", "$ip1$ip2";
72     } elsif ($range =~ /^\s*([0-9.]+)\s*-\s*([0-9.]+)\s*$/) {
73     unpack "N*", (inet_aton $1) . (inet_aton $2);
74     } else {
75     die "$range: unable to parse ip range";
76     }
77     }
78    
79 root 1.2 package Net::Whois::IP::base;
80    
81     sub new {
82     my $class = shift;
83     my $self = bless {
84     name => @_,
85     }, $class;
86     $self->{request} ||= new Coro::Channel $self->{request_backlog} || 10;
87 root 1.3 $self->{daemon} = Coro::async {
88     while () {
89     eval { $self->_daemon };
90     warn "restarting daemon for whois server $self->{name}: $@";
91     }
92     };
93     $self->{daemon}->prio(1);
94 root 1.2 $self->{cache} = Net::Whois::IP::new_btree "whois_" . lc $self->{name};
95     $server{$self->{name}} = $self;
96     }
97    
98     sub _failure {
99 root 1.3 my ($self, $min) = shift;
100 root 1.2 undef $self->{fh};
101 root 1.3 $self->{retry} = $min if $self->{retry} < $min;
102 root 1.2 Coro::Timer::sleep 1.5 ** $self->{retry} - 2 if $self->{retry};
103     $self->{retry}++ if $self->{retry} < 17;
104     }
105    
106     sub _ok {
107     my $self = shift;
108     $self->{retry} = 0;
109     }
110    
111     sub _query {
112     my ($self, $query) = @_;
113    
114     my $response;
115    
116     if (!$self->{cache}->db_get($query, $response)) {
117     $response =~ s/^[^\x00]+\x00//;
118     return $response;
119     }
120    
121     my $request = {
122     ready => (new Coro::Semaphore 0),
123     query => $query,
124     };
125    
126     $self->{request}->put($request);
127     $request->{ready}->down;
128    
129 root 1.3 $request->{response} =~ s/\015//g;
130     $request->{response} =~ s/\012/\n/g if "\012" ne "\n";
131    
132 root 1.2 $response = time . "\x00" . $request->{response};
133    
134     $self->{cache}->db_put($query, $response);
135 root 1.3
136 root 1.2 $request->{response};
137     }
138    
139     sub ip_query {
140     my ($self, $ip) = @_;
141 root 1.3 $self->sanitize_ip_response($self->_query($ip));
142     }
143    
144     sub sanitize_ip_response {
145     my ($self, $response) = @_;
146     $response;
147 root 1.2 }
148    
149     sub parse_ip_response {
150 root 1.3 my ($self, $response, $tags, $res) = @_;
151     $res->{whois_server} = $self->{name};
152     while ($response =~ /^([^:]+):[ \t]*
153     (
154     .*
155     (?:\n[\t ].*)*
156     )
157     /mgx) {
158     my ($tag, $val) = ($1, $2);
159     if (exists $tags->{$tag}) {
160     push @{$res->{$tags->{$tag}}}, $val if defined $tags->{$tag};
161     } else {
162     use Carp;
163     confess "unknown tag $tag=$val";
164     }
165     }
166     $res;
167     }
168    
169     # check wether the given response is a referral to another server
170     # find out which one, and the ip range we were referred
171     sub is_ip_referral {
172 root 1.2 my ($self, $response) = @_;
173 root 1.3 # ($iprange, $server)
174     ();
175 root 1.2 }
176    
177     package Net::Whois::IP::whois;
178    
179     # plain whois, as used by ripe and apnic
180    
181     use base Net::Whois::IP::base;
182    
183     sub _daemon {
184     my $self = shift;
185 root 1.3 my $request;
186     while ($request = $self->{request}->get) {
187 root 1.2 my $fh = new Coro::Socket PeerHost => $self->{server}
188     or $self->_failure, redo;
189    
190     $fh->print("$request->{query}\012");
191     $fh->sysread($request->{response}, 16384);
192 root 1.3
193     length $request->{response} or $self->_failure, redo;
194     $request->{response} =~ /^% query rate limit exceeded/im and $self->_failure(9), redo;
195 root 1.2
196     $self->_ok;
197     $request->{ready}->up;
198     }
199     }
200    
201 root 1.3 sub sanitize_ip_response {
202     my ($self, $response) = @_;
203     $response = "" if $response =~ /^% not assigned/im;
204     $response =~ s/^%.*//mg;
205     $response =~ s/[ \t]+$//mg; # brnic, maybe others
206     $response =~ s/^\n+//;
207     $response =~ s/\n\n.*//s;
208     $self->SUPER::sanitize_ip_response($response);
209 root 1.2 }
210    
211     sub parse_ip_response {
212 root 1.3 my ($self, $response, $tags) = @_;
213     $self->SUPER::parse_ip_response($response, $tags || {
214     inetnum => "netblock",
215     netname => "netname",
216     descr => "description",
217     owner => "description",
218     address => "description",
219     phone => "description",
220     country => "country",
221     ownerid => "owner-c",
222     "admin-c" => "admin-c",
223     "tech-c" => "tech-c",
224     "owner-c" => "owner-c",
225     "abuse-c" => "abuse-c",
226     status => "status",
227     notify => "notify",
228     changed => "changed",
229     created => "created",
230     source => "source",
231     remarks => "remarks",
232     "mnt-by" => "mnt-by",
233     "mnt-irt" => "irt-c", # incident response team
234     "rev-srv" => "nameserver",
235     nserver => "nameserver",
236     "mnt-lower" => undef,
237     "mnt-routes"=> undef,
238     responsible => undef,
239     nsstat => undef,
240     nslastaa => undef,
241     "aut-num" => undef,
242     inetrev => undef, # nameserver-dependent
243     "inetnum-up" => undef,
244     });
245     }
246    
247     sub is_ip_referral {
248 root 1.2 my ($self, $response) = @_;
249 root 1.3 $response =~ /^inetnum:\s+(.*)/m
250     or return;
251     my $iprange = $1;
252     if ($response =~ /^remarks:\s+These addresses have been further assigned to Brazilian users./m) {
253     return ($iprange, $server{BRNIC});
254     }
255     # ($iprange, $server)
256     ();
257 root 1.2 }
258    
259     package Net::Whois::IP::jpnic;
260    
261     # totally different result format
262    
263     use base Net::Whois::IP::whois;
264    
265     sub ip_query {
266     my ($self, $ip) = @_;
267     $self->SUPER::ip_query("$ip/e");
268     }
269    
270 root 1.3 sub sanitize_ip_response {
271     my ($self, $response) = @_;
272     $response =~ s/^\[\s.*//mg;
273     $response =~ s/^(?:\w\.\ )? \[ ([^\]]+) \] (.*)/$1: $2/mgx;
274     $self->SUPER::sanitize_ip_response($response);
275     }
276    
277 root 1.2 sub parse_ip_response {
278     my ($self, $response) = @_;
279 root 1.3 $self->SUPER::parse_ip_response($response, {
280     "Network Information" => undef,
281     "Network Number" => "netblock",
282     "Network Name" => "netname",
283     "Organization" => "description",
284     "Administrative Contact" => "admin-c",
285     "Technical Contact" => "tech-c",
286     "Nameserver" => "nameserver",
287     "Reply Mail" => undef,
288     "Assigned Date" => "created",
289     "Return Date" => "expires",
290     "Last Update" => "changed",
291     }, {
292     country => "JP",
293     });
294 root 1.2 }
295    
296     package Net::Whois::IP::ripe;
297    
298     # keepalive whois, as used by ripe and apnic
299    
300     use base Net::Whois::IP::whois;
301    
302     sub _daemon {
303     my $self = shift;
304     my $fh;
305    
306 root 1.3 my $request;
307     while ($request = $self->{request}->get) {
308 root 1.2 unless ($self->{fh}) {
309     $fh = new Coro::Socket PeerHost => $self->{server};
310     $fh or $self->_failure, redo;
311     }
312    
313     $fh->print("-k $request->{query}\012");
314     $request->{response} = $fh->readline("\012\012\012");
315     length $request->{response}
316     or $self->_failure, redo;
317    
318     $self->_ok;
319     $request->{ready}->up;
320     }
321     }
322    
323 root 1.3 sub sanitize_ip_response {
324     my ($self, $response) = @_;
325     $response = "" if $response =~ /^netname:\s+IANA-BLK$/m;
326     $response = "" if $response =~ /This network range is not allocated to APNIC./;
327     # $response = "" if $response =~ /^remarks:\s+.*the IP has not been allocated by APNIC./m;
328     $self->SUPER::sanitize_ip_response($response);
329     }
330    
331 root 1.2 package Net::Whois::IP::rwhois;
332    
333 root 1.3 # rwhois 1.5, of course, as usual arin's implementation is utterly broken
334     # and doesn't help very much
335 root 1.2
336     use base Net::Whois::IP::base;
337    
338     sub _request {
339     my $self = shift;
340     my ($response, $error);
341    
342     $self->{fh}->print("$_[0]\015\012");
343     while (defined (my $line = $self->{fh}->readline)) {
344 root 1.3 $line =~ s/[\015\012]+$/\012/g;
345 root 1.2 $response .= $line;
346     $line =~ /^%(ok|error)/ and return $response;
347     }
348     return;
349     }
350    
351     sub _daemon {
352     my $self = shift;
353 root 1.3 my $request;
354     while ($request = $self->{request}->get) {
355 root 1.2 unless ($self->{fh}) {
356     $self->{fh} = new Coro::Socket PeerHost => $self->{server};
357     $self->{fh} or $self->_failure, redo;
358     $self->{fh}->readline =~ /^%rwhois / or $self->_failure, redo;
359     $self->_request("-rwhois V-1.5 Net::Whois::IP") or $self->_failure, redo;
360     $self->_request("-forward off") or $self->_failure, redo;
361     $self->_request("-holdconnect on") or $self->_failure, redo;
362     }
363     $request->{response} = $self->_request($request->{query})
364     or $self->_failure, redo;
365     $request->{response} =~ s/^%error 330.*/%ok/m;
366     $self->_ok;
367     $request->{ready}->up;
368     }
369     }
370    
371     sub ip_query {
372     my ($self, $ip) = @_;
373     $self->SUPER::ip_query("network $ip");
374     }
375    
376 root 1.3 sub sanitize_ip_response {
377 root 1.2 my ($self, $response) = @_;
378 root 1.3 $response =~ s/^% referral/REFERRAL:/mg;
379     $response =~ s/^%.*//mg;
380     $response =~ s/\n\n.*//s;
381     $response = "" if $response =~ /^network:Org-Name:Various Registries \(Maintained by ARIN\)/m;
382 root 1.2 $response =~ s/^([^:]+):([^:]+):/$1-\L$2: /mg;
383 root 1.3 $response;
384 root 1.2 }
385    
386 root 1.3 sub parse_ip_response {
387     my ($self, $response) = @_;
388     $self->SUPER::parse_ip_response($response, {
389     "network-class-name" => undef,
390     "network-auth-area" => "auth-area",
391     "network-id" => "id",
392     "network-handle" => "handle",
393     "network-network-name" => "netname",
394     "network-ip-network" => undef,
395     "network-ip-network-block" => "netblock",
396     "network-org-name" => "description",
397     "network-street-address" => "address",
398     "network-city" => "address",
399     "network-state" => "address",
400     "network-postal-code" => "address",
401     "network-country-code" => "country",
402     "network-tech-contact;i" => "tech-c",
403     "network-admin-contact;i" => "admin-c",
404     "network-created" => "created",
405     "network-updated" => "changed",
406     });
407     }
408 root 1.2
409 root 1.3 sub is_ip_referral {
410     my ($self, $response) = @_;
411     $response =~ /^network-ip-network-block:(.*)$/m
412     or return ();
413     my $iprange = $1;
414     if ($response =~ /^REFERRAL: rwhois:\/\/([^\/]+)/m) {
415     my $server = $1;
416     if ($server =~ /^whois.ripe.net/) {
417     return ($iprange, $server{RIPE});
418     } elsif ($server =~ /^rwhois.nic.ad.jp/) {
419     return ($iprange, $server{JPNIC});
420     } elsif ($server =~ /^r?whois.apnic.net/) {
421     return ($iprange, $server{APNIC});
422     } elsif ($server =~ /^rwhois.lacnic.net/) {
423     return ($iprange, $server{LACNIC}); # not yet seen, hope it will be implemented like this
424     } elsif ($server =~ /^rwhois.internex.net/) {
425     # alive(!)
426     return ();
427     } elsif ($server =~ /^rwhois.nstar.net/) {
428     return ();
429     } elsif ($server =~ /^rwhois.sesqui.net/) {
430     return ();
431     } elsif ($server =~ /^nic.ddn.mil/) {
432     # whois.nic.mil (the actual address) only supports the antique
433     # nonparsable arin whois format
434     return ();
435     } else {
436     die "$response\nreferral to whois server $server";
437     }
438     die "$server $iprange";
439     } elsif ($response =~ /^network-org-name: Latin American and Caribbean IP address Regional Registry/m) {
440     return ($iprange, $server{LACNIC});
441     } elsif ($response =~ /^network-org-name: Asia Pacific Network Information Centre/m) {
442     return ($iprange, $server{APNIC});
443     } elsif ($response =~ /^network-org-name: RIPE Network Coordination Centre/m) {
444     return ($iprange, $server{RIPE});
445 root 1.2 }
446 root 1.3 ();
447 root 1.2 }
448    
449 root 1.3 package Net::Whois::IP;
450    
451 root 1.2 # add a referral to a specific server
452     sub add_referral {
453     my ($netblock, $server) = @_;
454     my ($ip0, $ip1) = ip_range $netblock;
455 root 1.3 $db_referral->db_put(
456     (pack "N*", $ip1, $ip0) ^ "\x00\x00\x00\x00\xff\xff\xff\xff",
457     $server->{name},
458     );
459 root 1.2 }
460    
461     # get all referrals
462     sub parse_referral {
463     my ($query, $server) = @_;
464     my $referral = $arin->_query("referral $query");
465    
466     while ($referral =~ /^referral:Referred-Auth-Area:([0-9.\/]+)$/mg) {
467     add_referral $1, $server;
468     }
469     }
470    
471 root 1.3 =item init db_home => $path, ...
472    
473     Initializes the module.
474    
475     This function must be called before calling any of the other functions,
476     and the only required agrument is C<db_home>, the path where the module
477     will store it's cache (will be created if neccessary).
478    
479     Other Arguments:
480    
481     db_home database home directory
482     db_env the database env (should be created by the module itself)
483     db_errfile the file wheer the database will output and errors (/dev/fd/2)
484     db_cachesize size of the in-memory cache (1_000_000)
485    
486     =cut
487    
488 root 1.2 sub init(;%) {
489     my (%arg) = @_;
490     $arg{db_home} or $arg{db_env} or Carp::croak "either db_home or db_env home must be set";
491     $db_env = $arg{db_env} || do {
492     mkdir $arg{db_home}, 0700;
493    
494     $db_env = new BerkeleyDB::Env
495     -Home => $arg{db_home},
496     -Cachesize => $arg{db_cachesize} || 1_000_000,
497     -ErrFile => $arg{db_errfile} || "/dev/fd/2",
498     -ErrPrefix => "NET-WHOIS-IP",
499     -Verbose => 1,
500 root 1.3 -Flags => DB_CREATE|DB_RECOVER_FATAL|DB_INIT_MPOOL|DB_INIT_TXN
501 root 1.2 or die "$arg{db_home}: unable to create database home";
502     };
503    
504     $arin = new Net::Whois::IP::rwhois "ARIN", server => "rwhois.arin.net:rwhois(4321)";
505    
506     $ripe = new Net::Whois::IP::ripe "RIPE", server => "whois.ripe.net:whois(43)";
507     $lacnic = new Net::Whois::IP::whois "LACNIC", server => "whois.lacnic.net:whois(43)";
508     $brnic = new Net::Whois::IP::whois "BRNIC", server => "whois.registro.br:whois(43)";
509     $apnic = new Net::Whois::IP::ripe "APNIC", server => "whois.apnic.net:whois(43)";
510     $jpnic = new Net::Whois::IP::jpnic "JPNIC", server => "whois.nic.ad.jp:whois(43)";
511    
512     $db_referral = new_btree "referral";
513    
514     parse_referral "32.0.0.0", $ripe;
515     parse_referral "202.0.0.0", $apnic;
516     parse_referral "192.50.0.0", $apnic;
517    
518 root 1.3 add_referral "129.13.0.0/16", $ripe;
519 root 1.2 add_referral "133.0.0.0/8", $jpnic;
520 root 1.3 add_referral "200.0.0.0/8", $lacnic;
521     add_referral "200.128.0.0/9", $brnic;
522 root 1.2 }
523    
524 root 1.3 =item ip_query $ip
525    
526     Queries the whois server for the given ip address (which must be something
527     inet_aton understands).
528    
529     The return value currently is a hash, which looks slightly difefrent for
530     different registries, see examples.
531 root 1.2
532 root 1.3 =cut
533 root 1.2
534     sub ip_query {
535     my $ip = shift;
536    
537 root 1.3 my $server = $arin;
538 root 1.2
539 root 1.3 my ($prev_response, $prev_server);
540 root 1.2
541 root 1.3 referral:
542 root 1.2 my $c = $db_referral->db_cursor;
543    
544 root 1.3 # iterate over all possibly matching referral ranges
545     # and choose the one that fits tightest
546 root 1.2 my ($k, $v) = (inet_aton $ip);
547     if (!$c->c_get($k, $v, DB_SET_RANGE)) {
548 root 1.3 find: {
549     do {
550     my ($ip1, $ip0) = unpack "N*", $k ^ "\x00\x00\x00\x00\xff\xff\xff\xff";
551     my $ipn = unpack "N", inet_aton $ip;
552     0 && printf "Q=%15s < %15s < %15s, $v\n",
553     (inet_ntoa pack "N", $ip0),
554     (inet_ntoa pack "N", $ipn),
555     (inet_ntoa pack "N", $ip1);
556    
557     if ($ipn <= $ip1) {
558     if ($ip0 <= $ipn) {
559     my ($name, $rip) = split /\t/, $v;
560     $server = $server{$name} if $server{$name};
561     $ip = $ip if $rip;
562     last;
563     }
564     } else {
565     last;
566     }
567     } while !$c->c_get($k, $v, DB_NEXT);
568     }
569     }
570    
571     my $response = $server->ip_query($ip);
572     if ($response) {
573     if (my ($iprange, $refer) = $server->is_ip_referral($response)) {
574     ($prev_server, $prev_response) = ($server, $response);
575     $server = $refer;
576     add_referral $iprange, $server;
577     goto referral;
578     }
579     } else {
580     ($server, $response) = ($prev_server, $prev_response);
581     }
582 root 1.2
583 root 1.3 if (!$response) {
584     ($server, $response) = ($arin, $arin->ip_query($ip));
585     }
586    
587     my $res = $server->parse_ip_response($response);
588     for my $range (@{$res->{netblock} || []}) {
589     if (my ($ip0, $ip1) = eval { ip_range $range }) {
590     # limit to /24
591     my $ipn = unpack "N", inet_aton $ip;
592     $ip0 = $ipn & 0xffffff00 if $ip0 < ($ipn & 0xffffff00);
593     $ip1 = ($ipn + 256 & 0xffffff00) - 1 if $ip1 > ($ipn + 256 & 0xffffff00);
594     $db_referral->db_put(
595     (pack "N*", $ip1, $ip0) ^ "\x00\x00\x00\x00\xff\xff\xff\xff",
596     "$server->{name}\t$ip",
597     );
598 root 1.2 }
599     }
600    
601 root 1.3 $db_env->txn_checkpoint(10,1,0);
602    
603     $res;
604 root 1.2 }
605    
606 root 1.3 1;
607    
608     =back
609    
610     =head2 WHOIS EXAMPLES
611 root 1.2
612 root 1.3 =over 4
613 root 1.1
614 root 1.3 =item RIPE
615 root 1.1
616 root 1.3 ip_query "129.13.162.91" =>
617     (
618     source => [ "RIPE" ],
619     "mnt-by" => [ "DFN-NTFY" ],
620     country => [ "DE" ],
621     netname => [ "KLICK" ],
622     status => [ "ASSIGNED PI" ],
623     description => [ "Karlsruher Lichtleiter Kommunikationsnetz",
624     "University of Karlsruhe",
625     "Germany" ],
626     nameserver => [ "netserv.rz.uni-karlsruhe.de",
627     "iraun1.ira.uka.de",
628     "deneb.dfn.de",
629     "ns.germany.eu.net" ],
630     "tech-c" => [ "HUK2-RIPE" ],
631     changed => [ "lortz\@rz.uni-karlsruhe.de 19910212",
632     "nipper\@ira.uka.de 19920422",
633     "nipper\@xlink.net 19930513",
634     "rv\@Informatik.Uni-Dortmund.DE 19931129",
635     "poldi\@dfn.de 19940309",
636     "dolderer\@nic.de 19940930",
637     "dolderer\@nic.de 19970821",
638     "schweikh\@noc 19990819" ],
639     netblock => [ "129.13.0.0 - 129.13.255.255" ],
640     "admin-c" => [ "BL118" ],
641     notify => [ "guardian\@nic.de" ],
642     whois_server => "RIPE"
643     )
644    
645     =item LACNIC
646    
647     ip_query "200.5.0.0" =>
648     (
649     source => [ "ARIN-LACNIC-TRANSITION" ],
650     created => [ 19960205 ],
651     country => [ "PR" ],
652     changed => [ 19960205 ],
653     "owner-c" => [ "PR-PRMS-LACNIC",
654     "RS564-ARIN" ],
655     netblock => [ "200.5.0/21" ],
656     status => [ "assigned" ],
657     description => [ "Puerto Rico Medical Services Administration",
658     "Management Information Systems Department",
659     "PO Box 2129",
660     "San Juan, PR 00922-2129",
661     "PR" ],
662     whois_server => "LACNIC"
663     )
664    
665     =item BRNIC
666    
667     ip_query "200.128.0.0" =>
668     (
669     created => [ 20000215 ],
670     "tech-c" => [ "ALG149" ],
671     changed => [ 20001017 ],
672     "owner-c" => [ "003.508.097/0001-36",
673     "ALG149" ],
674     netblock => [ "200.128/16" ],
675     "abuse-c" => [ "SIC128" ],
676     description => [ "Associa\347\343o Rede Nacional de Ensino e Pesquisa",
677     "Estrada Dona Castorina, 110, 353",
678     "22460-320 - Rio de Janeiro - RJ",
679     "(021) 274-7445 []" ],
680     whois_server => "BRNIC",
681     nameserver => [ "NS.POP-BA.RNP.BR",
682     "SERVER1.POP-DF.RNP.BR",
683     "SERVER1.AGR.UFBA.BR",
684     "DNS.UFBA.BR" ]
685     )
686    
687     =item JPNIC
688    
689     ip_query "133.11.128.254" =>
690     (
691     created => [ "" ],
692     "tech-c" => [ "AK003JP",
693     "MN010JP" ],
694     netname => [ "UTSNET" ],
695     changed => [ "2002/10/15 13:06:48 (JST)\n kato\@wide.ad.jp" ],
696     netblock => [ "133.11.0.0" ],
697     "admin-c" => [ "MN010JP" ],
698     description => [ "University of Tokyo" ],
699     whois_server => "JPNIC",
700     expires => [ "" ],
701     nameserver => [ "dns1.nc.u-tokyo.ac.jp",
702     "dns2.nc.u-tokyo.ac.jp",
703     "dns3.nc.u-tokyo.ac.jp",
704     "ns.nc.u-tokyo.ac.jp",
705     "ns.s.u-tokyo.ac.jp" ]
706     )
707    
708     =item APNIC
709    
710     ip_query "203.2.75.99" =>
711     (
712     source => [ "APNIC" ],
713     "mnt-by" => [ "APNIC-HM" ],
714     "tech-c" => [ "TN38-AP" ],
715     country => [ "AU" ],
716     netname => [ "OPTUSINTERNET-AU" ],
717     changed => [ "nobody\@aunic.net 20010524",
718     "aunic-transfer\@apnic.net 20010525",
719     "hostmaster\@apnic.net 20011004" ],
720     netblock => [ "203.2.75.0 - 203.2.75.255" ],
721     status => [ "ALLOCATED PORTABLE" ],
722     "admin-c" => [ "TN38-AP" ],
723     description => [ "OPTUS INTERNET - RETAIL",
724     "INTERNET SERVICES",
725     "St Leonards, NSW" ],
726     whois_server => "APNIC"
727     )
728    
729     =item ARIN
730    
731     ip_query "68.52.164.8" =>
732     (
733     country => [ "US" ],
734     netname => [ "JUMPSTART-DC-5" ],
735     "auth-area" => [ "0.0.0.0/0" ],
736     description => [ "Comcast Cable Communications, Inc." ],
737     created => [ "20200718050000000" ],
738     "tech-c" => [ "FG200-ARIN.0.0.0.0/0" ],
739     changed => [ "19200307050000000" ],
740     handle => [ "NET-68-52-160-0-1" ],
741     netblock => [ "68.52.160.0 - 68.52.175.255" ],
742     id => [ "NET-68-52-160-0-1.0.0.0.0/0" ],
743     address => [ "3 Executive Campus Cherry Hill",
744     "NJ",
745     "08002" ],
746     whois_server => "ARIN"
747     )
748 root 1.1
749     =back
750    
751     =head1 AUTHOR
752    
753     Marc Lehmann <pcg@goof.com>
754     http://www.goof.com/pcg/marc/
755    
756     =cut
757