ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Coro/myhttpd/netgeo.pl
Revision: 1.5
Committed: Tue Aug 28 02:43:02 2001 UTC (22 years, 10 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.4: +0 -4 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 #!/usr/bin/perl
2    
3     # APNIC refer: KRNIC (for 211.104.0.0)
4    
5     use Socket;
6     use Fcntl;
7    
8     use Coro;
9     use Coro::Event;
10     use Coro::Semaphore;
11     use Coro::Socket;
12    
13     $Event::DIED = sub {
14     Event::verbose_exception_handler(@_);
15     #Event::unloop_all();
16     };
17    
18 root 1.4 tie %netgeo::whois, BerkeleyDB::Btree,
19     -Env => $db_env,
20     -Filename => "whois",
21     -Flags => DB_CREATE,
22     or die "unable to create/open whois table";
23     $netgeo::iprange = new BerkeleyDB::Btree
24     -Env => $db_env,
25     -Filename => "iprange",
26     -Flags => DB_CREATE,
27     or die "unable to create/open iprange table";
28    
29 root 1.1 package Whois;
30    
31     use Coro::Event;
32    
33     sub new {
34     my $class = shift;
35     my $name = shift;
36     my $ip = shift;
37     my $self = bless { name => $name, ip => $ip, @_ }, $class;
38     $self->{maxjobs} = new Coro::Semaphore $self->{maxjobs} || 1;
39     $self;
40     }
41    
42     sub ip {
43     $_[0]{ip};
44     }
45    
46     sub sanitize {
47     $_[1];
48     }
49    
50     sub whois_request {
51     my ($self, $query) = @_;
52    
53 root 1.4 my $id = "$self->{name}\x0$query";
54     my $whois = $netgeo::whois{$id};
55    
56     unless (defined $whois) {
57     print "WHOIS($self->{name},$query)\n";
58 root 1.1
59     my $guard = $self->{maxjobs}->guard;
60     my $timeout = 5;
61    
62     while () {
63     my $fh = new Coro::Socket
64     PeerAddr => $self->ip,
65     PeerPort => 'whois',
66     Timeout => 30;
67     if ($fh) {
68     print $fh "$query\n";
69     $fh->read($whois, 16*1024); # max 16k. whois stored
70     close $fh;
71     $whois =~ s/\015?\012/\n/g;
72     $whois = $self->sanitize($whois);
73     if ($whois eq ""
74     or ($whois =~ /query limit/i && $whois =~ /exceeded/i) # ARIN
75     or ($whois =~ /wait a while and try again/i) # ARIN
76     or ($whois =~ /^%ERROR:202:/) # RIPE/APNIC
77     ) {
78     print "retrying in $timeout seconds\n";#d#
79     do_timer(desc => "timer2", after => $timeout);
80     $timeout *= 2;
81     $timeout = 1 if $timeout > 600;
82     } else {
83     last;
84     }
85     }
86     }
87    
88 root 1.4 $netgeo::whois{$id} = $whois;
89 root 1.1 }
90    
91     $whois;
92     }
93    
94     package Whois::ARIN;
95    
96     use Date::Parse;
97    
98     use base Whois;
99    
100     sub sanitize {
101     local $_ = $_[1];
102     s/\n[\t ]{6,}([0-9.]+ - [0-9.]+)/ $1/g;
103     $_;
104     }
105    
106     # there are only two problems with arin's whois database:
107     # a) the data cannot be trusted and often is old or even wrong
108     # b) the database format is nonparsable
109     # (no spaces between netname/ip and netnames can end in digits ;)
110     # of course, the only source to find out about global
111     # address distribution is... arin.
112     sub ip_request {
113     my ($self, $ip) = @_;
114    
115     my $whois = $self->whois_request($ip);
116    
117     return () if $whois =~ /^No match/;
118    
119     if ($whois =~ /^To single out one record/m) {
120     my $handle;
121     while ($whois =~ /\G\S.*\(([A-Z0-9\-]+)\).*\n/mg) {
122     $handle = $1;
123     #return if $handle =~ /-(RIPE|APNIC)/; # heuristic, bbut bad because ripe might not have better info
124     }
125     $handle or die "$whois ($ip): unparseable multimatch\n";
126     $whois = $self->whois_request("!$handle");
127     }
128    
129     my ($address, $info, $coordinator, undef) = split /\n\n/, $whois;
130    
131     $info =~ /^\s+Netname: (\S+)$/mi
132     or die "$whois($ip): no netname\n";
133     my $netname = $1;
134    
135     $info =~ /^\s+Netblock: ([0-9.]+\s+-\s+[0-9.]+)\s*$/mi
136     or die "$whois($ip): no netblock\n";
137     my $netblock = $1;
138    
139     my $maintainer;
140    
141     if ($info =~ /^\s+Maintainer: (\S+)\s*$/mi) {
142     $maintainer = "*ma: $1\n";
143     return if $1 =~ /^(?:AP|RIPE)$/;
144     }
145    
146     $coordinator =~ s/^\s+Coordinator:\s*//si
147     or $coordinator = "";
148    
149     $address =~ s/\n\s*(\S+)$//
150     or die "$whois($ip): no parseable country ($address)\n";
151     my $country = $1;
152    
153     $address =~ s/^\s*/*de: /mg;
154     $coordinator =~ s/^\s*/*ad: /mg;
155    
156     $whois = <<EOF;
157     *in: $netblock
158     *na: $netname
159     *cy: $country
160     $maintainer$address
161     $coordinator
162     EOF
163     $whois =~ s/\n+$//;
164     $whois;
165     }
166    
167     package Whois::RIPE;
168    
169     use base Whois;
170    
171     sub sanitize {
172     local $_ = $_[1];
173     s/^%.*\n//gm;
174     s/^\n+//;
175     s/\n*$/\n/;
176     $_;
177     }
178    
179     sub ip_request {
180     my ($self, $ip) = @_;
181    
182     my $whois = $self->whois_request("-FSTin $ip");
183    
184     $whois =~ /^\*in: 0\.0\.0\.0 - 255\.255\.255\.255/
185     and return;
186    
187     $whois =~ /^\*ac: XXX0/m # 192.0.0.0
188     and return;
189    
190     $whois =~ /^%ERROR:/m
191     and return;
192    
193     #while ($whois =~ s/^\*(?:ac|tc):\s+(\S+)\n//m) {
194     # $whois .= $self->whois_request("-FSTpn $1");
195     #}
196    
197     $whois =~ s/^\*(?:pn|nh|mb|ch|so|rz|ny|st|rm):.*\n//mg;
198    
199     $whois =~ s/\n+$//;
200    
201     $whois;
202     }
203    
204 root 1.4 package netgeo;
205    
206     use BerkeleyDB;
207     use Socket;
208 root 1.1
209     sub ip2int($) {
210     unpack "N", inet_aton $_[0];
211     }
212    
213     sub int2ip($) {
214     inet_ntoa pack "N", $_[0];
215     }
216    
217     our %WHOIS;
218    
219     $WHOIS{ARIN} = new Whois::ARIN ARIN => "whois.arin.net", maxjobs => 12;
220     $WHOIS{RIPE} = new Whois::RIPE RIPE => "whois.ripe.net", maxjobs => 20;
221     $WHOIS{APNIC} = new Whois::RIPE APNIC => "whois.apnic.net", maxjobs => 20;
222    
223     sub ip_request {
224     my $ip = $_[0];
225    
226 root 1.4 my $c = $iprange->db_cursor;
227     my $v;
228    
229     if (!$c->c_get((inet_aton $ip), $v, DB_SET_RANGE)) {
230     my ($ip0, $ip1, $whois) = split /\x0/, $v;
231     my $_ip = ip2int $ip;
232     if ($ip0 <= $_ip && $_ip <= $ip1) {
233     return $whois;
234 root 1.1 }
235 root 1.4 }
236    
237     my ($arin, $ripe, $apnic);
238    
239     $whois = $WHOIS{APNIC}->ip_request($ip)
240     || $WHOIS{RIPE} ->ip_request($ip)
241     || $WHOIS{ARIN} ->ip_request($ip);
242 root 1.1
243 root 1.4 $whois =~ /^\*in: ([0-9.]+)\s+-\s+([0-9.]+)\s*$/mi
244     or do { warn "$whois($ip): no addresses found\n", last };
245 root 1.1
246 root 1.4 my ($ip0, $ip1) = ($1, $2);
247    
248     my $_ip = ip2int($ip);
249     my $_ip0 = ip2int($ip0);
250     my $_ip1 = ip2int($ip1);
251    
252     if ($_ip0 + 256 < $_ip1) {
253     $_ip = $_ip & 0xffffff00;
254     $_ip0 = $_ip if $_ip0 < $_ip;
255     $_ip1 = $_ip + 255 if $_ip1 > $_ip + 255;
256 root 1.1 }
257 root 1.4
258     $iprange->db_put((pack "N", $_ip1), (join "\x0", $_ip0, $_ip1, $whois));
259     (tied %whois)->db_sync;
260     $iprange->db_sync;
261 root 1.1
262     $whois;
263     }
264    
265    
266