source: trunk/cgi-bin/extras/db2rwhois.pl@ 954

Last change on this file since 954 was 906, checked in by Kris Deugau, 7 years ago

/trunk

Bulk addition of "add 'the directory the script is in' to @INC" for Perls
that have dropped '.' from @INC

  • Property svn:executable set to *
  • Property svn:keywords set to Date Rev Author
File size: 11.8 KB
RevLine 
[15]1#!/usr/bin/perl
2# -T
3# ipdb/cgi-bin/extras/db2rwhois.pl
[2]4# Pull data from ipdb and mangle it into RWHOIS
[15]5###
6# Revision info
7# $Date: 2017-08-15 17:53:23 +0000 (Tue, 15 Aug 2017) $
8# SVN revision $Rev: 906 $
9# Last update by $Author: kdeugau $
10###
[893]11# Copyright (C) 2004-2010,2015,2016 - Kris Deugau
[2]12
[15]13use strict;
14use warnings;
[2]15use DBI;
16use NetAddr::IP;
[371]17use File::Path 'rmtree';
[417]18use POSIX qw(strftime);
[2]19
[417]20# don't remove! required for GNU/FHS-ish install from tarball
21##uselib##
[2]22
[906]23# push "the directory the script is in" into @INC
24use FindBin;
25use lib "$FindBin::RealBin/";
26
[417]27use MyIPDB;
28
29#$ENV{"PATH"} = "/bin;/usr/bin";
30
[371]31my @autharea;
32my $authrw;
33# Use the template file to allow us to keep persistent nodes aside from netblock data
[420]34open AUTHTEMPLATE, "<$IPDB::rwhoisDataPath/rwhoisd.auth_template";
[371]35my $template_persist;
36while (<AUTHTEMPLATE>) {
37 next if /^##/;
38 $template_persist = 1 if /^[a-z]/i;
39 $autharea[0] .= $_;
40}
41
[143]42my ($dbh,$msg) = connectDB_My;
[2]43
[143]44# For WHOIS purposes this may not be very useful. YMMV, we'll see.
45#initIPDBGlobals($dbh);
46
[15]47my @masterblocks;
[324]48my %netnameprefix;
[15]49
[371]50# Get the list of live directories for potential deletion
[420]51opendir RWHOISROOT, $IPDB::rwhoisDataPath;
[371]52my %rwhoisdirs;
53foreach (readdir RWHOISROOT) {
54 $rwhoisdirs{$_} = 1 if /^net-/;
55}
56closedir RWHOISROOT;
57
58# prefetch alloctype data
59my $sth = $dbh->prepare("select type,def_custid,arin_netname from alloctypes");
60$sth->execute;
61while (my @data = $sth->fetchrow_array) {
62 $netnameprefix{$data[0]} = $data[2];
63}
64
65# Get the list of masters to export
[680]66my $msth = $dbh->prepare(q(
67 SELECT cidr, createstamp, modifystamp, id
68 FROM allocations
69 WHERE type='mm' AND swip='y'
70 ) );
[371]71$msth->execute;
72
73# Prepare to select subblocks for each master
74# Make sure to remove the private netblocks from this,
75# no use or point in broadcasting our use of them.
76# Also remove the details of our "reserved CORE/WAN" blocks; they're not critical.
[680]77my $ssth = $dbh->prepare(q(
[856]78 SELECT a.cidr, a.custid, a.type, a.city, a.description, a.createstamp, a.modifystamp, a.swip, a.custid=t.def_custid AS isdef
79 FROM allocations a JOIN alloctypes t ON a.type=t.type
[680]80 WHERE
[856]81 NOT (a.cidr <<= '192.168.0.0/16') AND
82 NOT (a.cidr <<= '172.16.0.0/12') AND
83 NOT (a.cidr <<= '10.0.0.0/8') AND
[858]84 NOT (a.type = 'wr' OR a.type LIKE '_m') AND
[856]85 ((masklen(a.cidr) <=30 AND family(a.cidr)=4) OR (masklen(a.cidr) <=64 AND family(a.cidr)=6)) AND
86 a.master_id = ? AND
87 a.cidr <<= ?
[858]88 ORDER BY a.cidr
[680]89 ) );
[371]90
91# Customer data, for those rare blocks we really need to delegate.
[680]92my $custsth = $dbh->prepare(q(
93 SELECT name, street, city, province, country, pocode, phone, tech_handle, special
94 FROM customers
95 WHERE custid = ?
96 ) );
[371]97
[2]98# Fill in data about our master blocks as allocated from ARIN
99# We open separate files for each of these as appropriate.
[371]100# Changes in master blocks are treated as complete new masters - since we're exporting
101# all data every time, this isn't so terrible as it might seem.
[15]102my $i=0;
[680]103while (my ($master, $mcreate, $mmod, $mid) = $msth->fetchrow_array()) {
[2]104
[680]105 $masterblocks[$i] = new NetAddr::IP $master;
106 my ($ctime,undef) = split /\s/, $mcreate;
[859]107 my ($mtime,undef) = split /\s/, $mmod;
[2]108
[859]109 print "$masterblocks[$i] $ctime $mtime\n";
[371]110
[417]111 my $date = strftime("%Y-%m-%d", localtime);
[2]112
[371]113 my $rwnet = "net-".$masterblocks[$i]->addr."-".$masterblocks[$i]->masklen;
[2]114
[371]115 # unflag the directory for deletion. Whee! Roundabout!
116 delete $rwhoisdirs{$rwnet};
[2]117
[371]118# Hokay. Gonna do checks *here* to see if we need to create new master trees
[420]119 my $netdatadir = "$IPDB::rwhoisDataPath/$rwnet";
[371]120 if (! -e $netdatadir) {
121 print " New master $masterblocks[$i]!\n";
122 print " Creating directories...\n";
123 mkdir $netdatadir;
124 mkdir "$netdatadir/attribute_defs";
125 mkdir "$netdatadir/data";
126 mkdir "$netdatadir/data/network";
127 mkdir "$netdatadir/data/org";
128 mkdir "$netdatadir/data/referral";
129
[417]130 my $serial = strftime("%Y%m%d%H%M%S000", localtime);
[371]131
[417]132##fixme: SOA should be different every time data changes, therefore need to rewrite this ~~ every export :(
[371]133 print " Creating SOA...\n";
134 open SOAFILE, ">$netdatadir/soa";
135 print SOAFILE qq(Serial-Number: $serial
136Refresh-Interval: 3600
137Increment-Interval: 1800
138Retry-Interval: 1800
139Time-To-Live: 86400
[437]140Primary-Server: rwhois.$IPDB::domain:4321
141Hostmaster: $IPDB::hostmaster
[371]142);
143 close SOAFILE;
144
145 print " Creating Schema...\n";
146 open SCHEMAFILE, ">$netdatadir/schema";
147 print SCHEMAFILE qq(name: network
148attributedef: $rwnet/attribute_defs/network.tmpl
149dbdir: $rwnet/data/network
150Schema-Version: $serial
151---
152name: organization
153attributedef: $rwnet/attribute_defs/org.tmpl
154dbdir: $rwnet/data/org
155description: Organization object
156Schema-Version: $serial
157---
158name: referral
159attributedef:$rwnet/attribute_defs/referral.tmpl
160dbdir:$rwnet/data/referral
161Schema-Version: $serial
162);
163 close SCHEMAFILE;
164
165 print " Copying template files...\n";
[417]166##fixme: find a way to do this without a shell (or functional equivalent)
[420]167 qx { /bin/cp $IPDB::rwhoisDataPath/skel/attribute_defs/* $netdatadir/attribute_defs/ };
[371]168
[417]169##fixme: not sure if this is even necessary, since it's not referenced anywhere I can recall...
[371]170 print " Creating org data...\n";
[417]171 open ORGDATAFILE, ">$netdatadir/data/org/ourorg.txt";
[680]172 print ORGDATAFILE qq(ID: NETBLK-$netnameprefix{mm}.$masterblocks[$i]
[371]173Auth-Area: $masterblocks[$i]
[417]174Org-Name: $IPDB::org_name
175Street-Address: $IPDB::org_street
176City: $IPDB::org_city
177State: $IPDB::org_prov_state
178Postal-Code: $IPDB::org_pocode
179Country-Code: $IPDB::org_country
180Phone: $IPDB::org_phone
[371]181Created: 20040308
182Updated: 20040308
183);
184 close ORGDATAFILE;
185
186 # Generate auth_area record, and add it to the array.
187 $authrw = 1; # Flag for rewrite and daemon reload/restart
188
189 } # new master
190
191 # do this for all masters, so that we can use this array to export the data
192 # to rwhoisd.auth_area later if we need to
193 push @autharea, qq(type:master
194name:$masterblocks[$i]
195data-dir: $rwnet/data
196schema-file: $rwnet/schema
197soa-file: $rwnet/soa
198);
199
200 # Recreate the net-nnn.nnn.nnn.nnn-nn.txt data file
201 my $masterfilename = "$rwnet/data/network/".$masterblocks[$i]->addr."-".$masterblocks[$i]->masklen.".txt";
202
[420]203 open MASTERFILE,">$IPDB::rwhoisDataPath/$masterfilename";
[15]204
[680]205 print MASTERFILE "ID: NETBLK-$netnameprefix{mm}.$masterblocks[$i]\n".
[2]206 "Auth-Area: $masterblocks[$i]\n".
[680]207 "Network-Name: $netnameprefix{mm}-".$masterblocks[$i]->network."\n".
[2]208 "IP-Network: $masterblocks[$i]\n".
209 "IP-Network-Block: ".$masterblocks[$i]->range."\n".
[417]210 "Org-Name: $IPDB::org_name\n".
211 "Street-Address: $IPDB::org_street\n".
212 "City: $IPDB::org_city\n".
213 "StateProv: $IPDB::org_prov_state\n".
214 "Postal-Code: $IPDB::org_pocode\n".
215 "Country-Code: $IPDB::org_country\n".
216 "Tech-Contact: $IPDB::org_techhandle\n".
[218]217 "Created: $ctime\n".
[859]218 "Updated: $mtime\n".
[434]219 "Updated-By: $IPDB::org_email\n";
[2]220
[371]221 # And now the subblocks
[680]222 $ssth->execute($mid, $master) or die "nosubs: $!\n".$dbh->errstr."\n";
[859]223 while (my ($cidr, $custid, $type, $city, $desc, $bctime, $bmtime, $swip, $defcust) = $ssth->fetchrow_array) {
[2]224
225# We get master block info from @masterblocks.
[680]226 # ID: NETBLK-$netnameprefix{mm}.10.0.0.0/8
[2]227 # Auth-Area: 10.0.0.0/8
[680]228 # Network-Name: $netnameprefix{$type}-10.0.2.144
[2]229 # IP-Network: 10.0.2.144.144/29
230 # IP-Network-Block: 10.0.2.144 - 10.0.2.151
231 # Organization: WidgetCorp
232 # Tech-Contact: bob@widgetcorp.com
233 # Admin-Contact: ISP-ARIN-HANDLE
234 # Created: 20040314
235 # Updated: 20040314
236 # Updated-By: noc@example.com
237
[371]238 # Get the "full" network number
239 my $net = new NetAddr::IP $cidr;
[2]240
241# Assumptions: All data in ipdb is public
242# If not, we need another field to indicate "public/private".
243
[311]244# cidr custid type city description notes maskbits
245
[680]246 # Fill in a generic entry for nameless allocations
247 if ($desc =~ /^\s*$/) { $desc = $IPDB::org_name; }
[311]248
[371]249 # Fix up datestamps. We don't *really* need sub-microsecond resolution on our exports...
[859]250 ($bctime) = ($bctime =~ /^(\d+-\d+-\d+)\s+/);
251 ($bmtime) = ($bmtime =~ /^(\d+-\d+-\d+)\s+/);
[311]252
[324]253# Notes:
254# Network-name should contain some component of "description"
255# Cust address/contact data should be included; NB, no phone for ARIN!
256# network:ID: NET-WIDGET
257# network:Network-Name: WIDGET [IPDB description, sort of]
258# network:IP-Network: 10.1.1.0/24
259# network:Org-Name: Widget Corp [Cust name; from billing?]
260# network:Street-Address: 211 Oak Drive [May need more than one line, OR...]
261# network:City: Pineville [...this line...]
262# network:StateProv: WI [...and this line...]
263# network:Postal-Code: 48888 [...and this line]
264# network:Country-Code: US
265# network:Tech-Contact: BZ142-MYRWHOIS [ARIN handle?]
266# network:Updated: 19991221 [timestamp from db]
267# network:Updated-By: jo@myrwhois.net [noc@example, since that's our POC for IP netspace issues]
268# network:Class-Name:network [Provided by rWHOIS protocol]
[2]269
[371]270 my $netname = $netnameprefix{$type};
[2]271
[856]272 if ($swip eq 'n' || $defcust) {
[680]273 print MASTERFILE "---\nID: NETBLK-$netnameprefix{mm}.$masterblocks[$i]\n".
[371]274 "Auth-Area: $masterblocks[$i]\n".
275 "Network-Name: $netname-".$net->network."\n".
276 "IP-Network: $net\n".
277 "IP-Network-Block: ".$net->range."\n".
[417]278 "Org-Name: $IPDB::org_name\n".
279 "Street-Address: $IPDB::org_street\n".
280 "City: $IPDB::org_city\n".
281 "StateProv: $IPDB::org_prov_state\n".
282 "Postal-Code: $IPDB::org_pocode\n".
283 "Country-Code: $IPDB::org_country\n".
284 "Tech-Contact: $IPDB::org_techhandle\n".
[859]285 "Created: $bctime\n".
286 "Updated: $bmtime\n".
[434]287 "Updated-By: $IPDB::org_email\n";
[371]288 } else {
289 $custsth->execute($custid);
290 my ($name, $street, $city, $prov, $country, $pocode, $phone, $tech, $special) = $custsth->fetchrow_array;
291 $custsth->finish;
292 if ($special && $special =~ /NetName/ && $special =~ /$cidr/) {
293 ($netname) = ($special =~ /NetName$cidr: ([A-Z0-9_-]+)/);
[328]294 } else {
[371]295 $netname .= "-".$net->network;
296 }
[680]297 print MASTERFILE "---\nID: NETBLK-$netnameprefix{mm}.$masterblocks[$i]\n".
[371]298 "Auth-Area: $masterblocks[$i]\n".
299 "Network-Name: $netname\n".
300 "IP-Network: $net\n".
301 "IP-Network-Block: ".$net->range."\n".
[417]302 "Org-Name: ".($name ? $name : $IPDB::org_name)."\n".
303 "Street-Address: ".($street ? $street : $IPDB::org_street)."\n".
304 "City: ".($city ? $city : $IPDB::org_city)."\n".
305 "StateProv: ".($prov ? $prov : $IPDB::org_prov_state)."\n".
306 "Postal-Code: ".($pocode ? $pocode : $IPDB::org_pocode)."\n".
307 "Country-Code: ".($country ? $country : $IPDB::org_country)."\n".
308 "Tech-Contact: ".($tech ? $tech : $IPDB::org_techhandle)."\n".
[859]309 "Created: $bctime\n".
310 "Updated: $bmtime\n".
[434]311 "Updated-By: $IPDB::org_email\n";
[371]312 } # swip
[324]313
[371]314 } # while $ssth->fetchrow_array()
[324]315
[371]316 close MASTERFILE;
[324]317
[311]318 $i++;
[371]319} # while $msth->fetchrow_array()
[2]320
[371]321# Now we see if there's obsolete netdata directories to be deleted,
322# and therefore an auth-area file to regenerate
323foreach my $netdir (keys %rwhoisdirs) {
324 print "deleting obsolete directory $netdir...\n";
[420]325 rmtree ( "$IPDB::rwhoisDataPath/$netdir", { verbose => 1, error => \my $errlist } );
[371]326 for my $diag (@$errlist) {
327 my ($file, $message) = each %$diag;
328 if ($file eq '') {
329 print "general error: $message\n";
330 }
331 }
332 $authrw = 1; # there's probably a more efficient place to put this. Feh.
333}
[2]334
[371]335# Regenerate rwhoisd.auth_area if needed
336if ($authrw) {
337 print "Regenerating auth_area\n";
[420]338 open RWHOISDAUTH, ">$IPDB::rwhoisDataPath/rwhoisd.auth_area";
[371]339 print RWHOISDAUTH "# WARNING: This file is autogenerated! Any static nodes should\n".
340 "# be entered in /etc/rwhoisd/rwhoisd.auth_template\n";
341 if ($template_persist) {
342 print RWHOISDAUTH shift @autharea;
343 print RWHOISDAUTH "---\n";
344 }
345 # feh. we need to know when we're at the end of the loop, because then
346 # we DON'T want to write the separator...
347 for (;@autharea;) { # my head hurts.
348 print RWHOISDAUTH shift @autharea;
349 print RWHOISDAUTH "---\n" if @autharea;
350 }
351 close RWHOISDAUTH;
352
353 # restart/reload rwhoisd
[420]354 if (-e "$IPDB::rwhoisDataPath/rwhoisd.pid") { # no pidfile, no restart.
[371]355 print "Restarting rwhoisd\n";
[420]356 open PIDFILE, "<$IPDB::rwhoisDataPath/rwhoisd.pid";
[371]357 my ($rwpid) = (<PIDFILE> =~ /^(\d+)/);
358 close PIDFILE;
359 kill 'HUP', $rwpid;
360 }
361}
362
363# and finally
[2]364$dbh->disconnect;
Note: See TracBrowser for help on using the repository browser.