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